mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-12 02:56:37 +00:00
two-steps bootstrapping
This commit is contained in:
parent
9f89625a0d
commit
8cc4463bbd
5 changed files with 99 additions and 27 deletions
|
|
@ -32,6 +32,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -370,8 +372,17 @@ func (bc *BlockChain) TxIndexDone() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
|
// It avoids using OpenTrie which has transition-aware logic that may try
|
||||||
|
// to open a binary tree before it exists. Instead, it directly attempts
|
||||||
|
// to decode the root node as an MPT first, and if that fails, as a binary
|
||||||
|
// trie.
|
||||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||||
_, err := bc.statedb.OpenTrie(hash)
|
// Try to open as a Merkle Patricia Trie first.
|
||||||
|
if _, err := trie.NewStateTrie(trie.StateTrieID(hash), bc.triedb); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Fall back to trying as a binary trie.
|
||||||
|
_, err := bintrie.NewBinaryTrie(hash, bc.triedb)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,7 @@ var (
|
||||||
conversionProgressSlotKey = common.Hash{2} // slot 2: current slot pointer
|
conversionProgressSlotKey = common.Hash{2} // slot 2: current slot pointer
|
||||||
conversionProgressStorageProcessed = common.Hash{3} // slot 3: storage processed flag
|
conversionProgressStorageProcessed = common.Hash{3} // slot 3: storage processed flag
|
||||||
transitionEndedKey = common.Hash{4} // slot 4: non-zero if transition ended
|
transitionEndedKey = common.Hash{4} // slot 4: non-zero if transition ended
|
||||||
|
baseRootKey = common.Hash{5} // slot 5: MPT base root at transition start
|
||||||
)
|
)
|
||||||
|
|
||||||
// isTransitionActive checks if the binary tree transition has been activated
|
// isTransitionActive checks if the binary tree transition has been activated
|
||||||
|
|
@ -232,12 +233,18 @@ func LoadTransitionState(reader StateReader, root common.Hash) *overlay.Transiti
|
||||||
}
|
}
|
||||||
storageProcessed := storageProcessedBytes[0] == 1
|
storageProcessed := storageProcessedBytes[0] == 1
|
||||||
|
|
||||||
|
baseRoot, err := reader.Storage(params.BinaryTransitionRegistryAddress, baseRootKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return &overlay.TransitionState{
|
return &overlay.TransitionState{
|
||||||
Started: started,
|
Started: started,
|
||||||
Ended: ended,
|
Ended: ended,
|
||||||
CurrentAccountAddress: ¤tAccount,
|
CurrentAccountAddress: ¤tAccount,
|
||||||
CurrentSlotHash: currentSlotHash,
|
CurrentSlotHash: currentSlotHash,
|
||||||
StorageProcessed: storageProcessed,
|
StorageProcessed: storageProcessed,
|
||||||
|
BaseRoot: baseRoot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,30 +314,45 @@ func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (ReaderWithSta
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
reader, err := db.triedb.StateReader(root)
|
// Only attempt transition-aware trie opening in path scheme, since
|
||||||
if err != nil {
|
// hashdb does not implement StateReader.
|
||||||
return nil, err
|
if db.TrieDB().Scheme() == rawdb.PathScheme {
|
||||||
}
|
reader, err := db.triedb.StateReader(root)
|
||||||
flatReader := newFlatReader(reader)
|
|
||||||
|
|
||||||
ts := LoadTransitionState(flatReader, root)
|
|
||||||
if isTransitionActive(flatReader) || db.triedb.IsVerkle() {
|
|
||||||
bt, err := bintrie.NewBinaryTrie(root, db.triedb)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open the overlay tree: %w", err)
|
return nil, err
|
||||||
}
|
|
||||||
if !ts.InTransition() {
|
|
||||||
// Transition complete, use BinaryTrie only
|
|
||||||
return bt, nil
|
|
||||||
}
|
}
|
||||||
|
flatReader := newFlatReader(reader)
|
||||||
|
|
||||||
base, err := trie.NewStateTrie(trie.StateTrieID(ts.BaseRoot), db.triedb)
|
ts := LoadTransitionState(flatReader, root)
|
||||||
if err != nil {
|
if isTransitionActive(flatReader) || db.triedb.IsVerkle() {
|
||||||
return nil, fmt.Errorf("could not create base trie in OpenTrie: %w", err)
|
fmt.Printf("Opening transition-aware trie for root %s with transition state: %+v\n", root, ts)
|
||||||
|
|
||||||
|
// special case of the tree bootsrap: the root will be that of the MPT, so in that
|
||||||
|
// case, open an empty binary tree.
|
||||||
|
var bt *bintrie.BinaryTrie
|
||||||
|
if ts.BaseRoot == (common.Hash{}) {
|
||||||
|
bt, err = bintrie.NewBinaryTrie(common.Hash{}, db.triedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not bootstrap the overlay tree: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bt, err = bintrie.NewBinaryTrie(root, db.triedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not open the overlay tree: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ts.InTransition() {
|
||||||
|
// Transition complete, use BinaryTrie only
|
||||||
|
return bt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := trie.NewStateTrie(trie.StateTrieID(ts.BaseRoot), db.triedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not create base trie in OpenTrie: %w", err)
|
||||||
|
}
|
||||||
|
return transitiontrie.NewTransitionTrie(base, bt, false), nil
|
||||||
}
|
}
|
||||||
return transitiontrie.NewTransitionTrie(base, bt, false), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
return trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,15 +87,28 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
context = NewEVMBlockContext(header, p.chain, nil)
|
context = NewEVMBlockContext(header, p.chain, nil)
|
||||||
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
||||||
|
|
||||||
|
if config.IsVerkle(header.Number, header.Time) {
|
||||||
|
// Bootstrap part deux: initialize the base root in the registry,
|
||||||
|
// as this is the first UBT block (which is the _second_ block of
|
||||||
|
// the transition, after the bootstrapping block that initializes
|
||||||
|
// the registry).
|
||||||
|
parentHeader := p.chain.GetHeaderByHash(block.ParentHash())
|
||||||
|
// Confusingly, the first IsVerkle block isn't "verkle"
|
||||||
|
if config.IsVerkle(parentHeader.Number, parentHeader.Time) {
|
||||||
|
// Store the parent's state root as the MPT base root for the
|
||||||
|
// binary trie transition. Only written once (first verkle block),
|
||||||
|
// before InitializeBinaryTransitionRegistry sets slot 0.
|
||||||
|
if statedb.GetState(params.BinaryTransitionRegistryAddress, common.Hash{5}) == (common.Hash{}) {
|
||||||
|
statedb.SetState(params.BinaryTransitionRegistryAddress, common.Hash{5}, parentHeader.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
if config.IsPrague(block.Number(), block.Time()) || config.IsVerkle(block.Number(), block.Time()) {
|
if config.IsPrague(block.Number(), block.Time()) || config.IsVerkle(block.Number(), block.Time()) {
|
||||||
ProcessParentBlockHash(block.ParentHash(), evm)
|
ProcessParentBlockHash(block.ParentHash(), evm)
|
||||||
}
|
}
|
||||||
if config.IsVerkle(header.Number, header.Time) {
|
|
||||||
InitializeBinaryTransitionRegistry(statedb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
|
@ -129,6 +142,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
return nil, fmt.Errorf("failed to process consolidation queue: %w", err)
|
return nil, fmt.Errorf("failed to process consolidation queue: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if config.IsVerkle(header.Number, header.Time) {
|
||||||
|
// Bootstrap part one: initialize the registry to mark the transition as started,
|
||||||
|
// which has to be done at the end of the _previous_ block, so that the information
|
||||||
|
// can bee made available inside the tree.
|
||||||
|
parentHeader := p.chain.GetHeaderByHash(block.ParentHash())
|
||||||
|
if !config.IsVerkle(parentHeader.Number, parentHeader.Time) {
|
||||||
|
InitializeBinaryTransitionRegistry(statedb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body())
|
p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,5 @@ func InitializeBinaryTransitionRegistry(statedb *state.StateDB) {
|
||||||
}
|
}
|
||||||
statedb.SetCode(params.BinaryTransitionRegistryAddress, []byte{1, 2, 3}, tracing.CodeChangeUnspecified)
|
statedb.SetCode(params.BinaryTransitionRegistryAddress, []byte{1, 2, 3}, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetNonce(params.BinaryTransitionRegistryAddress, 1, tracing.NonceChangeUnspecified)
|
statedb.SetNonce(params.BinaryTransitionRegistryAddress, 1, tracing.NonceChangeUnspecified)
|
||||||
statedb.SetState(params.BinaryTransitionRegistryAddress, common.Hash{}, common.Hash{1})
|
statedb.SetState(params.BinaryTransitionRegistryAddress, common.Hash{}, common.Hash{1}) // slot 0: started
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,15 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if miner.chainConfig.IsVerkle(work.header.Number, work.header.Time) {
|
||||||
|
// Bootstrap part one: initialize the registry to mark the transition as started,
|
||||||
|
// which has to be done at the end of the _previous_ block, so that the information
|
||||||
|
// can bee made available inside the tree.
|
||||||
|
parentHeader := miner.chain.GetHeaderByHash(work.header.ParentHash) // XXX parent could be added to the environment in prepareWork to avoid this lookup
|
||||||
|
if !miner.chainConfig.IsVerkle(parentHeader.Number, parentHeader.Time) {
|
||||||
|
core.InitializeBinaryTransitionRegistry(work.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
if requests != nil {
|
if requests != nil {
|
||||||
reqHash := types.CalcRequestsHash(requests)
|
reqHash := types.CalcRequestsHash(requests)
|
||||||
work.header.RequestsHash = &reqHash
|
work.header.RequestsHash = &reqHash
|
||||||
|
|
@ -260,15 +269,23 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir
|
||||||
log.Error("Failed to create sealing context", "err", err)
|
log.Error("Failed to create sealing context", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if miner.chainConfig.IsVerkle(header.Number, header.Time) {
|
||||||
|
// Bootstrap part deux: initialize the base root in the registry,
|
||||||
|
// as this is the first UBT block (which is the _second_ block of
|
||||||
|
// the transition, after the bootstrapping block that initializes
|
||||||
|
// the registry).
|
||||||
|
if miner.chainConfig.IsVerkle(parent.Number, parent.Time) {
|
||||||
|
if env.state.GetState(params.BinaryTransitionRegistryAddress, common.Hash{5}) == (common.Hash{}) {
|
||||||
|
env.state.SetState(params.BinaryTransitionRegistryAddress, common.Hash{5}, parent.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if header.ParentBeaconRoot != nil {
|
if header.ParentBeaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm)
|
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm)
|
||||||
}
|
}
|
||||||
if miner.chainConfig.IsPrague(header.Number, header.Time) {
|
if miner.chainConfig.IsPrague(header.Number, header.Time) {
|
||||||
core.ProcessParentBlockHash(header.ParentHash, env.evm)
|
core.ProcessParentBlockHash(header.ParentHash, env.evm)
|
||||||
}
|
}
|
||||||
if miner.chainConfig.IsVerkle(header.Number, header.Time) {
|
|
||||||
core.InitializeBinaryTransitionRegistry(env.state)
|
|
||||||
}
|
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue