From 3249aa6949b1d4df862d44c6435a12222fc69cc8 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 6 Oct 2023 15:23:59 +0200
Subject: [PATCH] cmd, core, trie: verkle-capable `geth init`
---
cmd/geth/chaincmd.go | 5 +-
cmd/geth/dbcmd.go | 2 +-
cmd/geth/snapshot.go | 8 +-
cmd/geth/verkle.go | 6 +-
cmd/utils/flags.go | 3 +-
core/genesis.go | 21 ++-
core/genesis_test.go | 2 +-
core/state/database.go | 4 +
go.mod | 6 +-
go.sum | 15 +-
trie/database.go | 5 +
trie/utils/verkle.go | 300 +++++++++++++++++++++++++++++++
trie/verkle.go | 388 ++++++++++++++++++++++++++++++++++++++++
trie/verkle_iterator.go | 218 ++++++++++++++++++++++
14 files changed, 956 insertions(+), 27 deletions(-)
create mode 100644 trie/utils/verkle.go
create mode 100644 trie/verkle.go
create mode 100644 trie/verkle_iterator.go
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 5663963e3c..b4afe0b4c6 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "math/big"
"os"
"runtime"
"strconv"
@@ -211,7 +212,7 @@ func initGenesis(ctx *cli.Context) error {
}
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.Config.IsVerkle(big.NewInt(0), genesis.Timestamp))
defer triedb.Close()
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
@@ -485,7 +486,7 @@ func dump(ctx *cli.Context) error {
if err != nil {
return err
}
- triedb := utils.MakeTrieDatabase(ctx, db, true, true) // always enable preimage lookup
+ triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
defer triedb.Close()
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go
index 6f802716c5..3d34a9e2d6 100644
--- a/cmd/geth/dbcmd.go
+++ b/cmd/geth/dbcmd.go
@@ -482,7 +482,7 @@ func dbDumpTrie(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
- triedb := utils.MakeTrieDatabase(ctx, db, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
defer triedb.Close()
var (
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index 6413482511..048aac2af9 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -205,7 +205,7 @@ func verifyState(ctx *cli.Context) error {
log.Error("Failed to load head block")
return errors.New("no head block")
}
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
snapConfig := snapshot.Config{
@@ -260,7 +260,7 @@ func traverseState(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -369,7 +369,7 @@ func traverseRawState(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -533,7 +533,7 @@ func dumpState(ctx *cli.Context) error {
if err != nil {
return err
}
- triedb := utils.MakeTrieDatabase(ctx, db, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
defer triedb.Close()
snapConfig := snapshot.Config{
diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go
index aa79889e8c..420b063d8b 100644
--- a/cmd/geth/verkle.go
+++ b/cmd/geth/verkle.go
@@ -84,7 +84,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
return fmt.Errorf("could not find child %x in db: %w", childC, err)
}
// depth is set to 0, the tree isn't rebuilt so it's not a problem
- childN, err := verkle.ParseNode(childS, 0, childC[:])
+ childN, err := verkle.ParseNode(childS, 0)
if err != nil {
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
}
@@ -145,7 +145,7 @@ func verifyVerkle(ctx *cli.Context) error {
if err != nil {
return err
}
- root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
+ root, err := verkle.ParseNode(serializedRoot, 0)
if err != nil {
return err
}
@@ -195,7 +195,7 @@ func expandVerkle(ctx *cli.Context) error {
if err != nil {
return err
}
- root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
+ root, err := verkle.ParseNode(serializedRoot, 0)
if err != nil {
return err
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index db226c73d8..e80a18b7c3 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -2213,9 +2213,10 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
}
// MakeTrieDatabase constructs a trie database based on the configured scheme.
-func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
+func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *trie.Database {
config := &trie.Config{
Preimages: preimage,
+ IsVerkle: isVerkle,
}
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
if err != nil {
diff --git a/core/genesis.go b/core/genesis.go
index 1045815fab..d438808e8f 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
)
//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 {
}
// hash computes the state root according to the genesis specification.
-func (ga *GenesisAlloc) hash() (common.Hash, error) {
+func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
+ // If a genesis-time verkle trie is requested, create a trie config
+ // with the verkle trie enabled so that the tree can be initialized
+ // as such.
+ var config *trie.Config
+ if isVerkle {
+ config = &trie.Config{
+ PathDB: pathdb.Defaults,
+ IsVerkle: true,
+ }
+ }
// Create an ephemeral in-memory database for computing hash,
// all the derived states will be discarded to not pollute disk.
- db := state.NewDatabase(rawdb.NewMemoryDatabase())
+ db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config)
statedb, err := state.New(types.EmptyRootHash, db, nil)
if err != nil {
return common.Hash{}, err
@@ -410,9 +421,13 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
}
}
+func (g *Genesis) isVerkle() bool {
+ return g.Config.IsVerkle(new(big.Int).SetUint64(g.Number), g.Timestamp)
+}
+
// ToBlock returns the genesis block according to genesis specification.
func (g *Genesis) ToBlock() *types.Block {
- root, err := g.Alloc.hash()
+ root, err := g.Alloc.hash(g.isVerkle())
if err != nil {
panic(err)
}
diff --git a/core/genesis_test.go b/core/genesis_test.go
index fac88ff373..f8d1558505 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
}
- hash, _ = alloc.hash()
+ hash, _ = alloc.hash(false)
)
blob, _ := json.Marshal(alloc)
rawdb.WriteGenesisStateSpec(db, hash, blob)
diff --git a/core/state/database.go b/core/state/database.go
index 9467c8f72e..ada643596c 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
+ "github.com/ethereum/go-ethereum/trie/utils"
)
const (
@@ -170,6 +171,9 @@ type cachingDB struct {
// OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
+ if db.triedb.IsVerkle() {
+ return trie.NewVerkleTrie(nil, db.triedb, utils.NewPointCache(), true)
+ }
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
diff --git a/go.mod b/go.mod
index 4901030311..b80abda46f 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,7 @@ require (
github.com/cockroachdb/errors v1.8.1
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
github.com/consensys/gnark-crypto v0.12.1
+ github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58
github.com/crate-crypto/go-kzg-4844 v0.7.0
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
@@ -27,7 +28,7 @@ require (
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/fsnotify/fsnotify v1.6.0
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
- github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b
+ github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b
github.com/go-stack/stack v1.8.1
github.com/gofrs/flock v0.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
@@ -66,7 +67,7 @@ require (
go.uber.org/automaxprocs v1.5.2
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
- golang.org/x/sync v0.3.0
+ golang.org/x/sync v0.4.0
golang.org/x/sys v0.13.0
golang.org/x/text v0.13.0
golang.org/x/time v0.3.0
@@ -98,7 +99,6 @@ require (
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
- github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
diff --git a/go.sum b/go.sum
index 6017c9f77e..3169a473a7 100644
--- a/go.sum
+++ b/go.sum
@@ -145,8 +145,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0=
-github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
+github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58 h1:PwUlswsGOrLB677lW4XrlWLeszY3BaDGbvZ6dYk28tQ=
+github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58/go.mod h1:J+gsi6D4peY0kyhaklyXFRVHOQWI2I5uU0c2+/90HYc=
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -203,8 +203,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
-github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b h1:vMT47RYsrftsHSTQhqXwC3BYflo38OLC3Y4LtXtLyU0=
-github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI=
+github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b h1:LHeiiSTL2FEGCP1ov6FqkikiViqygeVo1ZwJ1x3nYSE=
+github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b/go.mod h1:7JamHhSTnnHDhcI3G8r4sWaD9XlleriqVlC3FeAQJKM=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
@@ -420,7 +420,6 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
-github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -715,9 +714,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
+golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -771,7 +769,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/trie/database.go b/trie/database.go
index 1e59f0908f..1166b2acbb 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -31,6 +31,7 @@ import (
// Config defines all necessary options for database.
type Config struct {
Preimages bool // Flag whether the preimage of node key is recorded
+ IsVerkle bool // Flag whether the db is holding a verkle tree
HashDB *hashdb.Config // Configs for hash-based scheme
PathDB *pathdb.Config // Configs for experimental path-based scheme
}
@@ -318,3 +319,7 @@ func (db *Database) SetBufferSize(size int) error {
}
return pdb.SetBufferSize(size)
}
+
+func (db *Database) IsVerkle() bool {
+ return db.config.IsVerkle
+}
diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go
new file mode 100644
index 0000000000..17fdf1ade3
--- /dev/null
+++ b/trie/utils/verkle.go
@@ -0,0 +1,300 @@
+// Copyright 2021 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package utils
+
+import (
+ "encoding/binary"
+ "sync"
+
+ "github.com/crate-crypto/go-ipa/bandersnatch/fr"
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+const (
+ VersionLeafKey = 0
+ BalanceLeafKey = 1
+ NonceLeafKey = 2
+ CodeKeccakLeafKey = 3
+ CodeSizeLeafKey = 4
+)
+
+var (
+ zero = uint256.NewInt(0)
+ VerkleNodeWidthLog2 = 8
+ HeaderStorageOffset = uint256.NewInt(64)
+ mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(VerkleNodeWidthLog2))
+ CodeOffset = uint256.NewInt(128)
+ MainStorageOffset = new(uint256.Int).Lsh(uint256.NewInt(256), 31)
+ VerkleNodeWidth = uint256.NewInt(256)
+ codeStorageDelta = uint256.NewInt(0).Sub(CodeOffset, HeaderStorageOffset)
+
+ getTreePolyIndex0Point *verkle.Point
+)
+
+type PointCache struct {
+ cache map[string]*verkle.Point
+ lock sync.RWMutex
+}
+
+func NewPointCache() *PointCache {
+ return &PointCache{
+ cache: make(map[string]*verkle.Point),
+ }
+}
+
+func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point {
+ pc.lock.RLock()
+ point, ok := pc.cache[string(addr)]
+ pc.lock.RUnlock()
+ if ok {
+ return point
+ }
+
+ point = EvaluateAddressPoint(addr)
+ pc.lock.Lock()
+ pc.cache[string(addr)] = point
+ pc.lock.Unlock()
+ return point
+}
+
+func (pc *PointCache) GetTreeKeyVersionCached(addr []byte) []byte {
+ p := pc.GetTreeKeyHeader(addr)
+ v := PointToHash(p, VersionLeafKey)
+ return v[:]
+}
+
+func init() {
+ // The byte array is the Marshalled output of the point computed as such:
+ //cfg, _ := verkle.GetConfig()
+ //verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64})
+ //= cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1)
+ getTreePolyIndex0Point = new(verkle.Point)
+ err := getTreePolyIndex0Point.SetBytes([]byte{34, 25, 109, 242, 193, 5, 144, 224, 76, 52, 189, 92, 197, 126, 9, 145, 27, 152, 199, 130, 165, 3, 210, 27, 193, 131, 142, 28, 110, 26, 16, 191})
+ if err != nil {
+ panic(err)
+ }
+}
+
+// GetTreeKey performs both the work of the spec's get_tree_key function, and that
+// of pedersen_hash: it builds the polynomial in pedersen_hash without having to
+// create a mostly zero-filled buffer and "type cast" it to a 128-long 16-byte
+// array. Since at most the first 5 coefficients of the polynomial will be non-zero,
+// these 5 coefficients are created directly.
+func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
+ if len(address) < 32 {
+ var aligned [32]byte
+ address = append(aligned[:32-len(address)], address...)
+ }
+
+ // poly = [2+256*64, address_le_low, address_le_high, tree_index_le_low, tree_index_le_high]
+ var poly [5]fr.Element
+
+ // 32-byte address, interpreted as two little endian
+ // 16-byte numbers.
+ verkle.FromLEBytes(&poly[1], address[:16])
+ verkle.FromLEBytes(&poly[2], address[16:])
+
+ // treeIndex must be interpreted as a 32-byte aligned little-endian integer.
+ // e.g: if treeIndex is 0xAABBCC, we need the byte representation to be 0xCCBBAA00...00.
+ // poly[3] = LE({CC,BB,AA,00...0}) (16 bytes), poly[4]=LE({00,00,...}) (16 bytes).
+ //
+ // To avoid unnecessary endianness conversions for go-ipa, we do some trick:
+ // - poly[3]'s byte representation is the same as the *top* 16 bytes (trieIndexBytes[16:]) of
+ // 32-byte aligned big-endian representation (BE({00,...,AA,BB,CC})).
+ // - poly[4]'s byte representation is the same as the *low* 16 bytes (trieIndexBytes[:16]) of
+ // the 32-byte aligned big-endian representation (BE({00,00,...}).
+ trieIndexBytes := treeIndex.Bytes32()
+ verkle.FromBytes(&poly[3], trieIndexBytes[16:])
+ verkle.FromBytes(&poly[4], trieIndexBytes[:16])
+
+ cfg := verkle.GetConfig()
+ ret := cfg.CommitToPoly(poly[:], 0)
+
+ // add a constant point corresponding to poly[0]=[2+256*64].
+ ret.Add(ret, getTreePolyIndex0Point)
+
+ return PointToHash(ret, subIndex)
+}
+
+func GetTreeKeyAccountLeaf(address []byte, leaf byte) []byte {
+ return GetTreeKey(address, zero, leaf)
+}
+
+func GetTreeKeyVersion(address []byte) []byte {
+ return GetTreeKey(address, zero, VersionLeafKey)
+}
+
+func GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte {
+ return GetTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey)
+}
+
+func GetTreeKeyBalance(address []byte) []byte {
+ return GetTreeKey(address, zero, BalanceLeafKey)
+}
+
+func GetTreeKeyNonce(address []byte) []byte {
+ return GetTreeKey(address, zero, NonceLeafKey)
+}
+
+func GetTreeKeyCodeKeccak(address []byte) []byte {
+ return GetTreeKey(address, zero, CodeKeccakLeafKey)
+}
+
+func GetTreeKeyCodeSize(address []byte) []byte {
+ return GetTreeKey(address, zero, CodeSizeLeafKey)
+}
+
+func GetTreeKeyCodeChunk(address []byte, chunk *uint256.Int) []byte {
+ treeIndex, subIndex := GetTreeKeyCodeChunkIndices(chunk)
+ return GetTreeKey(address, treeIndex, subIndex)
+}
+
+func GetTreeKeyCodeChunkIndices(chunk *uint256.Int) (*uint256.Int, byte) {
+ chunkOffset := new(uint256.Int).Add(CodeOffset, chunk)
+ treeIndex := new(uint256.Int).Div(chunkOffset, VerkleNodeWidth)
+ subIndexMod := new(uint256.Int).Mod(chunkOffset, VerkleNodeWidth)
+ var subIndex byte
+ if len(subIndexMod) != 0 {
+ subIndex = byte(subIndexMod[0])
+ }
+ return treeIndex, subIndex
+}
+
+func GetTreeKeyCodeChunkWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256.Int) []byte {
+ chunkOffset := new(uint256.Int).Add(CodeOffset, chunk)
+ treeIndex := new(uint256.Int).Div(chunkOffset, VerkleNodeWidth)
+ subIndexMod := new(uint256.Int).Mod(chunkOffset, VerkleNodeWidth)
+ var subIndex byte
+ if len(subIndexMod) != 0 {
+ subIndex = byte(subIndexMod[0])
+ }
+ return GetTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex)
+}
+
+func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte {
+ pos := storageKey.Clone()
+ if storageKey.Cmp(codeStorageDelta) < 0 {
+ pos.Add(HeaderStorageOffset, storageKey)
+ } else {
+ pos.Add(MainStorageOffset, storageKey)
+ }
+ treeIndex := new(uint256.Int).Div(pos, VerkleNodeWidth)
+
+ // calculate the sub_index, i.e. the index in the stem tree.
+ // Because the modulus is 256, it's the last byte of treeIndex
+ subIndexMod := new(uint256.Int).Mod(pos, VerkleNodeWidth)
+ var subIndex byte
+ if len(subIndexMod) != 0 {
+ // uint256 is broken into 4 little-endian quads,
+ // each with native endianness. Extract the least
+ // significant byte.
+ subIndex = byte(subIndexMod[0])
+ }
+ return GetTreeKey(address, treeIndex, subIndex)
+}
+
+func PointToHash(evaluated *verkle.Point, suffix byte) []byte {
+ // The output of Byte() is big engian for banderwagon. This
+ // introduces an imbalance in the tree, because hashes are
+ // elements of a 253-bit field. This means more than half the
+ // tree would be empty. To avoid this problem, use a little
+ // endian commitment and chop the MSB.
+ retb := evaluated.Bytes()
+ for i := 0; i < 16; i++ {
+ retb[31-i], retb[i] = retb[i], retb[31-i]
+ }
+ retb[31] = suffix
+ return retb[:]
+}
+
+func GetTreeKeyWithEvaluatedAddess(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte {
+ var poly [5]fr.Element
+
+ poly[0].SetZero()
+ poly[1].SetZero()
+ poly[2].SetZero()
+
+ // little-endian, 32-byte aligned treeIndex
+ var index [32]byte
+ for i := 0; i < len(treeIndex); i++ {
+ binary.LittleEndian.PutUint64(index[i*8:(i+1)*8], treeIndex[i])
+ }
+ verkle.FromLEBytes(&poly[3], index[:16])
+ verkle.FromLEBytes(&poly[4], index[16:])
+
+ cfg := verkle.GetConfig()
+ ret := cfg.CommitToPoly(poly[:], 0)
+
+ // add the pre-evaluated address
+ ret.Add(ret, evaluated)
+
+ return PointToHash(ret, subIndex)
+}
+
+func EvaluateAddressPoint(address []byte) *verkle.Point {
+ if len(address) < 32 {
+ var aligned [32]byte
+ address = append(aligned[:32-len(address)], address...)
+ }
+ var poly [3]fr.Element
+
+ poly[0].SetZero()
+
+ // 32-byte address, interpreted as two little endian
+ // 16-byte numbers.
+ verkle.FromLEBytes(&poly[1], address[:16])
+ verkle.FromLEBytes(&poly[2], address[16:])
+
+ cfg := verkle.GetConfig()
+ ret := cfg.CommitToPoly(poly[:], 0)
+
+ // add a constant point
+ ret.Add(ret, getTreePolyIndex0Point)
+
+ return ret
+}
+
+func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte {
+ treeIndex, subIndex := GetTreeKeyStorageSlotTreeIndexes(storageKey)
+ return GetTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex)
+}
+
+func GetTreeKeyStorageSlotTreeIndexes(storageKey []byte) (*uint256.Int, byte) {
+ var pos uint256.Int
+ pos.SetBytes(storageKey)
+
+ // If the storage slot is in the header, we need to add the header offset.
+ if pos.Cmp(codeStorageDelta) < 0 {
+ // This addition is always safe; it can't ever overflow since pos.
+
+package trie
+
+import (
+ "crypto/sha256"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/trie/trienode"
+ "github.com/ethereum/go-ethereum/trie/utils"
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+// VerkleTrie is a wrapper around VerkleNode that implements the trie.Trie
+// interface so that Verkle trees can be reused verbatim.
+type VerkleTrie struct {
+ root verkle.VerkleNode
+ db *Database
+ pointCache *utils.PointCache
+ ended bool
+ reader *trieReader
+}
+
+func (vt *VerkleTrie) ToDot() string {
+ return verkle.ToDot(vt.root)
+}
+
+func NewVerkleTrie(root verkle.VerkleNode, db *Database, pointCache *utils.PointCache, ended bool) (*VerkleTrie, error) {
+ comm := root.Commit().Bytes()
+ rootHash := sha256.Sum256(comm[:])
+ reader, err := newTrieReader(rootHash, common.Hash{}, db)
+ if err != nil {
+ return nil, err
+ }
+ return &VerkleTrie{
+ root: root,
+ db: db,
+ pointCache: pointCache,
+ ended: ended,
+ reader: reader,
+ }, nil
+}
+
+func (trie *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) {
+ // NOTE: I use common.Hash{} as the hash, as I expect it to be ignored
+ // since we are using the pathdb. @rjl493456442 please confirm that this
+ // works.
+ return trie.reader.reader.Node(trie.reader.owner, path, common.Hash{})
+}
+
+var errInvalidRootType = errors.New("invalid node type for root")
+
+// GetKey returns the sha3 preimage of a hashed key that was previously used
+// to store a value.
+func (trie *VerkleTrie) GetKey(key []byte) []byte {
+ return key
+}
+
+// Get returns the value for key stored in the trie. The value bytes must
+// not be modified by the caller. If a node was not found in the database, a
+// trie.MissingNodeError is returned.
+func (trie *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+ pointEval := trie.pointCache.GetTreeKeyHeader(addr[:])
+ k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
+ return trie.root.Get(k, trie.FlatdbNodeResolver)
+}
+
+// GetWithHashedKey returns the value, assuming that the key has already
+// been hashed.
+func (trie *VerkleTrie) GetWithHashedKey(key []byte) ([]byte, error) {
+ return trie.root.Get(key, trie.FlatdbNodeResolver)
+}
+
+func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
+ acc := &types.StateAccount{}
+ versionkey := t.pointCache.GetTreeKeyVersionCached(addr[:])
+ var (
+ values [][]byte
+ err error
+ )
+ switch t.root.(type) {
+ case *verkle.InternalNode:
+ values, err = t.root.(*verkle.InternalNode).GetStem(versionkey[:31], t.FlatdbNodeResolver)
+ default:
+ return nil, errInvalidRootType
+ }
+ if err != nil {
+ return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
+ }
+
+ if values == nil {
+ return nil, nil
+ }
+ if len(values[utils.NonceLeafKey]) > 0 {
+ acc.Nonce = binary.LittleEndian.Uint64(values[utils.NonceLeafKey])
+ }
+
+ var balance [32]byte
+ copy(balance[:], values[utils.BalanceLeafKey])
+ for i := 0; i < len(balance)/2; i++ {
+ balance[len(balance)-i-1], balance[i] = balance[i], balance[len(balance)-i-1]
+ }
+ acc.Balance = new(big.Int).SetBytes(balance[:])
+ acc.CodeHash = values[utils.CodeKeccakLeafKey]
+
+ return acc, nil
+}
+
+var zero [32]byte
+
+func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error {
+ var (
+ err error
+ nonce, balance [32]byte
+ values = make([][]byte, verkle.NodeWidth)
+ stem = t.pointCache.GetTreeKeyVersionCached(addr[:])
+ )
+
+ // Only evaluate the polynomial once
+ values[utils.VersionLeafKey] = zero[:]
+ values[utils.NonceLeafKey] = nonce[:]
+ values[utils.BalanceLeafKey] = balance[:]
+ values[utils.CodeKeccakLeafKey] = acc.CodeHash[:]
+
+ binary.LittleEndian.PutUint64(nonce[:], acc.Nonce)
+ bbytes := acc.Balance.Bytes()
+ if len(bbytes) > 0 {
+ for i, b := range bbytes {
+ balance[len(bbytes)-i-1] = b
+ }
+ }
+
+ switch root := t.root.(type) {
+ case *verkle.InternalNode:
+ err = root.InsertStem(stem, values, t.FlatdbNodeResolver)
+ default:
+ return errInvalidRootType
+ }
+ if err != nil {
+ return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err)
+ }
+ // TODO figure out if the code size needs to be updated, too
+
+ return nil
+}
+
+func (trie *VerkleTrie) UpdateStem(key []byte, values [][]byte) error {
+ switch root := trie.root.(type) {
+ case *verkle.InternalNode:
+ return root.InsertStem(key, values, trie.FlatdbNodeResolver)
+ default:
+ panic("invalid root type")
+ }
+}
+
+// Update associates key with value in the trie. If value has length zero, any
+// existing value is deleted from the trie. The value bytes must not be modified
+// by the caller while they are stored in the trie. If a node was not found in the
+// database, a trie.MissingNodeError is returned.
+func (trie *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error {
+ k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(trie.pointCache.GetTreeKeyHeader(address[:]), key)
+ var v [32]byte
+ if len(value) >= 32 {
+ copy(v[:], value[:32])
+ } else {
+ copy(v[32-len(value):], value[:])
+ }
+ return trie.root.Insert(k, v[:], trie.FlatdbNodeResolver)
+}
+
+func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
+ var (
+ err error
+ values = make([][]byte, verkle.NodeWidth)
+ stem = t.pointCache.GetTreeKeyVersionCached(addr[:])
+ )
+
+ for i := 0; i < verkle.NodeWidth; i++ {
+ values[i] = zero[:]
+ }
+
+ switch root := t.root.(type) {
+ case *verkle.InternalNode:
+ err = root.InsertStem(stem, values, t.FlatdbNodeResolver)
+ default:
+ return errInvalidRootType
+ }
+ if err != nil {
+ return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
+ }
+ // TODO figure out if the code size needs to be updated, too
+
+ return nil
+}
+
+// Delete removes any existing value for key from the trie. If a node was not
+// found in the database, a trie.MissingNodeError is returned.
+func (trie *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
+ pointEval := trie.pointCache.GetTreeKeyHeader(addr[:])
+ k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
+ var zero [32]byte
+ return trie.root.Insert(k, zero[:], trie.FlatdbNodeResolver)
+}
+
+// Hash returns the root hash of the trie. It does not write to the database and
+// can be used even if the trie doesn't have one.
+func (trie *VerkleTrie) Hash() common.Hash {
+ return trie.root.Commit().Bytes()
+}
+
+func nodeToDBKey(n verkle.VerkleNode) []byte {
+ ret := n.Commitment().Bytes()
+ return ret[:]
+}
+
+// Commit writes all nodes to the trie's memory database, tracking the internal
+// and external (for account tries) references.
+func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
+ root, ok := trie.root.(*verkle.InternalNode)
+ if !ok {
+ return common.Hash{}, nil, errors.New("unexpected root node type")
+ }
+ nodes, err := root.BatchSerialize()
+ if err != nil {
+ return common.Hash{}, nil, fmt.Errorf("serializing tree nodes: %s", err)
+ }
+
+ nodeset := trienode.NewNodeSet(common.Hash{})
+ for _, node := range nodes {
+ comm := node.Node.Commitment().Bytes()
+ hash := sha256.Sum256(comm[:])
+ nodeset.AddNode(node.Path, trienode.New(common.BytesToHash(hash[:]), node.SerializedBytes))
+ }
+
+ // Serialize root commitment form
+ rootH := root.Hash().BytesLE()
+ return common.BytesToHash(rootH[:]), nodeset, nil
+}
+
+// NodeIterator returns an iterator that returns nodes of the trie. Iteration
+// starts at the key after the given start key.
+func (trie *VerkleTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
+ return newVerkleNodeIterator(trie, nil)
+}
+
+// Prove constructs a Merkle proof for key. The result contains all encoded nodes
+// on the path to the value at key. The value itself is also included in the last
+// node and can be retrieved by verifying the proof.
+//
+// If the trie does not contain a value for key, the returned proof contains all
+// nodes of the longest existing prefix of the key (at least the root), ending
+// with the node that proves the absence of the key.
+func (trie *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
+ panic("not implemented")
+}
+
+func (trie *VerkleTrie) Copy() *VerkleTrie {
+ return &VerkleTrie{
+ root: trie.root.Copy(),
+ db: trie.db,
+ pointCache: trie.pointCache,
+ reader: trie.reader,
+ }
+}
+
+func (trie *VerkleTrie) IsVerkle() bool {
+ return true
+}
+
+// ChunkedCode represents a sequence of 32-bytes chunks of code (31 bytes of which
+// are actual code, and 1 byte is the pushdata offset).
+type ChunkedCode []byte
+
+// Copy the values here so as to avoid an import cycle
+const (
+ PUSH1 = byte(0x60)
+ PUSH3 = byte(0x62)
+ PUSH4 = byte(0x63)
+ PUSH7 = byte(0x66)
+ PUSH21 = byte(0x74)
+ PUSH30 = byte(0x7d)
+ PUSH32 = byte(0x7f)
+)
+
+// ChunkifyCode generates the chunked version of an array representing EVM bytecode
+func ChunkifyCode(code []byte) ChunkedCode {
+ var (
+ chunkOffset = 0 // offset in the chunk
+ chunkCount = len(code) / 31
+ codeOffset = 0 // offset in the code
+ )
+ if len(code)%31 != 0 {
+ chunkCount++
+ }
+ chunks := make([]byte, chunkCount*32)
+ for i := 0; i < chunkCount; i++ {
+ // number of bytes to copy, 31 unless
+ // the end of the code has been reached.
+ end := 31 * (i + 1)
+ if len(code) < end {
+ end = len(code)
+ }
+
+ // Copy the code itself
+ copy(chunks[i*32+1:], code[31*i:end])
+
+ // chunk offset = taken from the
+ // last chunk.
+ if chunkOffset > 31 {
+ // skip offset calculation if push
+ // data covers the whole chunk
+ chunks[i*32] = 31
+ chunkOffset = 1
+ continue
+ }
+ chunks[32*i] = byte(chunkOffset)
+ chunkOffset = 0
+
+ // Check each instruction and update the offset
+ // it should be 0 unless a PUSHn overflows.
+ for ; codeOffset < end; codeOffset++ {
+ if code[codeOffset] >= PUSH1 && code[codeOffset] <= PUSH32 {
+ codeOffset += int(code[codeOffset] - PUSH1 + 1)
+ if codeOffset+1 >= 31*(i+1) {
+ codeOffset++
+ chunkOffset = codeOffset - 31*(i+1)
+ break
+ }
+ }
+ }
+ }
+
+ return chunks
+}
+
+func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
+ var (
+ chunks = ChunkifyCode(code)
+ values [][]byte
+ key []byte
+ err error
+ )
+ for i, chunknr := 0, uint64(0); i < len(chunks); i, chunknr = i+32, chunknr+1 {
+ groupOffset := (chunknr + 128) % 256
+ if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
+ values = make([][]byte, verkle.NodeWidth)
+ key = utils.GetTreeKeyCodeChunkWithEvaluatedAddress(t.pointCache.GetTreeKeyHeader(addr[:]), uint256.NewInt(chunknr))
+ }
+ values[groupOffset] = chunks[i : i+32]
+
+ // Reuse the calculated key to also update the code size.
+ if i == 0 {
+ cs := make([]byte, 32)
+ binary.LittleEndian.PutUint64(cs, uint64(len(code)))
+ values[utils.CodeSizeLeafKey] = cs
+ }
+
+ if groupOffset == 255 || len(chunks)-i <= 32 {
+ err = t.UpdateStem(key[:31], values)
+
+ if err != nil {
+ return fmt.Errorf("UpdateContractCode (addr=%x) error: %w", addr[:], err)
+ }
+ }
+ }
+ return nil
+}
diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go
new file mode 100644
index 0000000000..c5f59a0f59
--- /dev/null
+++ b/trie/verkle_iterator.go
@@ -0,0 +1,218 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package trie
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+
+ "github.com/gballet/go-verkle"
+)
+
+type verkleNodeIteratorState struct {
+ Node verkle.VerkleNode
+ Index int
+}
+
+type verkleNodeIterator struct {
+ trie *VerkleTrie
+ current verkle.VerkleNode
+ lastErr error
+
+ stack []verkleNodeIteratorState
+}
+
+func newVerkleNodeIterator(trie *VerkleTrie, start []byte) (NodeIterator, error) {
+ if trie.Hash() == zero {
+ return new(nodeIterator), nil
+ }
+ it := &verkleNodeIterator{trie: trie, current: trie.root}
+ // it.err = it.seek(start)
+ return it, nil
+}
+
+// Next moves the iterator to the next node. If the parameter is false, any child
+// nodes will be skipped.
+func (it *verkleNodeIterator) Next(descend bool) bool {
+ if it.lastErr == errIteratorEnd {
+ it.lastErr = errIteratorEnd
+ return false
+ }
+
+ if len(it.stack) == 0 {
+ it.stack = append(it.stack, verkleNodeIteratorState{Node: it.trie.root, Index: 0})
+ it.current = it.trie.root
+
+ return true
+ }
+
+ switch node := it.current.(type) {
+ case *verkle.InternalNode:
+ context := &it.stack[len(it.stack)-1]
+
+ // Look for the next non-empty child
+ children := node.Children()
+ for ; context.Index < len(children); context.Index++ {
+ if _, ok := children[context.Index].(verkle.Empty); !ok {
+ it.stack = append(it.stack, verkleNodeIteratorState{Node: children[context.Index], Index: 0})
+ it.current = children[context.Index]
+ return it.Next(descend)
+ }
+ }
+
+ // Reached the end of this node, go back to the parent, if
+ // this isn't root.
+ if len(it.stack) == 1 {
+ it.lastErr = errIteratorEnd
+ return false
+ }
+ it.stack = it.stack[:len(it.stack)-1]
+ it.current = it.stack[len(it.stack)-1].Node
+ it.stack[len(it.stack)-1].Index++
+ return it.Next(descend)
+ case *verkle.LeafNode:
+ // Look for the next non-empty value
+ for i := it.stack[len(it.stack)-1].Index; i < 256; i++ {
+ if node.Value(i) != nil {
+ it.stack[len(it.stack)-1].Index = i + 1
+ return true
+ }
+ }
+
+ // go back to parent to get the next leaf
+ it.stack = it.stack[:len(it.stack)-1]
+ it.current = it.stack[len(it.stack)-1].Node
+ it.stack[len(it.stack)-1].Index++
+ return it.Next(descend)
+ case *verkle.HashedNode:
+ // resolve the node
+ data, err := it.trie.db.diskdb.Get(nodeToDBKey(node))
+ if err != nil {
+ panic(err)
+ }
+ it.current, err = verkle.ParseNode(data, byte(len(it.stack)-1))
+ if err != nil {
+ panic(err)
+ }
+
+ // update the stack and parent with the resolved node
+ it.stack[len(it.stack)-1].Node = it.current
+ parent := &it.stack[len(it.stack)-2]
+ parent.Node.(*verkle.InternalNode).SetChild(parent.Index, it.current)
+ return true
+ default:
+ panic("invalid node type")
+ }
+}
+
+// Error returns the error status of the iterator.
+func (it *verkleNodeIterator) Error() error {
+ if it.lastErr == errIteratorEnd {
+ return nil
+ }
+ return it.lastErr
+}
+
+// Hash returns the hash of the current node.
+func (it *verkleNodeIterator) Hash() common.Hash {
+ return it.current.Commit().Bytes()
+}
+
+// Parent returns the hash of the parent of the current node. The hash may be the one
+// grandparent if the immediate parent is an internal node with no hash.
+func (it *verkleNodeIterator) Parent() common.Hash {
+ return it.stack[len(it.stack)-1].Node.Commit().Bytes()
+}
+
+// Path returns the hex-encoded path to the current node.
+// Callers must not retain references to the return value after calling Next.
+// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
+func (it *verkleNodeIterator) Path() []byte {
+ if it.Leaf() {
+ return it.LeafKey()
+ }
+ var path []byte
+ for i, state := range it.stack {
+ // skip the last byte
+ if i <= len(it.stack)-1 {
+ break
+ }
+ path = append(path, byte(state.Index))
+ }
+ return path
+}
+
+func (it *verkleNodeIterator) NodeBlob() []byte {
+ panic("not completely implemented")
+}
+
+// Leaf returns true iff the current node is a leaf node.
+func (it *verkleNodeIterator) Leaf() bool {
+ _, ok := it.current.(*verkle.LeafNode)
+ return ok
+}
+
+// LeafKey returns the key of the leaf. The method panics if the iterator is not
+// positioned at a leaf. Callers must not retain references to the value after
+// calling Next.
+func (it *verkleNodeIterator) LeafKey() []byte {
+ leaf, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("Leaf() called on an verkle node iterator not at a leaf location")
+ }
+
+ return leaf.Key(it.stack[len(it.stack)-1].Index - 1)
+}
+
+// LeafBlob returns the content of the leaf. The method panics if the iterator
+// is not positioned at a leaf. Callers must not retain references to the value
+// after calling Next.
+func (it *verkleNodeIterator) LeafBlob() []byte {
+ leaf, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("LeafBlob() called on an verkle node iterator not at a leaf location")
+ }
+
+ return leaf.Value(it.stack[len(it.stack)-1].Index - 1)
+}
+
+// LeafProof returns the Merkle proof of the leaf. The method panics if the
+// iterator is not positioned at a leaf. Callers must not retain references
+// to the value after calling Next.
+func (it *verkleNodeIterator) LeafProof() [][]byte {
+ _, ok := it.current.(*verkle.LeafNode)
+ if !ok {
+ panic("LeafProof() called on an verkle node iterator not at a leaf location")
+ }
+
+ // return it.trie.Prove(leaf.Key())
+ panic("not completely implemented")
+}
+
+// AddResolver sets an intermediate database to use for looking up trie nodes
+// before reaching into the real persistent layer.
+//
+// This is not required for normal operation, rather is an optimization for
+// cases where trie nodes can be recovered from some external mechanism without
+// reading from disk. In those cases, this resolver allows short circuiting
+// accesses and returning them from memory.
+//
+// Before adding a similar mechanism to any other place in Geth, consider
+// making trie.Database an interface and wrapping at that level. It's a huge
+// refactor, but it could be worth it if another occurrence arises.
+func (it *verkleNodeIterator) AddResolver(NodeResolver) {
+ // Not implemented, but should not panic
+}