Skip to content

Commit

Permalink
feat: pickup pathdb diskroot as rewind root when mismatch snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
joeylichang authored and will@2012 committed Mar 8, 2024
1 parent cf4451a commit 1735ea9
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 5 deletions.
6 changes: 6 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if bc.cacheConfig.SnapshotLimit > 0 {
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
}
if bc.triedb.Scheme() == rawdb.PathScheme {
recoverable, _ := bc.triedb.Recoverable(diskRoot)
if !bc.HasState(diskRoot) && !recoverable {
diskRoot = bc.triedb.Head()
}
}
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "snaproot", diskRoot)

Expand Down
2 changes: 1 addition & 1 deletion core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1844,7 +1844,7 @@ func TestTrieForkGC(t *testing.T) {
chain.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
chain.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
}
if _, nodes, _ := chain.TrieDB().Size(); nodes > 0 { // all memory is returned in the nodes return for hashdb
if _, nodes, _, _ := chain.TrieDB().Size(); nodes > 0 { // all memory is returned in the nodes return for hashdb
t.Fatalf("stale tries still alive after garbase collection")
}
}
Expand Down
11 changes: 11 additions & 0 deletions trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,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 empty hash 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 @@ -461,6 +461,13 @@ 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()
}

// modifyAllowed returns the indicator if mutation is allowed. This function
// assumes the db.lock is already held.
func (db *Database) modifyAllowed() error {
Expand Down
10 changes: 6 additions & 4 deletions trie/triedb/pathdb/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ func (dl *diffLayer) journal(w io.Writer) error {
// flattening everything down (bad for reorgs). And this function will mark the
// database as read-only to prevent all following mutation to disk.
func (db *Database) Journal(root common.Hash) error {
// Run the journaling
db.lock.Lock()
defer db.lock.Unlock()

// Retrieve the head layer to journal from.
l := db.tree.get(root)
if l == nil {
Expand All @@ -351,10 +355,8 @@ func (db *Database) Journal(root common.Hash) error {
}
start := time.Now()

// Run the journaling
db.lock.Lock()
defer db.lock.Unlock()

// wait and stop the flush trienodebuffer, for asyncnodebuffer need fixed diskroot
disk.buffer.waitAndStopFlushing()
// Short circuit if the database is in read only mode.
if db.readOnly {
return errDatabaseReadOnly
Expand Down
44 changes: 44 additions & 0 deletions trie/triedb/pathdb/layertree.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate"
)
Expand Down Expand Up @@ -212,3 +213,46 @@ 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 {
switch dl := layer.(type) {
case *diskLayer:
if dl.stale {
log.Info("pathdb top disklayer is stale")
return base
}
base = dl.rootHash()
case *diffLayer:
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())
default:
log.Crit("unsupported layer type")
}
}
if (base == common.Hash{}) {
log.Info("pathdb top difflayer is empty")
return base
}
parent := base
for {
children, ok := chain[parent]
if !ok {
log.Info("pathdb top difflayer", "root", parent)
return parent
}
if len(children) != 1 {
log.Info("pathdb top difflayer is forked", "common ancestor root", parent)
return parent
}
parent = children[0]
}
}

0 comments on commit 1735ea9

Please sign in to comment.