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()) +}