Skip to content

Commit

Permalink
fix: use the top root hash for rewinding under path schema
Browse files Browse the repository at this point in the history
  • Loading branch information
joeylichang committed Oct 31, 2023
1 parent 1b70455 commit cf15dff
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 21 deletions.
27 changes: 6 additions & 21 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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()) {
Expand Down
11 changes: 11 additions & 0 deletions trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
7 changes: 7 additions & 0 deletions trie/triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
39 changes: 39 additions & 0 deletions trie/triedb/pathdb/layertree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}

0 comments on commit cf15dff

Please sign in to comment.