From 521183581afce30fd9f2d5a42930f8e18a33066e Mon Sep 17 00:00:00 2001
From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Date: Wed, 6 Mar 2024 11:50:28 +0800
Subject: [PATCH] feat: add zktrie (#621)
* init
* add trie/database_types.go
* update params/config.go
* update les/server.go
* update core/types/state_account_marshalling.go
* add core/types/state_account_marshalling_test.go
* update eth/backend.go
* update trie/zk_trie.go
* update trie/zk_trie_database.go
* update trie/zk_trie_database_test.go
* update trie/zk_trie_impl_test.go
* update trie/zk_trie_proof_test.go
* update trie/zk_trie_test.go
* minor
* init database_supplement.go
* minor
* add some supplements
* fix
* fix
* add `zkproof` package
* add trie/zktrie_deletionproof.go
* init core/state/state_prove.go
* fix
* fix `(t *ZkTrie) Commit`
* fix
* update trie/proof.go
* fix trie/zk_trie_database.go
* update core/blockchain.go
* update core/genesis.go
* fix init trie_db (#639)
* add config
* update cmd/evm/internal/t8ntool/execution.go
* update core/chain_makers.go
* update cmd/evm/runner.go
fix cmd/evm/runner.go
* update core/chain_makers.go
* refactor `triedbConfig`
* update core/genesis.go
* refactor `genesis.ToBlock()`
* fix core/genesis_test.go
* update core/state/database.go
* update trie/database.go
* update core/state/state_object.go
* clean up
* fix tests
* fix `TestDump` & `TestIterativeDump` (#651)
* fix `TestDump`
* fix `compareStateObjects`
* fix `TestIterativeDump`
* fix `TestTinyTrie` & `TestCommitSequence` (#652)
* zktrie: fix tests (#656)
* fix `TestOdrContractCallLes2`
* fix `internal/ethapi` tests
* fix `TestFilters`
* fix `core/state/snapshot/generate_test.go
* update core/genesis_test.go (#658)
---
cmd/evm/internal/t8ntool/execution.go | 2 +-
cmd/evm/runner.go | 2 +
core/blockchain.go | 16 +-
core/chain_makers.go | 12 +-
core/genesis.go | 35 +-
core/genesis_test.go | 66 +-
core/state/database.go | 16 +
core/state/snapshot/generate_test.go | 22 +-
core/state/state_object.go | 17 +-
core/state/state_prove.go | 85 ++
core/state/state_test.go | 30 +-
core/state/statedb.go | 4 +
core/types/state_account_marshalling.go | 107 +++
core/types/state_account_marshalling_test.go | 189 ++++
eth/backend.go | 14 +-
eth/filters/filter_test.go | 20 +-
go.mod | 3 +-
go.sum | 10 +-
.../testdata/eth_getBlockByHash-hash-1.json | 6 +-
.../eth_getBlockByHash-hash-genesis.json | 4 +-
...h_getBlockByHash-hash-latest-1-fullTx.json | 11 +-
.../eth_getBlockByHash-hash-latest.json | 6 +-
.../eth_getBlockByNumber-number-0.json | 4 +-
.../eth_getBlockByNumber-number-1.json | 6 +-
.../eth_getBlockByNumber-number-latest-1.json | 11 +-
.../eth_getBlockByNumber-tag-latest.json | 6 +-
...h_getBlockByNumber-tag-pending-fullTx.json | 3 +-
...h_getBlockReceipts-block-with-blob-tx.json | 3 +-
...eceipts-block-with-contract-create-tx.json | 3 +-
...ockReceipts-block-with-dynamic-fee-tx.json | 3 +-
...ts-block-with-legacy-contract-call-tx.json | 5 +-
...eceipts-block-with-legacy-transfer-tx.json | 3 +-
.../eth_getBlockReceipts-tag-latest.json | 3 +-
.../testdata/eth_getHeaderByHash-hash-0.json | 4 +-
.../testdata/eth_getHeaderByHash-hash-1.json | 6 +-
.../eth_getHeaderByHash-hash-latest-1.json | 6 +-
.../eth_getHeaderByHash-hash-latest.json | 6 +-
.../eth_getHeaderByNumber-number-0.json | 4 +-
.../eth_getHeaderByNumber-number-1.json | 6 +-
...eth_getHeaderByNumber-number-latest-1.json | 6 +-
.../eth_getHeaderByNumber-tag-latest.json | 6 +-
.../eth_getTransactionReceipt-blob-tx.json | 3 +-
...TransactionReceipt-create-contract-tx.json | 3 +-
...eipt-create-contract-with-access-list.json | 3 +-
...ansactionReceipt-dynamic-tx-with-logs.json | 3 +-
...TransactionReceipt-normal-transfer-tx.json | 3 +-
.../eth_getTransactionReceipt-with-logs.json | 5 +-
les/server.go | 6 +
params/config.go | 7 +
trie/database.go | 58 ++
trie/database_supplement.go | 32 +
trie/database_types.go | 47 +
trie/proof.go | 5 +
trie/trie_test.go | 14 +-
trie/triedb/hashdb/database.go | 18 +-
trie/triedb/hashdb/database_supplement.go | 15 +
trie/triedb/pathdb/database_supplement.go | 9 +
trie/zk_trie.go | 287 ++++++
trie/zk_trie_database.go | 172 ++++
trie/zk_trie_database_test.go | 63 ++
trie/zk_trie_impl_test.go | 289 ++++++
trie/zk_trie_proof_test.go | 284 ++++++
trie/zk_trie_test.go | 270 ++++++
trie/zkproof/orderer.go | 349 ++++++++
trie/zkproof/types.go | 59 ++
trie/zkproof/writer.go | 826 ++++++++++++++++++
trie/zktrie_deletionproof.go | 183 ++++
67 files changed, 3613 insertions(+), 171 deletions(-)
create mode 100644 core/state/state_prove.go
create mode 100644 core/types/state_account_marshalling.go
create mode 100644 core/types/state_account_marshalling_test.go
create mode 100644 trie/database_supplement.go
create mode 100644 trie/database_types.go
create mode 100644 trie/triedb/hashdb/database_supplement.go
create mode 100644 trie/triedb/pathdb/database_supplement.go
create mode 100644 trie/zk_trie.go
create mode 100644 trie/zk_trie_database.go
create mode 100644 trie/zk_trie_database_test.go
create mode 100644 trie/zk_trie_impl_test.go
create mode 100644 trie/zk_trie_proof_test.go
create mode 100644 trie/zk_trie_test.go
create mode 100644 trie/zkproof/orderer.go
create mode 100644 trie/zkproof/types.go
create mode 100644 trie/zkproof/writer.go
create mode 100644 trie/zktrie_deletionproof.go
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index a17e01c6ac..38b7d38bb2 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -347,7 +347,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
- sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
+ sdb := state.NewDatabaseWithConfig(db, trie.HashDefaultsWithPreimages)
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 45fc985351..dc299f6c07 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -151,6 +151,8 @@ func runCmd(ctx *cli.Context) error {
triedb := trie.NewDatabase(db, &trie.Config{
Preimages: preimages,
HashDB: hashdb.Defaults,
+ // scroll related
+ IsUsingZktrie: genesisConfig.Config.Scroll.ZktrieEnabled(),
})
defer triedb.Close()
genesis := genesisConfig.MustCommit(db, triedb)
diff --git a/core/blockchain.go b/core/blockchain.go
index b1f583022b..0ee92d3683 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -149,8 +149,8 @@ type CacheConfig struct {
}
// triedbConfig derives the configures for trie database.
-func (c *CacheConfig) triedbConfig() *trie.Config {
- config := &trie.Config{Preimages: c.Preimages}
+func (c *CacheConfig) triedbConfig(isUsingZktrie bool) *trie.Config {
+ config := &trie.Config{Preimages: c.Preimages, IsUsingZktrie: isUsingZktrie}
if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
@@ -268,7 +268,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
cacheConfig = defaultCacheConfig
}
// Open trie database with provided config
- triedb := trie.NewDatabase(db, cacheConfig.triedbConfig())
+ triedbConfig := cacheConfig.triedbConfig(false)
+ if genesis != nil && genesis.Config != nil && genesis.Config.Scroll.ZktrieEnabled() {
+ cacheConfig.triedbConfig(true)
+ }
+ triedb := trie.NewDatabase(db, triedbConfig)
// Setup the genesis block, commit the provided genesis specification
// to database if the genesis block is not present yet, or load the
@@ -285,6 +289,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
log.Info(strings.Repeat("-", 153))
log.Info("")
+ // override snapshot setting
+ if chainConfig.Scroll.ZktrieEnabled() && cacheConfig.SnapshotLimit > 0 {
+ log.Warn("Snapshot has been disabled by zktrie")
+ cacheConfig.SnapshotLimit = 0
+ }
+
if chainConfig.Scroll.FeeVaultEnabled() {
log.Warn("Using fee vault address", "FeeVaultAddress", *chainConfig.Scroll.FeeVaultAddress)
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 25979fbb24..41feba7fec 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -349,7 +349,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
return nil, nil
}
// Forcibly use hash-based state scheme for retaining all nodes in disk.
- triedb := trie.NewDatabase(db, trie.HashDefaults)
+ trieConfig := trie.HashDefaults
+ if config.Scroll.ZktrieEnabled() {
+ trieConfig = trie.HashDefaultsWithZktrie
+ }
+ triedb := trie.NewDatabase(db, trieConfig)
defer triedb.Close()
for i := 0; i < n; i++ {
@@ -370,7 +374,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
// then generate chain on top.
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
db := rawdb.NewMemoryDatabase()
- triedb := trie.NewDatabase(db, trie.HashDefaults)
+ trieConfig := trie.HashDefaults
+ if genesis.Config != nil && genesis.Config.Scroll.ZktrieEnabled() {
+ trieConfig = trie.HashDefaultsWithZktrie
+ }
+ triedb := trie.NewDatabase(db, trieConfig)
defer triedb.Close()
_, err := genesis.Commit(db, triedb)
if err != nil {
diff --git a/core/genesis.go b/core/genesis.go
index 61672142e0..ac1072d009 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -121,10 +121,14 @@ 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(isUsingZktrie bool) (common.Hash, error) {
// 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())
+ trieConfig := trie.HashDefaults
+ if isUsingZktrie {
+ trieConfig = trie.HashDefaultsWithZktrie
+ }
+ db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), trieConfig)
statedb, err := state.New(types.EmptyRootHash, db, nil)
if err != nil {
return common.Hash{}, err
@@ -287,6 +291,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
} else {
log.Info("Writing custom genesis block")
}
+ if genesis.Config.Scroll.ZktrieEnabled() { // genesis.Config must be not nil atm
+ // overwrite triedb IsUsingZktrie config to be safe
+ triedb.SetIsUsingZktrie(genesis.Config.Scroll.ZktrieEnabled())
+ }
block, err := genesis.Commit(db, triedb)
if err != nil {
return genesis.Config, common.Hash{}, err
@@ -299,6 +307,14 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
// is initialized with an external ancient store. Commit genesis state
// in this case.
header := rawdb.ReadHeader(db, stored, 0)
+ storedcfg := rawdb.ReadChainConfig(db, stored)
+ if genesis != nil { // genesis.Config must be not nil atm
+ // overwrite triedb IsUsingZktrie config to be safe
+ triedb.SetIsUsingZktrie(genesis.Config.Scroll.ZktrieEnabled())
+ } else if storedcfg != nil && storedcfg.Scroll.ZktrieEnabled() {
+ // overwrite triedb IsUsingZktrie config to be safe
+ triedb.SetIsUsingZktrie(storedcfg.Scroll.ZktrieEnabled())
+ }
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) {
if genesis == nil {
genesis = DefaultGenesisBlock()
@@ -328,7 +344,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
if err := newcfg.CheckConfigForkOrder(); err != nil {
return newcfg, common.Hash{}, err
}
- storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg == nil {
log.Warn("Found genesis block without chain config")
rawdb.WriteChainConfig(db, stored, newcfg)
@@ -411,7 +426,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
// 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.Config.Scroll.ZktrieEnabled())
if err != nil {
panic(err)
}
@@ -471,14 +486,18 @@ func (g *Genesis) ToBlock() *types.Block {
// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) {
- block := g.ToBlock()
- if block.Number().Sign() != 0 {
- return nil, errors.New("can't commit genesis block with number > 0")
- }
config := g.Config
if config == nil {
config = params.AllEthashProtocolChanges
}
+ if config.Scroll.ZktrieEnabled() != triedb.IsUsingZktrie() {
+ return nil, fmt.Errorf("ZktrieEnabled mismatch. genesis: %v, triedb: %v", g.Config.Scroll.ZktrieEnabled(), triedb.IsUsingZktrie())
+ }
+
+ block := g.ToBlock()
+ if block.Number().Sign() != 0 {
+ return nil, errors.New("can't commit genesis block with number > 0")
+ }
if err := config.CheckConfigForkOrder(); err != nil {
return nil, err
}
diff --git a/core/genesis_test.go b/core/genesis_test.go
index fac88ff373..c85dbaf05f 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -49,7 +49,7 @@ func TestSetupGenesis(t *testing.T) {
func testSetupGenesis(t *testing.T, scheme string) {
var (
- customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
+ customghash = common.HexToHash("0x700380ab70d789c462c4e8f0db082842095321f390d0a3f25f400f0746db32bc")
customg = Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
Alloc: GenesisAlloc{
@@ -75,23 +75,23 @@ func testSetupGenesis(t *testing.T, scheme string) {
wantErr: errGenesisNoConfig,
wantConfig: params.AllEthashProtocolChanges,
},
- {
- name: "no block in DB, genesis == nil",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
- },
- wantHash: params.MainnetGenesisHash,
- wantConfig: params.MainnetChainConfig,
- },
- {
- name: "mainnet block in DB, genesis == nil",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
- return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
- },
- wantHash: params.MainnetGenesisHash,
- wantConfig: params.MainnetChainConfig,
- },
+ // {
+ // name: "no block in DB, genesis == nil",
+ // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
+ // return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
+ // },
+ // wantHash: params.MainnetGenesisHash,
+ // wantConfig: params.MainnetChainConfig,
+ // },
+ // {
+ // name: "mainnet block in DB, genesis == nil",
+ // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
+ // DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
+ // return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
+ // },
+ // wantHash: params.MainnetGenesisHash,
+ // wantConfig: params.MainnetChainConfig,
+ // },
{
name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
@@ -102,17 +102,17 @@ func testSetupGenesis(t *testing.T, scheme string) {
wantHash: customghash,
wantConfig: customg.Config,
},
- {
- name: "custom block in DB, genesis == goerli",
- fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
- customg.Commit(db, tdb)
- return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
- },
- wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
- wantHash: params.GoerliGenesisHash,
- wantConfig: params.GoerliChainConfig,
- },
+ // {
+ // name: "custom block in DB, genesis == goerli",
+ // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
+ // tdb := trie.NewDatabase(db, newDbConfig(scheme))
+ // customg.Commit(db, tdb)
+ // return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
+ // },
+ // wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
+ // wantHash: params.GoerliGenesisHash,
+ // wantConfig: params.GoerliChainConfig,
+ // },
{
name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
@@ -181,9 +181,9 @@ func TestGenesisHashes(t *testing.T) {
genesis *Genesis
want common.Hash
}{
- {DefaultGenesisBlock(), params.MainnetGenesisHash},
- {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
- {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
+ // {DefaultGenesisBlock(), params.MainnetGenesisHash},
+ // {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
+ // {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
} {
// Test via MustCommit
db := rawdb.NewMemoryDatabase()
@@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
}
- hash, _ = alloc.hash()
+ hash, _ = alloc.hash(false)
)
blob, _ := json.Marshal(alloc)
rawdb.WriteGenesisStateSpec(db, hash, blob)
diff --git a/core/state/database.go b/core/state/database.go
index 9467c8f72e..c88fb8d05e 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -170,6 +170,13 @@ 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.IsUsingZktrie() {
+ tr, err := trie.NewZkTrie(root, trie.NewZktrieDatabaseFromTriedb(db.triedb))
+ if err != nil {
+ return nil, err
+ }
+ return tr, nil
+ }
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
@@ -179,6 +186,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) {
+ if db.triedb.IsUsingZktrie() {
+ tr, err := trie.NewZkTrie(root, trie.NewZktrieDatabaseFromTriedb(db.triedb))
+ if err != nil {
+ return nil, err
+ }
+ return tr, nil
+ }
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
if err != nil {
return nil, err
@@ -191,6 +205,8 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
switch t := t.(type) {
case *trie.StateTrie:
return t.Copy()
+ case *trie.ZkTrie:
+ return t.Copy()
default:
panic(fmt.Errorf("unknown trie type %T", t))
}
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index 8875ad2e0b..e52c24ba27 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -66,7 +66,7 @@ func testGeneration(t *testing.T, scheme string) {
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
root, snap := helper.CommitAndGenerate()
- if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want {
+ if have, want := root, common.HexToHash("0x0bc6b6959d2589404dd3e4b25783a829b58625f6b673f095e9a97391b474c3f9"); have != want {
t.Fatalf("have %#x want %#x", have, want)
}
select {
@@ -419,14 +419,14 @@ func testGenerateCorruptAccountTrie(t *testing.T, scheme string) {
helper := newHelper(scheme)
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
// Delete an account trie node and ensure the generator chokes
targetPath := []byte{0xc}
- targetHash := common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7")
+ targetHash := common.HexToHash("0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366")
rawdb.DeleteTrieNode(helper.diskdb, common.Hash{}, targetPath, targetHash, scheme)
@@ -463,10 +463,10 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) {
helper = newHelper(scheme)
)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
root := helper.Commit()
@@ -503,10 +503,10 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) {
helper := newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
root := helper.Commit()
@@ -548,7 +548,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
)
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
- helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
// Identical in the snap
key := hashData([]byte("acc-1"))
@@ -624,7 +624,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
)
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
- helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
// Identical in the snap
key := hashData([]byte("acc-1"))
diff --git a/core/state/state_object.go b/core/state/state_object.go
index d19e8a9ccb..289c608880 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -102,6 +102,7 @@ func (s *stateObject) empty() bool {
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
origin := acct
if acct == nil {
+ // TODO: fix the root?
acct = types.NewEmptyStateAccount()
}
return &stateObject{
@@ -142,7 +143,7 @@ func (s *stateObject) touch() {
func (s *stateObject) getTrie() (Trie, error) {
if s.trie == nil {
// Try fetching from prefetcher first
- if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
+ if s.data.Root != s.db.db.TrieDB().EmptyRoot() && s.db.prefetcher != nil {
// When the miner is creating the pending state, there is no prefetcher
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
}
@@ -198,7 +199,9 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
if metrics.EnabledExpensive {
s.db.SnapshotStorageReads += time.Since(start)
}
- if len(enc) > 0 {
+ if s.db.db.TrieDB().IsUsingZktrie() {
+ value = common.BytesToHash(enc)
+ } else if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
s.db.setError(err)
@@ -258,7 +261,7 @@ func (s *stateObject) finalise(prefetch bool) {
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
}
}
- if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
+ if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != s.db.db.TrieDB().EmptyRoot() {
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch)
}
if len(s.dirtyStorage) > 0 {
@@ -312,9 +315,13 @@ func (s *stateObject) updateTrie() (Trie, error) {
}
s.db.StorageDeleted += 1
} else {
- // Encoding []byte cannot fail, ok to ignore the error.
trimmed := common.TrimLeftZeroes(value[:])
- encoded, _ = rlp.EncodeToBytes(trimmed)
+ if s.db.db.TrieDB().IsUsingZktrie() {
+ encoded = common.CopyBytes(value[:])
+ } else {
+ // Encoding []byte cannot fail, ok to ignore the error.
+ encoded, _ = rlp.EncodeToBytes(trimmed)
+ }
if err := tr.UpdateStorage(s.address, key[:], trimmed); err != nil {
s.db.setError(err)
return nil, err
diff --git a/core/state/state_prove.go b/core/state/state_prove.go
new file mode 100644
index 0000000000..17b99681a5
--- /dev/null
+++ b/core/state/state_prove.go
@@ -0,0 +1,85 @@
+package state
+
+import (
+ "fmt"
+
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ zktrie "github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/trie/zkproof"
+)
+
+type TrieProve interface {
+ Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error
+}
+
+type ZktrieProofTracer struct {
+ *zktrie.ProofTracer
+}
+
+// MarkDeletion overwrite the underlayer method with secure key
+func (t ZktrieProofTracer) MarkDeletion(key common.Hash) {
+ key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
+ t.ProofTracer.MarkDeletion(key_s.Bytes())
+}
+
+// Merge overwrite underlayer method with proper argument
+func (t ZktrieProofTracer) Merge(another ZktrieProofTracer) {
+ t.ProofTracer.Merge(another.ProofTracer)
+}
+
+func (t ZktrieProofTracer) Available() bool {
+ return t.ProofTracer != nil
+}
+
+// NewProofTracer is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
+func (s *StateDB) NewProofTracer(trieS Trie) ZktrieProofTracer {
+ if s.IsUsingZktrie() {
+ zkTrie := trieS.(*zktrie.ZkTrie)
+ if zkTrie == nil {
+ panic("unexpected trie type for zktrie")
+ }
+ return ZktrieProofTracer{zkTrie.NewProofTracer()}
+ }
+ return ZktrieProofTracer{}
+}
+
+// GetStorageTrieForProof is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
+func (s *StateDB) GetStorageTrieForProof(addr common.Address) (Trie, error) {
+ // try the trie in stateObject first, else we would create one
+ stateObject := s.getStateObject(addr)
+ if stateObject == nil {
+ // still return a empty trie
+ dummy_trie, _ := s.db.OpenStorageTrie(s.originalRoot, addr, common.Hash{})
+ return dummy_trie, nil
+ }
+
+ trie := stateObject.trie
+ var err error
+ if trie == nil {
+ // use a new, temporary trie
+ trie, err = s.db.OpenStorageTrie(s.originalRoot, stateObject.address, stateObject.data.Root)
+ if err != nil {
+ return nil, fmt.Errorf("can't create storage trie on root %s: %v ", stateObject.data.Root, err)
+ }
+ }
+
+ return trie, nil
+}
+
+// GetSecureTrieProof handle any interface with Prove (should be a Trie in most case) and
+// deliver the proof in bytes
+func (s *StateDB) GetSecureTrieProof(trieProve TrieProve, key common.Hash) ([][]byte, error) {
+ var proof zkproof.ProofList
+ var err error
+ if s.IsUsingZktrie() {
+ key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
+ err = trieProve.Prove(key_s.Bytes(), 0, &proof)
+ } else {
+ err = trieProve.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
+ }
+ return proof, err
+}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 57c8266043..c1f8325dbb 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -63,27 +63,33 @@ func TestDump(t *testing.T) {
s.state, _ = New(root, tdb, nil)
got := string(s.state.Dump(nil))
want := `{
- "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
+ "root": "789955993afb9d2a04b957a91be5d7b139aabb60fb7af63df6405021211c13c4",
"accounts": {
"0x0000000000000000000000000000000000000001": {
"balance": "22",
"nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
+ "codeSize": 0,
"key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
},
"0x0000000000000000000000000000000000000002": {
"balance": "44",
"nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
+ "codeSize": 0,
"key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
},
"0x0000000000000000000000000000000000000102": {
"balance": "0",
"nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
+ "keccakCodeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
+ "poseidonCodeHash": "0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c",
+ "codeSize": 7,
"code": "0x03030303030303",
"key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"
}
@@ -120,11 +126,11 @@ func TestIterativeDump(t *testing.T) {
s.state.IterativeDump(nil, json.NewEncoder(b))
// check that DumpToCollector contains the state objects that are in trie
got := b.String()
- want := `{"root":"0xd5710ea8166b7b04bc2bfb129d7db12931cee82f75ca8e2d075b4884322bf3de"}
-{"balance":"22","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000001","key":"0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"}
-{"balance":"1337","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000000","key":"0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"}
-{"balance":"0","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3","code":"0x03030303030303","address":"0x0000000000000000000000000000000000000102","key":"0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"}
-{"balance":"44","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000002","key":"0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"}
+ want := `{"root":"0xb52efb616edbf7ae4914e756bd3fe7ff4d06dd914d613123517171d610f33d5c"}
+{"balance":"22","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000001","key":"0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"}
+{"balance":"1337","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000000","key":"0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"}
+{"balance":"0","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3","poseidonCodeHash":"0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c","codeSize":7,"code":"0x03030303030303","address":"0x0000000000000000000000000000000000000102","key":"0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"}
+{"balance":"44","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000002","key":"0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"}
`
if got != want {
t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want)
@@ -261,6 +267,12 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
if !bytes.Equal(so0.KeccakCodeHash(), so1.KeccakCodeHash()) {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.KeccakCodeHash(), so1.KeccakCodeHash())
}
+ if !bytes.Equal(so0.PoseidonCodeHash(), so1.PoseidonCodeHash()) {
+ t.Fatalf("PoseidonCodeHash mismatch: have %v, want %v", so0.PoseidonCodeHash(), so1.PoseidonCodeHash())
+ }
+ if so0.CodeSize() != so1.CodeSize() {
+ t.Fatalf("CodeSize mismatch: have %v, want %v", so0.CodeSize(), so1.CodeSize())
+ }
if !bytes.Equal(so0.code, so1.code) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 4fca8de198..77bcb3f009 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -206,6 +206,10 @@ func (s *StateDB) Error() error {
return s.dbErr
}
+func (s *StateDB) IsUsingZktrie() bool {
+ return s.db.TrieDB().IsUsingZktrie()
+}
+
func (s *StateDB) AddLog(log *types.Log) {
s.journal.append(addLogChange{txhash: s.thash})
diff --git a/core/types/state_account_marshalling.go b/core/types/state_account_marshalling.go
new file mode 100644
index 0000000000..c4b970e9c8
--- /dev/null
+++ b/core/types/state_account_marshalling.go
@@ -0,0 +1,107 @@
+// 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 types
+
+import (
+ "encoding/binary"
+ "errors"
+ "math/big"
+
+ "github.com/iden3/go-iden3-crypto/utils"
+
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+var (
+ ErrInvalidLength = errors.New("StateAccount: invalid input length")
+)
+
+// MarshalFields marshalls a StateAccount into a sequence of bytes. The bytes scheme is:
+// [0:32] (bytes in big-endian)
+//
+// [0:16] Reserved with all 0
+// [16:24] CodeSize, uint64 in big-endian
+// [24:32] Nonce, uint64 in big-endian
+//
+// [32:64] Balance
+// [64:96] StorageRoot
+// [96:128] KeccakCodeHash
+// [128:160] PoseidonCodehash
+// (total 160 bytes)
+func (s *StateAccount) MarshalFields() ([]zkt.Byte32, uint32) {
+ fields := make([]zkt.Byte32, 5)
+
+ if s.Balance == nil {
+ panic("StateAccount balance nil")
+ }
+
+ if !utils.CheckBigIntInField(s.Balance) {
+ panic("StateAccount balance overflow")
+ }
+
+ if !utils.CheckBigIntInField(s.Root.Big()) {
+ panic("StateAccount root overflow")
+ }
+
+ if !utils.CheckBigIntInField(new(big.Int).SetBytes(s.PoseidonCodeHash)) {
+ panic("StateAccount poseidonCodeHash overflow")
+ }
+
+ binary.BigEndian.PutUint64(fields[0][16:], s.CodeSize)
+ binary.BigEndian.PutUint64(fields[0][24:], s.Nonce)
+ s.Balance.FillBytes(fields[1][:])
+ copy(fields[2][:], s.Root.Bytes())
+ copy(fields[3][:], s.KeccakCodeHash)
+ copy(fields[4][:], s.PoseidonCodeHash)
+
+ // The returned flag shows which items cannot be encoded as field elements.
+ // KeccakCodeHash can be larger than the field size so we set the 3rd (LSB) bit to 1.
+ //
+ // +---+---+---+---+---+
+ // | 0 | 1 | 2 | 3 | 4 |
+ // +---+---+---+---+---+
+ // 0 0 0 1 0
+
+ flag := uint32(8)
+
+ return fields, flag
+}
+
+func UnmarshalStateAccount(bytes []byte) (*StateAccount, error) {
+ if len(bytes) != 160 {
+ return nil, ErrInvalidLength
+ }
+
+ acc := new(StateAccount)
+
+ acc.CodeSize = binary.BigEndian.Uint64(bytes[16:24])
+ acc.Nonce = binary.BigEndian.Uint64(bytes[24:32])
+ acc.Balance = new(big.Int).SetBytes(bytes[32:64])
+
+ acc.Root = common.Hash{}
+ acc.Root.SetBytes(bytes[64:96])
+
+ acc.KeccakCodeHash = make([]byte, 32)
+ copy(acc.KeccakCodeHash, bytes[96:128])
+
+ acc.PoseidonCodeHash = make([]byte, 32)
+ copy(acc.PoseidonCodeHash, bytes[128:160])
+
+ return acc, nil
+}
diff --git a/core/types/state_account_marshalling_test.go b/core/types/state_account_marshalling_test.go
new file mode 100644
index 0000000000..b59f1af4dd
--- /dev/null
+++ b/core/types/state_account_marshalling_test.go
@@ -0,0 +1,189 @@
+package types
+
+import (
+ "math"
+ "math/big"
+ "testing"
+
+ "github.com/iden3/go-iden3-crypto/constants"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/codehash"
+)
+
+func assertAccountsEqual(t *testing.T, expected *StateAccount, actual *StateAccount) {
+ assert.Equal(t, expected.Nonce, actual.Nonce)
+ assert.Zero(t, expected.Balance.Cmp(actual.Balance))
+ assert.Equal(t, expected.Root, actual.Root)
+ assert.Equal(t, expected.KeccakCodeHash, actual.KeccakCodeHash)
+ assert.Equal(t, expected.PoseidonCodeHash, actual.PoseidonCodeHash)
+ assert.Equal(t, expected.CodeSize, actual.CodeSize)
+}
+
+func TestMarshalUnmarshalEmptyAccount(t *testing.T) {
+ acc := StateAccount{
+ Nonce: 0,
+ Balance: big.NewInt(0),
+ Root: common.Hash{},
+ KeccakCodeHash: codehash.EmptyKeccakCodeHash.Bytes(),
+ PoseidonCodeHash: codehash.EmptyPoseidonCodeHash.Bytes(),
+ CodeSize: 0,
+ }
+
+ // marshal account
+
+ bytes, flag := acc.MarshalFields()
+
+ assert.Equal(t, 5, len(bytes))
+ assert.Equal(t, uint32(8), flag)
+
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[0].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[1].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[2].Bytes())
+ assert.Equal(t, common.Hex2Bytes("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), bytes[3].Bytes())
+ assert.Equal(t, common.Hex2Bytes("2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864"), bytes[4].Bytes())
+
+ // unmarshal account
+
+ flatBytes := []byte("")
+
+ for _, item := range bytes {
+ flatBytes = append(flatBytes, item.Bytes()...)
+ }
+
+ acc2, err := UnmarshalStateAccount(flatBytes)
+
+ assert.Nil(t, err)
+ assertAccountsEqual(t, &acc, acc2)
+}
+
+func TestMarshalUnmarshalZeroAccount(t *testing.T) {
+ acc := StateAccount{
+ Nonce: 0,
+ Balance: big.NewInt(0),
+ Root: common.Hash{},
+ KeccakCodeHash: make([]byte, 0),
+ PoseidonCodeHash: make([]byte, 0),
+ CodeSize: 0,
+ }
+
+ // marshal account
+
+ bytes, flag := acc.MarshalFields()
+
+ assert.Equal(t, 5, len(bytes))
+ assert.Equal(t, uint32(8), flag)
+
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[0].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[1].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[2].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[3].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[4].Bytes())
+}
+
+func TestMarshalUnmarshalNonEmptyAccount(t *testing.T) {
+ acc := StateAccount{
+ Nonce: 0x11111111,
+ Balance: big.NewInt(0x33333333),
+ Root: common.HexToHash("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"),
+ KeccakCodeHash: common.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111"),
+ PoseidonCodeHash: common.Hex2Bytes("2222222222222222222222222222222222222222222222222222222222222222"),
+ CodeSize: 0x22222222,
+ }
+
+ // marshal account
+
+ bytes, flag := acc.MarshalFields()
+
+ assert.Equal(t, 5, len(bytes))
+ assert.Equal(t, uint32(8), flag)
+
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000222222220000000011111111"), bytes[0].Bytes())
+ assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000033333333"), bytes[1].Bytes())
+ assert.Equal(t, common.Hex2Bytes("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"), bytes[2].Bytes())
+ assert.Equal(t, common.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111"), bytes[3].Bytes())
+ assert.Equal(t, common.Hex2Bytes("2222222222222222222222222222222222222222222222222222222222222222"), bytes[4].Bytes())
+
+ // unmarshal account
+
+ flatBytes := []byte("")
+
+ for _, item := range bytes {
+ flatBytes = append(flatBytes, item.Bytes()...)
+ }
+
+ acc2, err := UnmarshalStateAccount(flatBytes)
+
+ assert.Nil(t, err)
+ assertAccountsEqual(t, &acc, acc2)
+}
+
+func TestMarshalUnmarshalAccountWithMaxFields(t *testing.T) {
+ maxFieldElement := new(big.Int).Sub(constants.Q, big.NewInt(1))
+
+ acc := StateAccount{
+ Nonce: math.MaxUint64,
+ Balance: maxFieldElement,
+ Root: common.BigToHash(maxFieldElement),
+ KeccakCodeHash: common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
+ PoseidonCodeHash: maxFieldElement.Bytes(),
+ CodeSize: math.MaxUint64,
+ }
+
+ // marshal account
+
+ bytes, flag := acc.MarshalFields()
+
+ assert.Equal(t, 5, len(bytes))
+ assert.Equal(t, uint32(8), flag)
+
+ assert.Equal(t, common.Hex2Bytes("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), bytes[0].Bytes())
+ assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[1].Bytes())
+ assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[2].Bytes())
+ assert.Equal(t, common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), bytes[3].Bytes())
+ assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[4].Bytes())
+
+ // unmarshal account
+
+ flatBytes := []byte("")
+
+ for _, item := range bytes {
+ flatBytes = append(flatBytes, item.Bytes()...)
+ }
+
+ acc2, err := UnmarshalStateAccount(flatBytes)
+
+ assert.Nil(t, err)
+ assertAccountsEqual(t, &acc, acc2)
+}
+
+func TestMarshalPanic(t *testing.T) {
+ assert.PanicsWithValue(t, "StateAccount balance nil", func() {
+ acc := StateAccount{}
+ acc.MarshalFields()
+ })
+
+ assert.PanicsWithValue(t, "StateAccount balance overflow", func() {
+ acc := StateAccount{Balance: constants.Q}
+ acc.MarshalFields()
+ })
+
+ assert.PanicsWithValue(t, "StateAccount balance overflow", func() {
+ balance := new(big.Int)
+ balance, ok := balance.SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
+ assert.True(t, ok)
+ acc := StateAccount{Balance: balance}
+ acc.MarshalFields()
+ })
+
+ assert.PanicsWithValue(t, "StateAccount root overflow", func() {
+ acc := StateAccount{Balance: big.NewInt(0), Root: common.BigToHash(constants.Q)}
+ acc.MarshalFields()
+ })
+
+ assert.PanicsWithValue(t, "StateAccount poseidonCodeHash overflow", func() {
+ acc := StateAccount{Balance: big.NewInt(0), PoseidonCodeHash: constants.Q.Bytes()}
+ acc.MarshalFields()
+ })
+}
diff --git a/eth/backend.go b/eth/backend.go
index c8eea074c7..85139c0237 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -514,7 +514,7 @@ func (s *Ethereum) SyncService() *sync_service.SyncService { return s.syncServic
// network protocols to start.
func (s *Ethereum) Protocols() []p2p.Protocol {
protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates)
- if s.config.SnapshotCache > 0 {
+ if !s.blockchain.Config().Scroll.ZktrieEnabled() && s.config.SnapshotCache > 0 {
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
}
return protos
@@ -533,12 +533,12 @@ func (s *Ethereum) Start() error {
// Figure out a max peers count based on the server limits
maxPeers := s.p2pServer.MaxPeers
- if s.config.LightServ > 0 {
- if s.config.LightPeers >= s.p2pServer.MaxPeers {
- return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
- }
- maxPeers -= s.config.LightPeers
- }
+ // if s.config.LightServ > 0 {
+ // if s.config.LightPeers >= s.p2pServer.MaxPeers {
+ // return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
+ // }
+ // maxPeers -= s.config.LightPeers
+ // }
// Start the networking layer and the light server if requested
s.handler.Start(maxPeers)
return nil
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index a076fb7377..28f77f19be 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -286,21 +286,21 @@ func TestFilters(t *testing.T) {
}{
{
f: sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
}, {
f: sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
- want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
}, {
@@ -309,13 +309,13 @@ func TestFilters(t *testing.T) {
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}),
}, {
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
- want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
- want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
}, {
@@ -329,10 +329,10 @@ func TestFilters(t *testing.T) {
err: "safe header not found",
}, {
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xfaf12ee5c179618e694f8d3f45e0d962e1e64a45a57627792a22379a49270c08","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
- want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
+ want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xfaf12ee5c179618e694f8d3f45e0d962e1e64a45a57627792a22379a49270c08","logIndex":"0x0","removed":false}]`,
}, {
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
err: "invalid block range",
diff --git a/go.mod b/go.mod
index 68cb194c86..67b7d8211d 100644
--- a/go.mod
+++ b/go.mod
@@ -42,7 +42,7 @@ require (
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.3
github.com/huin/goupnp v1.3.0
- github.com/iden3/go-iden3-crypto v0.0.15
+ github.com/iden3/go-iden3-crypto v0.0.12
github.com/influxdata/influxdb-client-go/v2 v2.4.0
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/jackpal/go-nat-pmp v1.0.2
@@ -57,6 +57,7 @@ require (
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7
github.com/rs/cors v1.7.0
+ github.com/scroll-tech/zktrie v0.6.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.2.0
github.com/stretchr/testify v1.8.4
diff --git a/go.sum b/go.sum
index 7fc5de3a6a..4a8641f70a 100644
--- a/go.sum
+++ b/go.sum
@@ -154,6 +154,7 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
@@ -351,8 +352,8 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4=
-github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
+github.com/iden3/go-iden3-crypto v0.0.12 h1:dXZF+R9iI07DK49LHX/EKC3jTa0O2z+TUyvxjGK7V38=
+github.com/iden3/go-iden3-crypto v0.0.12/go.mod h1:swXIv0HFbJKobbQBtsB50G7IHr6PbTowutSew/iBEoo=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k=
@@ -542,6 +543,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/scroll-tech/zktrie v0.6.0 h1:xLrMAO31Yo2BiPg1jtYKzcjpEFnXy8acbB7iIsyshPs=
+github.com/scroll-tech/zktrie v0.6.0/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
@@ -621,6 +624,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -696,6 +700,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
@@ -774,6 +779,7 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
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-20211216021012-1d35b9e2eb4e/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/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
index 379636d5f3..4c2393b90b 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
@@ -4,17 +4,17 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
+ "hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
+ "stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
index 759dbf69e9..6810490348 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x200",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
"timestamp": "0x0",
"totalDifficulty": "0x1",
"transactions": [],
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
index 3526da1219..241ca92afe 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
@@ -4,22 +4,22 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
+ "parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
+ "stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
"timestamp": "0x5a",
"totalDifficulty": "0x1",
"transactions": [
{
- "blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "blockHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"blockNumber": "0x9",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gas": "0x5208",
@@ -33,7 +33,8 @@
"type": "0x0",
"v": "0x1b",
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
- "s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
+ "s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d",
+ "sender": "0x0000000000000000000000000000000000000000"
}
],
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
index 32fee83268..80442928e5 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
@@ -4,17 +4,17 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
+ "hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
+ "stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
"timestamp": "0x64",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
index 759dbf69e9..6810490348 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x200",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
"timestamp": "0x0",
"totalDifficulty": "0x1",
"transactions": [],
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
index 379636d5f3..4c2393b90b 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
@@ -4,17 +4,17 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
+ "hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
+ "stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
index 3526da1219..241ca92afe 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
@@ -4,22 +4,22 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
+ "parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
+ "stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
"timestamp": "0x5a",
"totalDifficulty": "0x1",
"transactions": [
{
- "blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "blockHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"blockNumber": "0x9",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gas": "0x5208",
@@ -33,7 +33,8 @@
"type": "0x0",
"v": "0x1b",
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
- "s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
+ "s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d",
+ "sender": "0x0000000000000000000000000000000000000000"
}
],
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
index 32fee83268..80442928e5 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
@@ -4,17 +4,17 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
+ "hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x26a",
- "stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
+ "stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
"timestamp": "0x64",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-tag-pending-fullTx.json b/internal/ethapi/testdata/eth_getBlockByNumber-tag-pending-fullTx.json
index 1d524d6ece..b42900f0d9 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-pending-fullTx.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-pending-fullTx.json
@@ -33,7 +33,8 @@
"chainId": "0x7fffffffffffffee",
"v": "0x0",
"r": "0x0",
- "s": "0x0"
+ "s": "0x0",
+ "sender": "0x0000000000000000000000000000000000000000"
}
],
"transactionsRoot": "0x98d9f6dd0aa479c0fb448f2627e9f1964aca699fccab8f6e95861547a4699e37",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
index 591fab673d..28eaac9d50 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
@@ -2,13 +2,14 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
+ "blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x1b09d63b",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
index f1e0db22c2..2e32d35ecd 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
@@ -1,12 +1,13 @@
[
{
- "blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
+ "blockHash": "0x7afe7ea18e15ea1bf0b10ae924e42a8e01023c4b1f3c8058c45723a1e56b8868",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
"effectiveGasPrice": "0x2db16291",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0xcf50",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
index 520e30e4ea..5317535703 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
@@ -1,12 +1,13 @@
[
{
- "blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
+ "blockHash": "0xb2d0c9e52757eee01da7c6a6da6de23a4548c036c97f4f113c5e05d9daf17e1a",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
"effectiveGasPrice": "0x2325c42f",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x538d",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
index a71cf4b37f..a163d72ead 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
@@ -1,12 +1,13 @@
[
{
- "blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
+ "blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
"effectiveGasPrice": "0x281c2585",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5e28",
+ "l1Fee": "0x0",
"logs": [
{
"address": "0x0000000000000000000000000000000000031ec7",
@@ -19,7 +20,7 @@
"blockNumber": "0x3",
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
"transactionIndex": "0x0",
- "blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
+ "blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
"logIndex": "0x0",
"removed": false
}
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
index 3e16c3062e..379aa958d6 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
@@ -1,12 +1,13 @@
[
{
- "blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
+ "blockHash": "0x34044cfcfe129574966dc82cee6ec504c869d50ec2bd4a16eb330b04a277bcab",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
index 591fab673d..28eaac9d50 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
@@ -2,13 +2,14 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
+ "blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x1b09d63b",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
index dc61aa9a2e..1a5b1c6588 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -13,7 +13,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
"timestamp": "0x0",
"totalDifficulty": "0x1",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
index c1dc70f64f..7144cc2c29 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
+ "hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
+ "stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
index a63ff86700..68b3a0850b 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
+ "parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
+ "stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
"timestamp": "0x5a",
"totalDifficulty": "0x1",
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
index f2affcc1c9..cce8d31465 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
+ "hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
+ "stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
"timestamp": "0x64",
"totalDifficulty": "0x1",
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
index dc61aa9a2e..1a5b1c6588 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -13,7 +13,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
"timestamp": "0x0",
"totalDifficulty": "0x1",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
index c1dc70f64f..7144cc2c29 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
+ "hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
+ "parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
+ "stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
index a63ff86700..68b3a0850b 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
+ "parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
+ "stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
"timestamp": "0x5a",
"totalDifficulty": "0x1",
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
index f2affcc1c9..cce8d31465 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
@@ -4,16 +4,16 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x5208",
- "hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
+ "hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
+ "parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
+ "stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
"timestamp": "0x64",
"totalDifficulty": "0x1",
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
index c3a4a0deee..77e39d5544 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
@@ -1,13 +1,14 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
+ "blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x1b09d63b",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
index ad6d6152ec..1476a3450b 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
@@ -1,11 +1,12 @@
{
- "blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
+ "blockHash": "0x7afe7ea18e15ea1bf0b10ae924e42a8e01023c4b1f3c8058c45723a1e56b8868",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
"effectiveGasPrice": "0x2db16291",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0xcf50",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
index b3362260a0..a8bdbc2e4a 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
@@ -1,11 +1,12 @@
{
- "blockHash": "0x3fadc5bc916018a326732be829a2565b3acb960a8406f0f151a5e1fa971ea7dd",
+ "blockHash": "0xe1081b33b1e59d80de12700f8c123589b06d5629762a27d31919315e25265c9c",
"blockNumber": "0x5",
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
"cumulativeGasUsed": "0xe01c",
"effectiveGasPrice": "0x1ecb3fb4",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0xe01c",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
index cc0be1809e..602b4aa2b8 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
@@ -1,11 +1,12 @@
{
- "blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
+ "blockHash": "0xb2d0c9e52757eee01da7c6a6da6de23a4548c036c97f4f113c5e05d9daf17e1a",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
"effectiveGasPrice": "0x2325c42f",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x538d",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
index d3b6ef1c91..65a0661e00 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
@@ -1,11 +1,12 @@
{
- "blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
+ "blockHash": "0x34044cfcfe129574966dc82cee6ec504c869d50ec2bd4a16eb330b04a277bcab",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
+ "l1Fee": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
index 45a4f6d670..a5ab278da4 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
@@ -1,11 +1,12 @@
{
- "blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
+ "blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
"effectiveGasPrice": "0x281c2585",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5e28",
+ "l1Fee": "0x0",
"logs": [
{
"address": "0x0000000000000000000000000000000000031ec7",
@@ -18,7 +19,7 @@
"blockNumber": "0x3",
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
"transactionIndex": "0x0",
- "blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
+ "blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
"logIndex": "0x0",
"removed": false
}
diff --git a/les/server.go b/les/server.go
index d84856c7fb..bd7f3f026b 100644
--- a/les/server.go
+++ b/les/server.go
@@ -18,6 +18,7 @@ package les
import (
"crypto/ecdsa"
+ "errors"
"time"
"github.com/ethereum/go-ethereum/common/mclock"
@@ -82,6 +83,11 @@ func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*Les
if err != nil {
return nil, err
}
+ // Currently disable for zktrie
+ if e.BlockChain().Config().Scroll.ZktrieEnabled() {
+ return nil, errors.New("light server not work with zktrie storage")
+ }
+
// Calculate the number of threads used to service the light client
// requests based on the user-specified value.
threads := config.LightServ * 4 / 100
diff --git a/params/config.go b/params/config.go
index e85487bca6..2e26590ca4 100644
--- a/params/config.go
+++ b/params/config.go
@@ -339,6 +339,9 @@ type ChainConfig struct {
}
type ScrollConfig struct {
+ // Use zktrie [optional]
+ UseZktrie bool `json:"useZktrie,omitempty"`
+
// Maximum number of transactions per block [optional]
MaxTxPerBlock *int `json:"maxTxPerBlock,omitempty"`
@@ -373,6 +376,10 @@ func (s ScrollConfig) FeeVaultEnabled() bool {
return s.FeeVaultAddress != nil
}
+func (s ScrollConfig) ZktrieEnabled() bool {
+ return s.UseZktrie
+}
+
func (s ScrollConfig) ShouldIncludeL1Messages() bool {
return s.L1Config != nil && s.L1Config.NumL1MessagesPerBlock > 0
}
diff --git a/trie/database.go b/trie/database.go
index 1e59f0908f..54a5403bb8 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -18,6 +18,7 @@ package trie
import (
"errors"
+ "sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -33,6 +34,9 @@ type Config struct {
Preimages bool // Flag whether the preimage of node key is recorded
HashDB *hashdb.Config // Configs for hash-based scheme
PathDB *pathdb.Config // Configs for experimental path-based scheme
+
+ // zktrie related stuff
+ IsUsingZktrie bool
}
// HashDefaults represents a config for using hash-based scheme with
@@ -42,6 +46,19 @@ var HashDefaults = &Config{
HashDB: hashdb.Defaults,
}
+// HashDefaultsWithZktrie represents a config based on HashDefaults but with zktrie enabled.
+var HashDefaultsWithZktrie = &Config{
+ Preimages: false,
+ HashDB: hashdb.Defaults,
+ IsUsingZktrie: true,
+}
+
+// HashDefaultsWithPreimages represents a config based on HashDefaults but with Preimages enabled.
+var HashDefaultsWithPreimages = &Config{
+ Preimages: true,
+ HashDB: hashdb.Defaults,
+}
+
// backend defines the methods needed to access/update trie nodes in different
// state scheme.
type backend interface {
@@ -73,6 +90,9 @@ type backend interface {
// Close closes the trie database backend and releases all held resources.
Close() error
+
+ // database supplementary methods, to get the underlying fields
+ GetLock() *sync.RWMutex
}
// Database is the wrapper of the underlying backend which is shared by different
@@ -83,6 +103,10 @@ type Database struct {
diskdb ethdb.Database // Persistent database to store the snapshot
preimages *preimageStore // The store for caching preimages
backend backend // The backend for managing trie nodes
+
+ // zktrie related stuff
+ // TODO: It's a quick&dirty implementation. FIXME later.
+ rawDirties KvMap
}
// NewDatabase initializes the trie database with default settings, note
@@ -100,6 +124,8 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
config: config,
diskdb: diskdb,
preimages: preimages,
+ // scroll-related
+ rawDirties: make(KvMap),
}
if config.HashDB != nil && config.PathDB != nil {
log.Crit("Both 'hash' and 'path' mode are configured")
@@ -112,6 +138,19 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
return db
}
+func (db *Database) IsUsingZktrie() bool {
+ // compatible logic for light mode
+ if db == nil || db.config == nil {
+ return false
+ }
+ return db.config.IsUsingZktrie
+}
+
+func (db *Database) SetIsUsingZktrie(isUsingZktrie bool) {
+ // config must not be nil
+ db.config.IsUsingZktrie = isUsingZktrie
+}
+
// Reader returns a reader for accessing all trie nodes with provided state root.
// An error will be returned if the requested state is not available.
func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
@@ -142,6 +181,25 @@ func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, n
// to disk. As a side effect, all pre-images accumulated up to this point are
// also written.
func (db *Database) Commit(root common.Hash, report bool) error {
+ batch := db.diskdb.NewBatch()
+
+ db.GetLock().Lock()
+ for _, v := range db.rawDirties {
+ batch.Put(v.K, v.V)
+ }
+ for k := range db.rawDirties {
+ delete(db.rawDirties, k)
+ }
+ db.GetLock().Unlock()
+ if err := batch.Write(); err != nil {
+ return err
+ }
+ batch.Reset()
+
+ if (root == common.Hash{}) {
+ return nil
+ }
+
if db.preimages != nil {
db.preimages.commit(true)
}
diff --git a/trie/database_supplement.go b/trie/database_supplement.go
new file mode 100644
index 0000000000..fc58fbb11b
--- /dev/null
+++ b/trie/database_supplement.go
@@ -0,0 +1,32 @@
+package trie
+
+import (
+ "sync"
+
+ "github.com/VictoriaMetrics/fastcache"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
+)
+
+func (db *Database) GetLock() *sync.RWMutex {
+ return db.backend.GetLock()
+}
+
+func (db *Database) GetCleans() *fastcache.Cache {
+ hdb, ok := db.backend.(*hashdb.Database)
+ if !ok {
+ panic("only hashdb supported")
+ }
+ return hdb.GetCleans()
+}
+
+// EmptyRoot indicate what root is for an empty trie, it depends on its underlying implement (zktrie or common trie)
+func (db *Database) EmptyRoot() common.Hash {
+ if db.IsUsingZktrie() {
+ return common.Hash{}
+ } else {
+ return types.EmptyRootHash
+ }
+}
diff --git a/trie/database_types.go b/trie/database_types.go
new file mode 100644
index 0000000000..ce07aaade6
--- /dev/null
+++ b/trie/database_types.go
@@ -0,0 +1,47 @@
+package trie
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "errors"
+)
+
+// ErrNotFound is used by the implementations of the interface db.Storage for
+// when a key is not found in the storage
+var ErrNotFound = errors.New("key not found")
+
+// KV contains a key (K) and a value (V)
+type KV struct {
+ K []byte
+ V []byte
+}
+
+// KvMap is a key-value map between a sha256 byte array hash, and a KV struct
+type KvMap map[[sha256.Size]byte]KV
+
+// Get retreives the value respective to a key from the KvMap
+func (m KvMap) Get(k []byte) ([]byte, bool) {
+ v, ok := m[sha256.Sum256(k)]
+ return v.V, ok
+}
+
+// Put stores a key and a value in the KvMap
+func (m KvMap) Put(k, v []byte) {
+ m[sha256.Sum256(k)] = KV{k, v}
+}
+
+// Concat concatenates arrays of bytes
+func Concat(vs ...[]byte) []byte {
+ var b bytes.Buffer
+ for _, v := range vs {
+ b.Write(v)
+ }
+ return b.Bytes()
+}
+
+// Clone clones a byte array into a new byte array
+func Clone(b0 []byte) []byte {
+ b1 := make([]byte, len(b0))
+ copy(b1, b0)
+ return b1
+}
diff --git a/trie/proof.go b/trie/proof.go
index a526a53402..ab282e2f96 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -115,6 +115,11 @@ func (t *StateTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
// key in a trie with the given root hash. VerifyProof returns an error if the
// proof contains invalid trie nodes or the wrong value.
func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
+ // test the type of proof (for trie or SMT)
+ if buf, _ := proofDb.Get(magicHash); buf != nil {
+ return VerifyProofSMT(rootHash, key, proofDb)
+ }
+
key = keybytesToHex(key)
wantHash := rootHash
for i := 0; ; i++ {
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 292ca59497..fc55d664bc 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -708,15 +708,15 @@ func TestTinyTrie(t *testing.T) {
_, accounts := makeAccounts(5)
trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3])
- if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root {
+ if exp, root := common.HexToHash("fc516c51c03bf9f1a0eec6ed6f6f5da743c2745dcd5670007519e6ec056f95a8"), trie.Hash(); exp != root {
t.Errorf("1: got %x, exp %x", root, exp)
}
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001338"), accounts[4])
- if exp, root := common.HexToHash("ec63b967e98a5720e7f720482151963982890d82c9093c0d486b7eb8883a66b1"), trie.Hash(); exp != root {
+ if exp, root := common.HexToHash("5070d3f144546fd13589ad90cd153954643fa4ca6c1a5f08683cbfbbf76e960c"), trie.Hash(); exp != root {
t.Errorf("2: got %x, exp %x", root, exp)
}
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001339"), accounts[4])
- if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root {
+ if exp, root := common.HexToHash("aa3fba77e50f6e931d8aacde70912be5bff04c7862f518ae06f3418dd4d37be3"), trie.Hash(); exp != root {
t.Errorf("3: got %x, exp %x", root, exp)
}
checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
@@ -740,7 +740,7 @@ func TestCommitAfterHash(t *testing.T) {
trie.Hash()
trie.Commit(false)
root := trie.Hash()
- exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6")
+ exp := common.HexToHash("f0c0681648c93b347479cd58c61995557f01294425bd031ce1943c2799bbd4ec")
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
@@ -847,9 +847,9 @@ func TestCommitSequence(t *testing.T) {
count int
expWriteSeqHash []byte
}{
- {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066")},
- {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e")},
- {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7")},
+ {20, common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492")},
+ {200, common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a")},
+ {2000, common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac")},
} {
addresses, accounts := makeAccounts(tc.count)
// This spongeDb is used to check the sequence of disk-db-writes
diff --git a/trie/triedb/hashdb/database.go b/trie/triedb/hashdb/database.go
index 764ab24ec8..78547a1d93 100644
--- a/trie/triedb/hashdb/database.go
+++ b/trie/triedb/hashdb/database.go
@@ -36,10 +36,10 @@ import (
)
var (
- memcacheCleanHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/hit", nil)
- memcacheCleanMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/miss", nil)
- memcacheCleanReadMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/read", nil)
- memcacheCleanWriteMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/write", nil)
+ MemcacheCleanHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/hit", nil)
+ MemcacheCleanMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/miss", nil)
+ MemcacheCleanReadMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/read", nil)
+ MemcacheCleanWriteMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/write", nil)
memcacheDirtyHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/dirty/hit", nil)
memcacheDirtyMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/dirty/miss", nil)
@@ -193,8 +193,8 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
// Retrieve the node from the clean cache if available
if db.cleans != nil {
if enc := db.cleans.Get(nil, hash[:]); enc != nil {
- memcacheCleanHitMeter.Mark(1)
- memcacheCleanReadMeter.Mark(int64(len(enc)))
+ MemcacheCleanHitMeter.Mark(1)
+ MemcacheCleanReadMeter.Mark(int64(len(enc)))
return enc, nil
}
}
@@ -215,8 +215,8 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
if len(enc) != 0 {
if db.cleans != nil {
db.cleans.Set(hash[:], enc)
- memcacheCleanMissMeter.Mark(1)
- memcacheCleanWriteMeter.Mark(int64(len(enc)))
+ MemcacheCleanMissMeter.Mark(1)
+ MemcacheCleanWriteMeter.Mark(int64(len(enc)))
}
return enc, nil
}
@@ -554,7 +554,7 @@ func (c *cleaner) Put(key []byte, rlp []byte) error {
// Move the flushed node into the clean cache to prevent insta-reloads
if c.db.cleans != nil {
c.db.cleans.Set(hash[:], rlp)
- memcacheCleanWriteMeter.Mark(int64(len(rlp)))
+ MemcacheCleanWriteMeter.Mark(int64(len(rlp)))
}
return nil
}
diff --git a/trie/triedb/hashdb/database_supplement.go b/trie/triedb/hashdb/database_supplement.go
new file mode 100644
index 0000000000..841bcc3711
--- /dev/null
+++ b/trie/triedb/hashdb/database_supplement.go
@@ -0,0 +1,15 @@
+package hashdb
+
+import (
+ "sync"
+
+ "github.com/VictoriaMetrics/fastcache"
+)
+
+func (db *Database) GetLock() *sync.RWMutex {
+ return &db.lock
+}
+
+func (db *Database) GetCleans() *fastcache.Cache {
+ return db.cleans
+}
diff --git a/trie/triedb/pathdb/database_supplement.go b/trie/triedb/pathdb/database_supplement.go
new file mode 100644
index 0000000000..d3d6e052d5
--- /dev/null
+++ b/trie/triedb/pathdb/database_supplement.go
@@ -0,0 +1,9 @@
+package pathdb
+
+import (
+ "sync"
+)
+
+func (db *Database) GetLock() *sync.RWMutex {
+ return &db.lock
+}
diff --git a/trie/zk_trie.go b/trie/zk_trie.go
new file mode 100644
index 0000000000..2533ff8238
--- /dev/null
+++ b/trie/zk_trie.go
@@ -0,0 +1,287 @@
+// Copyright 2015 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 (
+ "fmt"
+
+ zktrie "github.com/scroll-tech/zktrie/trie"
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto/poseidon"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie/trienode"
+)
+
+var magicHash []byte = []byte("THIS IS THE MAGIC INDEX FOR ZKTRIE")
+
+// wrap zktrie for trie interface
+type ZkTrie struct {
+ *zktrie.ZkTrie
+ db *ZktrieDatabase
+}
+
+func init() {
+ zkt.InitHashScheme(poseidon.HashFixedWithDomain)
+}
+
+func sanityCheckByte32Key(b []byte) {
+ if len(b) != 32 && len(b) != 20 {
+ panic(fmt.Errorf("do not support length except for 120bit and 256bit now. data: %v len: %v", b, len(b)))
+ }
+}
+
+// NewZkTrie creates a trie
+// NewZkTrie bypasses all the buffer mechanism in *Database, it directly uses the
+// underlying diskdb
+func NewZkTrie(root common.Hash, db *ZktrieDatabase) (*ZkTrie, error) {
+ tr, err := zktrie.NewZkTrie(*zkt.NewByte32FromBytes(root.Bytes()), db)
+ if err != nil {
+ return nil, err
+ }
+ return &ZkTrie{tr, db}, nil
+}
+
+// Get returns the value for key stored in the trie.
+// The value bytes must not be modified by the caller.
+func (t *ZkTrie) Get(key []byte) []byte {
+ sanityCheckByte32Key(key)
+ res, err := t.TryGet(key)
+ if err != nil {
+ log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ }
+ return res
+}
+
+func (t *ZkTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
+ key := address.Bytes()
+ sanityCheckByte32Key(key)
+ res, err := t.TryGet(key)
+ if res == nil || err != nil {
+ return nil, err
+ }
+ return types.UnmarshalStateAccount(res)
+}
+
+func (t *ZkTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
+ sanityCheckByte32Key(key)
+ enc, err := t.TryGet(key)
+ if err != nil || len(enc) == 0 {
+ return nil, err
+ }
+ _, content, _, err := rlp.Split(enc)
+ return content, err
+}
+
+func (t *ZkTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error {
+ return t.TryUpdateAccount(address.Bytes(), acc)
+}
+
+// TryUpdateAccount will abstract the write of an account to the
+// secure trie.
+func (t *ZkTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
+ sanityCheckByte32Key(key)
+ value, flag := acc.MarshalFields()
+ return t.ZkTrie.TryUpdate(key, flag, value)
+}
+
+// Update associates key with value in the trie. Subsequent calls to
+// Get will return value. If value has length zero, any existing value
+// is deleted from the trie and calls to Get will return nil.
+//
+// The value bytes must not be modified by the caller while they are
+// stored in the trie.
+func (t *ZkTrie) Update(key, value []byte) {
+ if err := t.TryUpdate(key, value); err != nil {
+ log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ }
+}
+
+// NOTE: value is restricted to length of bytes32.
+// we override the underlying zktrie's TryUpdate method
+func (t *ZkTrie) TryUpdate(key, value []byte) error {
+ sanityCheckByte32Key(key)
+ return t.ZkTrie.TryUpdate(key, 1, []zkt.Byte32{*zkt.NewByte32FromBytes(value)})
+}
+
+func (t *ZkTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error {
+ return nil
+}
+
+func (t *ZkTrie) UpdateStorage(_ common.Address, key, value []byte) error {
+ v, _ := rlp.EncodeToBytes(value)
+ return t.TryUpdate(key, v)
+}
+
+// Delete removes any existing value for key from the trie.
+func (t *ZkTrie) Delete(key []byte) {
+ sanityCheckByte32Key(key)
+ if err := t.TryDelete(key); err != nil {
+ log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ }
+}
+
+func (t *ZkTrie) DeleteAccount(address common.Address) error {
+ key := address.Bytes()
+ sanityCheckByte32Key(key)
+ return t.TryDelete(key)
+}
+
+func (t *ZkTrie) DeleteStorage(_ common.Address, key []byte) error {
+ sanityCheckByte32Key(key)
+ return t.TryDelete(key)
+}
+
+// GetKey returns the preimage of a hashed key that was
+// previously used to store a value.
+func (t *ZkTrie) GetKey(kHashBytes []byte) []byte {
+ // TODO: use a kv cache in memory
+ k, err := zkt.NewBigIntFromHashBytes(kHashBytes)
+ if err != nil {
+ log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
+ }
+ if t.db.db.preimages != nil {
+ return t.db.db.preimages.preimage(common.BytesToHash(k.Bytes()))
+ }
+ return nil
+}
+
+// Commit writes all nodes and the secure hash pre-images to the trie's database.
+// Nodes are stored with their sha3 hash as the key.
+//
+// Committing flushes nodes from memory. Subsequent Get calls will load nodes
+// from the database.
+//
+// func (t *ZkTrie) Commit(LeafCallback) (common.Hash, int, error) {
+// // in current implmentation, every update of trie already writes into database
+// // so Commmit does nothing
+// return t.Hash(), 0, nil
+// }
+func (t *ZkTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) {
+ // in current implmentation, every update of trie already writes into database
+ // so Commmit does nothing
+ return t.Hash(), nil, nil
+}
+
+// Hash returns the root hash of SecureBinaryTrie. It does not write to the
+// database and can be used even if the trie doesn't have one.
+func (t *ZkTrie) Hash() common.Hash {
+ var hash common.Hash
+ hash.SetBytes(t.ZkTrie.Hash())
+ return hash
+}
+
+// Copy returns a copy of SecureBinaryTrie.
+func (t *ZkTrie) Copy() *ZkTrie {
+ return &ZkTrie{t.ZkTrie.Copy(), t.db}
+}
+
+// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
+// starts at the key after the given start key.
+func (t *ZkTrie) NodeIterator(start []byte) (NodeIterator, error) {
+ /// FIXME
+ panic("not implemented")
+}
+
+// hashKey returns the hash of key as an ephemeral buffer.
+// The caller must not hold onto the return value because it will become
+// invalid on the next call to hashKey or secKey.
+/*func (t *ZkTrie) hashKey(key []byte) []byte {
+ if len(key) != 32 {
+ panic("non byte32 input to hashKey")
+ }
+ low16 := new(big.Int).SetBytes(key[:16])
+ high16 := new(big.Int).SetBytes(key[16:])
+ hash, err := poseidon.Hash([]*big.Int{low16, high16})
+ if err != nil {
+ panic(err)
+ }
+ return hash.Bytes()
+}
+*/
+
+// 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 node), ending
+// with the node that proves the absence of the key.
+// func (t *ZkTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
+func (t *ZkTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
+ fromLevel := uint(0)
+ err := t.ZkTrie.Prove(key, fromLevel, func(n *zktrie.Node) error {
+ nodeHash, err := n.NodeHash()
+ if err != nil {
+ return err
+ }
+
+ if n.Type == zktrie.NodeTypeLeaf_New {
+ preImage := t.GetKey(n.NodeKey.Bytes())
+ if len(preImage) > 0 {
+ n.KeyPreimage = &zkt.Byte32{}
+ copy(n.KeyPreimage[:], preImage)
+ //return fmt.Errorf("key preimage not found for [%x] ref %x", n.NodeKey.Bytes(), k.Bytes())
+ }
+ }
+ return proofDb.Put(nodeHash[:], n.Value())
+ })
+ if err != nil {
+ return err
+ }
+
+ // we put this special kv pair in db so we can distinguish the type and
+ // make suitable Proof
+ return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
+}
+
+// VerifyProof checks merkle proofs. The given proof must contain the value for
+// key in a trie with the given root hash. VerifyProof returns an error if the
+// proof contains invalid trie nodes or the wrong value.
+func VerifyProofSMT(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
+ h := zkt.NewHashFromBytes(rootHash.Bytes())
+ k, err := zkt.ToSecureKey(key)
+ if err != nil {
+ return nil, err
+ }
+
+ proof, n, err := zktrie.BuildZkTrieProof(h, k, len(key)*8, func(key *zkt.Hash) (*zktrie.Node, error) {
+ buf, _ := proofDb.Get(key[:])
+ if buf == nil {
+ return nil, zktrie.ErrKeyNotFound
+ }
+ n, err := zktrie.NewNodeFromBytes(buf)
+ return n, err
+ })
+
+ if err != nil {
+ // do not contain the key
+ return nil, err
+ } else if !proof.Existence {
+ return nil, nil
+ }
+
+ if zktrie.VerifyProofZkTrie(h, proof, n) {
+ return n.Data(), nil
+ } else {
+ return nil, fmt.Errorf("bad proof node %v", proof)
+ }
+}
diff --git a/trie/zk_trie_database.go b/trie/zk_trie_database.go
new file mode 100644
index 0000000000..4e3435c010
--- /dev/null
+++ b/trie/zk_trie_database.go
@@ -0,0 +1,172 @@
+package trie
+
+import (
+ "math/big"
+
+ "github.com/syndtr/goleveldb/leveldb"
+
+ zktrie "github.com/scroll-tech/zktrie/trie"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
+)
+
+// ZktrieDatabase Database adaptor implements zktrie.ZktrieDatbase
+// It also reverses the bit order of the key being persisted.
+// This ensures that the adjacent leaf in zktrie maintains minimal
+// distance when persisted with dictionary order in LevelDB.
+// Consequently, this optimizes the snapshot operation, allowing it
+// to iterate through adjacent leaves at a reduced cost.
+
+type ZktrieDatabase struct {
+ db *Database
+ prefix []byte
+}
+
+func NewZktrieDatabase(diskdb ethdb.Database) *ZktrieDatabase {
+ db := NewDatabase(diskdb, nil)
+ db.config.IsUsingZktrie = true
+ return &ZktrieDatabase{db: db, prefix: []byte{}}
+}
+
+// adhoc wrapper...
+func NewZktrieDatabaseFromTriedb(db *Database) *ZktrieDatabase {
+ db.config.IsUsingZktrie = true
+ return &ZktrieDatabase{db: db, prefix: []byte{}}
+}
+
+// Put saves a key:value into the Storage
+func (l *ZktrieDatabase) Put(k, v []byte) error {
+ k = bitReverse(k)
+ l.db.GetLock().Lock()
+ l.db.rawDirties.Put(Concat(l.prefix, k[:]), v)
+ l.db.GetLock().Unlock()
+ return nil
+}
+
+// Get retrieves a value from a key in the Storage
+func (l *ZktrieDatabase) Get(key []byte) ([]byte, error) {
+ key = bitReverse(key)
+ concatKey := Concat(l.prefix, key[:])
+ l.db.GetLock().RLock()
+ value, ok := l.db.rawDirties.Get(concatKey)
+ l.db.GetLock().RUnlock()
+ if ok {
+ return value, nil
+ }
+
+ if l.db.GetCleans() != nil {
+ if enc := l.db.GetCleans().Get(nil, concatKey); enc != nil {
+ hashdb.MemcacheCleanHitMeter.Mark(1)
+ hashdb.MemcacheCleanReadMeter.Mark(int64(len(enc)))
+ return enc, nil
+ }
+ }
+
+ v, err := l.db.diskdb.Get(concatKey)
+ if err == leveldb.ErrNotFound {
+ return nil, zktrie.ErrKeyNotFound
+ }
+ if l.db.GetCleans() != nil {
+ l.db.GetCleans().Set(concatKey[:], v)
+ hashdb.MemcacheCleanMissMeter.Mark(1)
+ hashdb.MemcacheCleanWriteMeter.Mark(int64(len(v)))
+ }
+ return v, err
+}
+
+func (l *ZktrieDatabase) UpdatePreimage(preimage []byte, hashField *big.Int) {
+ db := l.db
+ if db.preimages != nil { // Ugly direct check but avoids the below write lock
+ // we must copy the input key
+ db.preimages.insertPreimage(map[common.Hash][]byte{common.BytesToHash(hashField.Bytes()): common.CopyBytes(preimage)})
+ }
+}
+
+// Iterate implements the method Iterate of the interface Storage
+func (l *ZktrieDatabase) Iterate(f func([]byte, []byte) (bool, error)) error {
+ iter := l.db.diskdb.NewIterator(l.prefix, nil)
+ defer iter.Release()
+ for iter.Next() {
+ localKey := bitReverse(iter.Key()[len(l.prefix):])
+ if cont, err := f(localKey, iter.Value()); err != nil {
+ return err
+ } else if !cont {
+ break
+ }
+ }
+ iter.Release()
+ return iter.Error()
+}
+
+// Close implements the method Close of the interface Storage
+func (l *ZktrieDatabase) Close() {
+ // FIXME: is this correct?
+ if err := l.db.diskdb.Close(); err != nil {
+ panic(err)
+ }
+}
+
+// List implements the method List of the interface Storage
+func (l *ZktrieDatabase) List(limit int) ([]KV, error) {
+ ret := []KV{}
+ err := l.Iterate(func(key []byte, value []byte) (bool, error) {
+ ret = append(ret, KV{K: Clone(key), V: Clone(value)})
+ if len(ret) == limit {
+ return false, nil
+ }
+ return true, nil
+ })
+ return ret, err
+}
+
+func bitReverseForNibble(b byte) byte {
+ switch b {
+ case 0:
+ return 0
+ case 1:
+ return 8
+ case 2:
+ return 4
+ case 3:
+ return 12
+ case 4:
+ return 2
+ case 5:
+ return 10
+ case 6:
+ return 6
+ case 7:
+ return 14
+ case 8:
+ return 1
+ case 9:
+ return 9
+ case 10:
+ return 5
+ case 11:
+ return 13
+ case 12:
+ return 3
+ case 13:
+ return 11
+ case 14:
+ return 7
+ case 15:
+ return 15
+ default:
+ panic("unexpected input")
+ }
+}
+
+func bitReverse(inp []byte) (out []byte) {
+ l := len(inp)
+ out = make([]byte, l)
+
+ for i, b := range inp {
+ out[l-i-1] = bitReverseForNibble(b&15)<<4 + bitReverseForNibble(b>>4)
+ }
+
+ return
+}
diff --git a/trie/zk_trie_database_test.go b/trie/zk_trie_database_test.go
new file mode 100644
index 0000000000..f7cbc2e797
--- /dev/null
+++ b/trie/zk_trie_database_test.go
@@ -0,0 +1,63 @@
+package trie
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// grep from `feat/snap`
+func reverseBitInPlace(b []byte) {
+ var v [8]uint8
+ for i := 0; i < len(b); i++ {
+ for j := 0; j < 8; j++ {
+ v[j] = (b[i] >> j) & 1
+ }
+ var tmp uint8 = 0
+ for j := 0; j < 8; j++ {
+ tmp |= v[8-j-1] << j
+ }
+ b[i] = tmp
+ }
+}
+
+func reverseBytesInPlace(b []byte) {
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+}
+
+func TestBitReverse(t *testing.T) {
+ for _, testBytes := range [][]byte{
+ common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"),
+ common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e"),
+ common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"),
+ common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f"),
+ common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"),
+ common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d"),
+ } {
+ b1 := bitReverse(testBytes)
+ reverseBitInPlace(testBytes)
+ reverseBytesInPlace(testBytes)
+ if !bytes.Equal(b1, testBytes) {
+ t.Errorf("unexpected bit reversed %x vs %x", b1, testBytes)
+ }
+ }
+}
+
+func TestBitDoubleReverse(t *testing.T) {
+ for _, testBytes := range [][]byte{
+ common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"),
+ common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e"),
+ common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"),
+ common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f"),
+ common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"),
+ common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d"),
+ } {
+ b := bitReverse(bitReverse(testBytes))
+ if !bytes.Equal(b, testBytes) {
+ t.Errorf("unexpected double bit reversed %x vs %x", b, testBytes)
+ }
+ }
+}
diff --git a/trie/zk_trie_impl_test.go b/trie/zk_trie_impl_test.go
new file mode 100644
index 0000000000..1add03d7db
--- /dev/null
+++ b/trie/zk_trie_impl_test.go
@@ -0,0 +1,289 @@
+package trie
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/iden3/go-iden3-crypto/constants"
+ cryptoUtils "github.com/iden3/go-iden3-crypto/utils"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ zktrie "github.com/scroll-tech/zktrie/trie"
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// we do not need zktrie impl anymore, only made a wrapper for adapting testing
+type zkTrieImplTestWrapper struct {
+ *zktrie.ZkTrieImpl
+}
+
+func newZkTrieImpl(storage *ZktrieDatabase, maxLevels int) (*zkTrieImplTestWrapper, error) {
+ return newZkTrieImplWithRoot(storage, &zkt.HashZero, maxLevels)
+}
+
+// NewZkTrieImplWithRoot loads a new ZkTrieImpl. If in the storage already exists one
+// will open that one, if not, will create a new one.
+func newZkTrieImplWithRoot(storage *ZktrieDatabase, root *zkt.Hash, maxLevels int) (*zkTrieImplTestWrapper, error) {
+ impl, err := zktrie.NewZkTrieImplWithRoot(storage, root, maxLevels)
+ if err != nil {
+ return nil, err
+ }
+
+ return &zkTrieImplTestWrapper{impl}, nil
+}
+
+// AddWord
+// Deprecated: Add a Bytes32 kv to ZkTrieImpl, only for testing
+func (mt *zkTrieImplTestWrapper) AddWord(kPreimage, vPreimage *zkt.Byte32) error {
+ k, err := kPreimage.Hash()
+ if err != nil {
+ return err
+ }
+
+ if v, _ := mt.TryGet(k.Bytes()); v != nil {
+ return zktrie.ErrEntryIndexAlreadyExists
+ }
+
+ return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBigInt(k), 1, []zkt.Byte32{*vPreimage})
+}
+
+// GetLeafNodeByWord
+// Deprecated: Get a Bytes32 kv to ZkTrieImpl, only for testing
+func (mt *zkTrieImplTestWrapper) GetLeafNodeByWord(kPreimage *zkt.Byte32) (*zktrie.Node, error) {
+ k, err := kPreimage.Hash()
+ if err != nil {
+ return nil, err
+ }
+ return mt.ZkTrieImpl.GetLeafNode(zkt.NewHashFromBigInt(k))
+}
+
+// Deprecated: only for testing
+func (mt *zkTrieImplTestWrapper) UpdateWord(kPreimage, vPreimage *zkt.Byte32) error {
+ k, err := kPreimage.Hash()
+ if err != nil {
+ return err
+ }
+
+ return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBigInt(k), 1, []zkt.Byte32{*vPreimage})
+}
+
+// Deprecated: only for testing
+func (mt *zkTrieImplTestWrapper) DeleteWord(kPreimage *zkt.Byte32) error {
+ k, err := kPreimage.Hash()
+ if err != nil {
+ return err
+ }
+ return mt.ZkTrieImpl.TryDelete(zkt.NewHashFromBigInt(k))
+}
+
+func (mt *zkTrieImplTestWrapper) TryGet(key []byte) ([]byte, error) {
+ return mt.ZkTrieImpl.TryGet(zkt.NewHashFromBytes(key))
+}
+
+func (mt *zkTrieImplTestWrapper) TryDelete(key []byte) error {
+ return mt.ZkTrieImpl.TryDelete(zkt.NewHashFromBytes(key))
+}
+
+// TryUpdateAccount will abstract the write of an account to the trie
+func (mt *zkTrieImplTestWrapper) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
+ value, flag := acc.MarshalFields()
+ return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBytes(key), flag, value)
+}
+
+// NewHashFromHex returns a *Hash representation of the given hex string
+func NewHashFromHex(h string) (*zkt.Hash, error) {
+ return zkt.NewHashFromCheckedBytes(common.FromHex(h))
+}
+
+type Fatalable interface {
+ Fatal(args ...interface{})
+}
+
+func newTestingMerkle(f Fatalable, numLevels int) *zkTrieImplTestWrapper {
+ mt, err := newZkTrieImpl(NewZktrieDatabase(rawdb.NewMemoryDatabase()), numLevels)
+ if err != nil {
+ f.Fatal(err)
+ return nil
+ }
+ return mt
+}
+
+func TestHashParsers(t *testing.T) {
+ h0 := zkt.NewHashFromBigInt(big.NewInt(0))
+ assert.Equal(t, "0", h0.String())
+ h1 := zkt.NewHashFromBigInt(big.NewInt(1))
+ assert.Equal(t, "1", h1.String())
+ h10 := zkt.NewHashFromBigInt(big.NewInt(10))
+ assert.Equal(t, "10", h10.String())
+
+ h7l := zkt.NewHashFromBigInt(big.NewInt(1234567))
+ assert.Equal(t, "1234567", h7l.String())
+ h8l := zkt.NewHashFromBigInt(big.NewInt(12345678))
+ assert.Equal(t, "12345678...", h8l.String())
+
+ b, ok := new(big.Int).SetString("4932297968297298434239270129193057052722409868268166443802652458940273154854", 10) //nolint:lll
+ assert.True(t, ok)
+ h := zkt.NewHashFromBigInt(b)
+ assert.Equal(t, "4932297968297298434239270129193057052722409868268166443802652458940273154854", h.BigInt().String()) //nolint:lll
+ assert.Equal(t, "49322979...", h.String())
+ assert.Equal(t, "0ae794eb9c3d8bbb9002e993fc2ed301dcbd2af5508ed072c375e861f1aa5b26", h.Hex())
+
+ b1, err := zkt.NewBigIntFromHashBytes(b.Bytes())
+ assert.Nil(t, err)
+ assert.Equal(t, new(big.Int).SetBytes(b.Bytes()).String(), b1.String())
+
+ b2, err := zkt.NewHashFromCheckedBytes(b.Bytes())
+ assert.Nil(t, err)
+ assert.Equal(t, b.String(), b2.BigInt().String())
+
+ h2, err := NewHashFromHex(h.Hex())
+ assert.Nil(t, err)
+ assert.Equal(t, h, h2)
+ _, err = NewHashFromHex("0x12")
+ assert.NotNil(t, err)
+
+ // check limits
+ a := new(big.Int).Sub(constants.Q, big.NewInt(1))
+ testHashParsers(t, a)
+ a = big.NewInt(int64(1))
+ testHashParsers(t, a)
+}
+
+func testHashParsers(t *testing.T, a *big.Int) {
+ require.True(t, cryptoUtils.CheckBigIntInField(a))
+ h := zkt.NewHashFromBigInt(a)
+ assert.Equal(t, a, h.BigInt())
+ hFromBytes, err := zkt.NewHashFromCheckedBytes(h.Bytes())
+ assert.Nil(t, err)
+ assert.Equal(t, h, hFromBytes)
+ assert.Equal(t, a, hFromBytes.BigInt())
+ assert.Equal(t, a.String(), hFromBytes.BigInt().String())
+ hFromHex, err := NewHashFromHex(h.Hex())
+ assert.Nil(t, err)
+ assert.Equal(t, h, hFromHex)
+
+ aBIFromHBytes, err := zkt.NewBigIntFromHashBytes(h.Bytes())
+ assert.Nil(t, err)
+ assert.Equal(t, a, aBIFromHBytes)
+ assert.Equal(t, new(big.Int).SetBytes(a.Bytes()).String(), aBIFromHBytes.String())
+}
+
+func TestMerkleTree_AddUpdateGetWord(t *testing.T) {
+ mt := newTestingMerkle(t, 10)
+ err := mt.AddWord(&zkt.Byte32{1}, &zkt.Byte32{2})
+ assert.Nil(t, err)
+ err = mt.AddWord(&zkt.Byte32{3}, &zkt.Byte32{4})
+ assert.Nil(t, err)
+ err = mt.AddWord(&zkt.Byte32{5}, &zkt.Byte32{6})
+ assert.Nil(t, err)
+ err = mt.AddWord(&zkt.Byte32{5}, &zkt.Byte32{7})
+ assert.Equal(t, zktrie.ErrEntryIndexAlreadyExists, err)
+
+ node, err := mt.GetLeafNodeByWord(&zkt.Byte32{1})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{2})[:], node.ValuePreimage[0][:])
+ node, err = mt.GetLeafNodeByWord(&zkt.Byte32{3})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{4})[:], node.ValuePreimage[0][:])
+ node, err = mt.GetLeafNodeByWord(&zkt.Byte32{5})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{6})[:], node.ValuePreimage[0][:])
+
+ err = mt.UpdateWord(&zkt.Byte32{1}, &zkt.Byte32{7})
+ assert.Nil(t, err)
+ err = mt.UpdateWord(&zkt.Byte32{3}, &zkt.Byte32{8})
+ assert.Nil(t, err)
+ err = mt.UpdateWord(&zkt.Byte32{5}, &zkt.Byte32{9})
+ assert.Nil(t, err)
+
+ node, err = mt.GetLeafNodeByWord(&zkt.Byte32{1})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{7})[:], node.ValuePreimage[0][:])
+ node, err = mt.GetLeafNodeByWord(&zkt.Byte32{3})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{8})[:], node.ValuePreimage[0][:])
+ node, err = mt.GetLeafNodeByWord(&zkt.Byte32{5})
+ assert.Nil(t, err)
+ assert.Equal(t, len(node.ValuePreimage), 1)
+ assert.Equal(t, (&zkt.Byte32{9})[:], node.ValuePreimage[0][:])
+ _, err = mt.GetLeafNodeByWord(&zkt.Byte32{100})
+ assert.Equal(t, zktrie.ErrKeyNotFound, err)
+}
+
+func TestMerkleTree_UpdateAccount(t *testing.T) {
+ mt := newTestingMerkle(t, 10)
+
+ acc1 := &types.StateAccount{
+ Nonce: 1,
+ Balance: big.NewInt(10000000),
+ Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"),
+ KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
+ PoseidonCodeHash: common.HexToHash("0c0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab").Bytes(),
+ CodeSize: 0,
+ }
+ err := mt.TryUpdateAccount(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes(), acc1)
+ assert.Nil(t, err)
+
+ acc2 := &types.StateAccount{
+ Nonce: 5,
+ Balance: big.NewInt(50000000),
+ Root: common.HexToHash("0"),
+ KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
+ PoseidonCodeHash: common.HexToHash("05d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
+ CodeSize: 5,
+ }
+ err = mt.TryUpdateAccount(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes(), acc2)
+ assert.Nil(t, err)
+
+ bt, err := mt.TryGet(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
+ assert.Nil(t, err)
+
+ acc, err := types.UnmarshalStateAccount(bt)
+ assert.Nil(t, err)
+ assert.Equal(t, acc1.Nonce, acc.Nonce)
+ assert.Equal(t, acc1.Balance.Uint64(), acc.Balance.Uint64())
+ assert.Equal(t, acc1.Root.Bytes(), acc.Root.Bytes())
+ assert.Equal(t, acc1.KeccakCodeHash, acc.KeccakCodeHash)
+ assert.Equal(t, acc1.PoseidonCodeHash, acc.PoseidonCodeHash)
+ assert.Equal(t, acc1.CodeSize, acc.CodeSize)
+
+ bt, err = mt.TryGet(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
+ assert.Nil(t, err)
+
+ acc, err = types.UnmarshalStateAccount(bt)
+ assert.Nil(t, err)
+ assert.Equal(t, acc2.Nonce, acc.Nonce)
+ assert.Equal(t, acc2.Balance.Uint64(), acc.Balance.Uint64())
+ assert.Equal(t, acc2.Root.Bytes(), acc.Root.Bytes())
+ assert.Equal(t, acc2.KeccakCodeHash, acc.KeccakCodeHash)
+ assert.Equal(t, acc2.PoseidonCodeHash, acc.PoseidonCodeHash)
+ assert.Equal(t, acc2.CodeSize, acc.CodeSize)
+
+ bt, err = mt.TryGet(common.HexToAddress("0x8dE13967F19410A7991D63c2c0179feBFDA0c261").Bytes())
+ assert.Nil(t, err)
+ assert.Nil(t, bt)
+
+ err = mt.TryDelete(common.HexToHash("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
+ assert.Nil(t, err)
+
+ bt, err = mt.TryGet(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
+ assert.Nil(t, err)
+ assert.Nil(t, bt)
+
+ err = mt.TryDelete(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
+ assert.Nil(t, err)
+
+ bt, err = mt.TryGet(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
+ assert.Nil(t, err)
+ assert.Nil(t, bt)
+}
diff --git a/trie/zk_trie_proof_test.go b/trie/zk_trie_proof_test.go
new file mode 100644
index 0000000000..99fefe474d
--- /dev/null
+++ b/trie/zk_trie_proof_test.go
@@ -0,0 +1,284 @@
+// Copyright 2015 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 (
+ "bytes"
+ mrand "math/rand"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb/memorydb"
+)
+
+func init() {
+ mrand.Seed(time.Now().Unix())
+}
+
+// makeProvers creates Merkle trie provers based on different implementations to
+// test all variations.
+func makeSMTProvers(mt *ZkTrie) []func(key []byte) *memorydb.Database {
+ var provers []func(key []byte) *memorydb.Database
+
+ // Create a direct trie based Merkle prover
+ provers = append(provers, func(key []byte) *memorydb.Database {
+ word := zkt.NewByte32FromBytesPaddingZero(key)
+ k, err := word.Hash()
+ if err != nil {
+ panic(err)
+ }
+ proofDB := memorydb.New()
+ err = mt.Prove(common.BytesToHash(k.Bytes()).Bytes(), proofDB)
+ if err != nil {
+ panic(err)
+ }
+
+ return proofDB
+ })
+ return provers
+}
+
+func verifyValue(proveVal []byte, vPreimage []byte) bool {
+ return bytes.Equal(proveVal, vPreimage)
+}
+
+func TestSMTOneElementProof(t *testing.T) {
+ tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
+ mt := &zkTrieImplTestWrapper{tr.Tree()}
+ err := mt.UpdateWord(
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("k"), 32)),
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
+ )
+ assert.Nil(t, err)
+ for i, prover := range makeSMTProvers(tr) {
+ keyBytes := bytes.Repeat([]byte("k"), 32)
+ proof := prover(keyBytes)
+ if proof == nil {
+ t.Fatalf("prover %d: nil proof", i)
+ }
+ if proof.Len() != 2 {
+ t.Errorf("prover %d: proof should have 1+1 element (including the magic kv)", i)
+ }
+ val, err := VerifyProof(common.BytesToHash(mt.Root().Bytes()), keyBytes, proof)
+ if err != nil {
+ t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
+ }
+ if !verifyValue(val, bytes.Repeat([]byte("v"), 32)) {
+ t.Fatalf("prover %d: verified value mismatch: want 'v' get %x", i, val)
+ }
+ }
+}
+
+func TestSMTProof(t *testing.T) {
+ mt, vals := randomZktrie(t, 500)
+ root := mt.Tree().Root()
+ for i, prover := range makeSMTProvers(mt) {
+ for _, kv := range vals {
+ proof := prover(kv.k)
+ if proof == nil {
+ t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
+ }
+ val, err := VerifyProof(common.BytesToHash(root.Bytes()), kv.k, proof)
+ if err != nil {
+ t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x\n", i, kv.k, err, proof)
+ }
+ if !verifyValue(val, zkt.NewByte32FromBytesPaddingZero(kv.v)[:]) {
+ t.Fatalf("prover %d: verified value mismatch for key %x, want %x, get %x", i, kv.k, kv.v, val)
+ }
+ }
+ }
+}
+
+func TestSMTBadProof(t *testing.T) {
+ mt, vals := randomZktrie(t, 500)
+ root := mt.Tree().Root()
+ for i, prover := range makeSMTProvers(mt) {
+ for _, kv := range vals {
+ proof := prover(kv.k)
+ if proof == nil {
+ t.Fatalf("prover %d: nil proof", i)
+ }
+ it := proof.NewIterator(nil, nil)
+ for i, d := 0, mrand.Intn(proof.Len()); i <= d; i++ {
+ it.Next()
+ }
+ key := it.Key()
+ val, _ := proof.Get(key)
+ proof.Delete(key)
+ it.Release()
+
+ mutateByte(val)
+ proof.Put(crypto.Keccak256(val), val)
+
+ if _, err := VerifyProof(common.BytesToHash(root.Bytes()), kv.k, proof); err == nil {
+ t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
+ }
+ }
+ }
+}
+
+// Tests that missing keys can also be proven. The test explicitly uses a single
+// entry trie and checks for missing keys both before and after the single entry.
+func TestSMTMissingKeyProof(t *testing.T) {
+ tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
+ mt := &zkTrieImplTestWrapper{tr.Tree()}
+ err := mt.UpdateWord(
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("k"), 32)),
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
+ )
+ assert.Nil(t, err)
+
+ prover := makeSMTProvers(tr)[0]
+
+ for i, key := range []string{"a", "j", "l", "z"} {
+ keyBytes := bytes.Repeat([]byte(key), 32)
+ proof := prover(keyBytes)
+
+ if proof.Len() != 2 {
+ t.Errorf("test %d: proof should have 2 element (with magic kv)", i)
+ }
+ val, err := VerifyProof(common.BytesToHash(mt.Root().Bytes()), keyBytes, proof)
+ if err != nil {
+ t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
+ }
+ if val != nil {
+ t.Fatalf("test %d: verified value mismatch: have %x, want nil", i, val)
+ }
+ }
+}
+
+func randomZktrie(t *testing.T, n int) (*ZkTrie, map[string]*kv) {
+ tr, err := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
+ if err != nil {
+ panic(err)
+ }
+ mt := &zkTrieImplTestWrapper{tr.Tree()}
+ vals := make(map[string]*kv)
+ for i := byte(0); i < 100; i++ {
+
+ value := &kv{common.LeftPadBytes([]byte{i}, 32), bytes.Repeat([]byte{i}, 32), false}
+ value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), bytes.Repeat([]byte{i}, 32), false}
+
+ err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value.k), zkt.NewByte32FromBytesPaddingZero(value.v))
+ assert.Nil(t, err)
+ err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value2.k), zkt.NewByte32FromBytesPaddingZero(value2.v))
+ assert.Nil(t, err)
+ vals[string(value.k)] = value
+ vals[string(value2.k)] = value2
+ }
+ for i := 0; i < n; i++ {
+ value := &kv{randBytes(32), randBytes(20), false}
+ err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value.k), zkt.NewByte32FromBytesPaddingZero(value.v))
+ assert.Nil(t, err)
+ vals[string(value.k)] = value
+ }
+
+ return tr, vals
+}
+
+// Tests that new "proof trace" feature
+func TestProofWithDeletion(t *testing.T) {
+ tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
+ mt := &zkTrieImplTestWrapper{tr.Tree()}
+ key1 := bytes.Repeat([]byte("l"), 32)
+ key2 := bytes.Repeat([]byte("m"), 32)
+ err := mt.UpdateWord(
+ zkt.NewByte32FromBytesPaddingZero(key1),
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
+ )
+ assert.NoError(t, err)
+ err = mt.UpdateWord(
+ zkt.NewByte32FromBytesPaddingZero(key2),
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("n"), 32)),
+ )
+ assert.NoError(t, err)
+
+ proof := memorydb.New()
+ s_key1, err := zkt.ToSecureKeyBytes(key1)
+ assert.NoError(t, err)
+
+ proofTracer := tr.NewProofTracer()
+
+ err = proofTracer.Prove(s_key1.Bytes(), 0, proof)
+ assert.NoError(t, err)
+ nd, err := tr.TryGet(key2)
+ assert.NoError(t, err)
+
+ s_key2, err := zkt.ToSecureKeyBytes(bytes.Repeat([]byte("x"), 32))
+ assert.NoError(t, err)
+
+ err = proofTracer.Prove(s_key2.Bytes(), 0, proof)
+ assert.NoError(t, err)
+ //assert.Equal(t, len(sibling1), len(delTracer.GetProofs()))
+
+ siblings, err := proofTracer.GetDeletionProofs()
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(siblings))
+
+ proofTracer.MarkDeletion(s_key1.Bytes())
+ siblings, err = proofTracer.GetDeletionProofs()
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(siblings))
+ l := len(siblings[0])
+ // a hacking to grep the value part directly from the encoded leaf node,
+ // notice the sibling of key `k*32`` is just the leaf of key `m*32`
+ assert.Equal(t, siblings[0][l-33:l-1], nd)
+
+ // Marking a key that is currently not hit (but terminated by an empty node)
+ // also causes it to be added to the deletion proof
+ proofTracer.MarkDeletion(s_key2.Bytes())
+ siblings, err = proofTracer.GetDeletionProofs()
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(siblings))
+
+ key3 := bytes.Repeat([]byte("x"), 32)
+ err = mt.UpdateWord(
+ zkt.NewByte32FromBytesPaddingZero(key3),
+ zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("z"), 32)),
+ )
+ assert.NoError(t, err)
+
+ proofTracer = tr.NewProofTracer()
+ err = proofTracer.Prove(s_key1.Bytes(), 0, proof)
+ assert.NoError(t, err)
+ err = proofTracer.Prove(s_key2.Bytes(), 0, proof)
+ assert.NoError(t, err)
+
+ proofTracer.MarkDeletion(s_key1.Bytes())
+ siblings, err = proofTracer.GetDeletionProofs()
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(siblings))
+
+ proofTracer.MarkDeletion(s_key2.Bytes())
+ siblings, err = proofTracer.GetDeletionProofs()
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(siblings))
+
+ // one of the siblings is just leaf for key2, while
+ // another one must be a middle node
+ match1 := bytes.Equal(siblings[0][l-33:l-1], nd)
+ match2 := bytes.Equal(siblings[1][l-33:l-1], nd)
+ assert.True(t, match1 || match2)
+ assert.False(t, match1 && match2)
+}
diff --git a/trie/zk_trie_test.go b/trie/zk_trie_test.go
new file mode 100644
index 0000000000..7675df013f
--- /dev/null
+++ b/trie/zk_trie_test.go
@@ -0,0 +1,270 @@
+// Copyright 2015 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 (
+ "bytes"
+ "encoding/binary"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
+)
+
+func newEmptyZkTrie() *ZkTrie {
+ trie, _ := NewZkTrie(
+ common.Hash{},
+ &ZktrieDatabase{
+ db: NewDatabase(rawdb.NewMemoryDatabase(),
+ &Config{Preimages: true}),
+ prefix: []byte{},
+ },
+ )
+ return trie
+}
+
+// makeTestSecureTrie creates a large enough secure trie for testing.
+func makeTestZkTrie() (*ZktrieDatabase, *ZkTrie, map[string][]byte) {
+ // Create an empty trie
+ triedb := NewZktrieDatabase(rawdb.NewMemoryDatabase())
+ trie, _ := NewZkTrie(common.Hash{}, triedb)
+
+ // Fill it with some arbitrary data
+ content := make(map[string][]byte)
+ for i := byte(0); i < 255; i++ {
+ // Map the same data under multiple keys
+ key, val := common.LeftPadBytes([]byte{1, i}, 32), bytes.Repeat([]byte{i}, 32)
+ content[string(key)] = val
+ trie.Update(key, val)
+
+ key, val = common.LeftPadBytes([]byte{2, i}, 32), bytes.Repeat([]byte{i}, 32)
+ content[string(key)] = val
+ trie.Update(key, val)
+
+ // Add some other data to inflate the trie
+ for j := byte(3); j < 13; j++ {
+ key, val = common.LeftPadBytes([]byte{j, i}, 32), bytes.Repeat([]byte{j, i}, 16)
+ content[string(key)] = val
+ trie.Update(key, val)
+ }
+ }
+ trie.Commit(false)
+
+ // Return the generated trie
+ return triedb, trie, content
+}
+
+func TestZktrieDelete(t *testing.T) {
+ t.Skip("var-len kv not supported")
+ trie := newEmptyZkTrie()
+ vals := []struct{ k, v string }{
+ {"do", "verb"},
+ {"ether", "wookiedoo"},
+ {"horse", "stallion"},
+ {"shaman", "horse"},
+ {"doge", "coin"},
+ {"ether", ""},
+ {"dog", "puppy"},
+ {"shaman", ""},
+ }
+ for _, val := range vals {
+ if val.v != "" {
+ trie.Update([]byte(val.k), []byte(val.v))
+ } else {
+ trie.Delete([]byte(val.k))
+ }
+ }
+ hash := trie.Hash()
+ exp := common.HexToHash("29b235a58c3c25ab83010c327d5932bcf05324b7d6b1185e650798034783ca9d")
+ if hash != exp {
+ t.Errorf("expected %x got %x", exp, hash)
+ }
+}
+
+func TestZktrieGetKey(t *testing.T) {
+ trie := newEmptyZkTrie()
+ key := []byte("0a1b2c3d4e5f6g7h8i9j0a1b2c3d4e5f")
+ value := []byte("9j8i7h6g5f4e3d2c1b0a9j8i7h6g5f4e")
+ trie.Update(key, value)
+
+ kPreimage := zkt.NewByte32FromBytesPaddingZero(key)
+ kHash, err := kPreimage.Hash()
+ assert.Nil(t, err)
+
+ if !bytes.Equal(trie.Get(key), value) {
+ t.Errorf("Get did not return bar")
+ }
+ if k := trie.GetKey(kHash.Bytes()); !bytes.Equal(k, key) {
+ t.Errorf("GetKey returned %q, want %q", k, key)
+ }
+}
+
+func TestZkTrieConcurrency(t *testing.T) {
+ // Create an initial trie and copy if for concurrent access
+ _, trie, _ := makeTestZkTrie()
+
+ threads := runtime.NumCPU()
+ tries := make([]*ZkTrie, threads)
+ for i := 0; i < threads; i++ {
+ cpy := *trie
+ tries[i] = &cpy
+ }
+ // Start a batch of goroutines interactng with the trie
+ pend := new(sync.WaitGroup)
+ pend.Add(threads)
+ for i := 0; i < threads; i++ {
+ go func(index int) {
+ defer pend.Done()
+
+ for j := byte(0); j < 255; j++ {
+ // Map the same data under multiple keys
+ key, val := common.LeftPadBytes([]byte{byte(index), 1, j}, 32), bytes.Repeat([]byte{j}, 32)
+ tries[index].Update(key, val)
+
+ key, val = common.LeftPadBytes([]byte{byte(index), 2, j}, 32), bytes.Repeat([]byte{j}, 32)
+ tries[index].Update(key, val)
+
+ // Add some other data to inflate the trie
+ for k := byte(3); k < 13; k++ {
+ key, val = common.LeftPadBytes([]byte{byte(index), k, j}, 32), bytes.Repeat([]byte{k, j}, 16)
+ tries[index].Update(key, val)
+ }
+ }
+ tries[index].Commit(false)
+ }(i)
+ }
+ // Wait for all threads to finish
+ pend.Wait()
+}
+
+func tempDBZK(b *testing.B) (string, *Database) {
+ dir, err := ioutil.TempDir("", "zktrie-bench")
+ assert.NoError(b, err)
+
+ diskdb, err := rawdb.NewLevelDBDatabase(dir, 256, 0, "", false)
+ assert.NoError(b, err)
+ config := &Config{
+ Preimages: true,
+ HashDB: &hashdb.Config{CleanCacheSize: 256},
+ IsUsingZktrie: true,
+ }
+ return dir, NewDatabase(diskdb, config)
+}
+
+const benchElemCountZk = 10000
+
+func BenchmarkZkTrieGet(b *testing.B) {
+ dir, tmpdb := tempDBZK(b)
+ zkTrie, _ := NewZkTrie(common.Hash{}, NewZktrieDatabaseFromTriedb(tmpdb))
+ defer func() {
+ ldb := zkTrie.db.db.diskdb
+ ldb.Close()
+ os.RemoveAll(dir)
+ }()
+
+ k := make([]byte, 32)
+ for i := 0; i < benchElemCountZk; i++ {
+ binary.LittleEndian.PutUint64(k, uint64(i))
+
+ err := zkTrie.TryUpdate(k, k)
+ assert.NoError(b, err)
+ }
+
+ zkTrie.db.db.Commit(common.Hash{}, true)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ binary.LittleEndian.PutUint64(k, uint64(i))
+ _, err := zkTrie.TryGet(k)
+ assert.NoError(b, err)
+ }
+ b.StopTimer()
+}
+
+func BenchmarkZkTrieUpdate(b *testing.B) {
+ dir, tmpdb := tempDBZK(b)
+ zkTrie, _ := NewZkTrie(common.Hash{}, NewZktrieDatabaseFromTriedb(tmpdb))
+ defer func() {
+ ldb := zkTrie.db.db.diskdb
+ ldb.Close()
+ os.RemoveAll(dir)
+ }()
+
+ k := make([]byte, 32)
+ v := make([]byte, 32)
+ b.ReportAllocs()
+
+ for i := 0; i < benchElemCountZk; i++ {
+ binary.LittleEndian.PutUint64(k, uint64(i))
+ err := zkTrie.TryUpdate(k, k)
+ assert.NoError(b, err)
+ }
+ binary.LittleEndian.PutUint64(k, benchElemCountZk/2)
+
+ //zkTrie.Commit(false)
+ zkTrie.db.db.Commit(common.Hash{}, true)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ binary.LittleEndian.PutUint64(k, uint64(i))
+ binary.LittleEndian.PutUint64(v, 0xffffffff+uint64(i))
+ err := zkTrie.TryUpdate(k, v)
+ assert.NoError(b, err)
+ }
+ b.StopTimer()
+}
+
+func TestZkTrieDelete(t *testing.T) {
+ key := make([]byte, 32)
+ value := make([]byte, 32)
+ trie1 := newEmptyZkTrie()
+
+ var count int = 6
+ var hashes []common.Hash
+ hashes = append(hashes, trie1.Hash())
+ for i := 0; i < count; i++ {
+ binary.LittleEndian.PutUint64(key, uint64(i))
+ binary.LittleEndian.PutUint64(value, uint64(i))
+ err := trie1.TryUpdate(key, value)
+ assert.NoError(t, err)
+ hashes = append(hashes, trie1.Hash())
+ }
+
+ // binary.LittleEndian.PutUint64(key, uint64(0xffffff))
+ // err := trie1.TryDelete(key)
+ // assert.Equal(t, err, zktrie.ErrKeyNotFound)
+
+ trie1.Commit(false)
+
+ for i := count - 1; i >= 0; i-- {
+ binary.LittleEndian.PutUint64(key, uint64(i))
+ v, err := trie1.TryGet(key)
+ assert.NoError(t, err)
+ assert.NotEmpty(t, v)
+ err = trie1.TryDelete(key)
+ assert.NoError(t, err)
+ hash := trie1.Hash()
+ assert.Equal(t, hashes[i].Hex(), hash.Hex())
+ }
+}
diff --git a/trie/zkproof/orderer.go b/trie/zkproof/orderer.go
new file mode 100644
index 0000000000..bf2715cf65
--- /dev/null
+++ b/trie/zkproof/orderer.go
@@ -0,0 +1,349 @@
+package zkproof
+
+import (
+ "math/big"
+ "sort"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+type opIterator interface {
+ next() *types.AccountWrapper
+}
+
+type opOrderer interface {
+ readonly(bool)
+ absorb(*types.AccountWrapper)
+ absorbStorage(*types.AccountWrapper, *types.StorageWrapper)
+ end_absorb() opIterator
+}
+
+type iterateOp []*types.AccountWrapper
+
+func (ops *iterateOp) next() *types.AccountWrapper {
+
+ sl := *ops
+
+ if len(sl) == 0 {
+ return nil
+ }
+
+ *ops = sl[1:]
+ return sl[0]
+}
+
+type simpleOrderer struct {
+ readOnly int
+ savedOp []*types.AccountWrapper
+}
+
+func (od *simpleOrderer) SavedOp() []*types.AccountWrapper { return od.savedOp }
+
+func (od *simpleOrderer) readonly(mode bool) {
+ if mode {
+ od.readOnly += 1
+ } else if od.readOnly == 0 {
+ panic("unexpected readonly mode stack pop")
+ } else {
+ od.readOnly -= 1
+ }
+}
+
+func (od *simpleOrderer) absorb(st *types.AccountWrapper) {
+ if od.readOnly > 0 {
+ return
+ }
+ od.savedOp = append(od.savedOp, st)
+}
+
+func (od *simpleOrderer) absorbStorage(st *types.AccountWrapper, _ *types.StorageWrapper) {
+ od.absorb(st)
+}
+
+func (od *simpleOrderer) end_absorb() opIterator {
+ ret := iterateOp(od.savedOp)
+ return &ret
+}
+
+type multiOpIterator []opIterator
+
+func (opss *multiOpIterator) next() *types.AccountWrapper {
+
+ sl := *opss
+ if len(sl) == 0 {
+ return nil
+ }
+
+ op := sl[0].next()
+
+ for op == nil {
+
+ sl = sl[1:]
+ *opss = sl
+ if len(sl) == 0 {
+ return nil
+ }
+ op = sl[0].next()
+ }
+ return op
+}
+
+type rwTblOrderer struct {
+ readOnly int
+ readOnlySnapshot struct {
+ accounts map[string]*types.AccountWrapper
+ storages map[string]map[string]*types.StorageWrapper
+ }
+ initedData map[common.Address]*types.AccountWrapper
+
+ // help to track all accounts being touched, and provide the
+ // completed account status for storage updating
+ traced map[string]*types.AccountWrapper
+
+ opAccNonce map[string]*types.AccountWrapper
+ opAccBalance map[string]*types.AccountWrapper
+ opAccCodeHash map[string]*types.AccountWrapper
+ opStorage map[string]map[string]*types.StorageWrapper
+}
+
+func NewSimpleOrderer() *simpleOrderer { return &simpleOrderer{} }
+
+func NewRWTblOrderer(inited map[common.Address]*types.StateAccount) *rwTblOrderer {
+
+ initedAcc := make(map[common.Address]*types.AccountWrapper)
+ for addr, data := range inited {
+ if data == nil {
+ initedAcc[addr] = &types.AccountWrapper{
+ Address: addr,
+ Balance: (*hexutil.Big)(big.NewInt(0)),
+ }
+ } else {
+ bl := data.Balance
+ if bl == nil {
+ bl = big.NewInt(0)
+ }
+
+ initedAcc[addr] = &types.AccountWrapper{
+ Address: addr,
+ Nonce: data.Nonce,
+ Balance: (*hexutil.Big)(bl),
+ KeccakCodeHash: common.BytesToHash(data.KeccakCodeHash),
+ PoseidonCodeHash: common.BytesToHash(data.PoseidonCodeHash),
+ CodeSize: data.CodeSize,
+ }
+ }
+
+ }
+
+ return &rwTblOrderer{
+ initedData: initedAcc,
+ traced: make(map[string]*types.AccountWrapper),
+ opAccNonce: make(map[string]*types.AccountWrapper),
+ opAccBalance: make(map[string]*types.AccountWrapper),
+ opAccCodeHash: make(map[string]*types.AccountWrapper),
+ opStorage: make(map[string]map[string]*types.StorageWrapper),
+ }
+}
+
+func (od *rwTblOrderer) readonly(mode bool) {
+ if mode {
+ if od.readOnly == 0 {
+ od.readOnlySnapshot.accounts = make(map[string]*types.AccountWrapper)
+ od.readOnlySnapshot.storages = make(map[string]map[string]*types.StorageWrapper)
+ }
+ od.readOnly += 1
+ } else if od.readOnly == 0 {
+ panic("unexpected readonly mode stack pop")
+ } else {
+ od.readOnly -= 1
+ if od.readOnly == 0 {
+ for addrS, st := range od.readOnlySnapshot.accounts {
+ od.absorb(st)
+ if m, existed := od.readOnlySnapshot.storages[addrS]; existed {
+ for _, stg := range m {
+ st.Storage = stg
+ od.absorbStorage(st, nil)
+ }
+ }
+ }
+ }
+ }
+}
+
+func (od *rwTblOrderer) absorbStorage(st *types.AccountWrapper, before *types.StorageWrapper) {
+ if st.Storage == nil {
+ panic("do not call absorbStorage ")
+ }
+
+ od.absorb(st)
+ addrStr := st.Address.String()
+
+ if stg := st.Storage; stg != nil {
+ m, existed := od.opStorage[addrStr]
+ if !existed {
+ m = make(map[string]*types.StorageWrapper)
+ od.opStorage[addrStr] = m
+ }
+
+ // key must be unified into 32 bytes
+ keyBytes := hexutil.MustDecode(stg.Key)
+ keyStr := common.BytesToHash(keyBytes).String()
+
+ // trace every "touched" status for readOnly
+ if od.readOnly > 0 {
+ m, existed := od.readOnlySnapshot.storages[addrStr]
+ if !existed {
+ m = make(map[string]*types.StorageWrapper)
+ od.readOnlySnapshot.storages[addrStr] = m
+ }
+ if _, hashTraced := m[keyStr]; !hashTraced {
+ if before != nil {
+ m[keyStr] = before
+ } else {
+ m[keyStr] = stg
+ }
+
+ }
+ }
+
+ m[keyStr] = stg
+ }
+
+}
+
+func (od *rwTblOrderer) absorb(st *types.AccountWrapper) {
+
+ initedRef, existed := od.initedData[st.Address]
+ if !existed {
+ panic("encounter unprepared status")
+ }
+
+ addrStr := st.Address.String()
+
+ // trace every "touched" status for readOnly
+ if od.readOnly > 0 {
+ snapShot, existed := od.traced[addrStr]
+ if !existed {
+ snapShot = initedRef
+ }
+
+ if _, hasTraced := od.readOnlySnapshot.accounts[addrStr]; !hasTraced {
+ od.readOnlySnapshot.accounts[addrStr] = copyAccountState(snapShot)
+ }
+ }
+
+ if isDeletedAccount(st) {
+ // for account delete, made a safer data for status
+ st = &types.AccountWrapper{
+ Address: st.Address,
+ Balance: (*hexutil.Big)(big.NewInt(0)),
+ }
+ }
+
+ od.traced[addrStr] = st
+
+ // notice there would be at least one entry for all 3 fields when accessing an address
+ // this may caused extract "read" op in mpt circuit which has no corresponding one in rwtable
+ // we can avoid it unless obtaining more tips from the understanding of opcode
+ // but it would be ok if we have adopted the new lookup way (root_prev, root_cur) under discussion:
+ // https://github.com/privacy-scaling-explorations/zkevm-specs/issues/217
+
+ if traced, existed := od.opAccNonce[addrStr]; !existed {
+ traced = copyAccountState(st)
+ traced.Balance = initedRef.Balance
+ traced.KeccakCodeHash = initedRef.KeccakCodeHash
+ traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
+ traced.CodeSize = initedRef.CodeSize
+ traced.Storage = nil
+ od.opAccNonce[addrStr] = traced
+ } else {
+ traced.Nonce = st.Nonce
+ }
+
+ if traced, existed := od.opAccBalance[addrStr]; !existed {
+ traced = copyAccountState(st)
+ traced.KeccakCodeHash = initedRef.KeccakCodeHash
+ traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
+ traced.CodeSize = initedRef.CodeSize
+ traced.Storage = nil
+ od.opAccBalance[addrStr] = traced
+ } else {
+ traced.Nonce = st.Nonce
+ traced.Balance = st.Balance
+ }
+
+ if traced, existed := od.opAccCodeHash[addrStr]; !existed {
+ traced = copyAccountState(st)
+ traced.Storage = nil
+ od.opAccCodeHash[addrStr] = traced
+ } else {
+ traced.Nonce = st.Nonce
+ traced.Balance = st.Balance
+ traced.KeccakCodeHash = st.KeccakCodeHash
+ traced.PoseidonCodeHash = st.PoseidonCodeHash
+ traced.CodeSize = st.CodeSize
+ }
+
+}
+
+func (od *rwTblOrderer) end_absorb() opIterator {
+ // now sort every map by address / key
+ // inited has collected all address, just sort address once
+ sortedAddrs := make([]string, 0, len(od.traced))
+ for addrs := range od.traced {
+ sortedAddrs = append(sortedAddrs, addrs)
+ }
+ sort.Strings(sortedAddrs)
+
+ var iterNonce []*types.AccountWrapper
+ var iterBalance []*types.AccountWrapper
+ var iterCodeHash []*types.AccountWrapper
+ var iterStorage []*types.AccountWrapper
+
+ for _, addrStr := range sortedAddrs {
+
+ if v, existed := od.opAccNonce[addrStr]; existed {
+ iterNonce = append(iterNonce, v)
+ }
+
+ if v, existed := od.opAccBalance[addrStr]; existed {
+ iterBalance = append(iterBalance, v)
+ }
+
+ if v, existed := od.opAccCodeHash[addrStr]; existed {
+ iterCodeHash = append(iterCodeHash, v)
+ }
+
+ if stgM, existed := od.opStorage[addrStr]; existed {
+
+ tracedStatus := od.traced[addrStr]
+ if tracedStatus == nil {
+ panic("missed traced status found in storage slot")
+ }
+
+ sortedKeys := make([]string, 0, len(stgM))
+ for key := range stgM {
+ sortedKeys = append(sortedKeys, key)
+ }
+ sort.Strings(sortedKeys)
+
+ for _, key := range sortedKeys {
+ st := copyAccountState(tracedStatus)
+ st.Storage = stgM[key]
+ iterStorage = append(iterStorage, st)
+ }
+ }
+
+ }
+
+ var finalRet []opIterator
+ for _, arr := range [][]*types.AccountWrapper{iterNonce, iterBalance, iterCodeHash, iterStorage} {
+ wrappedIter := iterateOp(arr)
+ finalRet = append(finalRet, &wrappedIter)
+ }
+
+ wrappedRet := multiOpIterator(finalRet)
+ return &wrappedRet
+}
diff --git a/trie/zkproof/types.go b/trie/zkproof/types.go
new file mode 100644
index 0000000000..2b1620d818
--- /dev/null
+++ b/trie/zkproof/types.go
@@ -0,0 +1,59 @@
+package zkproof
+
+import (
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+type MPTWitnessType int
+
+const (
+ MPTWitnessNothing MPTWitnessType = iota
+ MPTWitnessNatural
+ MPTWitnessRWTbl
+)
+
+// SMTPathNode represent a node in the SMT Path, all hash is saved by the present of
+// zktype.Hash
+type SMTPathNode struct {
+ Value hexutil.Bytes `json:"value"`
+ Sibling hexutil.Bytes `json:"sibling"`
+}
+
+// SMTPath is the whole path of SMT
+type SMTPath struct {
+ KeyPathPart *hexutil.Big `json:"pathPart"` //the path part in key
+ Root hexutil.Bytes `json:"root"`
+ Path []SMTPathNode `json:"path,omitempty"` //path start from top
+ Leaf *SMTPathNode `json:"leaf,omitempty"` //would be omitted for empty leaf, the sibling indicate key
+}
+
+// StateAccount is the represent of StateAccount in L2 circuit
+// Notice in L2 we have different hash scheme against StateAccount.MarshalByte
+type StateAccount struct {
+ Nonce int `json:"nonce"`
+ Balance *hexutil.Big `json:"balance"` //just the common hex expression of integer (big-endian)
+ KeccakCodeHash hexutil.Bytes `json:"keccakCodeHash,omitempty"`
+ PoseidonCodeHash hexutil.Bytes `json:"poseidonCodeHash,omitempty"`
+ CodeSize uint64 `json:"codeSize,omitempty"`
+}
+
+// StateStorage is the represent of a stored key-value pair for specified account
+type StateStorage struct {
+ Key hexutil.Bytes `json:"key"` //notice this is the preimage of storage key
+ Value hexutil.Bytes `json:"value"`
+}
+
+// StorageTrace record the updating on state trie and (if changed) account trie
+// represent by the [before, after] updating of SMTPath amont tries and Account
+type StorageTrace struct {
+ // which log the trace is responded for, -1 indicate not caused
+ // by opcode (like gasRefund, coinbase, setNonce, etc)
+ Address hexutil.Bytes `json:"address"`
+ AccountKey hexutil.Bytes `json:"accountKey"`
+ AccountPath [2]*SMTPath `json:"accountPath"`
+ AccountUpdate [2]*StateAccount `json:"accountUpdate"`
+ StateKey hexutil.Bytes `json:"stateKey,omitempty"`
+ CommonStateRoot hexutil.Bytes `json:"commonStateRoot,omitempty"` //CommonStateRoot is used if there is no update on state storage
+ StatePath [2]*SMTPath `json:"statePath,omitempty"`
+ StateUpdate [2]*StateStorage `json:"stateUpdate,omitempty"`
+}
diff --git a/trie/zkproof/writer.go b/trie/zkproof/writer.go
new file mode 100644
index 0000000000..efc51ca244
--- /dev/null
+++ b/trie/zkproof/writer.go
@@ -0,0 +1,826 @@
+package zkproof
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "math/big"
+
+ zktrie "github.com/scroll-tech/zktrie/trie"
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+type ProofList [][]byte
+
+func (n *ProofList) Put(key []byte, value []byte) error {
+ *n = append(*n, value)
+ return nil
+}
+
+func (n *ProofList) Delete(key []byte) error {
+ panic("not supported")
+}
+
+func addressToKey(addr common.Address) *zkt.Hash {
+ var preImage zkt.Byte32
+ copy(preImage[:], addr.Bytes())
+
+ h, err := preImage.Hash()
+ if err != nil {
+ log.Error("hash failure", "preImage", hexutil.Encode(preImage[:]))
+ return nil
+ }
+ return zkt.NewHashFromBigInt(h)
+}
+
+// resume the proof bytes into db and return the leaf node
+func resumeProofs(proof []hexutil.Bytes, db ethdb.Database) *zktrie.Node {
+ for _, buf := range proof {
+ n, err := zktrie.DecodeSMTProof(buf)
+ if err != nil {
+ log.Warn("decode proof string fail", "error", err)
+ } else if n != nil {
+ hash, err := n.NodeHash()
+ if err != nil {
+ log.Warn("node has no valid node hash", "error", err)
+ } else {
+ //notice: must consistent with trie/merkletree.go
+ bt := hash[:]
+ db.Put(bt, buf)
+ if n.Type == zktrie.NodeTypeLeaf_New || n.Type == zktrie.NodeTypeEmpty_New {
+ return n
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// we have a trick here which suppose the proof array include all middle nodes along the
+// whole path in sequence, from root to leaf
+func decodeProofForMPTPath(proof ProofList, path *SMTPath) {
+ var lastNode *zktrie.Node
+ keyPath := big.NewInt(0)
+ path.KeyPathPart = (*hexutil.Big)(keyPath)
+
+ keyCounter := big.NewInt(1)
+
+ for _, buf := range proof {
+ n, err := zktrie.DecodeSMTProof(buf)
+ if err != nil {
+ log.Warn("decode proof string fail", "error", err)
+ } else if n != nil {
+ hash, err := n.NodeHash()
+ if err != nil {
+ log.Warn("node has no valid node hash", "error", err)
+ return
+ }
+ if lastNode == nil {
+ // notice: use little-endian represent inside Hash ([:] or Byte32())
+ path.Root = hash[:]
+ } else {
+ if bytes.Equal(hash[:], lastNode.ChildL[:]) {
+ path.Path = append(path.Path, SMTPathNode{
+ Value: hash[:],
+ Sibling: lastNode.ChildR[:],
+ })
+ } else if bytes.Equal(hash[:], lastNode.ChildR[:]) {
+ path.Path = append(path.Path, SMTPathNode{
+ Value: hash[:],
+ Sibling: lastNode.ChildL[:],
+ })
+ keyPath.Add(keyPath, keyCounter)
+ } else {
+ panic("Unexpected proof form")
+ }
+ keyCounter.Mul(keyCounter, big.NewInt(2))
+ }
+ switch n.Type {
+ case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1, zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3:
+ lastNode = n
+ case zktrie.NodeTypeLeaf_New:
+ vhash, _ := n.ValueHash()
+ path.Leaf = &SMTPathNode{
+ //here we just return the inner represent of hash (little endian, reversed byte order to common hash)
+ Value: vhash[:],
+ Sibling: n.NodeKey[:],
+ }
+ //sanity check
+ keyPart := keyPath.Bytes()
+ for i, b := range keyPart {
+ ri := len(keyPart) - i
+ cb := path.Leaf.Sibling[ri-1] //notice the output is little-endian
+ if b&cb != b {
+ panic(fmt.Errorf("path key not match: part is %x but key is %x", keyPart, []byte(path.Leaf.Sibling[:])))
+ }
+ }
+ return
+ case zktrie.NodeTypeEmpty_New:
+ return
+ default:
+ panic(fmt.Errorf("unknown node type %d", n.Type))
+ }
+ }
+ }
+
+ panic("Unexpected finished here")
+}
+
+type zktrieProofWriter struct {
+ db *trie.ZktrieDatabase
+ tracingZktrie *trie.ZkTrie
+ tracingStorageTries map[common.Address]*trie.ZkTrie
+ tracingAccounts map[common.Address]*types.StateAccount
+}
+
+func (wr *zktrieProofWriter) TracingAccounts() map[common.Address]*types.StateAccount {
+ return wr.tracingAccounts
+}
+
+func NewZkTrieProofWriter(storage *types.StorageTrace) (*zktrieProofWriter, error) {
+ underlayerDb := rawdb.NewMemoryDatabase()
+ zkDb := trie.NewZktrieDatabase(underlayerDb)
+ accounts := make(map[common.Address]*types.StateAccount)
+
+ // resuming proof bytes to underlayerDb
+ for addrs, proof := range storage.Proofs {
+ if n := resumeProofs(proof, underlayerDb); n != nil {
+ addr := common.HexToAddress(addrs)
+ if n.Type == zktrie.NodeTypeEmpty_New {
+ accounts[addr] = nil
+ } else if acc, err := types.UnmarshalStateAccount(n.Data()); err == nil {
+ if bytes.Equal(n.NodeKey[:], addressToKey(addr)[:]) {
+ accounts[addr] = acc
+ } else {
+ // should still mark the address as being trace (data not existed yet)
+ accounts[addr] = nil
+ }
+
+ } else {
+ return nil, fmt.Errorf("decode account bytes fail: %s, raw data [%x]", err, n.Data())
+ }
+
+ } else {
+ return nil, fmt.Errorf("can not resume proof for address %s", addrs)
+ }
+ }
+
+ storages := make(map[common.Address]*trie.ZkTrie)
+
+ for addrs, stgLists := range storage.StorageProofs {
+ addr := common.HexToAddress(addrs)
+ accState, existed := accounts[addr]
+ if !existed {
+ // trace is malformed but currently we just warn about that
+ log.Warn("no account state found for this addr, mal records", "address", addrs)
+ continue
+ } else if accState == nil {
+ // create an empty zktrie for uninit address
+ storages[addr], _ = trie.NewZkTrie(common.Hash{}, zkDb)
+ continue
+ }
+
+ for keys, proof := range stgLists {
+ if n := resumeProofs(proof, underlayerDb); n != nil {
+ var err error
+ storages[addr], err = trie.NewZkTrie(accState.Root, zkDb)
+ if err != nil {
+ return nil, fmt.Errorf("zktrie create failure for storage in addr <%s>: %s, (root %s)", addrs, err, accState.Root)
+ }
+ } else {
+ return nil, fmt.Errorf("can not resume proof for storage %s@%s", keys, addrs)
+ }
+ }
+ }
+
+ for _, delProof := range storage.DeletionProofs {
+ n, err := zktrie.DecodeSMTProof(delProof)
+ if err != nil {
+ log.Warn("decode delproof string fail", "error", err, "node", delProof)
+ } else if n != nil {
+ hash, err := n.NodeHash()
+ if err != nil {
+ log.Warn("node has no valid node hash", "error", err)
+ } else {
+ //notice: must consistent with trie/merkletree.go
+ bt := hash[:]
+ underlayerDb.Put(bt, delProof)
+ }
+ }
+ }
+
+ zktrie, err := trie.NewZkTrie(
+ storage.RootBefore,
+ trie.NewZktrieDatabase(underlayerDb),
+ )
+ if err != nil {
+ return nil, fmt.Errorf("zktrie create failure: %s", err)
+ }
+
+ // sanity check
+ if !bytes.Equal(zktrie.Hash().Bytes(), storage.RootBefore.Bytes()) {
+ return nil, fmt.Errorf("unmatch init trie hash: expected %x but has %x", storage.RootBefore.Bytes(), zktrie.Hash().Bytes())
+ }
+
+ return &zktrieProofWriter{
+ db: zkDb,
+ tracingZktrie: zktrie,
+ tracingAccounts: accounts,
+ tracingStorageTries: storages,
+ }, nil
+}
+
+const (
+ posSSTOREBefore = 0
+ posCREATE = 0
+ posCREATEAfter = 1
+ posCALL = 2
+ posSTATICCALL = 0
+
+ // posSELFDESTRUCT = 2
+)
+
+func getAccountState(l *types.StructLogRes, pos int) *types.AccountWrapper {
+ if exData := l.ExtraData; exData == nil {
+ return nil
+ } else if len(exData.StateList) < pos {
+ return nil
+ } else {
+ return exData.StateList[pos]
+ }
+}
+
+func copyAccountState(st *types.AccountWrapper) *types.AccountWrapper {
+ var stg *types.StorageWrapper
+ if st.Storage != nil {
+ stg = &types.StorageWrapper{
+ Key: st.Storage.Key,
+ Value: st.Storage.Value,
+ }
+ }
+
+ return &types.AccountWrapper{
+ Nonce: st.Nonce,
+ Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())),
+ KeccakCodeHash: st.KeccakCodeHash,
+ PoseidonCodeHash: st.PoseidonCodeHash,
+ CodeSize: st.CodeSize,
+ Address: st.Address,
+ Storage: stg,
+ }
+}
+
+func isDeletedAccount(state *types.AccountWrapper) bool {
+ return state.Nonce == 0 && bytes.Equal(state.KeccakCodeHash.Bytes(), common.Hash{}.Bytes())
+}
+
+func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount {
+ if isDeletedAccount(state) {
+ return nil
+ }
+
+ return &types.StateAccount{
+ Nonce: state.Nonce,
+ Balance: (*big.Int)(state.Balance),
+ KeccakCodeHash: state.KeccakCodeHash.Bytes(),
+ PoseidonCodeHash: state.PoseidonCodeHash.Bytes(),
+ CodeSize: state.CodeSize,
+ // Root omitted intentionally
+ }
+}
+
+// for sanity check
+func verifyAccount(addr common.Address, data *types.StateAccount, leaf *SMTPathNode) error {
+ if leaf == nil {
+ if data != nil {
+ return fmt.Errorf("path has no corresponding leaf for account")
+ } else {
+ return nil
+ }
+ }
+
+ addrKey := addressToKey(addr)
+ if !bytes.Equal(addrKey[:], leaf.Sibling) {
+ if data != nil {
+ return fmt.Errorf("unmatch leaf node in address: %s", addr)
+ }
+ } else if data != nil {
+ arr, flag := data.MarshalFields()
+ h, err := zkt.HandlingElemsAndByte32(flag, arr)
+ //log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16))
+
+ if err != nil {
+ return fmt.Errorf("fail to hash account: %v", err)
+ }
+ if !bytes.Equal(h[:], leaf.Value) {
+ return fmt.Errorf("unmatch data in leaf for address %s", addr)
+ }
+ }
+ return nil
+}
+
+// for sanity check
+func verifyStorage(key *zkt.Byte32, data *zkt.Byte32, leaf *SMTPathNode) error {
+ emptyData := bytes.Equal(data[:], common.Hash{}.Bytes())
+
+ if leaf == nil {
+ if !emptyData {
+ return fmt.Errorf("path has no corresponding leaf for storage")
+ } else {
+ return nil
+ }
+ }
+
+ keyHash, err := key.Hash()
+ if err != nil {
+ return err
+ }
+
+ if !bytes.Equal(zkt.NewHashFromBigInt(keyHash)[:], leaf.Sibling) {
+ if !emptyData {
+ return fmt.Errorf("unmatch leaf node in storage: %x", key[:])
+ }
+ } else {
+ h, err := data.Hash()
+ //log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16))
+
+ if err != nil {
+ return fmt.Errorf("fail to hash data: %v", err)
+ }
+ if !bytes.Equal(zkt.NewHashFromBigInt(h)[:], leaf.Value) {
+ return fmt.Errorf("unmatch data in leaf for storage %x", key[:])
+ }
+ }
+ return nil
+}
+
+// update traced account state, and return the corresponding trace object which
+// is still opened for more infos
+// the updated accData state is obtained by a closure which enable it being derived from current status
+func (w *zktrieProofWriter) traceAccountUpdate(addr common.Address, updateAccData func(*types.StateAccount) *types.StateAccount) (*StorageTrace, error) {
+ out := new(StorageTrace)
+ //account trie
+ out.Address = addr.Bytes()
+ out.AccountPath = [2]*SMTPath{{}, {}}
+ //fill dummy
+ out.AccountUpdate = [2]*StateAccount{}
+
+ accDataBefore, existed := w.tracingAccounts[addr]
+ if !existed {
+ //sanity check
+ panic(fmt.Errorf("code do not add initialized status for account %s", addr))
+ }
+
+ var proof ProofList
+ s_key, _ := zkt.ToSecureKeyBytes(addr.Bytes())
+ if err := w.tracingZktrie.Prove(s_key.Bytes(), &proof); err != nil {
+ return nil, fmt.Errorf("prove BEFORE state fail: %s", err)
+ }
+
+ decodeProofForMPTPath(proof, out.AccountPath[0])
+ if err := verifyAccount(addr, accDataBefore, out.AccountPath[0].Leaf); err != nil {
+ panic(fmt.Errorf("code fail to trace account status correctly: %s", err))
+ }
+ if accDataBefore != nil {
+ // we have ensured the nBefore has a key corresponding to the query one
+ out.AccountKey = out.AccountPath[0].Leaf.Sibling
+ out.AccountUpdate[0] = &StateAccount{
+ Nonce: int(accDataBefore.Nonce),
+ Balance: (*hexutil.Big)(big.NewInt(0).Set(accDataBefore.Balance)),
+ KeccakCodeHash: accDataBefore.KeccakCodeHash,
+ PoseidonCodeHash: accDataBefore.PoseidonCodeHash,
+ CodeSize: accDataBefore.CodeSize,
+ }
+ }
+
+ accData := updateAccData(accDataBefore)
+ if accData != nil {
+ out.AccountUpdate[1] = &StateAccount{
+ Nonce: int(accData.Nonce),
+ Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)),
+ KeccakCodeHash: accData.KeccakCodeHash,
+ PoseidonCodeHash: accData.PoseidonCodeHash,
+ CodeSize: accData.CodeSize,
+ }
+ }
+
+ if accData != nil {
+ if err := w.tracingZktrie.TryUpdateAccount(addr.Bytes32(), accData); err != nil {
+ return nil, fmt.Errorf("update zktrie account state fail: %s", err)
+ }
+ w.tracingAccounts[addr] = accData
+ } else if accDataBefore != nil {
+ if err := w.tracingZktrie.TryDelete(addr.Bytes32()); err != nil {
+ return nil, fmt.Errorf("delete zktrie account state fail: %s", err)
+ }
+ w.tracingAccounts[addr] = nil
+ } // notice if both before/after is nil, we do not touch zktrie
+
+ proof = ProofList{}
+ if err := w.tracingZktrie.Prove(s_key.Bytes(), &proof); err != nil {
+ return nil, fmt.Errorf("prove AFTER state fail: %s", err)
+ }
+
+ decodeProofForMPTPath(proof, out.AccountPath[1])
+ if err := verifyAccount(addr, accData, out.AccountPath[1].Leaf); err != nil {
+ panic(fmt.Errorf("state AFTER has no valid account: %s", err))
+ }
+ if accData != nil {
+ if out.AccountKey == nil {
+ out.AccountKey = out.AccountPath[1].Leaf.Sibling[:]
+ }
+ //now accountKey must has been filled
+ }
+
+ // notice we have change that no leaf (account data) exist in either before or after,
+ // for that case we had to calculate the nodeKey here
+ if out.AccountKey == nil {
+ word := zkt.NewByte32FromBytesPaddingZero(addr.Bytes())
+ k, err := word.Hash()
+ if err != nil {
+ panic(fmt.Errorf("unexpected hash error for address: %s", err))
+ }
+ kHash := zkt.NewHashFromBigInt(k)
+ out.AccountKey = hexutil.Bytes(kHash[:])
+ }
+
+ return out, nil
+}
+
+// update traced storage state, and return the corresponding trace object
+func (w *zktrieProofWriter) traceStorageUpdate(addr common.Address, key, value []byte) (*StorageTrace, error) {
+ trie := w.tracingStorageTries[addr]
+ if trie == nil {
+ return nil, fmt.Errorf("no trace storage trie for %s", addr)
+ }
+
+ statePath := [2]*SMTPath{{}, {}}
+ stateUpdate := [2]*StateStorage{}
+
+ storeKey := zkt.NewByte32FromBytesPaddingZero(common.BytesToHash(key).Bytes())
+ storeValueBefore := trie.Get(storeKey[:])
+ storeValue := zkt.NewByte32FromBytes(value)
+ valZero := zkt.Byte32{}
+
+ if storeValueBefore != nil && !bytes.Equal(storeValueBefore[:], common.Hash{}.Bytes()) {
+ stateUpdate[0] = &StateStorage{
+ Key: storeKey.Bytes(),
+ Value: storeValueBefore,
+ }
+ }
+
+ var storageBeforeProof, storageAfterProof ProofList
+ s_key, _ := zkt.ToSecureKeyBytes(storeKey.Bytes())
+ if err := trie.Prove(s_key.Bytes(), &storageBeforeProof); err != nil {
+ return nil, fmt.Errorf("prove BEFORE storage state fail: %s", err)
+ }
+
+ decodeProofForMPTPath(storageBeforeProof, statePath[0])
+ if err := verifyStorage(storeKey, zkt.NewByte32FromBytes(storeValueBefore), statePath[0].Leaf); err != nil {
+ panic(fmt.Errorf("storage BEFORE has no valid data: %s (%v)", err, statePath[0]))
+ }
+
+ if !bytes.Equal(storeValue.Bytes(), common.Hash{}.Bytes()) {
+ if err := trie.TryUpdate(storeKey.Bytes(), storeValue.Bytes()); err != nil {
+ return nil, fmt.Errorf("update zktrie storage fail: %s", err)
+ }
+ stateUpdate[1] = &StateStorage{
+ Key: storeKey.Bytes(),
+ Value: storeValue.Bytes(),
+ }
+ } else {
+ if err := trie.TryDelete(storeKey.Bytes()); err != nil {
+ return nil, fmt.Errorf("delete zktrie storage fail: %s", err)
+ }
+ }
+
+ if err := trie.Prove(s_key.Bytes(), &storageAfterProof); err != nil {
+ return nil, fmt.Errorf("prove AFTER storage state fail: %s", err)
+ }
+ decodeProofForMPTPath(storageAfterProof, statePath[1])
+ if err := verifyStorage(storeKey, storeValue, statePath[1].Leaf); err != nil {
+ panic(fmt.Errorf("storage AFTER has no valid data: %s (%v)", err, statePath[1]))
+ }
+
+ out, err := w.traceAccountUpdate(addr,
+ func(acc *types.StateAccount) *types.StateAccount {
+ if acc == nil {
+ // in case we read an unexist account
+ if !bytes.Equal(valZero.Bytes(), value) {
+ panic(fmt.Errorf("write to an unexist account [%s] which is not allowed", addr))
+ }
+ return nil
+ }
+
+ //sanity check
+ if accRootFromState := zkt.ReverseByteOrder(statePath[0].Root); !bytes.Equal(acc.Root[:], accRootFromState) {
+ panic(fmt.Errorf("unexpected storage root before: [%s] vs [%x]", acc.Root, accRootFromState))
+ }
+ return &types.StateAccount{
+ Nonce: acc.Nonce,
+ Balance: acc.Balance,
+ Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)),
+ KeccakCodeHash: acc.KeccakCodeHash,
+ PoseidonCodeHash: acc.PoseidonCodeHash,
+ CodeSize: acc.CodeSize,
+ }
+ })
+ if err != nil {
+ return nil, fmt.Errorf("update account %s in SSTORE fail: %s", addr, err)
+ }
+
+ if stateUpdate[1] != nil {
+ out.StateKey = statePath[1].Leaf.Sibling
+ } else if stateUpdate[0] != nil {
+ out.StateKey = statePath[0].Leaf.Sibling
+ } else {
+ // it occurs when we are handling SLOAD with non-exist value
+ // still no pretty idea, had to touch the internal behavior in zktrie ....
+ if h, err := storeKey.Hash(); err != nil {
+ return nil, fmt.Errorf("hash storekey fail: %s", err)
+ } else {
+ out.StateKey = zkt.NewHashFromBigInt(h)[:]
+ }
+ stateUpdate[1] = &StateStorage{
+ Key: storeKey.Bytes(),
+ Value: valZero.Bytes(),
+ }
+ stateUpdate[0] = stateUpdate[1]
+ }
+
+ out.StatePath = statePath
+ out.StateUpdate = stateUpdate
+ return out, nil
+}
+
+func (w *zktrieProofWriter) HandleNewState(accountState *types.AccountWrapper) (*StorageTrace, error) {
+ if accountState.Storage != nil {
+ storeAddr := hexutil.MustDecode(accountState.Storage.Key)
+ storeValue := hexutil.MustDecode(accountState.Storage.Value)
+ return w.traceStorageUpdate(accountState.Address, storeAddr, storeValue)
+ } else {
+ var stateRoot common.Hash
+ accData := getAccountDataFromLogState(accountState)
+
+ out, err := w.traceAccountUpdate(accountState.Address, func(accBefore *types.StateAccount) *types.StateAccount {
+ if accBefore != nil {
+ stateRoot = accBefore.Root
+ }
+ // we need to restore stateRoot from before
+ if accData != nil {
+ accData.Root = stateRoot
+ }
+ return accData
+ })
+ if err != nil {
+ return nil, fmt.Errorf("update account state %s fail: %s", accountState.Address, err)
+ }
+
+ hash := zkt.NewHashFromBytes(stateRoot[:])
+ out.CommonStateRoot = hash[:]
+ return out, nil
+ }
+}
+
+func handleLogs(od opOrderer, currentContract common.Address, logs []*types.StructLogRes) {
+ logStack := []int{0}
+ contractStack := map[int]common.Address{}
+ callEnterAddress := currentContract
+
+ // now trace every OP which could cause changes on state:
+ for i, sLog := range logs {
+ //trace log stack by depth rather than scanning specified op
+ if sl := len(logStack); sl < sLog.Depth {
+ logStack = append(logStack, i)
+ //update currentContract according to previous op
+ contractStack[sl] = currentContract
+ currentContract = callEnterAddress
+ } else if sl > sLog.Depth {
+ logStack = logStack[:sl-1]
+ currentContract = contractStack[sLog.Depth]
+ resumePos := logStack[len(logStack)-1]
+ calledLog := logs[resumePos]
+
+ //no need to handle fail calling
+ if calledLog.ExtraData != nil {
+ if !calledLog.ExtraData.CallFailed {
+ //reentry the last log which "cause" the calling, some handling may needed
+ switch calledLog.Op {
+ case "CREATE", "CREATE2":
+ //addr, accDataBefore := getAccountDataFromProof(calledLog, posCALLBefore)
+ od.absorb(getAccountState(calledLog, posCREATEAfter))
+ }
+ } else {
+ od.readonly(false)
+ }
+ }
+ } else {
+ logStack[sl-1] = i
+ }
+ //sanity check
+ if len(logStack) != sLog.Depth {
+ panic("tracking log stack failure")
+ }
+ callEnterAddress = currentContract
+
+ //check extra status for current op if it is a call
+ if extraData := sLog.ExtraData; extraData != nil {
+ if extraData.CallFailed || len(sLog.ExtraData.Caller) < 2 {
+ // no enough caller data (2) is being capture indicate we are in an immediate failure
+ // i.e. it fail before stack entry (like no enough balance for a "call with value"),
+ // or we just not handle this calling op correctly yet
+
+ // for a failed option, now we just purpose nothing happens (FIXME: it is inconsentent with mpt_table)
+ // except for CREATE, for which the callee's nonce would be increased
+ switch sLog.Op {
+ case "CREATE", "CREATE2":
+ st := copyAccountState(extraData.Caller[0])
+ st.Nonce += 1
+ od.absorb(st)
+ }
+ }
+
+ if extraData.CallFailed {
+ od.readonly(true)
+ }
+ // now trace caller's status first
+ if caller := extraData.Caller; len(caller) >= 2 {
+ od.absorb(caller[1])
+ }
+ }
+
+ switch sLog.Op {
+ case "SELFDESTRUCT":
+ // NOTE: this op code has been disabled so we treat it as nothing now
+
+ //in SELFDESTRUCT, a call on target address is made so the balance would be updated
+ //in the last item
+ //stateTarget := getAccountState(sLog, posSELFDESTRUCT)
+ //od.absorb(stateTarget)
+ //then build an "deleted state", only address and other are default
+ //od.absorb(&types.AccountWrapper{Address: currentContract})
+
+ case "CREATE", "CREATE2":
+ // notice in immediate failure we have no enough tracing in extraData
+ if len(sLog.ExtraData.StateList) >= 2 {
+ state := getAccountState(sLog, posCREATE)
+ od.absorb(state)
+ //update contract to CREATE addr
+ callEnterAddress = state.Address
+ }
+
+ case "CALL", "CALLCODE":
+ // notice in immediate failure we have no enough tracing in extraData
+ if len(sLog.ExtraData.StateList) >= 3 {
+ state := getAccountState(sLog, posCALL)
+ od.absorb(state)
+ callEnterAddress = state.Address
+ }
+ case "STATICCALL":
+ //static call has no update on target address (and no immediate failure?)
+ callEnterAddress = getAccountState(sLog, posSTATICCALL).Address
+ case "DELEGATECALL":
+
+ case "SLOAD":
+ accountState := getAccountState(sLog, posSSTOREBefore)
+ od.absorbStorage(accountState, nil)
+ case "SSTORE":
+ log.Debug("build SSTORE", "pc", sLog.Pc, "key", sLog.Stack[len(sLog.Stack)-1])
+ accountState := copyAccountState(getAccountState(sLog, posSSTOREBefore))
+ // notice the log only provide the value BEFORE store and it is not suitable for our protocol,
+ // here we change it into value AFTER update
+ before := accountState.Storage
+ accountState.Storage = &types.StorageWrapper{
+ Key: sLog.Stack[len(sLog.Stack)-1],
+ Value: sLog.Stack[len(sLog.Stack)-2],
+ }
+ od.absorbStorage(accountState, before)
+
+ default:
+ }
+ }
+}
+
+func HandleTx(od opOrderer, txResult *types.ExecutionResult) {
+ // the from state is read before tx is handled and nonce is added, we combine both
+ preTxSt := copyAccountState(txResult.From)
+ preTxSt.Nonce += 1
+ od.absorb(preTxSt)
+
+ if txResult.Failed {
+ od.readonly(true)
+ }
+
+ var toAddr common.Address
+ if state := txResult.AccountCreated; state != nil {
+ od.absorb(state)
+ toAddr = state.Address
+ } else {
+ toAddr = txResult.To.Address
+ }
+
+ handleLogs(od, toAddr, txResult.StructLogs)
+ if txResult.Failed {
+ od.readonly(false)
+ }
+
+ for _, state := range txResult.AccountsAfter {
+ // special case: for suicide, the state has been captured in SELFDESTRUCT
+ // and we skip it here
+ if isDeletedAccount(state) {
+ log.Debug("skip suicide address", "address", state.Address)
+ continue
+ }
+
+ od.absorb(state)
+ }
+
+}
+
+const defaultOrdererScheme = MPTWitnessRWTbl
+
+var usedOrdererScheme = defaultOrdererScheme
+
+func SetOrderScheme(t MPTWitnessType) { usedOrdererScheme = t }
+
+// HandleBlockTrace only for backward compatibility
+func HandleBlockTrace(block *types.BlockTrace) ([]*StorageTrace, error) {
+ return HandleBlockTraceEx(block, usedOrdererScheme)
+}
+
+func HandleBlockTraceEx(block *types.BlockTrace, ordererScheme MPTWitnessType) ([]*StorageTrace, error) {
+ writer, err := NewZkTrieProofWriter(block.StorageTrace)
+ if err != nil {
+ return nil, err
+ }
+
+ var od opOrderer
+ switch ordererScheme {
+ case MPTWitnessNothing:
+ panic("should not come here when scheme is 0")
+ case MPTWitnessNatural:
+ od = &simpleOrderer{}
+ case MPTWitnessRWTbl:
+ od = NewRWTblOrderer(writer.tracingAccounts)
+ default:
+ return nil, fmt.Errorf("unrecognized scheme %d", ordererScheme)
+ }
+
+ for _, tx := range block.ExecutionResults {
+ HandleTx(od, tx)
+ }
+
+ // notice some coinbase addr (like all zero) is in fact not exist and should not be update
+ // TODO: not a good solution, just for patch ...
+ if coinbaseData := writer.tracingAccounts[block.Coinbase.Address]; coinbaseData != nil {
+ od.absorb(block.Coinbase)
+ }
+
+ opDisp := od.end_absorb()
+ var outTrace []*StorageTrace
+
+ for op := opDisp.next(); op != nil; op = opDisp.next() {
+ trace, err := writer.HandleNewState(op)
+ if err != nil {
+ return nil, err
+ }
+ outTrace = append(outTrace, trace)
+ }
+
+ finalHash := writer.tracingZktrie.Hash()
+ if !bytes.Equal(finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes()) {
+ return outTrace, fmt.Errorf("unmatch hash: [%x] vs [%x]", finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes())
+ }
+
+ return outTrace, nil
+
+}
+
+func FillBlockTraceForMPTWitness(order MPTWitnessType, block *types.BlockTrace) error {
+ if order == MPTWitnessNothing {
+ return nil
+ }
+
+ trace, err := HandleBlockTraceEx(block, order)
+ if err != nil {
+ return err
+ }
+
+ msg, err := json.Marshal(trace)
+ if err != nil {
+ return err
+ }
+
+ rawmsg := json.RawMessage(msg)
+
+ block.MPTWitness = &rawmsg
+ return nil
+}
diff --git a/trie/zktrie_deletionproof.go b/trie/zktrie_deletionproof.go
new file mode 100644
index 0000000000..db7beca9a5
--- /dev/null
+++ b/trie/zktrie_deletionproof.go
@@ -0,0 +1,183 @@
+package trie
+
+import (
+ "bytes"
+ "fmt"
+
+ zktrie "github.com/scroll-tech/zktrie/trie"
+ zkt "github.com/scroll-tech/zktrie/types"
+
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+// Pick Node from its hash directly from database, notice it has different
+// interface with the function of same name in `trie`
+func (t *ZkTrie) TryGetNode(nodeHash *zkt.Hash) (*zktrie.Node, error) {
+ if bytes.Equal(nodeHash[:], zkt.HashZero[:]) {
+ return zktrie.NewEmptyNode(), nil
+ }
+ nBytes, err := t.db.Get(nodeHash[:])
+ if err == zktrie.ErrKeyNotFound {
+ return nil, zktrie.ErrKeyNotFound
+ } else if err != nil {
+ return nil, err
+ }
+ return zktrie.NewNodeFromBytes(nBytes)
+}
+
+type ProofTracer struct {
+ *ZkTrie
+ deletionTracer map[zkt.Hash]struct{}
+ rawPaths map[string][]*zktrie.Node
+ emptyTermPaths map[string][]*zktrie.Node
+}
+
+// NewProofTracer create a proof tracer object
+func (t *ZkTrie) NewProofTracer() *ProofTracer {
+ return &ProofTracer{
+ ZkTrie: t,
+ // always consider 0 is "deleted"
+ deletionTracer: map[zkt.Hash]struct{}{zkt.HashZero: {}},
+ rawPaths: make(map[string][]*zktrie.Node),
+ emptyTermPaths: make(map[string][]*zktrie.Node),
+ }
+}
+
+// Merge merge the input tracer into current and return current tracer
+func (t *ProofTracer) Merge(another *ProofTracer) *ProofTracer {
+
+ // sanity checking
+ if !bytes.Equal(t.Hash().Bytes(), another.Hash().Bytes()) {
+ panic("can not merge two proof tracer base on different trie")
+ }
+
+ for k := range another.deletionTracer {
+ t.deletionTracer[k] = struct{}{}
+ }
+
+ for k, v := range another.rawPaths {
+ t.rawPaths[k] = v
+ }
+
+ for k, v := range another.emptyTermPaths {
+ t.emptyTermPaths[k] = v
+ }
+
+ return t
+}
+
+// GetDeletionProofs generate current deletionTracer and collect deletion proofs
+// which is possible to be used from all rawPaths, which enabling witness generator
+// to predict the final state root after executing any deletion
+// along any of the rawpath, no matter of the deletion occurs in any position of the mpt ops
+// Note the collected sibling node has no key along with it since witness generator would
+// always decode the node for its purpose
+func (t *ProofTracer) GetDeletionProofs() ([][]byte, error) {
+
+ retMap := map[zkt.Hash][]byte{}
+
+ // check each path: reversively, skip the final leaf node
+ for _, path := range t.rawPaths {
+
+ checkPath := path[:len(path)-1]
+ for i := len(checkPath); i > 0; i-- {
+ n := checkPath[i-1]
+ _, deletedL := t.deletionTracer[*n.ChildL]
+ _, deletedR := t.deletionTracer[*n.ChildR]
+ if deletedL && deletedR {
+ nodeHash, _ := n.NodeHash()
+ t.deletionTracer[*nodeHash] = struct{}{}
+ } else {
+ var siblingHash *zkt.Hash
+ if deletedL {
+ siblingHash = n.ChildR
+ } else if deletedR {
+ siblingHash = n.ChildL
+ }
+ if siblingHash != nil {
+ sibling, err := t.TryGetNode(siblingHash)
+ if err != nil {
+ return nil, err
+ }
+ if sibling.Type != zktrie.NodeTypeEmpty_New {
+ retMap[*siblingHash] = sibling.Value()
+ }
+ }
+ break
+ }
+ }
+ }
+
+ var ret [][]byte
+ for _, bt := range retMap {
+ ret = append(ret, bt)
+ }
+
+ return ret, nil
+
+}
+
+// MarkDeletion mark a key has been involved into deletion
+func (t *ProofTracer) MarkDeletion(key []byte) {
+ if path, existed := t.emptyTermPaths[string(key)]; existed {
+ // copy empty node terminated path for final scanning
+ t.rawPaths[string(key)] = path
+ } else if path, existed = t.rawPaths[string(key)]; existed {
+ // sanity check
+ leafNode := path[len(path)-1]
+
+ if leafNode.Type != zktrie.NodeTypeLeaf_New {
+ panic("all path recorded in proofTrace should be ended with leafNode")
+ }
+
+ nodeHash, _ := leafNode.NodeHash()
+ t.deletionTracer[*nodeHash] = struct{}{}
+ }
+}
+
+// Prove act the same as zktrie.Prove, while also collect the raw path
+// for collecting deletion proofs in a post-work
+func (t *ProofTracer) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
+ var mptPath []*zktrie.Node
+ err := t.ZkTrie.ProveWithDeletion(key, fromLevel,
+ func(n *zktrie.Node) error {
+ nodeHash, err := n.NodeHash()
+ if err != nil {
+ return err
+ }
+
+ switch n.Type {
+ case zktrie.NodeTypeLeaf_New:
+ preImage := t.GetKey(n.NodeKey.Bytes())
+ if len(preImage) > 0 {
+ n.KeyPreimage = &zkt.Byte32{}
+ copy(n.KeyPreimage[:], preImage)
+ }
+ case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1,
+ zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3:
+ mptPath = append(mptPath, n)
+ case zktrie.NodeTypeEmpty_New:
+ // empty node is considered as "unhit" but it should be also being added
+ // into a temporary slot for possibly being marked as deletion later
+ mptPath = append(mptPath, n)
+ t.emptyTermPaths[string(key)] = mptPath
+ default:
+ panic(fmt.Errorf("unexpected node type %d", n.Type))
+ }
+
+ return proofDb.Put(nodeHash[:], n.Value())
+ },
+ func(n *zktrie.Node, _ *zktrie.Node) {
+ // only "hit" path (i.e. the leaf node corresponding the input key can be found)
+ // would be add into tracer
+ mptPath = append(mptPath, n)
+ t.rawPaths[string(key)] = mptPath
+ },
+ )
+ if err != nil {
+ return err
+ }
+ // we put this special kv pair in db so we can distinguish the type and
+ // make suitable Proof
+ return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
+}