Skip to content

Commit 82e963e

Browse files
authored
triedb/pathdb: configure different node hasher in pathdb (#31008)
As the node hash scheme in verkle and merkle are totally different, the original default node hasher in pathdb is no longer suitable. Therefore, this pull request configures different node hasher respectively.
1 parent 033de2a commit 82e963e

File tree

9 files changed

+71
-50
lines changed

9 files changed

+71
-50
lines changed

core/genesis.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,12 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
127127
}
128128
// Create an ephemeral in-memory database for computing hash,
129129
// all the derived states will be discarded to not pollute disk.
130+
emptyRoot := types.EmptyRootHash
131+
if isVerkle {
132+
emptyRoot = types.EmptyVerkleHash
133+
}
130134
db := rawdb.NewMemoryDatabase()
131-
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb.NewDatabase(db, config), nil))
135+
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil))
132136
if err != nil {
133137
return common.Hash{}, err
134138
}
@@ -148,7 +152,11 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
148152
// flushAlloc is very similar with hash, but the main difference is all the
149153
// generated states will be persisted into the given database.
150154
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, error) {
151-
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb, nil))
155+
emptyRoot := types.EmptyRootHash
156+
if triedb.IsVerkle() {
157+
emptyRoot = types.EmptyVerkleHash
158+
}
159+
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, nil))
152160
if err != nil {
153161
return common.Hash{}, err
154162
}

core/state/stateupdate.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"maps"
2121

2222
"github.com/ethereum/go-ethereum/common"
23-
"github.com/ethereum/go-ethereum/core/types"
2423
"github.com/ethereum/go-ethereum/trie/trienode"
2524
"github.com/ethereum/go-ethereum/triedb"
2625
)
@@ -133,8 +132,8 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
133132
}
134133
}
135134
return &stateUpdate{
136-
originRoot: types.TrieRootHash(originRoot),
137-
root: types.TrieRootHash(root),
135+
originRoot: originRoot,
136+
root: root,
138137
accounts: accounts,
139138
accountsOrigin: accountsOrigin,
140139
storages: storages,

core/types/hashes.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package types
1919
import (
2020
"github.com/ethereum/go-ethereum/common"
2121
"github.com/ethereum/go-ethereum/crypto"
22-
"github.com/ethereum/go-ethereum/log"
2322
)
2423

2524
var (
@@ -47,13 +46,3 @@ var (
4746
// EmptyVerkleHash is the known hash of an empty verkle trie.
4847
EmptyVerkleHash = common.Hash{}
4948
)
50-
51-
// TrieRootHash returns the hash itself if it's non-empty or the predefined
52-
// emptyHash one instead.
53-
func TrieRootHash(hash common.Hash) common.Hash {
54-
if hash == (common.Hash{}) {
55-
log.Error("Zero trie root hash!")
56-
return EmptyRootHash
57-
}
58-
return hash
59-
}

internal/ethapi/override/override_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ethereum/go-ethereum/common/hexutil"
2525
"github.com/ethereum/go-ethereum/core/rawdb"
2626
"github.com/ethereum/go-ethereum/core/state"
27+
"github.com/ethereum/go-ethereum/core/types"
2728
"github.com/ethereum/go-ethereum/core/vm"
2829
"github.com/ethereum/go-ethereum/triedb"
2930
)
@@ -36,7 +37,7 @@ func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil
3637

3738
func TestStateOverrideMovePrecompile(t *testing.T) {
3839
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
39-
statedb, err := state.New(common.Hash{}, db)
40+
statedb, err := state.New(types.EmptyRootHash, db)
4041
if err != nil {
4142
t.Fatalf("failed to create statedb: %v", err)
4243
}

trie/trie_reader.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package trie
1919
import (
2020
"github.com/ethereum/go-ethereum/common"
2121
"github.com/ethereum/go-ethereum/core/types"
22-
"github.com/ethereum/go-ethereum/log"
2322
"github.com/ethereum/go-ethereum/triedb/database"
2423
)
2524

@@ -34,9 +33,6 @@ type trieReader struct {
3433
// newTrieReader initializes the trie reader with the given node reader.
3534
func newTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*trieReader, error) {
3635
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
37-
if stateRoot == (common.Hash{}) {
38-
log.Error("Zero state root hash!")
39-
}
4036
return &trieReader{owner: owner}, nil
4137
}
4238
reader, err := db.NodeReader(stateRoot)

triedb/pathdb/database.go

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/ethereum/go-ethereum/log"
3232
"github.com/ethereum/go-ethereum/params"
3333
"github.com/ethereum/go-ethereum/trie/trienode"
34+
"github.com/ethereum/go-verkle"
3435
)
3536

3637
const (
@@ -148,6 +149,29 @@ var Defaults = &Config{
148149
// ReadOnly is the config in order to open database in read only mode.
149150
var ReadOnly = &Config{ReadOnly: true}
150151

152+
// nodeHasher is the function to compute the hash of supplied node blob.
153+
type nodeHasher func([]byte) (common.Hash, error)
154+
155+
// merkleNodeHasher computes the hash of the given merkle node.
156+
func merkleNodeHasher(blob []byte) (common.Hash, error) {
157+
if len(blob) == 0 {
158+
return types.EmptyRootHash, nil
159+
}
160+
return crypto.Keccak256Hash(blob), nil
161+
}
162+
163+
// verkleNodeHasher computes the hash of the given verkle node.
164+
func verkleNodeHasher(blob []byte) (common.Hash, error) {
165+
if len(blob) == 0 {
166+
return types.EmptyVerkleHash, nil
167+
}
168+
n, err := verkle.ParseNode(blob, 0)
169+
if err != nil {
170+
return common.Hash{}, err
171+
}
172+
return n.Commit().Bytes(), nil
173+
}
174+
151175
// Database is a multiple-layered structure for maintaining in-memory states
152176
// along with its dirty trie nodes. It consists of one persistent base layer
153177
// backed by a key-value store, on top of which arbitrarily many in-memory diff
@@ -164,9 +188,10 @@ type Database struct {
164188
// readOnly is the flag whether the mutation is allowed to be applied.
165189
// It will be set automatically when the database is journaled during
166190
// the shutdown to reject all following unexpected mutations.
167-
readOnly bool // Flag if database is opened in read only mode
168-
waitSync bool // Flag if database is deactivated due to initial state sync
169-
isVerkle bool // Flag if database is used for verkle tree
191+
readOnly bool // Flag if database is opened in read only mode
192+
waitSync bool // Flag if database is deactivated due to initial state sync
193+
isVerkle bool // Flag if database is used for verkle tree
194+
hasher nodeHasher // Trie node hasher
170195

171196
config *Config // Configuration for database
172197
diskdb ethdb.Database // Persistent storage for matured trie nodes
@@ -184,19 +209,21 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database {
184209
}
185210
config = config.sanitize()
186211

212+
db := &Database{
213+
readOnly: config.ReadOnly,
214+
isVerkle: isVerkle,
215+
config: config,
216+
diskdb: diskdb,
217+
hasher: merkleNodeHasher,
218+
}
187219
// Establish a dedicated database namespace tailored for verkle-specific
188220
// data, ensuring the isolation of both verkle and merkle tree data. It's
189221
// important to note that the introduction of a prefix won't lead to
190222
// substantial storage overhead, as the underlying database will efficiently
191223
// compress the shared key prefix.
192224
if isVerkle {
193-
diskdb = rawdb.NewTable(diskdb, string(rawdb.VerklePrefix))
194-
}
195-
db := &Database{
196-
readOnly: config.ReadOnly,
197-
isVerkle: isVerkle,
198-
config: config,
199-
diskdb: diskdb,
225+
db.diskdb = rawdb.NewTable(diskdb, string(rawdb.VerklePrefix))
226+
db.hasher = verkleNodeHasher
200227
}
201228
// Construct the layer tree by resolving the in-disk singleton state
202229
// and in-memory layer journal.
@@ -277,6 +304,8 @@ func (db *Database) repairHistory() error {
277304
//
278305
// The passed in maps(nodes, states) will be retained to avoid copying everything.
279306
// Therefore, these maps must not be changed afterwards.
307+
//
308+
// The supplied parentRoot and root must be a valid trie hash value.
280309
func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *StateSetWithOrigin) error {
281310
// Hold the lock to prevent concurrent mutations.
282311
db.lock.Lock()
@@ -350,10 +379,9 @@ func (db *Database) Enable(root common.Hash) error {
350379
return errDatabaseReadOnly
351380
}
352381
// Ensure the provided state root matches the stored one.
353-
root = types.TrieRootHash(root)
354-
stored := types.EmptyRootHash
355-
if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 {
356-
stored = crypto.Keccak256Hash(blob)
382+
stored, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil))
383+
if err != nil {
384+
return err
357385
}
358386
if stored != root {
359387
return fmt.Errorf("state root mismatch: stored %x, synced %x", stored, root)
@@ -389,6 +417,8 @@ func (db *Database) Enable(root common.Hash) error {
389417
// Recover rollbacks the database to a specified historical point.
390418
// The state is supported as the rollback destination only if it's
391419
// canonical state and the corresponding trie histories are existent.
420+
//
421+
// The supplied root must be a valid trie hash value.
392422
func (db *Database) Recover(root common.Hash) error {
393423
db.lock.Lock()
394424
defer db.lock.Unlock()
@@ -401,7 +431,6 @@ func (db *Database) Recover(root common.Hash) error {
401431
return errors.New("state rollback is non-supported")
402432
}
403433
// Short circuit if the target state is not recoverable
404-
root = types.TrieRootHash(root)
405434
if !db.Recoverable(root) {
406435
return errStateUnrecoverable
407436
}
@@ -434,9 +463,10 @@ func (db *Database) Recover(root common.Hash) error {
434463
}
435464

436465
// Recoverable returns the indicator if the specified state is recoverable.
466+
//
467+
// The supplied root must be a valid trie hash value.
437468
func (db *Database) Recoverable(root common.Hash) bool {
438469
// Ensure the requested state is a known state.
439-
root = types.TrieRootHash(root)
440470
id := rawdb.ReadStateID(db.diskdb, root)
441471
if id == nil {
442472
return false

triedb/pathdb/database_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,8 @@ func TestDatabaseRecoverable(t *testing.T) {
458458
// Initial state should be recoverable
459459
{types.EmptyRootHash, true},
460460

461-
// Initial state should be recoverable
462-
{common.Hash{}, true},
461+
// common.Hash{} is not a valid state root for revert
462+
{common.Hash{}, false},
463463

464464
// Layers below current disk layer are recoverable
465465
{tester.roots[index-1], true},

triedb/pathdb/journal.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"github.com/ethereum/go-ethereum/common"
2727
"github.com/ethereum/go-ethereum/core/rawdb"
2828
"github.com/ethereum/go-ethereum/core/types"
29-
"github.com/ethereum/go-ethereum/crypto"
3029
"github.com/ethereum/go-ethereum/log"
3130
"github.com/ethereum/go-ethereum/rlp"
3231
)
@@ -93,9 +92,9 @@ func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) {
9392
// loadLayers loads a pre-existing state layer backed by a key-value store.
9493
func (db *Database) loadLayers() layer {
9594
// Retrieve the root node of persistent state.
96-
var root = types.EmptyRootHash
97-
if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 {
98-
root = crypto.Keccak256Hash(blob)
95+
root, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil))
96+
if err != nil {
97+
log.Crit("Failed to compute node hash", "err", err)
9998
}
10099
// Load the layers by resolving the journal
101100
head, err := db.loadJournal(root)
@@ -236,6 +235,8 @@ func (dl *diffLayer) journal(w io.Writer) error {
236235
// This is meant to be used during shutdown to persist the layer without
237236
// flattening everything down (bad for reorgs). And this function will mark the
238237
// database as read-only to prevent all following mutation to disk.
238+
//
239+
// The supplied root must be a valid trie hash value.
239240
func (db *Database) Journal(root common.Hash) error {
240241
// Retrieve the head layer to journal from.
241242
l := db.tree.get(root)
@@ -265,9 +266,9 @@ func (db *Database) Journal(root common.Hash) error {
265266
}
266267
// Secondly write out the state root in disk, ensure all layers
267268
// on top are continuous with disk.
268-
diskRoot := types.EmptyRootHash
269-
if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 {
270-
diskRoot = crypto.Keccak256Hash(blob)
269+
diskRoot, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil))
270+
if err != nil {
271+
return err
271272
}
272273
if err := rlp.Encode(journal, diskRoot); err != nil {
273274
return err

triedb/pathdb/layertree.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"sync"
2323

2424
"github.com/ethereum/go-ethereum/common"
25-
"github.com/ethereum/go-ethereum/core/types"
2625
"github.com/ethereum/go-ethereum/trie/trienode"
2726
)
2827

@@ -62,7 +61,7 @@ func (tree *layerTree) get(root common.Hash) layer {
6261
tree.lock.RLock()
6362
defer tree.lock.RUnlock()
6463

65-
return tree.layers[types.TrieRootHash(root)]
64+
return tree.layers[root]
6665
}
6766

6867
// forEach iterates the stored layers inside and applies the
@@ -92,7 +91,6 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6
9291
//
9392
// Although we could silently ignore this internally, it should be the caller's
9493
// responsibility to avoid even attempting to insert such a layer.
95-
root, parentRoot = types.TrieRootHash(root), types.TrieRootHash(parentRoot)
9694
if root == parentRoot {
9795
return errors.New("layer cycle")
9896
}
@@ -112,7 +110,6 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6
112110
// are crossed. All diffs beyond the permitted number are flattened downwards.
113111
func (tree *layerTree) cap(root common.Hash, layers int) error {
114112
// Retrieve the head layer to cap from
115-
root = types.TrieRootHash(root)
116113
l := tree.get(root)
117114
if l == nil {
118115
return fmt.Errorf("triedb layer [%#x] missing", root)

0 commit comments

Comments
 (0)