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 01/10] 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 5663963e3cf..b4afe0b4c67 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 6f802716c5e..3d34a9e2d6a 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 64134825116..048aac2af95 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 aa79889e8c1..420b063d8ba 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 db226c73d82..e80a18b7c30 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 1045815fab9..d438808e8f2 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 fac88ff373b..f8d15585058 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 9467c8f72ee..ada643596ce 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 49010303111..b80abda46f7 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 6017c9f77e8..3169a473a74 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 1e59f0908f3..1166b2acbba 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 00000000000..17fdf1ade34
--- /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 00000000000..c5f59a0f593
--- /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
+}
From 9a95be4ba18fb164fcd30c5c686926854a1e86f4 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 9 Oct 2023 15:42:10 +0200
Subject: [PATCH 02/10] review feedback + root node resolution in OpenTrie
---
core/genesis.go | 6 ++++--
core/state/database.go | 15 ++++++++++++++-
trie/verkle.go | 1 +
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/core/genesis.go b/core/genesis.go
index d438808e8f2..60c2f9a8bc3 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -421,13 +421,15 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
}
}
-func (g *Genesis) isVerkle() bool {
+// IsVerkle indicates whether the state is already stored in a verkle
+// tree at genesis time.
+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(g.isVerkle())
+ root, err := g.Alloc.hash(g.IsVerkle())
if err != nil {
panic(err)
}
diff --git a/core/state/database.go b/core/state/database.go
index ada643596ce..8f9733edf9f 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/utils"
+ "github.com/gballet/go-verkle"
)
const (
@@ -172,7 +173,19 @@ 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)
+ reader, err := db.triedb.Reader(root)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get node reader in OpenTrie: %w", err)
+ }
+ verklerootbytes, err := reader.Node(common.Hash{}, nil, common.Hash{})
+ if err != nil {
+ return nil, fmt.Errorf("failed to get serialized root node in OpenTrie: %w", err)
+ }
+ verkleroot, err := verkle.ParseNode(verklerootbytes, 0)
+ if err != nil {
+ return nil, fmt.Errorf("failed to deserialize root node in OpenTrie: %w", err)
+ }
+ return trie.NewVerkleTrie(verkleroot, db.triedb, utils.NewPointCache(), true)
}
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
diff --git a/trie/verkle.go b/trie/verkle.go
index c7ba7cbfdc5..c10dee3f3d1 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -284,6 +284,7 @@ func (trie *VerkleTrie) Copy() *VerkleTrie {
}
}
+// IsVerkle indicates if the trie is a Verkle trie.
func (trie *VerkleTrie) IsVerkle() bool {
return true
}
From 30daf0ac274c36e1ae7866f79b0c68e6c05658ef Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 9 Oct 2023 15:52:16 +0200
Subject: [PATCH 03/10] more review feedback
---
trie/database.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/trie/database.go b/trie/database.go
index 1166b2acbba..321b4f8955b 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -320,6 +320,7 @@ func (db *Database) SetBufferSize(size int) error {
return pdb.SetBufferSize(size)
}
+// IsVerkle returns the indicator if the database is holding a verkle tree.
func (db *Database) IsVerkle() bool {
return db.config.IsVerkle
}
From 6bd99818e8f3d0dfff353423a7754468a9beb765 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 9 Oct 2023 19:50:02 +0200
Subject: [PATCH 04/10] add a trie parameter to OpenStorageTrie
---
core/state/database.go | 10 ++++++++--
core/state/iterator.go | 2 +-
core/state/state_object.go | 2 +-
core/state/statedb.go | 2 +-
core/state/trie_prefetcher.go | 3 ++-
les/server_requests.go | 2 +-
light/trie.go | 2 +-
7 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/core/state/database.go b/core/state/database.go
index 8f9733edf9f..986e6f39b79 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -46,7 +46,7 @@ type Database interface {
OpenTrie(root common.Hash) (Trie, error)
// OpenStorageTrie opens the storage trie of an account.
- OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error)
+ OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
// CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie
@@ -195,7 +195,13 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
}
// OpenStorageTrie opens the storage trie of an account.
-func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
+func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
+ // In the verkle case, there is only one tree. But the two-tree structure
+ // is hardcoded in the codebase. So we need to return the same trie in this
+ // case.
+ if db.triedb.IsVerkle() {
+ return self, nil
+ }
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
if err != nil {
return nil, err
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 683efd73de7..dc84ce689be 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -123,7 +123,7 @@ func (it *nodeIterator) step() error {
address := common.BytesToAddress(preimage)
// Traverse the storage slots belong to the account
- dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root)
+ dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root, it.state.trie)
if err != nil {
return err
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index d42d2c34d87..fc66b48114b 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -145,7 +145,7 @@ func (s *stateObject) getTrie() (Trie, error) {
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
}
if s.trie == nil {
- tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
+ tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root, s.db.trie)
if err != nil {
return nil, err
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 195e463c28f..674227857c1 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -998,7 +998,7 @@ func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (boo
// employed when the associated state snapshot is not available. It iterates the
// storage slots along with all internal trie nodes via trie directly.
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
- tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root)
+ tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
if err != nil {
return false, 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
}
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index 772c698dd0e..5a1f9aa080a 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -305,7 +305,8 @@ func (sf *subfetcher) loop() {
}
sf.trie = trie
} else {
- trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root)
+ // the trie argument can be nil as verkle doesn't support prefetching
+ trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
if err != nil {
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
return
diff --git a/les/server_requests.go b/les/server_requests.go
index 9a249f04c92..cc5b6017139 100644
--- a/les/server_requests.go
+++ b/les/server_requests.go
@@ -430,7 +430,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
p.bumpInvalid()
continue
}
- trie, err = statedb.OpenStorageTrie(root, address, account.Root)
+ trie, err = statedb.OpenStorageTrie(root, address, account.Root, nil)
if trie == nil || err != nil {
p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", address, "root", account.Root, "err", err)
continue
diff --git a/light/trie.go b/light/trie.go
index 1847f1e71b3..1d93bdf4159 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -55,7 +55,7 @@ func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: db.id}, nil
}
-func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (state.Trie, error) {
+func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ state.Trie) (state.Trie, error) {
return &odrTrie{db: db, id: StorageTrieID(db.id, address, root)}, nil
}
From a02c50cd97cd87ae4a420f61e823bc386a201e97 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 19 Oct 2023 11:06:43 +0200
Subject: [PATCH 05/10] review feedback + add test
---
cmd/geth/chaincmd.go | 3 +--
core/genesis_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++
core/state/database.go | 22 ++++++++++++-------
trie/verkle.go | 19 ++++++-----------
4 files changed, 70 insertions(+), 22 deletions(-)
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index b4afe0b4c67..b65827f5bc6 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -20,7 +20,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "math/big"
"os"
"runtime"
"strconv"
@@ -212,7 +211,7 @@ func initGenesis(ctx *cli.Context) error {
}
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.Config.IsVerkle(big.NewInt(0), genesis.Timestamp))
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
defer triedb.Close()
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
diff --git a/core/genesis_test.go b/core/genesis_test.go
index f8d15585058..9f2c527cc02 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "bytes"
"encoding/json"
"math/big"
"reflect"
@@ -261,3 +262,50 @@ func newDbConfig(scheme string) *trie.Config {
}
return &trie.Config{PathDB: pathdb.Defaults}
}
+
+func TestVerkleGenesisCommit(t *testing.T) {
+ var verkleTime uint64 = 0
+ verkleConfig := ¶ms.ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
+ BerlinBlock: big.NewInt(0),
+ LondonBlock: big.NewInt(0),
+ ArrowGlacierBlock: big.NewInt(0),
+ GrayGlacierBlock: big.NewInt(0),
+ MergeNetsplitBlock: nil,
+ ShanghaiTime: nil,
+ CancunTime: nil,
+ PragueTime: nil,
+ VerkleTime: &verkleTime,
+ TerminalTotalDifficulty: nil,
+ TerminalTotalDifficultyPassed: true,
+ Ethash: nil,
+ Clique: nil,
+ }
+
+ genesis := &Genesis{
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ Config: verkleConfig,
+ Timestamp: verkleTime,
+ // difficulty is nil
+ Alloc: GenesisAlloc{
+ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
+ },
+ }
+
+ expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
+ got := genesis.ToBlock().Root().Bytes()
+ if !bytes.Equal(got, expected) {
+ t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
+ }
+}
diff --git a/core/state/database.go b/core/state/database.go
index 986e6f39b79..9f183da0f7f 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -177,15 +177,21 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
if err != nil {
return nil, fmt.Errorf("failed to get node reader in OpenTrie: %w", err)
}
- verklerootbytes, err := reader.Node(common.Hash{}, nil, common.Hash{})
- if err != nil {
- return nil, fmt.Errorf("failed to get serialized root node in OpenTrie: %w", err)
- }
- verkleroot, err := verkle.ParseNode(verklerootbytes, 0)
- if err != nil {
- return nil, fmt.Errorf("failed to deserialize root node in OpenTrie: %w", err)
+
+ var verkleroot verkle.VerkleNode
+ if root != (common.Hash{}) && root != types.EmptyRootHash {
+ verklerootbytes, err := reader.Node(common.Hash{}, nil, common.Hash{})
+ if err != nil {
+ return nil, fmt.Errorf("failed to get serialized root node in OpenTrie: %w", err)
+ }
+ verkleroot, err = verkle.ParseNode(verklerootbytes, 0)
+ if err != nil {
+ return nil, fmt.Errorf("failed to deserialize root node in OpenTrie: %w", err)
+ }
+ } else {
+ verkleroot = verkle.New()
}
- return trie.NewVerkleTrie(verkleroot, db.triedb, utils.NewPointCache(), true)
+ return trie.NewVerkleTrie(root, verkleroot, db.triedb, utils.NewPointCache(), true)
}
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
diff --git a/trie/verkle.go b/trie/verkle.go
index c10dee3f3d1..9e780c70481 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -17,7 +17,6 @@
package trie
import (
- "crypto/sha256"
"encoding/binary"
"errors"
"fmt"
@@ -39,6 +38,7 @@ type VerkleTrie struct {
db *Database
pointCache *utils.PointCache
ended bool
+ rootHash common.Hash
reader *trieReader
}
@@ -46,9 +46,7 @@ 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[:])
+func NewVerkleTrie(rootHash common.Hash, root verkle.VerkleNode, db *Database, pointCache *utils.PointCache, ended bool) (*VerkleTrie, error) {
reader, err := newTrieReader(rootHash, common.Hash{}, db)
if err != nil {
return nil, err
@@ -58,14 +56,12 @@ func NewVerkleTrie(root verkle.VerkleNode, db *Database, pointCache *utils.Point
db: db,
pointCache: pointCache,
ended: ended,
+ rootHash: rootHash,
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{})
}
@@ -248,14 +244,13 @@ func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
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))
+ // hash parameter is not used in pathdb
+ nodeset.AddNode(node.Path, trienode.New(common.Hash{}, node.SerializedBytes))
}
// Serialize root commitment form
- rootH := root.Hash().BytesLE()
- return common.BytesToHash(rootH[:]), nodeset, nil
+ trie.rootHash = trie.Hash()
+ return trie.rootHash, nodeset, nil
}
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
From 40ebb2b82812e0755ac5b7a7d1ddbccfb2b0156e Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 19 Oct 2023 15:45:58 +0200
Subject: [PATCH 06/10] adding some (failing) tests
---
core/genesis_test.go | 33 +++++++++++++++++++++++++--------
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/core/genesis_test.go b/core/genesis_test.go
index 9f2c527cc02..c6f6109964b 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -283,21 +283,21 @@ func TestVerkleGenesisCommit(t *testing.T) {
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: nil,
- ShanghaiTime: nil,
- CancunTime: nil,
- PragueTime: nil,
+ ShanghaiTime: &verkleTime,
+ CancunTime: &verkleTime,
+ PragueTime: &verkleTime,
VerkleTime: &verkleTime,
- TerminalTotalDifficulty: nil,
+ TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
Ethash: nil,
Clique: nil,
}
genesis := &Genesis{
- BaseFee: big.NewInt(params.InitialBaseFee),
- Config: verkleConfig,
- Timestamp: verkleTime,
- // difficulty is nil
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ Config: verkleConfig,
+ Timestamp: verkleTime,
+ Difficulty: big.NewInt(0),
Alloc: GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
@@ -308,4 +308,21 @@ func TestVerkleGenesisCommit(t *testing.T) {
if !bytes.Equal(got, expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
}
+
+ db := rawdb.NewMemoryDatabase()
+ triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
+ block := genesis.MustCommit(db, triedb)
+ if !bytes.Equal(block.Root().Bytes(), expected) {
+ t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
+ }
+
+ // Test that the trie is verkle
+ if !triedb.IsVerkle() {
+ t.Fatalf("expected trie to be verkle")
+ }
+
+ serialized := rawdb.ReadTrieNode(db, common.Hash{}, []byte{}, common.Hash{}, "path")
+ if len(serialized) == 0 {
+ t.Fatal("could not find node")
+ }
}
From 412075ba550e644b4b098d494ddf3dc66fc4a4c1 Mon Sep 17 00:00:00 2001
From: Gary Rong
Date: Fri, 20 Oct 2023 11:37:07 +0800
Subject: [PATCH 07/10] core, trie: fix failed test
---
core/genesis_test.go | 3 +-
trie/trienode/node.go | 2 +-
trie/verkle.go | 82 +++++++++++++++++++++----------------------
3 files changed, 43 insertions(+), 44 deletions(-)
diff --git a/core/genesis_test.go b/core/genesis_test.go
index c6f6109964b..1d85b510caa 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -321,8 +321,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
t.Fatalf("expected trie to be verkle")
}
- serialized := rawdb.ReadTrieNode(db, common.Hash{}, []byte{}, common.Hash{}, "path")
- if len(serialized) == 0 {
+ if !rawdb.ExistsAccountTrieNode(db, nil) {
t.Fatal("could not find node")
}
}
diff --git a/trie/trienode/node.go b/trie/trienode/node.go
index 98d5588b6d3..95315c2e9a4 100644
--- a/trie/trienode/node.go
+++ b/trie/trienode/node.go
@@ -39,7 +39,7 @@ func (n *Node) Size() int {
// IsDeleted returns the indicator if the node is marked as deleted.
func (n *Node) IsDeleted() bool {
- return n.Hash == (common.Hash{})
+ return len(n.Blob) == 0
}
// New constructs a node with provided node information.
diff --git a/trie/verkle.go b/trie/verkle.go
index 9e780c70481..68ace10a683 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -42,8 +42,8 @@ type VerkleTrie struct {
reader *trieReader
}
-func (vt *VerkleTrie) ToDot() string {
- return verkle.ToDot(vt.root)
+func (t *VerkleTrie) ToDot() string {
+ return verkle.ToDot(t.root)
}
func NewVerkleTrie(rootHash common.Hash, root verkle.VerkleNode, db *Database, pointCache *utils.PointCache, ended bool) (*VerkleTrie, error) {
@@ -61,31 +61,31 @@ func NewVerkleTrie(rootHash common.Hash, root verkle.VerkleNode, db *Database, p
}, nil
}
-func (trie *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) {
- return trie.reader.reader.Node(trie.reader.owner, path, common.Hash{})
+func (t *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) {
+ return t.reader.reader.Node(t.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 {
+func (t *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[:])
+// GetStorage 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 (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+ pointEval := t.pointCache.GetTreeKeyHeader(addr[:])
k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
- return trie.root.Get(k, trie.FlatdbNodeResolver)
+ return t.root.Get(k, t.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) GetWithHashedKey(key []byte) ([]byte, error) {
+ return t.root.Get(key, t.FlatdbNodeResolver)
}
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
@@ -161,28 +161,28 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount)
return nil
}
-func (trie *VerkleTrie) UpdateStem(key []byte, values [][]byte) error {
- switch root := trie.root.(type) {
+func (t *VerkleTrie) UpdateStem(key []byte, values [][]byte) error {
+ switch root := t.root.(type) {
case *verkle.InternalNode:
- return root.InsertStem(key, values, trie.FlatdbNodeResolver)
+ return root.InsertStem(key, values, t.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
+// UpdateStorage 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)
+func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error {
+ k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(t.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)
+ return t.root.Insert(k, v[:], t.FlatdbNodeResolver)
}
func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
@@ -210,19 +210,19 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
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[:])
+// DeleteStorage removes any existing value for key from the trie. If a node was
+// not found in the database, a trie.MissingNodeError is returned.
+func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
+ pointEval := t.pointCache.GetTreeKeyHeader(addr[:])
k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
var zero [32]byte
- return trie.root.Insert(k, zero[:], trie.FlatdbNodeResolver)
+ return t.root.Insert(k, zero[:], t.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 (t *VerkleTrie) Hash() common.Hash {
+ return t.root.Commit().Bytes()
}
func nodeToDBKey(n verkle.VerkleNode) []byte {
@@ -232,8 +232,8 @@ func nodeToDBKey(n verkle.VerkleNode) []byte {
// 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)
+func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
+ root, ok := t.root.(*verkle.InternalNode)
if !ok {
return common.Hash{}, nil, errors.New("unexpected root node type")
}
@@ -249,14 +249,14 @@ func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
}
// Serialize root commitment form
- trie.rootHash = trie.Hash()
- return trie.rootHash, nodeset, nil
+ t.rootHash = t.Hash()
+ return t.rootHash, 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)
+func (t *VerkleTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
+ return newVerkleNodeIterator(t, nil)
}
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
@@ -266,21 +266,21 @@ func (trie *VerkleTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
// 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 {
+func (t *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
panic("not implemented")
}
-func (trie *VerkleTrie) Copy() *VerkleTrie {
+func (t *VerkleTrie) Copy() *VerkleTrie {
return &VerkleTrie{
- root: trie.root.Copy(),
- db: trie.db,
- pointCache: trie.pointCache,
- reader: trie.reader,
+ root: t.root.Copy(),
+ db: t.db,
+ pointCache: t.pointCache,
+ reader: t.reader,
}
}
// IsVerkle indicates if the trie is a Verkle trie.
-func (trie *VerkleTrie) IsVerkle() bool {
+func (t *VerkleTrie) IsVerkle() bool {
return true
}
From d326ba15359a9c5642ede6acb5a7f9b5cda9dbad Mon Sep 17 00:00:00 2001
From: rjl493456442
Date: Tue, 31 Oct 2023 18:42:14 +0800
Subject: [PATCH 08/10] core, light, trie: polish code (#303)
* core, light, trie: polish code
* core, trie: address comments
* trie/utils: add benchmark
* trie/utils: fix lint
* core/state: add description
* trie/utils: add function description
* use new InsertValuesAtStem function name
---------
Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
---
core/state/database.go | 58 +++---
core/state/trie_prefetcher.go | 3 +-
core/types/hashes.go | 5 +-
light/odr_test.go | 2 +-
trie/utils/verkle.go | 352 +++++++++++++++++++---------------
trie/utils/verkle_test.go | 139 ++++++++++++++
trie/verkle.go | 273 +++++++++++++-------------
trie/verkle_iterator.go | 218 ---------------------
trie/verkle_test.go | 97 ++++++++++
9 files changed, 599 insertions(+), 548 deletions(-)
create mode 100644 trie/utils/verkle_test.go
delete mode 100644 trie/verkle_iterator.go
create mode 100644 trie/verkle_test.go
diff --git a/core/state/database.go b/core/state/database.go
index 9f183da0f7f..b55f870d906 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
+ "github.com/crate-crypto/go-ipa/banderwagon"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -29,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/utils"
- "github.com/gballet/go-verkle"
)
const (
@@ -38,6 +38,12 @@ const (
// Cache size granted for caching clean code.
codeCacheSize = 64 * 1024 * 1024
+
+ // commitmentSize is the size of commitment stored in cache.
+ commitmentSize = banderwagon.UncompressedSize
+
+ // Cache item granted for caching commitment results.
+ commitmentCacheItems = 64 * 1024 * 1024 / (commitmentSize + common.AddressLength)
)
// Database wraps access to tries and contract code.
@@ -72,11 +78,6 @@ type Trie interface {
// TODO(fjl): remove this when StateTrie is removed
GetKey([]byte) []byte
- // GetStorage 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.
- GetStorage(addr common.Address, key []byte) ([]byte, error)
-
// GetAccount abstracts an account read from the trie. It retrieves the
// account blob from the trie with provided account address and decodes it
// with associated decoding algorithm. If the specified account is not in
@@ -85,27 +86,32 @@ type Trie interface {
// be returned.
GetAccount(address common.Address) (*types.StateAccount, error)
- // UpdateStorage 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.
- UpdateStorage(addr common.Address, key, value []byte) error
+ // GetStorage 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.
+ GetStorage(addr common.Address, key []byte) ([]byte, error)
// UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it
// in the trie with provided address.
UpdateAccount(address common.Address, account *types.StateAccount) error
- // UpdateContractCode abstracts code write to the trie. It is expected
- // to be moved to the stateWriter interface when the latter is ready.
- UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
+ // UpdateStorage 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.
+ UpdateStorage(addr common.Address, key, value []byte) error
+
+ // DeleteAccount abstracts an account deletion from the trie.
+ DeleteAccount(address common.Address) error
// DeleteStorage removes any existing value for key from the trie. If a node
// was not found in the database, a trie.MissingNodeError is returned.
DeleteStorage(addr common.Address, key []byte) error
- // DeleteAccount abstracts an account deletion from the trie.
- DeleteAccount(address common.Address) error
+ // UpdateContractCode abstracts code write to the trie. It is expected
+ // to be moved to the stateWriter interface when the latter is ready.
+ UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
// 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.
@@ -173,25 +179,7 @@ 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() {
- reader, err := db.triedb.Reader(root)
- if err != nil {
- return nil, fmt.Errorf("failed to get node reader in OpenTrie: %w", err)
- }
-
- var verkleroot verkle.VerkleNode
- if root != (common.Hash{}) && root != types.EmptyRootHash {
- verklerootbytes, err := reader.Node(common.Hash{}, nil, common.Hash{})
- if err != nil {
- return nil, fmt.Errorf("failed to get serialized root node in OpenTrie: %w", err)
- }
- verkleroot, err = verkle.ParseNode(verklerootbytes, 0)
- if err != nil {
- return nil, fmt.Errorf("failed to deserialize root node in OpenTrie: %w", err)
- }
- } else {
- verkleroot = verkle.New()
- }
- return trie.NewVerkleTrie(root, verkleroot, db.triedb, utils.NewPointCache(), true)
+ return trie.NewVerkleTrie(root, db.triedb, utils.NewPointCache(commitmentCacheItems))
}
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index 5a1f9aa080a..c2a49417d45 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -305,7 +305,8 @@ func (sf *subfetcher) loop() {
}
sf.trie = trie
} else {
- // the trie argument can be nil as verkle doesn't support prefetching
+ // The trie argument can be nil as verkle doesn't support prefetching
+ // yet. TODO FIX IT(rjl493456442), otherwise code will panic here.
trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
if err != nil {
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
diff --git a/core/types/hashes.go b/core/types/hashes.go
index 3a787aa136f..43e9130fd17 100644
--- a/core/types/hashes.go
+++ b/core/types/hashes.go
@@ -23,7 +23,7 @@ import (
)
var (
- // EmptyRootHash is the known root hash of an empty trie.
+ // EmptyRootHash is the known root hash of an empty merkle trie.
EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// EmptyUncleHash is the known hash of the empty uncle set.
@@ -40,6 +40,9 @@ var (
// EmptyWithdrawalsHash is the known hash of the empty withdrawal set.
EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyVerkleHash is the known hash of an empty verkle trie.
+ EmptyVerkleHash = common.Hash{}
)
// TrieRootHash returns the hash itself if it's non-empty or the predefined
diff --git a/light/odr_test.go b/light/odr_test.go
index c415d73e7ef..de12f9b7ef0 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -89,7 +89,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
t state.Trie
)
if len(req.Id.AccountAddress) > 0 {
- t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root)
+ t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root, nil)
} else {
t, err = odr.serverState.OpenTrie(req.Id.Root)
}
diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go
index 17fdf1ade34..0ed8f83e750 100644
--- a/trie/utils/verkle.go
+++ b/trie/utils/verkle.go
@@ -1,4 +1,4 @@
-// Copyright 2021 go-ethereum Authors
+// Copyright 2023 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
@@ -21,11 +21,15 @@ import (
"sync"
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
+ "github.com/ethereum/go-ethereum/common/lru"
+ "github.com/ethereum/go-ethereum/metrics"
"github.com/gballet/go-verkle"
"github.com/holiman/uint256"
)
const (
+ // The spec of verkle key encoding can be found here.
+ // https://notes.ethereum.org/@vbuterin/verkle_tree_eip#Tree-embedding
VersionLeafKey = 0
BalanceLeafKey = 1
NonceLeafKey = 2
@@ -35,59 +39,81 @@ const (
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
+ verkleNodeWidthLog2 = 8
+ headerStorageOffset = uint256.NewInt(64)
+ mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(verkleNodeWidthLog2))
+ codeOffset = uint256.NewInt(128)
+ verkleNodeWidth = uint256.NewInt(256)
+ codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset)
+
+ index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64]
+
+ // cacheHitGauge is the metric to track how many cache hit occurred.
+ cacheHitGauge = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil)
+
+ // cacheMissGauge is the metric to track how many cache miss occurred.
+ cacheMissGauge = metrics.NewRegisteredGauge("trie/verkle/cache/miss", nil)
)
+func init() {
+ // The byte array is the Marshalled output of the point computed as such:
+ //
+ // var (
+ // config = verkle.GetConfig()
+ // fr verkle.Fr
+ // )
+ // verkle.FromLEBytes(&fr, []byte{2, 64})
+ // point := config.CommitToPoly([]verkle.Fr{fr}, 1)
+ index0Point = new(verkle.Point)
+ err := index0Point.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)
+ }
+}
+
+// PointCache is the LRU cache for storing evaluated address commitment.
type PointCache struct {
- cache map[string]*verkle.Point
- lock sync.RWMutex
+ lru lru.BasicLRU[string, *verkle.Point]
+ lock sync.RWMutex
}
-func NewPointCache() *PointCache {
+// NewPointCache returns the cache with specified size.
+func NewPointCache(maxItems int) *PointCache {
return &PointCache{
- cache: make(map[string]*verkle.Point),
+ lru: lru.NewBasicLRU[string, *verkle.Point](maxItems),
}
}
-func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point {
- pc.lock.RLock()
- point, ok := pc.cache[string(addr)]
- pc.lock.RUnlock()
+// get loads the cached commitment, or nil if it's not existent.
+func (c *PointCache) get(addr string) (*verkle.Point, bool) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
+ return c.lru.Get(addr)
+}
+
+// Get returns the cached commitment for the specified address, or computing
+// it on the flight.
+func (c *PointCache) Get(addr []byte) *verkle.Point {
+ p, ok := c.get(string(addr))
if ok {
- return point
+ cacheHitGauge.Inc(1)
+ return p
}
+ cacheMissGauge.Inc(1)
+ p = evaluateAddressPoint(addr)
- point = EvaluateAddressPoint(addr)
- pc.lock.Lock()
- pc.cache[string(addr)] = point
- pc.lock.Unlock()
- return point
+ c.lock.Lock()
+ c.lru.Add(string(addr), p)
+ c.lock.Unlock()
+ return p
}
-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)
- }
+// GetStem returns the first 31 bytes of the tree key as the tree stem. It only
+// works for the account metadata whose treeIndex is 0.
+func (c *PointCache) GetStem(addr []byte) []byte {
+ p := c.Get(addr)
+ return pointToHash(p, 0)[:31]
}
// GetTreeKey performs both the work of the spec's get_tree_key function, and that
@@ -100,7 +126,6 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
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
@@ -126,48 +151,73 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
ret := cfg.CommitToPoly(poly[:], 0)
// add a constant point corresponding to poly[0]=[2+256*64].
- ret.Add(ret, getTreePolyIndex0Point)
+ ret.Add(ret, index0Point)
- return PointToHash(ret, subIndex)
+ return pointToHash(ret, subIndex)
}
-func GetTreeKeyAccountLeaf(address []byte, leaf byte) []byte {
- return GetTreeKey(address, zero, leaf)
-}
+// GetTreeKeyWithEvaluatedAddress is basically identical to GetTreeKey, the only
+// difference is a part of polynomial is already evaluated.
+//
+// Specifically, poly = [2+256*64, address_le_low, address_le_high] is already
+// evaluated.
+func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte {
+ var poly [5]fr.Element
-func GetTreeKeyVersion(address []byte) []byte {
- return GetTreeKey(address, zero, VersionLeafKey)
+ 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 GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte {
- return GetTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey)
+// VersionKey returns the verkle tree key of the version field for the specified account.
+func VersionKey(address []byte) []byte {
+ return GetTreeKey(address, zero, VersionLeafKey)
}
-func GetTreeKeyBalance(address []byte) []byte {
+// BalanceKey returns the verkle tree key of the balance field for the specified account.
+func BalanceKey(address []byte) []byte {
return GetTreeKey(address, zero, BalanceLeafKey)
}
-func GetTreeKeyNonce(address []byte) []byte {
+// NonceKey returns the verkle tree key of the nonce field for the specified account.
+func NonceKey(address []byte) []byte {
return GetTreeKey(address, zero, NonceLeafKey)
}
-func GetTreeKeyCodeKeccak(address []byte) []byte {
+// CodeKeccakKey returns the verkle tree key of the code keccak field for
+// the specified account.
+func CodeKeccakKey(address []byte) []byte {
return GetTreeKey(address, zero, CodeKeccakLeafKey)
}
-func GetTreeKeyCodeSize(address []byte) []byte {
+// CodeSizeKey returns the verkle tree key of the code size field for the
+// specified account.
+func CodeSizeKey(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)
+func codeChunkIndex(chunk *uint256.Int) (*uint256.Int, byte) {
+ var (
+ 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])
@@ -175,78 +225,109 @@ func GetTreeKeyCodeChunkIndices(chunk *uint256.Int) (*uint256.Int, byte) {
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)
+// CodeChunkKey returns the verkle tree key of the code chunk for the
+// specified account.
+func CodeChunkKey(address []byte, chunk *uint256.Int) []byte {
+ treeIndex, subIndex := codeChunkIndex(chunk)
+ return GetTreeKey(address, 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)
+func storageIndex(bytes []byte) (*uint256.Int, byte) {
+ // If the storage slot is in the header, we need to add the header offset.
+ var key uint256.Int
+ key.SetBytes(bytes)
+ if key.Cmp(codeStorageDelta) < 0 {
+ // This addition is always safe; it can't ever overflow since pos
+
+package utils
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+func TestTreeKey(t *testing.T) {
+ var (
+ address = []byte{0x01}
+ addressEval = evaluateAddressPoint(address)
+ smallIndex = uint256.NewInt(1)
+ largeIndex = uint256.NewInt(10000)
+ smallStorage = []byte{0x1}
+ largeStorage = bytes.Repeat([]byte{0xff}, 16)
+ )
+ if !bytes.Equal(VersionKey(address), VersionKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched version key")
+ }
+ if !bytes.Equal(BalanceKey(address), BalanceKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched balance key")
+ }
+ if !bytes.Equal(NonceKey(address), NonceKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched nonce key")
+ }
+ if !bytes.Equal(CodeKeccakKey(address), CodeKeccakKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched code keccak key")
+ }
+ if !bytes.Equal(CodeSizeKey(address), CodeSizeKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched code size key")
+ }
+ if !bytes.Equal(CodeChunkKey(address, smallIndex), CodeChunkKeyWithEvaluatedAddress(addressEval, smallIndex)) {
+ t.Fatal("Unmatched code chunk key")
+ }
+ if !bytes.Equal(CodeChunkKey(address, largeIndex), CodeChunkKeyWithEvaluatedAddress(addressEval, largeIndex)) {
+ t.Fatal("Unmatched code chunk key")
+ }
+ if !bytes.Equal(StorageSlotKey(address, smallStorage), StorageSlotKeyWithEvaluatedAddress(addressEval, smallStorage)) {
+ t.Fatal("Unmatched storage slot key")
+ }
+ if !bytes.Equal(StorageSlotKey(address, largeStorage), StorageSlotKeyWithEvaluatedAddress(addressEval, largeStorage)) {
+ t.Fatal("Unmatched storage slot key")
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkTreeKey
+// BenchmarkTreeKey-8 398731 2961 ns/op 32 B/op 1 allocs/op
+func BenchmarkTreeKey(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ BalanceKey([]byte{0x01})
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkTreeKeyWithEvaluation
+// BenchmarkTreeKeyWithEvaluation-8 513855 2324 ns/op 32 B/op 1 allocs/op
+func BenchmarkTreeKeyWithEvaluation(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ addr := []byte{0x01}
+ eval := evaluateAddressPoint(addr)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ BalanceKeyWithEvaluatedAddress(eval)
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkStorageKey
+// BenchmarkStorageKey-8 230516 4584 ns/op 96 B/op 3 allocs/op
+func BenchmarkStorageKey(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ StorageSlotKey([]byte{0x01}, bytes.Repeat([]byte{0xff}, 32))
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkStorageKeyWithEvaluation
+// BenchmarkStorageKeyWithEvaluation-8 320125 3753 ns/op 96 B/op 3 allocs/op
+func BenchmarkStorageKeyWithEvaluation(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ addr := []byte{0x01}
+ eval := evaluateAddressPoint(addr)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ StorageSlotKeyWithEvaluatedAddress(eval, bytes.Repeat([]byte{0xff}, 32))
+ }
+}
diff --git a/trie/verkle.go b/trie/verkle.go
index 68ace10a683..89e2e534089 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -1,4 +1,4 @@
-// Copyright 2021 go-ethereum Authors
+// Copyright 2023 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
@@ -31,207 +31,194 @@ import (
"github.com/holiman/uint256"
)
+var (
+ zero [32]byte
+ errInvalidRootType = errors.New("invalid node type for root")
+)
+
// 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
- rootHash common.Hash
- reader *trieReader
+ root verkle.VerkleNode
+ db *Database
+ cache *utils.PointCache
+ reader *trieReader
}
-func (t *VerkleTrie) ToDot() string {
- return verkle.ToDot(t.root)
-}
-
-func NewVerkleTrie(rootHash common.Hash, root verkle.VerkleNode, db *Database, pointCache *utils.PointCache, ended bool) (*VerkleTrie, error) {
- reader, err := newTrieReader(rootHash, common.Hash{}, db)
+// NewVerkleTrie constructs a verkle tree based on the specified root hash.
+func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*VerkleTrie, error) {
+ reader, err := newTrieReader(root, common.Hash{}, db)
if err != nil {
return nil, err
}
+ // Parse the root verkle node if it's not empty.
+ node := verkle.New()
+ if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
+ blob, err := reader.node(nil, common.Hash{})
+ if err != nil {
+ return nil, err
+ }
+ node, err = verkle.ParseNode(blob, 0)
+ if err != nil {
+ return nil, err
+ }
+ }
return &VerkleTrie{
- root: root,
- db: db,
- pointCache: pointCache,
- ended: ended,
- rootHash: rootHash,
- reader: reader,
+ root: node,
+ db: db,
+ cache: cache,
+ reader: reader,
}, nil
}
-func (t *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) {
- return t.reader.reader.Node(t.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 (t *VerkleTrie) GetKey(key []byte) []byte {
return key
}
-// GetStorage 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 (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
- pointEval := t.pointCache.GetTreeKeyHeader(addr[:])
- k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
- return t.root.Get(k, t.FlatdbNodeResolver)
-}
-
-// GetWithHashedKey returns the value, assuming that the key has already
-// been hashed.
-func (t *VerkleTrie) GetWithHashedKey(key []byte) ([]byte, error) {
- return t.root.Get(key, t.FlatdbNodeResolver)
-}
-
+// GetAccount implements state.Trie, retrieving the account with the specified
+// account address. If the specified account is not in the verkle tree, nil will
+// be returned. If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
- acc := &types.StateAccount{}
- versionkey := t.pointCache.GetTreeKeyVersionCached(addr[:])
var (
+ acc = &types.StateAccount{}
values [][]byte
err error
)
- switch t.root.(type) {
+ switch n := t.root.(type) {
case *verkle.InternalNode:
- values, err = t.root.(*verkle.InternalNode).GetStem(versionkey[:31], t.FlatdbNodeResolver)
+ values, err = n.GetValuesAtStem(t.cache.GetStem(addr[:]), t.nodeResolver)
+ if err != nil {
+ return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
+ }
default:
return nil, errInvalidRootType
}
- if err != nil {
- return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
- }
-
if values == nil {
return nil, nil
}
+ // Decode nonce in little-endian
if len(values[utils.NonceLeafKey]) > 0 {
acc.Nonce = binary.LittleEndian.Uint64(values[utils.NonceLeafKey])
}
-
+ // Decode balance in little-endian
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[:])
+
+ // Decode codehash
acc.CodeHash = values[utils.CodeKeccakLeafKey]
+ // TODO account.Root is leave as empty. How should we handle the legacy account?
return acc, nil
}
-var zero [32]byte
+// GetStorage implements state.Trie, retrieving the storage slot with the specified
+// account address and storage key. If the specified slot is not in the verkle tree,
+// nil will be returned. If the tree is corrupted, an error will be returned.
+func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
+ val, err := t.root.Get(k, t.nodeResolver)
+ if err != nil {
+ return nil, err
+ }
+ return common.TrimLeftZeroes(val), nil
+}
+// UpdateAccount implements state.Trie, writing the provided account into the tree.
+// If the tree is corrupted, an error will be returned.
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[:]
+ // Encode nonce in little-endian
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
+ values[utils.NonceLeafKey] = nonce[:]
+
+ // Encode balance in little-endian
+ bytes := acc.Balance.Bytes()
+ if len(bytes) > 0 {
+ for i, b := range bytes {
+ balance[len(bytes)-i-1] = b
}
}
+ values[utils.BalanceLeafKey] = balance[:]
- switch root := t.root.(type) {
+ switch n := t.root.(type) {
case *verkle.InternalNode:
- err = root.InsertStem(stem, values, t.FlatdbNodeResolver)
+ err = n.InsertValuesAtStem(t.cache.GetStem(addr[:]), values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err)
+ }
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 (t *VerkleTrie) UpdateStem(key []byte, values [][]byte) error {
- switch root := t.root.(type) {
- case *verkle.InternalNode:
- return root.InsertStem(key, values, t.FlatdbNodeResolver)
- default:
- panic("invalid root type")
- }
-}
-
-// UpdateStorage 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.
+// UpdateStorage implements state.Trie, writing the provided storage slot into
+// the tree. If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error {
- k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(t.pointCache.GetTreeKeyHeader(address[:]), key)
+ // Left padding the slot value to 32 bytes.
var v [32]byte
if len(value) >= 32 {
copy(v[:], value[:32])
} else {
copy(v[32-len(value):], value[:])
}
- return t.root.Insert(k, v[:], t.FlatdbNodeResolver)
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(address.Bytes()), key)
+ return t.root.Insert(k, v[:], t.nodeResolver)
}
+// DeleteAccount implements state.Trie, deleting the specified account from the
+// trie. If the account was not existent in the trie, no error will be returned.
+// If the trie is corrupted, an error will be returned.
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) {
+ switch n := t.root.(type) {
case *verkle.InternalNode:
- err = root.InsertStem(stem, values, t.FlatdbNodeResolver)
+ err = n.InsertValuesAtStem(t.cache.GetStem(addr.Bytes()), values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
+ }
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
}
-// DeleteStorage removes any existing value for key from the trie. If a node was
-// not found in the database, a trie.MissingNodeError is returned.
+// DeleteStorage implements state.Trie, deleting the specified storage slot from
+// the trie. If the storage slot was not existent in the trie, no error will be
+// returned. If the trie is corrupted, an error will be returned.
func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
- pointEval := t.pointCache.GetTreeKeyHeader(addr[:])
- k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key)
var zero [32]byte
- return t.root.Insert(k, zero[:], t.FlatdbNodeResolver)
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
+ return t.root.Insert(k, zero[:], t.nodeResolver)
}
-// 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.
+// Hash returns the root hash of the tree. It does not write to the database and
+// can be used even if the tree doesn't have one.
func (t *VerkleTrie) Hash() common.Hash {
return t.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.
+// Commit writes all nodes to the tree's memory database.
func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
root, ok := t.root.(*verkle.InternalNode)
if !ok {
@@ -241,41 +228,43 @@ func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
if err != nil {
return common.Hash{}, nil, fmt.Errorf("serializing tree nodes: %s", err)
}
-
nodeset := trienode.NewNodeSet(common.Hash{})
for _, node := range nodes {
// hash parameter is not used in pathdb
nodeset.AddNode(node.Path, trienode.New(common.Hash{}, node.SerializedBytes))
}
-
// Serialize root commitment form
- t.rootHash = t.Hash()
- return t.rootHash, nodeset, nil
+ return t.Hash(), nodeset, nil
}
-// NodeIterator returns an iterator that returns nodes of the trie. Iteration
-// starts at the key after the given start key.
+// NodeIterator implements state.Trie, returning an iterator that returns
+// nodes of the trie. Iteration starts at the key after the given start key.
+//
+// TODO(gballet, rjl493456442) implement it.
func (t *VerkleTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
- return newVerkleNodeIterator(t, nil)
+ panic("not implemented")
}
-// 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.
+// Prove implements state.Trie, constructing 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.
+//
+// TODO(gballet, rjl493456442) implement it.
func (t *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
panic("not implemented")
}
+// Copy returns a deep-copied verkle tree.
func (t *VerkleTrie) Copy() *VerkleTrie {
return &VerkleTrie{
- root: t.root.Copy(),
- db: t.db,
- pointCache: t.pointCache,
- reader: t.reader,
+ root: t.root.Copy(),
+ db: t.db,
+ cache: t.cache,
+ reader: t.reader,
}
}
@@ -291,11 +280,6 @@ 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)
)
@@ -311,21 +295,16 @@ func ChunkifyCode(code []byte) ChunkedCode {
}
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.
+ // 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(chunks[i*32+1:], code[31*i:end]) // copy the code itself
- // Copy the code itself
- copy(chunks[i*32+1:], code[31*i:end])
-
- // chunk offset = taken from the
- // last chunk.
+ // chunk offset = taken from the last chunk.
if chunkOffset > 31 {
- // skip offset calculation if push
- // data covers the whole chunk
+ // skip offset calculation if push data covers the whole chunk
chunks[i*32] = 31
chunkOffset = 1
continue
@@ -333,8 +312,8 @@ func ChunkifyCode(code []byte) ChunkedCode {
chunks[32*i] = byte(chunkOffset)
chunkOffset = 0
- // Check each instruction and update the offset
- // it should be 0 unless a PUSHn overflows.
+ // Check each instruction and update the offset it should be 0 unless
+ // a PUSH-N overflows.
for ; codeOffset < end; codeOffset++ {
if code[codeOffset] >= PUSH1 && code[codeOffset] <= PUSH32 {
codeOffset += int(code[codeOffset] - PUSH1 + 1)
@@ -346,10 +325,11 @@ func ChunkifyCode(code []byte) ChunkedCode {
}
}
}
-
return chunks
}
+// UpdateContractCode implements state.Trie, writing the provided contract code
+// into the trie.
func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
var (
chunks = ChunkifyCode(code)
@@ -361,7 +341,7 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has
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))
+ key = utils.CodeChunkKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), uint256.NewInt(chunknr))
}
values[groupOffset] = chunks[i : i+32]
@@ -371,14 +351,25 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has
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)
+ switch root := t.root.(type) {
+ case *verkle.InternalNode:
+ err = root.InsertValuesAtStem(key[:31], values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("UpdateContractCode (addr=%x) error: %w", addr[:], err)
+ }
+ default:
+ return errInvalidRootType
}
}
}
return nil
}
+
+func (t *VerkleTrie) ToDot() string {
+ return verkle.ToDot(t.root)
+}
+
+func (t *VerkleTrie) nodeResolver(path []byte) ([]byte, error) {
+ return t.reader.node(path, common.Hash{})
+}
diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go
deleted file mode 100644
index c5f59a0f593..00000000000
--- a/trie/verkle_iterator.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// 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
-}
diff --git a/trie/verkle_test.go b/trie/verkle_test.go
new file mode 100644
index 00000000000..44fb7dc29e9
--- /dev/null
+++ b/trie/verkle_test.go
@@ -0,0 +1,97 @@
+// Copyright 2023 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 (
+ "bytes"
+ "math/big"
+ "reflect"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
+ "github.com/ethereum/go-ethereum/trie/utils"
+)
+
+var (
+ accounts = map[common.Address]*types.StateAccount{
+ common.Address{1}: {
+ Nonce: 100,
+ Balance: big.NewInt(100),
+ CodeHash: common.Hash{0x1}.Bytes(),
+ },
+ common.Address{2}: {
+ Nonce: 200,
+ Balance: big.NewInt(200),
+ CodeHash: common.Hash{0x2}.Bytes(),
+ },
+ }
+ storages = map[common.Address]map[common.Hash][]byte{
+ common.Address{1}: {
+ common.Hash{10}: []byte{10},
+ common.Hash{11}: []byte{11},
+ common.MaxHash: []byte{0xff},
+ },
+ common.Address{2}: {
+ common.Hash{20}: []byte{20},
+ common.Hash{21}: []byte{21},
+ common.MaxHash: []byte{0xff},
+ },
+ }
+)
+
+func TestVerkleTreeReadWrite(t *testing.T) {
+ db := NewDatabase(rawdb.NewMemoryDatabase(), &Config{
+ IsVerkle: true,
+ PathDB: pathdb.Defaults,
+ })
+ defer db.Close()
+
+ tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100))
+
+ for addr, acct := range accounts {
+ if err := tr.UpdateAccount(addr, acct); err != nil {
+ t.Fatalf("Failed to update account, %v", err)
+ }
+ for key, val := range storages[addr] {
+ if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil {
+ t.Fatalf("Failed to update account, %v", err)
+ }
+ }
+ }
+
+ for addr, acct := range accounts {
+ stored, err := tr.GetAccount(addr)
+ if err != nil {
+ t.Fatalf("Failed to get account, %v", err)
+ }
+ if !reflect.DeepEqual(stored, acct) {
+ t.Fatal("account is not matched")
+ }
+ for key, val := range storages[addr] {
+ stored, err := tr.GetStorage(addr, key.Bytes())
+ if err != nil {
+ t.Fatalf("Failed to get storage, %v", err)
+ }
+ if !bytes.Equal(stored, val) {
+ t.Fatal("storage is not matched")
+ }
+ }
+ }
+}
From 8754ebc1685536e82e2f68c6ffbf3b9b2dd15d9c Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 31 Oct 2023 11:51:16 +0100
Subject: [PATCH 09/10] update go-verkle
---
go.mod | 6 +++---
go.sum | 6 ++++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index b80abda46f7..94be584ac21 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +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-ipa v0.0.0-20231025140028-3c0104f4b233
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
@@ -28,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.1.1-0.20231004173727-0a4e93ed640b
+ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
github.com/go-stack/stack v1.8.1
github.com/gofrs/flock v0.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
@@ -91,7 +91,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect
github.com/aws/smithy-go v1.15.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/bits-and-blooms/bitset v1.7.0 // indirect
+ github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect
github.com/cockroachdb/redact v1.0.8 // indirect
diff --git a/go.sum b/go.sum
index 3169a473a74..e8c5cac2517 100644
--- a/go.sum
+++ b/go.sum
@@ -99,6 +99,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
+github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
@@ -147,6 +149,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
+github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
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=
@@ -205,6 +209,8 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
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/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
+github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
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=
From 1379079d5b18da3dd32a3cc9b2e2954e74ad9045 Mon Sep 17 00:00:00 2001
From: Gary Rong
Date: Mon, 13 Nov 2023 18:44:44 +0800
Subject: [PATCH 10/10] trie/utils: address comment from martin
---
trie/utils/verkle.go | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go
index 0ed8f83e750..ce059edc643 100644
--- a/trie/utils/verkle.go
+++ b/trie/utils/verkle.go
@@ -84,28 +84,20 @@ func NewPointCache(maxItems int) *PointCache {
}
}
-// get loads the cached commitment, or nil if it's not existent.
-func (c *PointCache) get(addr string) (*verkle.Point, bool) {
- c.lock.RLock()
- defer c.lock.RUnlock()
-
- return c.lru.Get(addr)
-}
-
// Get returns the cached commitment for the specified address, or computing
// it on the flight.
func (c *PointCache) Get(addr []byte) *verkle.Point {
- p, ok := c.get(string(addr))
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ p, ok := c.lru.Get(string(addr))
if ok {
cacheHitGauge.Inc(1)
return p
}
cacheMissGauge.Inc(1)
p = evaluateAddressPoint(addr)
-
- c.lock.Lock()
c.lru.Add(string(addr), p)
- c.lock.Unlock()
return p
}