Skip to content

Commit fa8d398

Browse files
authored
cmd, core, trie: verkle-capable geth init (#28270)
This change allows the creation of a genesis block for verkle testnets. This makes for a chunk of code that is easier to review and still touches many discussion points.
1 parent f265cc2 commit fa8d398

24 files changed

+1120
-54
lines changed

cmd/geth/chaincmd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ func initGenesis(ctx *cli.Context) error {
211211
}
212212
defer chaindb.Close()
213213

214-
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false)
214+
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
215215
defer triedb.Close()
216216

217217
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
@@ -485,7 +485,7 @@ func dump(ctx *cli.Context) error {
485485
if err != nil {
486486
return err
487487
}
488-
triedb := utils.MakeTrieDatabase(ctx, db, true, true) // always enable preimage lookup
488+
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
489489
defer triedb.Close()
490490

491491
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)

cmd/geth/dbcmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ func dbDumpTrie(ctx *cli.Context) error {
482482
db := utils.MakeChainDatabase(ctx, stack, true)
483483
defer db.Close()
484484

485-
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
485+
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
486486
defer triedb.Close()
487487

488488
var (

cmd/geth/snapshot.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func verifyState(ctx *cli.Context) error {
205205
log.Error("Failed to load head block")
206206
return errors.New("no head block")
207207
}
208-
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
208+
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
209209
defer triedb.Close()
210210

211211
snapConfig := snapshot.Config{
@@ -260,7 +260,7 @@ func traverseState(ctx *cli.Context) error {
260260
chaindb := utils.MakeChainDatabase(ctx, stack, true)
261261
defer chaindb.Close()
262262

263-
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
263+
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
264264
defer triedb.Close()
265265

266266
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -369,7 +369,7 @@ func traverseRawState(ctx *cli.Context) error {
369369
chaindb := utils.MakeChainDatabase(ctx, stack, true)
370370
defer chaindb.Close()
371371

372-
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
372+
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
373373
defer triedb.Close()
374374

375375
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -533,7 +533,7 @@ func dumpState(ctx *cli.Context) error {
533533
if err != nil {
534534
return err
535535
}
536-
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
536+
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
537537
defer triedb.Close()
538538

539539
snapConfig := snapshot.Config{

cmd/geth/verkle.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
8484
return fmt.Errorf("could not find child %x in db: %w", childC, err)
8585
}
8686
// depth is set to 0, the tree isn't rebuilt so it's not a problem
87-
childN, err := verkle.ParseNode(childS, 0, childC[:])
87+
childN, err := verkle.ParseNode(childS, 0)
8888
if err != nil {
8989
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
9090
}
@@ -145,7 +145,7 @@ func verifyVerkle(ctx *cli.Context) error {
145145
if err != nil {
146146
return err
147147
}
148-
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
148+
root, err := verkle.ParseNode(serializedRoot, 0)
149149
if err != nil {
150150
return err
151151
}
@@ -195,7 +195,7 @@ func expandVerkle(ctx *cli.Context) error {
195195
if err != nil {
196196
return err
197197
}
198-
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
198+
root, err := verkle.ParseNode(serializedRoot, 0)
199199
if err != nil {
200200
return err
201201
}

cmd/utils/flags.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2212,9 +2212,10 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
22122212
}
22132213

22142214
// MakeTrieDatabase constructs a trie database based on the configured scheme.
2215-
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
2215+
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *trie.Database {
22162216
config := &trie.Config{
22172217
Preimages: preimage,
2218+
IsVerkle: isVerkle,
22182219
}
22192220
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
22202221
if err != nil {

core/genesis.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/ethereum/go-ethereum/params"
3838
"github.com/ethereum/go-ethereum/rlp"
3939
"github.com/ethereum/go-ethereum/trie"
40+
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
4041
)
4142

4243
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
@@ -121,10 +122,20 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
121122
}
122123

123124
// hash computes the state root according to the genesis specification.
124-
func (ga *GenesisAlloc) hash() (common.Hash, error) {
125+
func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
126+
// If a genesis-time verkle trie is requested, create a trie config
127+
// with the verkle trie enabled so that the tree can be initialized
128+
// as such.
129+
var config *trie.Config
130+
if isVerkle {
131+
config = &trie.Config{
132+
PathDB: pathdb.Defaults,
133+
IsVerkle: true,
134+
}
135+
}
125136
// Create an ephemeral in-memory database for computing hash,
126137
// all the derived states will be discarded to not pollute disk.
127-
db := state.NewDatabase(rawdb.NewMemoryDatabase())
138+
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config)
128139
statedb, err := state.New(types.EmptyRootHash, db, nil)
129140
if err != nil {
130141
return common.Hash{}, err
@@ -410,9 +421,15 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
410421
}
411422
}
412423

424+
// IsVerkle indicates whether the state is already stored in a verkle
425+
// tree at genesis time.
426+
func (g *Genesis) IsVerkle() bool {
427+
return g.Config.IsVerkle(new(big.Int).SetUint64(g.Number), g.Timestamp)
428+
}
429+
413430
// ToBlock returns the genesis block according to genesis specification.
414431
func (g *Genesis) ToBlock() *types.Block {
415-
root, err := g.Alloc.hash()
432+
root, err := g.Alloc.hash(g.IsVerkle())
416433
if err != nil {
417434
panic(err)
418435
}

core/genesis_test.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package core
1818

1919
import (
20+
"bytes"
2021
"encoding/json"
2122
"math/big"
2223
"reflect"
@@ -231,7 +232,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
231232
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
232233
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
233234
}
234-
hash, _ = alloc.hash()
235+
hash, _ = alloc.hash(false)
235236
)
236237
blob, _ := json.Marshal(alloc)
237238
rawdb.WriteGenesisStateSpec(db, hash, blob)
@@ -261,3 +262,66 @@ func newDbConfig(scheme string) *trie.Config {
261262
}
262263
return &trie.Config{PathDB: pathdb.Defaults}
263264
}
265+
266+
func TestVerkleGenesisCommit(t *testing.T) {
267+
var verkleTime uint64 = 0
268+
verkleConfig := &params.ChainConfig{
269+
ChainID: big.NewInt(1),
270+
HomesteadBlock: big.NewInt(0),
271+
DAOForkBlock: nil,
272+
DAOForkSupport: false,
273+
EIP150Block: big.NewInt(0),
274+
EIP155Block: big.NewInt(0),
275+
EIP158Block: big.NewInt(0),
276+
ByzantiumBlock: big.NewInt(0),
277+
ConstantinopleBlock: big.NewInt(0),
278+
PetersburgBlock: big.NewInt(0),
279+
IstanbulBlock: big.NewInt(0),
280+
MuirGlacierBlock: big.NewInt(0),
281+
BerlinBlock: big.NewInt(0),
282+
LondonBlock: big.NewInt(0),
283+
ArrowGlacierBlock: big.NewInt(0),
284+
GrayGlacierBlock: big.NewInt(0),
285+
MergeNetsplitBlock: nil,
286+
ShanghaiTime: &verkleTime,
287+
CancunTime: &verkleTime,
288+
PragueTime: &verkleTime,
289+
VerkleTime: &verkleTime,
290+
TerminalTotalDifficulty: big.NewInt(0),
291+
TerminalTotalDifficultyPassed: true,
292+
Ethash: nil,
293+
Clique: nil,
294+
}
295+
296+
genesis := &Genesis{
297+
BaseFee: big.NewInt(params.InitialBaseFee),
298+
Config: verkleConfig,
299+
Timestamp: verkleTime,
300+
Difficulty: big.NewInt(0),
301+
Alloc: GenesisAlloc{
302+
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
303+
},
304+
}
305+
306+
expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
307+
got := genesis.ToBlock().Root().Bytes()
308+
if !bytes.Equal(got, expected) {
309+
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
310+
}
311+
312+
db := rawdb.NewMemoryDatabase()
313+
triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
314+
block := genesis.MustCommit(db, triedb)
315+
if !bytes.Equal(block.Root().Bytes(), expected) {
316+
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
317+
}
318+
319+
// Test that the trie is verkle
320+
if !triedb.IsVerkle() {
321+
t.Fatalf("expected trie to be verkle")
322+
}
323+
324+
if !rawdb.ExistsAccountTrieNode(db, nil) {
325+
t.Fatal("could not find node")
326+
}
327+
}

core/state/database.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222

23+
"github.com/crate-crypto/go-ipa/banderwagon"
2324
"github.com/ethereum/go-ethereum/common"
2425
"github.com/ethereum/go-ethereum/common/lru"
2526
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -28,6 +29,7 @@ import (
2829
"github.com/ethereum/go-ethereum/ethdb"
2930
"github.com/ethereum/go-ethereum/trie"
3031
"github.com/ethereum/go-ethereum/trie/trienode"
32+
"github.com/ethereum/go-ethereum/trie/utils"
3133
)
3234

3335
const (
@@ -36,6 +38,12 @@ const (
3638

3739
// Cache size granted for caching clean code.
3840
codeCacheSize = 64 * 1024 * 1024
41+
42+
// commitmentSize is the size of commitment stored in cache.
43+
commitmentSize = banderwagon.UncompressedSize
44+
45+
// Cache item granted for caching commitment results.
46+
commitmentCacheItems = 64 * 1024 * 1024 / (commitmentSize + common.AddressLength)
3947
)
4048

4149
// Database wraps access to tries and contract code.
@@ -44,7 +52,7 @@ type Database interface {
4452
OpenTrie(root common.Hash) (Trie, error)
4553

4654
// OpenStorageTrie opens the storage trie of an account.
47-
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error)
55+
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
4856

4957
// CopyTrie returns an independent copy of the given trie.
5058
CopyTrie(Trie) Trie
@@ -70,11 +78,6 @@ type Trie interface {
7078
// TODO(fjl): remove this when StateTrie is removed
7179
GetKey([]byte) []byte
7280

73-
// GetStorage returns the value for key stored in the trie. The value bytes
74-
// must not be modified by the caller. If a node was not found in the database,
75-
// a trie.MissingNodeError is returned.
76-
GetStorage(addr common.Address, key []byte) ([]byte, error)
77-
7881
// GetAccount abstracts an account read from the trie. It retrieves the
7982
// account blob from the trie with provided account address and decodes it
8083
// with associated decoding algorithm. If the specified account is not in
@@ -83,27 +86,32 @@ type Trie interface {
8386
// be returned.
8487
GetAccount(address common.Address) (*types.StateAccount, error)
8588

86-
// UpdateStorage associates key with value in the trie. If value has length zero,
87-
// any existing value is deleted from the trie. The value bytes must not be modified
88-
// by the caller while they are stored in the trie. If a node was not found in the
89-
// database, a trie.MissingNodeError is returned.
90-
UpdateStorage(addr common.Address, key, value []byte) error
89+
// GetStorage returns the value for key stored in the trie. The value bytes
90+
// must not be modified by the caller. If a node was not found in the database,
91+
// a trie.MissingNodeError is returned.
92+
GetStorage(addr common.Address, key []byte) ([]byte, error)
9193

9294
// UpdateAccount abstracts an account write to the trie. It encodes the
9395
// provided account object with associated algorithm and then updates it
9496
// in the trie with provided address.
9597
UpdateAccount(address common.Address, account *types.StateAccount) error
9698

97-
// UpdateContractCode abstracts code write to the trie. It is expected
98-
// to be moved to the stateWriter interface when the latter is ready.
99-
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
99+
// UpdateStorage associates key with value in the trie. If value has length zero,
100+
// any existing value is deleted from the trie. The value bytes must not be modified
101+
// by the caller while they are stored in the trie. If a node was not found in the
102+
// database, a trie.MissingNodeError is returned.
103+
UpdateStorage(addr common.Address, key, value []byte) error
104+
105+
// DeleteAccount abstracts an account deletion from the trie.
106+
DeleteAccount(address common.Address) error
100107

101108
// DeleteStorage removes any existing value for key from the trie. If a node
102109
// was not found in the database, a trie.MissingNodeError is returned.
103110
DeleteStorage(addr common.Address, key []byte) error
104111

105-
// DeleteAccount abstracts an account deletion from the trie.
106-
DeleteAccount(address common.Address) error
112+
// UpdateContractCode abstracts code write to the trie. It is expected
113+
// to be moved to the stateWriter interface when the latter is ready.
114+
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
107115

108116
// Hash returns the root hash of the trie. It does not write to the database and
109117
// can be used even if the trie doesn't have one.
@@ -170,6 +178,9 @@ type cachingDB struct {
170178

171179
// OpenTrie opens the main account trie at a specific root hash.
172180
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
181+
if db.triedb.IsVerkle() {
182+
return trie.NewVerkleTrie(root, db.triedb, utils.NewPointCache(commitmentCacheItems))
183+
}
173184
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
174185
if err != nil {
175186
return nil, err
@@ -178,7 +189,13 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
178189
}
179190

180191
// OpenStorageTrie opens the storage trie of an account.
181-
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
192+
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
193+
// In the verkle case, there is only one tree. But the two-tree structure
194+
// is hardcoded in the codebase. So we need to return the same trie in this
195+
// case.
196+
if db.triedb.IsVerkle() {
197+
return self, nil
198+
}
182199
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
183200
if err != nil {
184201
return nil, err

core/state/iterator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (it *nodeIterator) step() error {
123123
address := common.BytesToAddress(preimage)
124124

125125
// Traverse the storage slots belong to the account
126-
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root)
126+
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root, it.state.trie)
127127
if err != nil {
128128
return err
129129
}

core/state/state_object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (s *stateObject) getTrie() (Trie, error) {
145145
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
146146
}
147147
if s.trie == nil {
148-
tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
148+
tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root, s.db.trie)
149149
if err != nil {
150150
return nil, err
151151
}

0 commit comments

Comments
 (0)