diff --git a/core/blockchain.go b/core/blockchain.go index a9de1aabd3..0c1e2b9e56 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -397,10 +397,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis if bc.cacheConfig.SnapshotLimit > 0 { diskRoot = rawdb.ReadSnapshotRoot(bc.db) } else if bc.triedb.Scheme() == rawdb.PathScheme { - _, diskRoot = rawdb.ReadAccountTrieNode(bc.db, nil) + diskRoot = bc.triedb.Head() } if diskRoot != (common.Hash{}) { - log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "snaproot", diskRoot) + log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "diskRoot", diskRoot) snapDisk, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, diskRoot, true) if err != nil { @@ -802,6 +802,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha // resetState resets the persistent state to genesis if it's not available. resetState := func() { + log.Info("Reset to block with genesis state", "number", bc.genesisBlock.NumberU64(), "hash", bc.genesisBlock.Hash()) // Short circuit if the genesis state is already present. if bc.HasState(bc.genesisBlock.Root()) { return @@ -825,7 +826,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha // chain reparation mechanism without deleting any data! if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() <= currentBlock.Number.Uint64() { newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64()) - lastBlockNum := header.Number.Uint64() if newHeadBlock == nil { log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash()) newHeadBlock = bc.genesisBlock @@ -835,10 +835,8 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha // keeping rewinding until we exceed the optional threshold // root hash beyondRoot := (root == common.Hash{}) // Flag whether we're beyond the requested root (no root, always true) - enoughBeyondCount := false - beyondCount := 0 + for { - beyondCount++ // If a root threshold was requested but not yet crossed, check if root != (common.Hash{}) && !beyondRoot && newHeadBlock.Root() == root { beyondRoot, rootNumber = true, newHeadBlock.NumberU64() @@ -854,24 +852,11 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha log.Error("Missing block in the middle, aiming genesis", "number", newHeadBlock.NumberU64()-1, "hash", newHeadBlock.ParentHash()) newHeadBlock = bc.genesisBlock } else { - log.Trace("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot) + log.Info("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot) newHeadBlock = bc.genesisBlock } } - if beyondRoot || (enoughBeyondCount && root != common.Hash{}) || newHeadBlock.NumberU64() == 0 { - if enoughBeyondCount && (root != common.Hash{}) && rootNumber == 0 { - for { - lastBlockNum++ - block := bc.GetBlockByNumber(lastBlockNum) - if block == nil { - break - } - if block.Root() == root { - rootNumber = block.NumberU64() - break - } - } - } + if beyondRoot || newHeadBlock.NumberU64() == 0 { if newHeadBlock.NumberU64() == 0 { resetState() } else if !bc.HasState(newHeadBlock.Root()) { diff --git a/trie/database.go b/trie/database.go index 7bad532dde..e0bc51dc04 100644 --- a/trie/database.go +++ b/trie/database.go @@ -353,3 +353,14 @@ func (db *Database) SetBufferSize(size int) error { } return pdb.SetBufferSize(size) } + +// Head return the top non-fork difflayer/disklayer root hash for rewinding. +// It's only supported by path-based database and will return an error for +// others. +func (db *Database) Head() common.Hash { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return common.Hash{} + } + return pdb.Head() +} diff --git a/trie/triedb/pathdb/database.go b/trie/triedb/pathdb/database.go index ee57e50b6a..cf91cc38ef 100644 --- a/trie/triedb/pathdb/database.go +++ b/trie/triedb/pathdb/database.go @@ -425,3 +425,10 @@ func (db *Database) SetBufferSize(size int) error { func (db *Database) Scheme() string { return rawdb.PathScheme } + +// Head return the top non-fork difflayer/disklayer root hash for rewinding. +func (db *Database) Head() common.Hash { + db.lock.Lock() + defer db.lock.Unlock() + return db.tree.front() +} diff --git a/trie/triedb/pathdb/layertree.go b/trie/triedb/pathdb/layertree.go index d314779910..047b3e23fa 100644 --- a/trie/triedb/pathdb/layertree.go +++ b/trie/triedb/pathdb/layertree.go @@ -212,3 +212,42 @@ func (tree *layerTree) bottom() *diskLayer { } return current.(*diskLayer) } + +// front return the top non-fork difflayer/disklayer root hash for rewinding. +func (tree *layerTree) front() common.Hash { + tree.lock.RLock() + defer tree.lock.RUnlock() + + chain := make(map[common.Hash][]common.Hash) + var base common.Hash + for _, layer := range tree.layers { + if dl, ok := layer.(*diskLayer); ok { + if dl.stale { + return base + } + base = dl.rootHash() + } else { + if dl.stale { + continue + } + if _, ok := chain[dl.parentLayer().rootHash()]; !ok { + chain[dl.parentLayer().rootHash()] = make([]common.Hash, 0) + } + chain[dl.parentLayer().rootHash()] = append(chain[dl.parentLayer().rootHash()], dl.rootHash()) + } + } + if (base == common.Hash{}) { + return base + } + parent := base + for { + children, ok := chain[parent] + if !ok { + return parent + } + if len(children) != 1 { + return parent + } + parent = children[0] + } +}