From 81e7775aa8a016af4ab632d70731b50488f53bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 9 Feb 2023 01:12:41 +0100 Subject: [PATCH] feat: dual code hash (#188) * add KeccakCodeHash and CodeSize to StateAccount * update StateAccount marshalling logic * change emptyCodeHash to poseidon(nil) * purge StateAccount.hash * fix/disable failing tests * change keccak and poseidon hash order in StateAccount * fix lint * update l2trace account wrapper * update eth_getProof response type * fix eth_getProof response type * goimports * update the codehash computation * update the codehash test cases * go mod tidy * fix tests * use keccak instead of poseidon * update trace codehash field name * upgrade zktrie to 4.2 * trigger ci * update state account marshalling according to spec * improve generatorStats estimation * add comment * upgrade zktrie to 4.3 * go mod tidy * misc fixes * fix TestDump * fix snap sync tests * handle err in the codehash * fix tests in snapshot/generate_test.go * remove prevhash from state journal * add state_account_marshalling_test.go * goimports * add more tests --------- Co-authored-by: Ho Vei Co-authored-by: Haichen Shen --- cmd/geth/snapshot.go | 30 +-- core/blockchain_test.go | 28 ++- core/genesis_test.go | 102 ++++----- core/state/dump.go | 48 +++-- core/state/iterator.go | 8 +- core/state/journal.go | 6 +- core/state/pruner/pruner.go | 8 +- core/state/snapshot/account.go | 28 ++- core/state/snapshot/conversion.go | 4 +- core/state/snapshot/generate.go | 22 +- core/state/snapshot/generate_test.go | 116 +++++----- core/state/snapshot/snapshot_test.go | 10 +- core/state/state_object.go | 51 +++-- core/state/state_test.go | 31 ++- core/state/statedb.go | 39 ++-- core/state/statedb_test.go | 3 +- core/state/sync.go | 2 +- core/state/sync_test.go | 8 +- core/state_processor_test.go | 2 +- core/state_transition.go | 4 +- core/types/l2trace.go | 16 +- core/types/state_account.go | 12 +- core/types/state_account_marshalling.go | 108 +++++----- core/types/state_account_marshalling_test.go | 212 ++++++++++++++++--- core/vm/contract.go | 2 +- core/vm/evm.go | 16 +- core/vm/instructions.go | 4 +- core/vm/interface.go | 5 +- core/vm/logger_trace.go | 20 +- crypto/codehash/codehash.go | 15 +- crypto/poseidon/codehash.go | 38 ++-- crypto/poseidon/codehash_test.go | 22 +- crypto/poseidon/poseidon.go | 49 ++--- eth/protocols/snap/handler.go | 2 +- eth/protocols/snap/sync.go | 19 +- eth/protocols/snap/sync_test.go | 102 ++++++--- eth/tracers/api_blocktrace.go | 44 ++-- ethclient/gethclient/gethclient.go | 46 ++-- go.mod | 2 +- go.sum | 4 +- internal/ethapi/api.go | 38 ++-- les/server_requests.go | 4 +- light/trie.go | 2 +- trie/sync.go | 2 +- trie/trie_test.go | 32 +-- trie/zk_trie_impl_test.go | 28 ++- trie/zkproof/orderer.go | 22 +- trie/zkproof/types.go | 8 +- trie/zkproof/writer.go | 54 +++-- 49 files changed, 891 insertions(+), 587 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index a6356d733f..4af78a36d3 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -42,8 +42,8 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // emptyCode is the known hash of the empty EVM bytecode. - emptyCode = codehash.EmptyCodeHash.Bytes() + // emptyKeccakCodeHash is the known hash of the empty EVM bytecode. + emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash.Bytes() ) var ( @@ -314,10 +314,10 @@ func traverseState(ctx *cli.Context) error { return storageIter.Err } } - if !bytes.Equal(acc.CodeHash, emptyCode) { - code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) + if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) { + code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.KeccakCodeHash)) if len(code) == 0 { - log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) + log.Error("Code is missing", "hash", common.BytesToHash(acc.KeccakCodeHash)) return errors.New("missing code") } codes += 1 @@ -435,8 +435,8 @@ func traverseRawState(ctx *cli.Context) error { return storageIter.Error() } } - if !bytes.Equal(acc.CodeHash, emptyCode) { - code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) + if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) { + code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.KeccakCodeHash)) if len(code) == 0 { log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) return errors.New("missing code") @@ -499,14 +499,16 @@ func dumpState(ctx *cli.Context) error { return err } da := &state.DumpAccount{ - Balance: account.Balance.String(), - Nonce: account.Nonce, - Root: account.Root, - CodeHash: account.CodeHash, - SecureKey: accIt.Hash().Bytes(), + Balance: account.Balance.String(), + Nonce: account.Nonce, + Root: account.Root, + KeccakCodeHash: account.KeccakCodeHash, + PoseidonCodeHash: account.PoseidonCodeHash, + CodeSize: account.CodeSize, + SecureKey: accIt.Hash().Bytes(), } - if !conf.SkipCode && !bytes.Equal(account.CodeHash, emptyCode) { - da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) + if !conf.SkipCode && !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash) { + da.Code = rawdb.ReadCode(db, common.BytesToHash(account.KeccakCodeHash)) } if !conf.SkipStorage { da.Storage = make(map[common.Hash]string) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 20e1fed727..d6c6f7739f 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3043,8 +3043,11 @@ func TestPoseidonCodeHash(t *testing.T) { // check empty code hash state, _ := blockchain.State() - codeHash := state.GetCodeHash(addr1) - assert.Equal(t, codeHash, common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), "code hash mismatch") + poseidonCodeHash := state.GetPoseidonCodeHash(addr1) + keccakCodeHash := state.GetKeccakCodeHash(addr1) + + assert.Equal(t, common.HexToHash("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864"), poseidonCodeHash, "code hash mismatch") + assert.Equal(t, common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), keccakCodeHash, "code hash mismatch") // deploy contract through transaction chain, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, gen *BlockGen) { @@ -3061,10 +3064,11 @@ func TestPoseidonCodeHash(t *testing.T) { assert.Equal(t, common.HexToAddress("0x3A220f351252089D385b29beca14e27F204c296A"), contractAddress, "address mismatch") state, _ = blockchain.State() - codeHash = state.GetCodeHash(contractAddress) + poseidonCodeHash = state.GetPoseidonCodeHash(contractAddress) + keccakCodeHash = state.GetKeccakCodeHash(contractAddress) - // keccak: 0x089bfd332dfa6117cbc20756f31801ce4f5a175eb258e46bf8123317da54cd96 - assert.Equal(t, codeHash, common.HexToHash("0x28ec09723b285e17caabc4a8d52dbd097feddf408aee115cbb57c3c9c814d2b2"), "code hash mismatch") + assert.Equal(t, common.HexToHash("0x0df04366a061c969e08137570a59536df95672ec21d00cb738fb90cac8e78bcc"), poseidonCodeHash, "code hash mismatch") + assert.Equal(t, common.HexToHash("0x089bfd332dfa6117cbc20756f31801ce4f5a175eb258e46bf8123317da54cd96"), keccakCodeHash, "code hash mismatch") // deploy contract through another contract (CREATE and CREATE2) chain, receipts = GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), engine, db, 1, func(i int, gen *BlockGen) { @@ -3086,12 +3090,16 @@ func TestPoseidonCodeHash(t *testing.T) { assert.Equal(t, common.HexToAddress("0x4099734c88B7D091E744da0E849df0e818e7E208"), address2, "address mismatch") state, _ = blockchain.State() - codeHash1 := state.GetCodeHash(address1) - codeHash2 := state.GetCodeHash(address2) + poseidonCodeHash1 := state.GetPoseidonCodeHash(address1) + poseidonCodeHash2 := state.GetPoseidonCodeHash(address2) + keccakCodeHash1 := state.GetKeccakCodeHash(address1) + keccakCodeHash2 := state.GetKeccakCodeHash(address2) - // keccak: 0xfb5cd93a70ce47f91d33fac3afdb7b54680a6b0683506646a108ef4dfc047583 - assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash1, "code hash mismatch") - assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash2, "code hash mismatch") + assert.Equal(t, common.HexToHash("0x12eb18061d12f883c4d4c2041925cc2916c86fcfcb9458c8b9a3fb32257215d0"), poseidonCodeHash1, "code hash mismatch") + assert.Equal(t, common.HexToHash("0x12eb18061d12f883c4d4c2041925cc2916c86fcfcb9458c8b9a3fb32257215d0"), poseidonCodeHash2, "code hash mismatch") + + assert.Equal(t, common.HexToHash("0xfb5cd93a70ce47f91d33fac3afdb7b54680a6b0683506646a108ef4dfc047583"), keccakCodeHash1, "code hash mismatch") + assert.Equal(t, common.HexToHash("0xfb5cd93a70ce47f91d33fac3afdb7b54680a6b0683506646a108ef4dfc047583"), keccakCodeHash2, "code hash mismatch") } // TestFeeVault tests that the fee vault receives all tx fees correctly. diff --git a/core/genesis_test.go b/core/genesis_test.go index 9264bb62ec..57ca690b44 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -41,7 +41,7 @@ func TestInvalidCliqueConfig(t *testing.T) { func TestSetupGenesis(t *testing.T) { var ( - customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") + customghash = common.HexToHash("0x700380ab70d789c462c4e8f0db082842095321f390d0a3f25f400f0746db32bc") customg = Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)}, Alloc: GenesisAlloc{ @@ -66,23 +66,23 @@ func TestSetupGenesis(t *testing.T) { 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, 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) - return SetupGenesisBlock(db, 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, 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) + // return SetupGenesisBlock(db, nil) + // }, + // wantHash: params.MainnetGenesisHash, + // wantConfig: params.MainnetChainConfig, + // }, { name: "custom block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { @@ -92,16 +92,16 @@ func TestSetupGenesis(t *testing.T) { wantHash: customghash, wantConfig: customg.Config, }, - { - name: "custom block in DB, genesis == ropsten", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - customg.MustCommit(db) - return SetupGenesisBlock(db, DefaultRopstenGenesisBlock()) - }, - wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash}, - wantHash: params.RopstenGenesisHash, - wantConfig: params.RopstenChainConfig, - }, + // { + // name: "custom block in DB, genesis == ropsten", + // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + // customg.MustCommit(db) + // return SetupGenesisBlock(db, DefaultRopstenGenesisBlock()) + // }, + // wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash}, + // wantHash: params.RopstenGenesisHash, + // wantConfig: params.RopstenChainConfig, + // }, { name: "compatible config in DB", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { @@ -161,29 +161,29 @@ func TestSetupGenesis(t *testing.T) { } } -// TestGenesisHashes checks the congruity of default genesis data to -// corresponding hardcoded genesis hash values. -func TestGenesisHashes(t *testing.T) { - for i, c := range []struct { - genesis *Genesis - want common.Hash - }{ - {DefaultGenesisBlock(), params.MainnetGenesisHash}, - {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash}, - {DefaultRopstenGenesisBlock(), params.RopstenGenesisHash}, - {DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash}, - {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, - } { - // Test via MustCommit - if have := c.genesis.MustCommit(rawdb.NewMemoryDatabase()).Hash(); have != c.want { - t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) - } - // Test via ToBlock - if have := c.genesis.ToBlock(nil).Hash(); have != c.want { - t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) - } - } -} +// // TestGenesisHashes checks the congruity of default genesis data to +// // corresponding hardcoded genesis hash values. +// func TestGenesisHashes(t *testing.T) { +// for i, c := range []struct { +// genesis *Genesis +// want common.Hash +// }{ +// {DefaultGenesisBlock(), params.MainnetGenesisHash}, +// {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash}, +// {DefaultRopstenGenesisBlock(), params.RopstenGenesisHash}, +// {DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash}, +// {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, +// } { +// // Test via MustCommit +// if have := c.genesis.MustCommit(rawdb.NewMemoryDatabase()).Hash(); have != c.want { +// t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) +// } +// // Test via ToBlock +// if have := c.genesis.ToBlock(nil).Hash(); have != c.want { +// t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) +// } +// } +// } func TestGenesis_Commit(t *testing.T) { genesis := &Genesis{ diff --git a/core/state/dump.go b/core/state/dump.go index 030f22da33..17b5f078ac 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -49,15 +49,16 @@ type DumpCollector interface { // DumpAccount represents an account in the state. type DumpAccount struct { - Balance string `json:"balance"` - Nonce uint64 `json:"nonce"` - Root hexutil.Bytes `json:"root"` - CodeHash hexutil.Bytes `json:"codeHash"` - Code hexutil.Bytes `json:"code,omitempty"` - Storage map[common.Hash]string `json:"storage,omitempty"` - Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode - SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key - + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + Root hexutil.Bytes `json:"root"` + KeccakCodeHash hexutil.Bytes `json:"keccakCodeHash"` + PoseidonCodeHash hexutil.Bytes `json:"poseidonCodeHash"` + CodeSize uint64 `json:"codeSize"` + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[common.Hash]string `json:"storage,omitempty"` + Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode + SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key } // Dump represents the full dump in a collected format, as one large map. @@ -101,14 +102,15 @@ type iterativeDump struct { // OnAccount implements DumpCollector interface func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) { dumpAccount := &DumpAccount{ - Balance: account.Balance, - Nonce: account.Nonce, - Root: account.Root, - CodeHash: account.CodeHash, - Code: account.Code, - Storage: account.Storage, - SecureKey: account.SecureKey, - Address: nil, + Balance: account.Balance, + Nonce: account.Nonce, + Root: account.Root, + KeccakCodeHash: account.KeccakCodeHash, + PoseidonCodeHash: account.PoseidonCodeHash, + Code: account.Code, + Storage: account.Storage, + SecureKey: account.SecureKey, + Address: nil, } if addr != (common.Address{}) { dumpAccount.Address = &addr @@ -146,11 +148,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] panic(err) } account := DumpAccount{ - Balance: data.Balance.String(), - Nonce: data.Nonce, - Root: data.Root[:], - CodeHash: data.CodeHash, - SecureKey: it.Key, + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: data.Root[:], + KeccakCodeHash: data.KeccakCodeHash, + PoseidonCodeHash: data.PoseidonCodeHash, + CodeSize: data.CodeSize, + SecureKey: it.Key, } addrBytes := s.trie.GetKey(it.Key) if addrBytes == nil { diff --git a/core/state/iterator.go b/core/state/iterator.go index a26fbf5ec1..b699c54aa2 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -117,12 +117,12 @@ func (it *NodeIterator) step() error { if !it.dataIt.Next(true) { it.dataIt = nil } - if !bytes.Equal(account.CodeHash, emptyCodeHash) { - it.codeHash = common.BytesToHash(account.CodeHash) + if !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash) { + it.codeHash = common.BytesToHash(account.KeccakCodeHash) addrHash := common.BytesToHash(it.stateIt.LeafKey()) - it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash)) + it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.KeccakCodeHash)) if err != nil { - return fmt.Errorf("code %x: %v", account.CodeHash, err) + return fmt.Errorf("code %x: %v", account.KeccakCodeHash, err) } } it.accountHash = it.stateIt.Parent() diff --git a/core/state/journal.go b/core/state/journal.go index 2e280622d1..72239bfe52 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -113,8 +113,8 @@ type ( key, prevalue common.Hash } codeChange struct { - account *common.Address - prevcode, prevhash []byte + account *common.Address + prevcode []byte } // Changes to other state values. @@ -198,7 +198,7 @@ func (ch nonceChange) dirtied() *common.Address { } func (ch codeChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) + s.getStateObject(*ch.account).setCode(ch.prevcode) } func (ch codeChange) dirtied() *common.Address { diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index a20d8720a2..4d3845ca09 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -59,8 +59,8 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // emptyCode is the known hash of the empty EVM bytecode. - emptyCode = codehash.EmptyCodeHash.Bytes() + // emptyKeccakCodeHash is the known hash of the empty EVM bytecode. + emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash.Bytes() ) // Pruner is an offline tool to prune the stale state with the @@ -445,8 +445,8 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { return storageIter.Error() } } - if !bytes.Equal(acc.CodeHash, emptyCode) { - stateBloom.Put(acc.CodeHash, nil) + if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) { + stateBloom.Put(acc.KeccakCodeHash, nil) } } } diff --git a/core/state/snapshot/account.go b/core/state/snapshot/account.go index 111ea3a023..13bbbbd788 100644 --- a/core/state/snapshot/account.go +++ b/core/state/snapshot/account.go @@ -29,14 +29,16 @@ import ( // or slim-snapshot format which replaces the empty root and code hash as nil // byte slice. type Account struct { - Nonce uint64 - Balance *big.Int - Root []byte - CodeHash []byte + Nonce uint64 + Balance *big.Int + Root []byte + KeccakCodeHash []byte + PoseidonCodeHash []byte + CodeSize uint64 } // SlimAccount converts a state.Account content into a slim snapshot account -func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) Account { +func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, keccakcodehash []byte, poseidoncodehash []byte, codesize uint64) Account { slim := Account{ Nonce: nonce, Balance: balance, @@ -44,16 +46,18 @@ func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []by if root != emptyRoot { slim.Root = root[:] } - if !bytes.Equal(codehash, emptyCode[:]) { - slim.CodeHash = codehash + if !bytes.Equal(keccakcodehash, emptyKeccakCode[:]) { + slim.KeccakCodeHash = keccakcodehash + slim.PoseidonCodeHash = poseidoncodehash + slim.CodeSize = codesize } return slim } // SlimAccountRLP converts a state.Account content into a slim snapshot // version RLP encoded. -func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) []byte { - data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, codehash)) +func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, keccakcodehash []byte, poseidoncodehash []byte, codesize uint64) []byte { + data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, keccakcodehash, poseidoncodehash, codesize)) if err != nil { panic(err) } @@ -70,8 +74,10 @@ func FullAccount(data []byte) (Account, error) { if len(account.Root) == 0 { account.Root = emptyRoot[:] } - if len(account.CodeHash) == 0 { - account.CodeHash = emptyCode[:] + if len(account.KeccakCodeHash) == 0 { + account.KeccakCodeHash = emptyKeccakCode[:] + account.PoseidonCodeHash = emptyPoseidonCode[:] + account.CodeSize = 0 } return account, nil } diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index 29024bf0f8..2be49d237f 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -73,7 +73,7 @@ func GenerateTrie(snaptree *Tree, root common.Hash, src ethdb.Database, dst ethd got, err := generateTrieRoot(dst, acctIt, common.Hash{}, stackTrieGenerate, func(dst ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { // Migrate the code first, commit the contract code into the tmp db. - if codeHash != emptyCode { + if codeHash != emptyKeccakCode { code := rawdb.ReadCode(src, codeHash) if len(code) == 0 { return common.Hash{}, errors.New("failed to read contract code") @@ -316,7 +316,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash, return stop(err) } go func(hash common.Hash) { - subroot, err := leafCallback(db, hash, common.BytesToHash(account.CodeHash), stats) + subroot, err := leafCallback(db, hash, common.BytesToHash(account.KeccakCodeHash), stats) if err != nil { results <- err return diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index be68b4e879..e5e2b42001 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -43,8 +43,9 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // emptyCode is the known hash of the empty EVM bytecode. - emptyCode = codehash.EmptyCodeHash + // emptyPoseidonCode is the known hash of the empty EVM bytecode. + emptyPoseidonCode = codehash.EmptyPoseidonCodeHash + emptyKeccakCode = codehash.EmptyKeccakCodeHash // accountCheckRange is the upper limit of the number of accounts involved in // each range check. This is a value estimated based on experience. If this @@ -613,10 +614,12 @@ func (dl *diskLayer) generate(stats *generatorStats) { } // Retrieve the current account and flatten it into the internal format var acc struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte + Nonce uint64 + Balance *big.Int + Root common.Hash + KeccakCodeHash []byte + PoseidonCodeHash []byte + CodeSize uint64 } if err := rlp.DecodeBytes(val, &acc); err != nil { log.Crit("Invalid account encountered during snapshot creation", "err", err) @@ -625,15 +628,16 @@ func (dl *diskLayer) generate(stats *generatorStats) { if accMarker == nil || !bytes.Equal(accountHash[:], accMarker) { dataLen := len(val) // Approximate size, saves us a round of RLP-encoding if !write { - if bytes.Equal(acc.CodeHash, emptyCode[:]) { - dataLen -= 32 + if bytes.Equal(acc.KeccakCodeHash, emptyKeccakCode[:]) { + // account for keccakCodeHash, poseidonCodeHash, and CodeSize + dataLen = dataLen - 32 - 32 - 8 } if acc.Root == emptyRoot { dataLen -= 32 } snapRecoveredAccountMeter.Mark(1) } else { - data := SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash) + data := SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.KeccakCodeHash, acc.PoseidonCodeHash, acc.CodeSize) dataLen = len(data) rawdb.WriteAccountSnapshot(batch, accountHash, data) snapGeneratedAccountMeter.Mark(1) diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index e108d06428..43a6946fc2 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -50,21 +50,21 @@ func TestGeneration(t *testing.T) { stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 accTrie, _ := trie.NewSecure(common.Hash{}, triedb) - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e - acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 - acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 - root, _, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd + root, _, _ := accTrie.Commit(nil) // Root: 0x0bc6b6959d2589404dd3e4b25783a829b58625f6b673f095e9a97391b474c3f9 triedb.Commit(root, false, nil) - 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) } snap := generateSnapshot(diskdb, triedb, 16, root) @@ -107,7 +107,7 @@ func TestGenerateExistentState(t *testing.T) { stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 accTrie, _ := trie.NewSecure(common.Hash{}, triedb) - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-1")), val) @@ -115,13 +115,13 @@ func TestGenerateExistentState(t *testing.T) { rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-2")), []byte("val-2")) rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-3")), []byte("val-3")) - acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 diskdb.Put(hashData([]byte("acc-2")).Bytes(), val) rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-2")), val) - acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-3")), val) @@ -248,58 +248,58 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) { stRoot := helper.makeStorageTrie([]string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) // Account one, empty root but non-empty database - helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) // Account two, non empty root but empty database - helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) // Miss slots { // Account three, non empty root but misses slots in the beginning - helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) // Account four, non empty root but misses slots in the middle - helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) // Account five, non empty root but misses slots in the end - helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) } // Wrong storage slots { // Account six, non empty root but wrong slots in the beginning - helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) // Account seven, non empty root but wrong slots in the middle - helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) // Account eight, non empty root but wrong slots in the end - helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) // Account 9, non empty root but rotated slots - helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) } // Extra storage slots { // Account 10, non empty root but extra slots in the beginning - helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) // Account 11, non empty root but extra slots in the middle - helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) // Account 12, non empty root but extra slots in the end - helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) } @@ -334,25 +334,25 @@ func TestGenerateExistentStateWithWrongAccounts(t *testing.T) { // Missing accounts, only in the trie { - helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Beginning - helper.addTrieAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Middle - helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // End + helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) // Beginning + helper.addTrieAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) // Middle + helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) // End } // Wrong accounts { - helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) - helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) + helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) + helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: common.Hex2Bytes("0x1234"), PoseidonCodeHash: common.Hex2Bytes("0x1234"), CodeSize: 1}) - helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) - helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) + helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) + helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) } // Extra accounts, only in the snap { - helper.addSnapAccount("acc-0", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyRoot.Bytes()}) // before the beginning - helper.addSnapAccount("acc-5", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: common.Hex2Bytes("0x1234")}) // Middle - helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyRoot.Bytes()}) // after the end + helper.addSnapAccount("acc-0", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyRoot.Bytes(), PoseidonCodeHash: emptyRoot.Bytes(), CodeSize: 1}) // before the beginning + helper.addSnapAccount("acc-5", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: common.Hex2Bytes("0x1234"), PoseidonCodeHash: common.Hex2Bytes("0x1234"), CodeSize: 1}) // Middle + helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyRoot.Bytes(), PoseidonCodeHash: emptyRoot.Bytes(), CodeSize: 1}) // after the end } root, snap := helper.Generate() @@ -384,15 +384,15 @@ func TestGenerateCorruptAccountTrie(t *testing.T) { triedb = trie.NewDatabase(diskdb) ) tr, _ := trie.NewSecure(common.Hash{}, triedb) - acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) tr.Update([]byte("acc-1"), val) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 - acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) tr.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 - acc = &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) tr.Update([]byte("acc-3"), val) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 tr.Commit(nil) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 @@ -434,27 +434,27 @@ func TestGenerateMissingStorageTrie(t *testing.T) { stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 accTrie, _ := trie.NewSecure(common.Hash{}, triedb) - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + accTrie.Update([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f - acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + accTrie.Update([]byte("acc-2"), val) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366 - acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + accTrie.Update([]byte("acc-3"), val) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd // We can only corrupt the disk database, so flush the tries out triedb.Reference( common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), - common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), + common.HexToHash("0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f"), ) triedb.Reference( common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), - common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), + common.HexToHash("0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b"), ) triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) @@ -493,27 +493,27 @@ func TestGenerateCorruptStorageTrie(t *testing.T) { stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 accTrie, _ := trie.NewSecure(common.Hash{}, triedb) - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + accTrie.Update([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f - acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + accTrie.Update([]byte("acc-2"), val) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366 - acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ = rlp.EncodeToBytes(acc) - accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + accTrie.Update([]byte("acc-3"), val) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd // We can only corrupt the disk database, so flush the tries out triedb.Reference( common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), - common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), + common.HexToHash("0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f"), ) triedb.Reference( common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), - common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), + common.HexToHash("0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b"), ) triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) @@ -555,7 +555,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) { ) accTrie, _ := trie.NewSecure(common.Hash{}, triedb) { // Account one in the trie - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e // Identical in the snap @@ -568,7 +568,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) { rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-5")), []byte("val-5")) } { // Account two exists only in the snapshot - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) key := hashData([]byte("acc-2")) rawdb.WriteAccountSnapshot(diskdb, key, val) @@ -619,7 +619,7 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) { ) accTrie, _ := trie.NewSecure(common.Hash{}, triedb) { // Account one in the trie - acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e // Identical in the snap @@ -631,8 +631,8 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) { } { // 100 accounts exist only in snapshot for i := 0; i < 1000; i++ { - //acc := &Account{Balance: big.NewInt(int64(i)), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} - acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + //acc := &Account{Balance: big.NewInt(int64(i)), Root: stTrie.Hash().Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes()} + acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) key := hashData([]byte(fmt.Sprintf("acc-%d", i))) rawdb.WriteAccountSnapshot(diskdb, key, val) @@ -677,7 +677,7 @@ func TestGenerateWithExtraBeforeAndAfter(t *testing.T) { ) accTrie, _ := trie.New(common.Hash{}, triedb) { - acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update(common.HexToHash("0x03").Bytes(), val) accTrie.Update(common.HexToHash("0x07").Bytes(), val) @@ -723,7 +723,7 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) { ) accTrie, _ := trie.New(common.Hash{}, triedb) { - acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0} val, _ := rlp.EncodeToBytes(acc) accTrie.Update(common.HexToHash("0x03").Bytes(), val) @@ -767,7 +767,7 @@ func TestGenerateFromEmptySnap(t *testing.T) { // Add 1K accounts to the trie for i := 0; i < 400; i++ { helper.addTrieAccount(fmt.Sprintf("acc-%d", i), - &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) + &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) } root, snap := helper.Generate() t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 @@ -804,7 +804,7 @@ func TestGenerateWithIncompleteStorage(t *testing.T) { // on the sensitive spots at the boundaries for i := 0; i < 8; i++ { accKey := fmt.Sprintf("acc-%d", i) - helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: emptyCode.Bytes()}) + helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) var moddedKeys []string var moddedVals []string for ii := 0; ii < 8; ii++ { diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go index a28231952b..065e2a8bb2 100644 --- a/core/state/snapshot/snapshot_test.go +++ b/core/state/snapshot/snapshot_test.go @@ -44,10 +44,12 @@ func randomHash() common.Hash { func randomAccount() []byte { root := randomHash() a := Account{ - Balance: big.NewInt(rand.Int63()), - Nonce: rand.Uint64(), - Root: root[:], - CodeHash: emptyCode[:], + Balance: big.NewInt(rand.Int63()), + Nonce: rand.Uint64(), + Root: root[:], + KeccakCodeHash: emptyKeccakCode[:], + PoseidonCodeHash: emptyPoseidonCode[:], + CodeSize: 0, } data, _ := rlp.EncodeToBytes(a) return data diff --git a/core/state/state_object.go b/core/state/state_object.go index dfa80d84eb..f9213a0a31 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -31,7 +31,8 @@ import ( "github.com/scroll-tech/go-ethereum/rlp" ) -var emptyCodeHash = codehash.EmptyCodeHash.Bytes() +var emptyPoseidonCodeHash = codehash.EmptyPoseidonCodeHash.Bytes() +var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash.Bytes() type Code []byte @@ -96,7 +97,8 @@ type stateObject struct { // empty returns whether the account is considered empty. func (s *stateObject) empty() bool { - return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) + // note: if KeccakCodeHash is empty then PoseidonCodeHash and CodeSize will also be empty + return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.KeccakCodeHash, emptyKeccakCodeHash) } // newObject creates a state object. @@ -104,8 +106,10 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st if data.Balance == nil { data.Balance = new(big.Int) } - if data.CodeHash == nil { - data.CodeHash = emptyCodeHash + if data.KeccakCodeHash == nil { + data.KeccakCodeHash = emptyKeccakCodeHash + data.PoseidonCodeHash = emptyPoseidonCodeHash + data.CodeSize = 0 } if data.Root == (common.Hash{}) { data.Root = db.db.TrieDB().EmptyRoot() @@ -482,12 +486,12 @@ func (s *stateObject) Code(db Database) []byte { if s.code != nil { return s.code } - if bytes.Equal(s.CodeHash(), emptyCodeHash) { + if bytes.Equal(s.KeccakCodeHash(), emptyKeccakCodeHash) { return nil } - code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash())) + code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.KeccakCodeHash())) if err != nil { - s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) + s.setError(fmt.Errorf("can't load code hash %x: %v", s.KeccakCodeHash(), err)) } s.code = code return code @@ -496,33 +500,24 @@ func (s *stateObject) Code(db Database) []byte { // CodeSize returns the size of the contract code associated with this object, // or zero if none. This method is an almost mirror of Code, but uses a cache // inside the database to avoid loading codes seen recently. -func (s *stateObject) CodeSize(db Database) int { - if s.code != nil { - return len(s.code) - } - if bytes.Equal(s.CodeHash(), emptyCodeHash) { - return 0 - } - size, err := db.ContractCodeSize(s.addrHash, common.BytesToHash(s.CodeHash())) - if err != nil { - s.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) - } - return size +func (s *stateObject) CodeSize() uint64 { + return s.data.CodeSize } -func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { +func (s *stateObject) SetCode(code []byte) { prevcode := s.Code(s.db.db) s.db.journal.append(codeChange{ account: &s.address, - prevhash: s.CodeHash(), prevcode: prevcode, }) - s.setCode(codeHash, code) + s.setCode(code) } -func (s *stateObject) setCode(codeHash common.Hash, code []byte) { +func (s *stateObject) setCode(code []byte) { s.code = code - s.data.CodeHash = codeHash[:] + s.data.KeccakCodeHash = codehash.KeccakCodeHash(code).Bytes() + s.data.PoseidonCodeHash = codehash.PoseidonCodeHash(code).Bytes() + s.data.CodeSize = uint64(len(code)) s.dirtyCode = true } @@ -538,8 +533,12 @@ func (s *stateObject) setNonce(nonce uint64) { s.data.Nonce = nonce } -func (s *stateObject) CodeHash() []byte { - return s.data.CodeHash +func (s *stateObject) PoseidonCodeHash() []byte { + return s.data.PoseidonCodeHash +} + +func (s *stateObject) KeccakCodeHash() []byte { + return s.data.KeccakCodeHash } func (s *stateObject) Balance() *big.Int { diff --git a/core/state/state_test.go b/core/state/state_test.go index 20c8546214..0c1469ff96 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -23,7 +23,6 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/rawdb" - "github.com/scroll-tech/go-ethereum/crypto/codehash" "github.com/scroll-tech/go-ethereum/ethdb" ) @@ -47,7 +46,7 @@ func TestDump(t *testing.T) { obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1.AddBalance(big.NewInt(22)) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) - obj2.SetCode(codehash.CodeHash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) + obj2.SetCode([]byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) obj3.SetBalance(big.NewInt(44)) @@ -59,27 +58,33 @@ func TestDump(t *testing.T) { // check that DumpToCollector contains the state objects that are in trie got := string(s.state.Dump(nil)) want := `{ - "root": "bfd17853c56ac63be7327da432ef90a9b04376d71efede2a60765d2c94704cfe", + "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": "0x2263940ad476ca23bf01a9b033d65cd6b8bf9b7224a7ef2a4f10b61a2c039083", + "keccakCodeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3", + "poseidonCodeHash": "0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c", + "codeSize": 7, "code": "0x03030303030303", "key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1" } @@ -165,7 +170,7 @@ func TestSnapshot2(t *testing.T) { so0 := state.getStateObject(stateobjaddr0) so0.SetBalance(big.NewInt(42)) so0.SetNonce(43) - so0.SetCode(codehash.CodeHash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) + so0.SetCode([]byte{'c', 'a', 'f', 'e'}) so0.suicided = false so0.deleted = false state.setStateObject(so0) @@ -177,7 +182,7 @@ func TestSnapshot2(t *testing.T) { so1 := state.getStateObject(stateobjaddr1) so1.SetBalance(big.NewInt(52)) so1.SetNonce(53) - so1.SetCode(codehash.CodeHash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) + so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'}) so1.suicided = true so1.deleted = true state.setStateObject(so1) @@ -217,8 +222,14 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) { if so0.data.Root != so1.data.Root { t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:]) } - if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) { - t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) + if !bytes.Equal(so0.KeccakCodeHash(), so1.KeccakCodeHash()) { + t.Fatalf("KeccakCodeHash 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 c89466ee46..61d5eceaa7 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -31,7 +31,6 @@ import ( "github.com/scroll-tech/go-ethereum/core/state/snapshot" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto" - "github.com/scroll-tech/go-ethereum/crypto/codehash" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/metrics" "github.com/scroll-tech/go-ethereum/rlp" @@ -290,20 +289,28 @@ func (s *StateDB) GetCode(addr common.Address) []byte { return nil } -func (s *StateDB) GetCodeSize(addr common.Address) int { +func (s *StateDB) GetCodeSize(addr common.Address) uint64 { stateObject := s.getStateObject(addr) if stateObject != nil { - return stateObject.CodeSize(s.db) + return stateObject.CodeSize() } return 0 } -func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { +func (s *StateDB) GetPoseidonCodeHash(addr common.Address) common.Hash { stateObject := s.getStateObject(addr) if stateObject == nil { return common.Hash{} } - return common.BytesToHash(stateObject.CodeHash()) + return common.BytesToHash(stateObject.PoseidonCodeHash()) +} + +func (s *StateDB) GetKeccakCodeHash(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.KeccakCodeHash()) } // GetState retrieves a value from the given account's storage trie. @@ -460,7 +467,7 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { func (s *StateDB) SetCode(addr common.Address, code []byte) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetCode(codehash.CodeHash(code), code) + stateObject.SetCode(code) } } @@ -522,7 +529,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) { // enough to track account updates at commit time, deletions need tracking // at transaction boundary level to ensure we capture state clearing. if s.snap != nil { - s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.KeccakCodeHash, obj.data.PoseidonCodeHash, obj.data.CodeSize) } } @@ -573,13 +580,17 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { return nil } data = &types.StateAccount{ - Nonce: acc.Nonce, - Balance: acc.Balance, - CodeHash: acc.CodeHash, - Root: common.BytesToHash(acc.Root), + Nonce: acc.Nonce, + Balance: acc.Balance, + Root: common.BytesToHash(acc.Root), + KeccakCodeHash: acc.KeccakCodeHash, + PoseidonCodeHash: acc.PoseidonCodeHash, + CodeSize: acc.CodeSize, } - if len(data.CodeHash) == 0 { - data.CodeHash = emptyCodeHash + if len(data.KeccakCodeHash) == 0 { + data.KeccakCodeHash = emptyKeccakCodeHash + data.PoseidonCodeHash = emptyPoseidonCodeHash + data.CodeSize = 0 } if data.Root == (common.Hash{}) { data.Root = s.db.TrieDB().EmptyRoot() @@ -969,7 +980,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { if obj := s.stateObjects[addr]; !obj.deleted { // Write any contract code associated with the state object if obj.code != nil && obj.dirtyCode { - rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code) + rawdb.WriteCode(codeWriter, common.BytesToHash(obj.KeccakCodeHash()), obj.code) obj.dirtyCode = false } // Write any storage changes in the state object to its storage trie diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index a5a3cafebd..90ef2bbd2d 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -443,7 +443,8 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) - checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) + checkeq("GetKeccakCodeHash", state.GetKeccakCodeHash(addr), checkstate.GetKeccakCodeHash(addr)) + checkeq("GetPoseidonCodeHash", state.GetPoseidonCodeHash(addr), checkstate.GetPoseidonCodeHash(addr)) checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) // Check storage. if obj := state.getStateObject(addr); obj != nil { diff --git a/core/state/sync.go b/core/state/sync.go index b48908d3b6..df80eadd80 100644 --- a/core/state/sync.go +++ b/core/state/sync.go @@ -49,7 +49,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S return err } syncer.AddSubTrie(obj.Root, hexpath, parent, onSlot) - syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), hexpath, parent) + syncer.AddCodeEntry(common.BytesToHash(obj.KeccakCodeHash), hexpath, parent) return nil } syncer = trie.NewSync(root, database, onAccount, bloom) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 613e11117a..7f2320d9f6 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -59,12 +59,12 @@ func makeTestState() (Database, common.Hash, []*testAccount) { acc.nonce = uint64(42 * i) if i%3 == 0 { - obj.SetCode(codehash.CodeHash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}) + obj.SetCode([]byte{i, i, i, i, i}) acc.code = []byte{i, i, i, i, i} } if i%5 == 0 { for j := byte(0); j < 5; j++ { - hash := codehash.CodeHash([]byte{i, i, i, i, i, j, j}) + hash := codehash.KeccakCodeHash([]byte{i, i, i, i, i, j, j}) obj.SetState(db, hash, hash) } } @@ -408,10 +408,10 @@ func TestIncompleteStateSync(t *testing.T) { var isCode = make(map[common.Hash]struct{}) for _, acc := range srcAccounts { if len(acc.code) > 0 { - isCode[codehash.CodeHash(acc.code)] = struct{}{} + isCode[codehash.KeccakCodeHash(acc.code)] = struct{}{} } } - isCode[common.BytesToHash(emptyCodeHash)] = struct{}{} + isCode[common.BytesToHash(emptyKeccakCodeHash)] = struct{}{} checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot) // Create a destination state and sync with the scheduler diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 8c1b276516..c1cde9cfcd 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -285,7 +285,7 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), }, - want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x14644f8304e8c7052dee61ac235af61e12e25a730d13beccac8b3b72f5705874", + want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", }, } { block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) diff --git a/core/state_transition.go b/core/state_transition.go index 48cb02bea9..1987e17cd0 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -29,7 +29,7 @@ import ( "github.com/scroll-tech/go-ethereum/params" ) -var emptyCodeHash = codehash.EmptyCodeHash +var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash /* The State Transitioning Model @@ -227,7 +227,7 @@ func (st *StateTransition) preCheck() error { st.msg.From().Hex(), stNonce) } // Make sure the sender is an EOA - if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + if codeHash := st.state.GetKeccakCodeHash(st.msg.From()); codeHash != emptyKeccakCodeHash && codeHash != (common.Hash{}) { return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, st.msg.From().Hex(), codeHash) } diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 5981f06a89..dacc9ac8de 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -66,8 +66,8 @@ type ExecutionResult struct { // currently they are just `from` and `to` account AccountsAfter []*AccountWrapper `json:"accountAfter"` - // `CodeHash` only exists when tx is a contract call. - CodeHash *common.Hash `json:"codeHash,omitempty"` + // `PoseidonCodeHash` only exists when tx is a contract call. + PoseidonCodeHash *common.Hash `json:"poseidonCodeHash,omitempty"` // If it is a contract call, the contract code is returned. ByteCode string `json:"byteCode,omitempty"` StructLogs []*StructLogRes `json:"structLogs"` @@ -134,11 +134,13 @@ type ExtraData struct { } type AccountWrapper struct { - Address common.Address `json:"address"` - Nonce uint64 `json:"nonce"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash,omitempty"` - Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation + Address common.Address `json:"address"` + Nonce uint64 `json:"nonce"` + Balance *hexutil.Big `json:"balance"` + KeccakCodeHash common.Hash `json:"keccakCodeHash,omitempty"` + PoseidonCodeHash common.Hash `json:"poseidonCodeHash,omitempty"` + CodeSize uint64 `json:"codeSize"` + Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation } // while key & value can also be retrieved from StructLogRes.Storage, diff --git a/core/types/state_account.go b/core/types/state_account.go index ac67cfd67f..bb396d439d 100644 --- a/core/types/state_account.go +++ b/core/types/state_account.go @@ -25,8 +25,12 @@ import ( // StateAccount is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. type StateAccount struct { - Nonce uint64 - Balance *big.Int - Root common.Hash // merkle root of the storage trie - CodeHash []byte + Nonce uint64 + Balance *big.Int + Root common.Hash // merkle root of the storage trie + KeccakCodeHash []byte + + // StateAccount Scroll extensions + PoseidonCodeHash []byte + CodeSize uint64 } diff --git a/core/types/state_account_marshalling.go b/core/types/state_account_marshalling.go index 4d309bf4cd..f9bb330e0f 100644 --- a/core/types/state_account_marshalling.go +++ b/core/types/state_account_marshalling.go @@ -21,7 +21,6 @@ import ( "errors" "math/big" - "github.com/iden3/go-iden3-crypto/poseidon" "github.com/iden3/go-iden3-crypto/utils" zkt "github.com/scroll-tech/zktrie/types" @@ -33,73 +32,74 @@ var ( ErrInvalidLength = errors.New("StateAccount: invalid input length") ) -// Hash of StateAccount -// AccountHash = Hash( -// Hash(nonce, balance), -// Hash( -// Root, -// Hash(codeHashFirst16, codeHashLast16) -// )) -func (s *StateAccount) Hash() (*big.Int, error) { - nonce := new(big.Int).SetUint64(s.Nonce) - if s.Balance == nil { - s.Balance = new(big.Int) - } - hash1, err := poseidon.Hash([]*big.Int{nonce, s.Balance}) - if err != nil { - return nil, err - } - - codeHashFirst16 := new(big.Int).SetBytes(s.CodeHash[0:16]) - codeHashLast16 := new(big.Int).SetBytes(s.CodeHash[16:32]) - hash2, err := poseidon.Hash([]*big.Int{codeHashFirst16, codeHashLast16}) - if err != nil { - return nil, err - } - - rootHash := zkt.NewHashFromBytes(s.Root.Bytes()) - hash3, err := poseidon.Hash([]*big.Int{hash2, rootHash.BigInt()}) - if err != nil { - return nil, err - } - - hash4, err := poseidon.Hash([]*big.Int{hash1, hash3}) - if err != nil { - return nil, err - } - return hash4, nil -} - -// MarshalFields, the bytes scheme is: -// [0:32] Nonce uint64 big-endian in 32 byte +// 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] Root -// [96:128] CodeHash +// [64:96] StorageRoot +// [96:128] KeccakCodeHash +// [128:160] PoseidonCodehash +// (total 160 bytes) func (s *StateAccount) MarshalFields() ([]zkt.Byte32, uint32) { - fields := make([]zkt.Byte32, 4) - binary.BigEndian.PutUint64(fields[0][24:], s.Nonce) + fields := make([]zkt.Byte32, 5) + + if s.Balance == nil { + panic("StateAccount balance nil") + } if !utils.CheckBigIntInField(s.Balance) { - panic("balance overflow") + panic("StateAccount balance overflow") } - s.Balance.FillBytes(fields[1][:]) - copy(fields[2][:], s.CodeHash) - copy(fields[3][:], s.Root.Bytes()) - return fields, 4 + 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) != 128 { + if len(bytes) != 160 { return nil, ErrInvalidLength } + acc := new(StateAccount) - acc.Nonce = binary.BigEndian.Uint64(bytes[24:]) + + 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.CodeHash = make([]byte, 32) - copy(acc.CodeHash, bytes[64:96]) + acc.Root = common.Hash{} - acc.Root.SetBytes(bytes[96:128]) + 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 index c6d0160067..b822329a50 100644 --- a/core/types/state_account_marshalling_test.go +++ b/core/types/state_account_marshalling_test.go @@ -1,45 +1,189 @@ package types import ( + "math" "math/big" "testing" - zktrie "github.com/scroll-tech/zktrie/types" + "github.com/iden3/go-iden3-crypto/constants" + "github.com/stretchr/testify/assert" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/crypto/codehash" ) -func TestAccountMarshalling(t *testing.T) { - //ensure the hash scheme consistent with designation - example1 := &StateAccount{ - Nonce: 5, - Balance: big.NewInt(0).SetBytes(common.Hex2Bytes("01fffffffffffffffffffffffffffffffffffffffffffffffff9c8672c6bc7b3")), - CodeHash: common.Hex2Bytes("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), - } - - example2 := &StateAccount{ - Nonce: 2, - Balance: big.NewInt(0), - CodeHash: common.Hex2Bytes("cc0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab"), - Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"), - } - - for i, example := range []*StateAccount{example1, example2} { - fields, flag := example.MarshalFields() - - h1, err := zktrie.PreHandlingElems(flag, fields) - if err != nil { - t.Fatal(err) - } - - h2, err := example.Hash() - if err != nil { - t.Fatal(err) - } - - if h1.BigInt().Cmp(h2) != 0 { - t.Errorf("hash <%d> unmatched, expected [%x], get [%x]", i, h2.Bytes(), h1.Bytes()) - } - } - +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/core/vm/contract.go b/core/vm/contract.go index f9a5afdb49..8a2cf786c7 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -55,7 +55,7 @@ type Contract struct { analysis bitvec // Locally cached result of JUMPDEST analysis Code []byte - CodeHash common.Hash + CodeHash common.Hash // Keccak code hash CodeAddr *common.Address Input []byte diff --git a/core/vm/evm.go b/core/vm/evm.go index 922176800d..83be42444a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -29,9 +29,9 @@ import ( "github.com/scroll-tech/go-ethereum/params" ) -// emptyCodeHash is used by create to ensure deployment is disallowed to already +// emptyKeccakCodeHash is used by create to ensure deployment is disallowed to already // deployed contract addresses (relevant after the account abstraction). -var emptyCodeHash = codehash.EmptyCodeHash +var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash type ( // CanTransferFunc is the signature of a transfer guard function @@ -229,7 +229,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // If the account has no code, we can abort here // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), code) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -289,7 +289,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -332,7 +332,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by addrCopy := addr // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -388,7 +388,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. @@ -438,8 +438,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, evm.StateDB.AddAddressToAccessList(address) } // Ensure there's no existing contract already at the designated address - contractHash := evm.StateDB.GetCodeHash(address) - if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + contractHash := evm.StateDB.GetKeccakCodeHash(address) + if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyKeccakCodeHash) { return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 90a723d124..42af498ec5 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -342,7 +342,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) + slot.SetUint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())) return nil, nil } @@ -420,7 +420,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.StateDB.Empty(address) { slot.Clear() } else { - slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) + slot.SetBytes(interpreter.evm.StateDB.GetKeccakCodeHash(address).Bytes()) } return nil, nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index bd594b9c82..809ce4484d 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -34,10 +34,11 @@ type StateDB interface { GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) - GetCodeHash(common.Address) common.Hash + GetKeccakCodeHash(common.Address) common.Hash GetCode(common.Address) []byte SetCode(common.Address, []byte) - GetCodeSize(common.Address) int + GetPoseidonCodeHash(common.Address) common.Hash + GetCodeSize(common.Address) uint64 AddRefund(uint64) SubRefund(uint64) diff --git a/core/vm/logger_trace.go b/core/vm/logger_trace.go index e7307464dc..8c2d52b467 100644 --- a/core/vm/logger_trace.go +++ b/core/vm/logger_trace.go @@ -104,19 +104,23 @@ func traceLastNAddressAccount(n int) traceFunc { // StorageWrapper will be empty func getWrappedAccountForAddr(l *StructLogger, address common.Address) *types.AccountWrapper { return &types.AccountWrapper{ - Address: address, - Nonce: l.env.StateDB.GetNonce(address), - Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), - CodeHash: l.env.StateDB.GetCodeHash(address), + Address: address, + Nonce: l.env.StateDB.GetNonce(address), + Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), + KeccakCodeHash: l.env.StateDB.GetKeccakCodeHash(address), + PoseidonCodeHash: l.env.StateDB.GetPoseidonCodeHash(address), + CodeSize: l.env.StateDB.GetCodeSize(address), } } func getWrappedAccountForStorage(l *StructLogger, address common.Address, key common.Hash) *types.AccountWrapper { return &types.AccountWrapper{ - Address: address, - Nonce: l.env.StateDB.GetNonce(address), - Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), - CodeHash: l.env.StateDB.GetCodeHash(address), + Address: address, + Nonce: l.env.StateDB.GetNonce(address), + Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), + KeccakCodeHash: l.env.StateDB.GetKeccakCodeHash(address), + PoseidonCodeHash: l.env.StateDB.GetPoseidonCodeHash(address), + CodeSize: l.env.StateDB.GetCodeSize(address), Storage: &types.StorageWrapper{ Key: key.String(), Value: l.env.StateDB.GetState(address, key).String(), diff --git a/crypto/codehash/codehash.go b/crypto/codehash/codehash.go index 54da5c59a6..463c92e33f 100644 --- a/crypto/codehash/codehash.go +++ b/crypto/codehash/codehash.go @@ -2,15 +2,22 @@ package codehash import ( "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/crypto" "github.com/scroll-tech/go-ethereum/crypto/poseidon" ) -var EmptyCodeHash common.Hash +var EmptyPoseidonCodeHash common.Hash +var EmptyKeccakCodeHash common.Hash -func CodeHash(code []byte) (h common.Hash) { +func PoseidonCodeHash(code []byte) (h common.Hash) { return poseidon.CodeHash(code) } -func init() { - EmptyCodeHash = poseidon.CodeHash(nil) +func KeccakCodeHash(code []byte) (h common.Hash) { + return crypto.Keccak256Hash(code) +} + +func init() { + EmptyPoseidonCodeHash = poseidon.CodeHash(nil) + EmptyKeccakCodeHash = crypto.Keccak256Hash(nil) } diff --git a/crypto/poseidon/codehash.go b/crypto/poseidon/codehash.go index 6bad67b8e4..42ed8be076 100644 --- a/crypto/poseidon/codehash.go +++ b/crypto/poseidon/codehash.go @@ -4,44 +4,40 @@ import ( "math/big" "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/crypto" ) const defaultPoseidonChunk = 3 +const nBytesToFieldElement = 31 func CodeHash(code []byte) (h common.Hash) { - // special case for nil hash - if len(code) == 0 { - return crypto.Keccak256Hash(nil) - } + nBytes := int64(len(code)) - cap := int64(len(code)) - - // step 1: pad code with 0x0 (STOP) so that len(code) % 16 == 0 - // step 2: for every 16 bytes, convert to Fr, so that we get a Fr array - var length = (len(code) + 15) / 16 + // step 1: pad code with 0x0 (STOP) so that len(code) % nBytesToFieldElement == 0 + // step 2: for every nBytesToFieldElement bytes, convert to Fr, so that we get a Fr array + var length = (len(code) + nBytesToFieldElement - 1) / nBytesToFieldElement Frs := make([]*big.Int, length) ii := 0 for ii < length-1 { Frs[ii] = big.NewInt(0) - Frs[ii].SetBytes(code[ii*16 : (ii+1)*16]) + Frs[ii].SetBytes(code[ii*nBytesToFieldElement : (ii+1)*nBytesToFieldElement]) ii++ } - Frs[ii] = big.NewInt(0) - bytes := make([]byte, 16) - copy(bytes, code[ii*16:]) - Frs[ii].SetBytes(bytes) + if length > 0 { + Frs[ii] = big.NewInt(0) + bytes := make([]byte, nBytesToFieldElement) + copy(bytes, code[ii*nBytesToFieldElement:]) + Frs[ii].SetBytes(bytes) + } // step 3: apply the array onto a sponge process with the current poseidon scheme // (3 Frs permutation and 1 Fr for output, so the throughout is 2 Frs) // step 4: convert final root Fr to u256 (big-endian representation) - hash, _ := HashWithCap(Frs, defaultPoseidonChunk, cap) - - codeHash := common.Hash{} - hash.FillBytes(codeHash[:]) - - return codeHash + hash, err := HashWithCap(Frs, defaultPoseidonChunk, nBytes) + if err != nil { + return common.Hash{} + } + return common.BigToHash(hash) } diff --git a/crypto/poseidon/codehash_test.go b/crypto/poseidon/codehash_test.go index 97574ffbfe..b5a584d651 100644 --- a/crypto/poseidon/codehash_test.go +++ b/crypto/poseidon/codehash_test.go @@ -8,7 +8,7 @@ import ( func TestPoseidonCodeHash(t *testing.T) { // nil got := fmt.Sprintf("%s", CodeHash(nil)) - want := "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + want := "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864" if got != want { t.Errorf("got %q, wanted %q", got, want) @@ -16,9 +16,27 @@ func TestPoseidonCodeHash(t *testing.T) { // single byte got = fmt.Sprintf("%s", CodeHash([]byte{0})) - want = "0x0ee069e6aa796ef0e46cbd51d10468393d443a00f5affe72898d9ab62e335e16" + want = "0x29f94b67ee4e78b2bb08da025f9943c1201a7af025a27600c2dd0a2e71c7cf8b" if got != want { t.Errorf("got %q, wanted %q", got, want) } + + got = fmt.Sprintf("%s", CodeHash([]byte{1})) + want = "0x246d3c06960643350a3e2d587fa16315c381635eb5ac1ac4501e195423dbf78e" + + if got != want { + t.Errorf("got %q, wanted %q", got, want) + } + + // 32 bytes + bytes := make([]byte, 32) + for i := range bytes { + bytes[i] = 1 + } + got = fmt.Sprintf("%s", CodeHash(bytes)) + want = "0x0b46d156183dffdbed8e6c6b0af139b95c058e735878ca7f4dca334e0ea8bd20" + if got != want { + t.Errorf("got %q, wanted %q", got, want) + } } diff --git a/crypto/poseidon/poseidon.go b/crypto/poseidon/poseidon.go index 3e5d53ce03..0175563f4f 100644 --- a/crypto/poseidon/poseidon.go +++ b/crypto/poseidon/poseidon.go @@ -110,7 +110,7 @@ func Hash(inpBI []*big.Int, width int) (*big.Int, error) { // Hash using possible sponge specs specified by width (rate from 1 to 15), the size of input is applied as capacity // (notice we do not include width in the capacity ) -func HashWithCap(inpBI []*big.Int, width int, cap int64) (*big.Int, error) { +func HashWithCap(inpBI []*big.Int, width int, nBytes int64) (*big.Int, error) { if width < 2 { return nil, fmt.Errorf("width must be ranged from 2 to 16") } @@ -118,8 +118,13 @@ func HashWithCap(inpBI []*big.Int, width int, cap int64) (*big.Int, error) { return nil, fmt.Errorf("invalid inputs width %d, max %d", width, len(NROUNDSP)+1) //nolint:gomnd,lll } - capflag := ff.NewElement().SetBigInt(big.NewInt(cap)) + // capflag = nBytes * 2^64 + pow64 := big.NewInt(1) + pow64.Lsh(pow64, 64) + capflag := ff.NewElement().SetBigInt(big.NewInt(nBytes)) + capflag.Mul(capflag, ff.NewElement().SetBigInt(pow64)) + // initialize the state state := make([]*ff.Element, width) state[0] = capflag for i := 1; i < width; i++ { @@ -127,40 +132,20 @@ func HashWithCap(inpBI []*big.Int, width int, cap int64) (*big.Int, error) { } rate := width - 1 - var absorb []*ff.Element - for len(inpBI) > 0 { - // sponge for fully absorb - if l := len(absorb); l != 0 { - //sanity check - if l != rate { - panic("unexpected absorption size") - } - - for i, elm := range absorb { - state[i+1].Add(state[i+1], elm) - } - state = permute(state, width) + i := 0 + // always perform one round of permutation even when input is empty + for { + // each round absorb at most `rate` elements from `inpBI` + for j := 0; j < rate && i < len(inpBI); i, j = i+1, j+1 { + state[j+1].Add(state[j+1], ff.NewElement().SetBigInt(inpBI[i])) } - - // absorb - if len(inpBI) < rate { - // padding zero() equal to no action on state - absorb = utils.BigIntArrayToElementArray(inpBI) - inpBI = nil - } else { - absorb = utils.BigIntArrayToElementArray(inpBI[:rate]) - inpBI = inpBI[rate:] + state = permute(state, width) + if i == len(inpBI) { + break } - } - //last time sponge (padding with unabsorb items) - for i, elm := range absorb { - state[i+1].Add(state[i+1], elm) - } - state = permute(state, width) - - //squeeze + // squeeze rE := state[0] r := big.NewInt(0) rE.ToBigIntRegular(r) diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index 5c3adce05b..e04b6bd9d6 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -390,7 +390,7 @@ func handleMessage(backend Backend, peer *Peer) error { bytes uint64 ) for _, hash := range req.Hashes { - if hash == emptyCode { + if hash == emptyKeccakCodeHash { // Peers should not request the empty code, but if they do, at // least sent them back a correct response without db lookups codes = append(codes, []byte{}) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 4535c42051..a39ac4bc3b 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -50,8 +50,11 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // emptyCode is the known hash of the empty EVM bytecode. - emptyCode = codehash.EmptyCodeHash + // emptyKeccakCodeHash is the known keccak hash of the empty EVM bytecode. + emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash + + // emptyPoseidonCodeHash is the known poseidon hash of the empty EVM bytecode. + emptyPoseidonCodeHash = codehash.EmptyPoseidonCodeHash ) const ( @@ -1754,9 +1757,9 @@ func (s *Syncer) processAccountResponse(res *accountResponse) { res.task.pend = 0 for i, account := range res.accounts { // Check if the account is a contract with an unknown code - if !bytes.Equal(account.CodeHash, emptyCode[:]) { - if code := rawdb.ReadCodeWithPrefix(s.db, common.BytesToHash(account.CodeHash)); code == nil { - res.task.codeTasks[common.BytesToHash(account.CodeHash)] = struct{}{} + if !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash[:]) { + if code := rawdb.ReadCodeWithPrefix(s.db, common.BytesToHash(account.KeccakCodeHash)); code == nil { + res.task.codeTasks[common.BytesToHash(account.KeccakCodeHash)] = struct{}{} res.task.needCode[i] = true res.task.pend++ } @@ -1820,7 +1823,7 @@ func (s *Syncer) processBytecodeResponse(res *bytecodeResponse) { } // Code was delivered, mark it not needed any more for j, account := range res.task.res.accounts { - if res.task.needCode[j] && hash == common.BytesToHash(account.CodeHash) { + if res.task.needCode[j] && hash == common.BytesToHash(account.KeccakCodeHash) { res.task.needCode[j] = false res.task.pend-- } @@ -2150,7 +2153,7 @@ func (s *Syncer) forwardAccountTask(task *accountTask) { if task.needCode[i] || task.needState[i] { break } - slim := snapshot.SlimAccountRLP(res.accounts[i].Nonce, res.accounts[i].Balance, res.accounts[i].Root, res.accounts[i].CodeHash) + slim := snapshot.SlimAccountRLP(res.accounts[i].Nonce, res.accounts[i].Balance, res.accounts[i].Root, res.accounts[i].KeccakCodeHash, res.accounts[i].PoseidonCodeHash, res.accounts[i].CodeSize) rawdb.WriteAccountSnapshot(batch, hash, slim) // If the task is complete, drop it into the stack trie to generate @@ -2747,7 +2750,7 @@ func (s *Syncer) onHealState(paths [][]byte, value []byte) error { if err := rlp.DecodeBytes(value, &account); err != nil { return nil } - blob := snapshot.SlimAccountRLP(account.Nonce, account.Balance, account.Root, account.CodeHash) + blob := snapshot.SlimAccountRLP(account.Nonce, account.Balance, account.Root, account.KeccakCodeHash, account.PoseidonCodeHash, account.CodeSize) rawdb.WriteAccountSnapshot(s.stateWriter, common.BytesToHash(paths[0]), blob) s.accountHealed += 1 s.accountHealedBytes += common.StorageSize(1 + common.HashLength + len(blob)) diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index bca0340c2a..db898241ee 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -33,6 +33,7 @@ import ( "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/crypto/codehash" "github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/light" "github.com/scroll-tech/go-ethereum/log" @@ -1313,7 +1314,7 @@ func key32(i uint64) []byte { } var ( - codehashes = []common.Hash{ + keccakCodehashes = []common.Hash{ crypto.Keccak256Hash([]byte{0}), crypto.Keccak256Hash([]byte{1}), crypto.Keccak256Hash([]byte{2}), @@ -1323,20 +1324,37 @@ var ( crypto.Keccak256Hash([]byte{6}), crypto.Keccak256Hash([]byte{7}), } + + poseidonCodehashes = []common.Hash{ + codehash.PoseidonCodeHash([]byte{0}), + codehash.PoseidonCodeHash([]byte{1}), + codehash.PoseidonCodeHash([]byte{2}), + codehash.PoseidonCodeHash([]byte{3}), + codehash.PoseidonCodeHash([]byte{4}), + codehash.PoseidonCodeHash([]byte{5}), + codehash.PoseidonCodeHash([]byte{6}), + codehash.PoseidonCodeHash([]byte{7}), + } ) -// getCodeHash returns a pseudo-random code hash -func getCodeHash(i uint64) []byte { - h := codehashes[int(i)%len(codehashes)] +// getKeccakCodeHash returns a pseudo-random code hash +func getKeccakCodeHash(i uint64) []byte { + h := keccakCodehashes[int(i)%len(keccakCodehashes)] + return common.CopyBytes(h[:]) +} + +// getPoseidonCodeHash returns a pseudo-random code hash +func getPoseidonCodeHash(i uint64) []byte { + h := poseidonCodehashes[int(i)%len(poseidonCodehashes)] return common.CopyBytes(h[:]) } // getCodeByHash convenience function to lookup the code from the code hash func getCodeByHash(hash common.Hash) []byte { - if hash == emptyCode { + if hash == emptyKeccakCodeHash { return nil } - for i, h := range codehashes { + for i, h := range keccakCodehashes { if h == hash { return []byte{byte(i)} } @@ -1351,10 +1369,12 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) { var entries entrySlice for i := uint64(1); i <= uint64(n); i++ { value, _ := rlp.EncodeToBytes(types.StateAccount{ - Nonce: i, - Balance: big.NewInt(int64(i)), - Root: emptyRoot, - CodeHash: getCodeHash(i), + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + KeccakCodeHash: getKeccakCodeHash(i), + PoseidonCodeHash: getPoseidonCodeHash(i), + CodeSize: 1, }) key := key32(i) elem := &kv{key, value} @@ -1396,10 +1416,12 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) { // Fill boundary accounts for i := 0; i < len(boundaries); i++ { value, _ := rlp.EncodeToBytes(types.StateAccount{ - Nonce: uint64(0), - Balance: big.NewInt(int64(i)), - Root: emptyRoot, - CodeHash: getCodeHash(uint64(i)), + Nonce: uint64(0), + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + KeccakCodeHash: getKeccakCodeHash(uint64(i)), + PoseidonCodeHash: getPoseidonCodeHash(uint64(i)), + CodeSize: 1, }) elem := &kv{boundaries[i].Bytes(), value} trie.Update(elem.k, elem.v) @@ -1408,10 +1430,12 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) { // Fill other accounts if required for i := uint64(1); i <= uint64(n); i++ { value, _ := rlp.EncodeToBytes(types.StateAccount{ - Nonce: i, - Balance: big.NewInt(int64(i)), - Root: emptyRoot, - CodeHash: getCodeHash(i), + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: emptyRoot, + KeccakCodeHash: getKeccakCodeHash(i), + PoseidonCodeHash: getPoseidonCodeHash(i), + CodeSize: 1, }) elem := &kv{key32(i), value} trie.Update(elem.k, elem.v) @@ -1435,19 +1459,23 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) // Create n accounts in the trie for i := uint64(1); i <= uint64(accounts); i++ { key := key32(i) - codehash := emptyCode[:] + keccakCodehash := emptyKeccakCodeHash[:] + poseidonCodeHash := emptyPoseidonCodeHash[:] if code { - codehash = getCodeHash(i) + keccakCodehash = getKeccakCodeHash(i) + poseidonCodeHash = getPoseidonCodeHash(i) } // Create a storage trie stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db) stRoot := stTrie.Hash() stTrie.Commit(nil) value, _ := rlp.EncodeToBytes(types.StateAccount{ - Nonce: i, - Balance: big.NewInt(int64(i)), - Root: stRoot, - CodeHash: codehash, + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: stRoot, + KeccakCodeHash: keccakCodehash, + PoseidonCodeHash: poseidonCodeHash, + CodeSize: 1, }) elem := &kv{key, value} accTrie.Update(elem.k, elem.v) @@ -1486,15 +1514,19 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie // Create n accounts in the trie for i := uint64(1); i <= uint64(accounts); i++ { key := key32(i) - codehash := emptyCode[:] + keccakCodehash := emptyKeccakCodeHash[:] + poseidonCodeHash := emptyPoseidonCodeHash[:] if code { - codehash = getCodeHash(i) + keccakCodehash = getKeccakCodeHash(i) + poseidonCodeHash = getPoseidonCodeHash(i) } value, _ := rlp.EncodeToBytes(types.StateAccount{ - Nonce: i, - Balance: big.NewInt(int64(i)), - Root: stRoot, - CodeHash: codehash, + Nonce: i, + Balance: big.NewInt(int64(i)), + Root: stRoot, + KeccakCodeHash: keccakCodehash, + PoseidonCodeHash: poseidonCodeHash, + CodeSize: 1, }) elem := &kv{key, value} accTrie.Update(elem.k, elem.v) @@ -1594,10 +1626,12 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { accIt := trie.NewIterator(accTrie.NodeIterator(nil)) for accIt.Next() { var acc struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte + Nonce uint64 + Balance *big.Int + Root common.Hash + KeccakCodeHash []byte + PoseidonCodeHash []byte + CodeSize uint64 } if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil { log.Crit("Invalid account encountered during snapshot creation", "err", err) diff --git a/eth/tracers/api_blocktrace.go b/eth/tracers/api_blocktrace.go index 79f1f42345..e26f6e0c01 100644 --- a/eth/tracers/api_blocktrace.go +++ b/eth/tracers/api_blocktrace.go @@ -206,18 +206,22 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc } sender := &types.AccountWrapper{ - Address: from, - Nonce: state.GetNonce(from), - Balance: (*hexutil.Big)(state.GetBalance(from)), - CodeHash: state.GetCodeHash(from), + Address: from, + Nonce: state.GetNonce(from), + Balance: (*hexutil.Big)(state.GetBalance(from)), + KeccakCodeHash: state.GetKeccakCodeHash(from), + PoseidonCodeHash: state.GetPoseidonCodeHash(from), + CodeSize: state.GetCodeSize(from), } var receiver *types.AccountWrapper if to != nil { receiver = &types.AccountWrapper{ - Address: *to, - Nonce: state.GetNonce(*to), - Balance: (*hexutil.Big)(state.GetBalance(*to)), - CodeHash: state.GetCodeHash(*to), + Address: *to, + Nonce: state.GetNonce(*to), + Balance: (*hexutil.Big)(state.GetBalance(*to)), + KeccakCodeHash: state.GetKeccakCodeHash(*to), + PoseidonCodeHash: state.GetPoseidonCodeHash(*to), + CodeSize: state.GetCodeSize(*to), } } @@ -250,10 +254,12 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc // collect affected account after tx being applied for _, acc := range []common.Address{from, *to, env.coinbase} { after = append(after, &types.AccountWrapper{ - Address: acc, - Nonce: state.GetNonce(acc), - Balance: (*hexutil.Big)(state.GetBalance(acc)), - CodeHash: state.GetCodeHash(acc), + Address: acc, + Nonce: state.GetNonce(acc), + Balance: (*hexutil.Big)(state.GetBalance(acc)), + KeccakCodeHash: state.GetKeccakCodeHash(acc), + PoseidonCodeHash: state.GetPoseidonCodeHash(acc), + CodeSize: state.GetCodeSize(acc), }) } @@ -339,10 +345,12 @@ func (api *API) fillBlockTrace(env *traceEnv, block *types.Block) (*types.BlockT blockTrace := &types.BlockTrace{ Coinbase: &types.AccountWrapper{ - Address: env.coinbase, - Nonce: statedb.GetNonce(env.coinbase), - Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)), - CodeHash: statedb.GetCodeHash(env.coinbase), + Address: env.coinbase, + Nonce: statedb.GetNonce(env.coinbase), + Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)), + KeccakCodeHash: statedb.GetKeccakCodeHash(env.coinbase), + PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase), + CodeSize: statedb.GetCodeSize(env.coinbase), }, Header: block.Header(), StorageTrace: env.StorageTrace, @@ -356,8 +364,8 @@ func (api *API) fillBlockTrace(env *traceEnv, block *types.Block) (*types.BlockT if len(tx.Data()) != 0 && tx.To() != nil { evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To())) // Get tx.to address's code hash. - codeHash := statedb.GetCodeHash(*tx.To()) - evmTrace.CodeHash = &codeHash + codeHash := statedb.GetPoseidonCodeHash(*tx.To()) + evmTrace.PoseidonCodeHash = &codeHash } else if tx.To() == nil { // Contract is created. evmTrace.ByteCode = hexutil.Encode(tx.Data()) } diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index ea9eadc954..38892df360 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -60,13 +60,15 @@ func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (* // AccountResult is the result of a GetProof operation. type AccountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *big.Int `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []StorageResult `json:"storageProof"` + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *big.Int `json:"balance"` + KeccakCodeHash common.Hash `json:"keccakCodeHash"` + PoseidonCodeHash common.Hash `json:"poseidonCodeHash"` + CodeSize common.Hash `json:"codeSize"` + Nonce uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` } // StorageResult provides a proof for a key-value pair. @@ -87,13 +89,15 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s } type accountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce hexutil.Uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []storageResult `json:"storageProof"` + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + KeccakCodeHash common.Hash `json:"keccakodeHash"` + PoseidonCodeHash common.Hash `json:"poseidonCodeHash"` + CodeSize common.Hash `json:"codeSize"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []storageResult `json:"storageProof"` } var res accountResult @@ -108,12 +112,14 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s }) } result := AccountResult{ - Address: res.Address, - AccountProof: res.AccountProof, - Balance: res.Balance.ToInt(), - Nonce: uint64(res.Nonce), - CodeHash: res.CodeHash, - StorageHash: res.StorageHash, + Address: res.Address, + AccountProof: res.AccountProof, + Balance: res.Balance.ToInt(), + Nonce: uint64(res.Nonce), + KeccakCodeHash: res.KeccakCodeHash, + PoseidonCodeHash: res.PoseidonCodeHash, + CodeSize: res.CodeSize, + StorageHash: res.StorageHash, } return &result, err } diff --git a/go.mod b/go.mod index 122d2a3a2b..abe3bfa698 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/scroll-tech/zktrie v0.3.0 + github.com/scroll-tech/zktrie v0.4.3 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 4fae0fe50a..6ae09ba9ee 100644 --- a/go.sum +++ b/go.sum @@ -380,8 +380,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scroll-tech/zktrie v0.3.0 h1:c0GRNELUyAtyuiwllQKT1XjNbs7NRGfguKouiyLfFNY= -github.com/scroll-tech/zktrie v0.3.0/go.mod h1:CuJFlG1/soTJJBAySxCZgTF7oPvd5qF6utHOEciC43Q= +github.com/scroll-tech/zktrie v0.4.3 h1:RyhusIu8F8u5ITmzqZjkAwlL6jdC9TK9i6tfuJoZcpk= +github.com/scroll-tech/zktrie v0.4.3/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f478ebfd7c..29a7b05959 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -636,13 +636,15 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add // Result structs for GetProof type AccountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce hexutil.Uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []StorageResult `json:"storageProof"` + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + PoseidonCodeHash common.Hash `json:"poseidonCodeHash"` + KeccakCodeHash common.Hash `json:"keccakCodeHash"` + CodeSize hexutil.Uint64 `json:"codeSize"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` } type StorageResult struct { @@ -665,7 +667,8 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre if !zktrie { storageHash = types.EmptyRootHash } - codeHash := state.GetCodeHash(address) + keccakCodeHash := state.GetKeccakCodeHash(address) + poseidonCodeHash := state.GetPoseidonCodeHash(address) storageProof := make([]StorageResult, len(storageKeys)) // if we have a storageTrie, (which means the account exists), we can update the storagehash @@ -673,7 +676,8 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre storageHash = storageTrie.Hash() } else { // no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray. - codeHash = codehash.EmptyCodeHash + keccakCodeHash = codehash.EmptyKeccakCodeHash + poseidonCodeHash = codehash.EmptyPoseidonCodeHash } // create the proof for the storageKeys @@ -696,13 +700,15 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre } return &AccountResult{ - Address: address, - AccountProof: toHexSlice(accountProof), - Balance: (*hexutil.Big)(state.GetBalance(address)), - CodeHash: codeHash, - Nonce: hexutil.Uint64(state.GetNonce(address)), - StorageHash: storageHash, - StorageProof: storageProof, + Address: address, + AccountProof: toHexSlice(accountProof), + Balance: (*hexutil.Big)(state.GetBalance(address)), + KeccakCodeHash: keccakCodeHash, + PoseidonCodeHash: poseidonCodeHash, + CodeSize: hexutil.Uint64(state.GetCodeSize(address)), + Nonce: hexutil.Uint64(state.GetNonce(address)), + StorageHash: storageHash, + StorageProof: storageProof, }, state.Error() } diff --git a/les/server_requests.go b/les/server_requests.go index 69f5dcef92..e7e6545446 100644 --- a/les/server_requests.go +++ b/les/server_requests.go @@ -310,9 +310,9 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) { p.bumpInvalid() continue } - code, err := bc.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash)) + code, err := bc.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.KeccakCodeHash)) if err != nil { - p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) + p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.KeccakCodeHash), "err", err) continue } // Accumulate the code and abort if enough data was retrieved diff --git a/light/trie.go b/light/trie.go index 1c5bde3659..1947c31409 100644 --- a/light/trie.go +++ b/light/trie.go @@ -70,7 +70,7 @@ func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie { } func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { - if codeHash == codehash.EmptyCodeHash { + if codeHash == codehash.EmptyKeccakCodeHash { return nil, nil } code := rawdb.ReadCode(db.backend.Database(), codeHash) diff --git a/trie/sync.go b/trie/sync.go index 995da125c9..3eaea7e57d 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -190,7 +190,7 @@ func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, cal // as is. func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash) { // Short circuit if the entry is empty or already known - if hash == codehash.EmptyCodeHash { + if hash == codehash.EmptyKeccakCodeHash { return } if s.membatch.hasCode(hash) { diff --git a/trie/trie_test.go b/trie/trie_test.go index 8b5d67dd26..23780a5ff8 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -593,15 +593,15 @@ func TestTinyTrie(t *testing.T) { _, accounts := makeAccounts(5) trie := newEmpty() trie.Update(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.Update(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.Update(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, _ := New(common.Hash{}, trie.db) @@ -625,7 +625,7 @@ func TestCommitAfterHash(t *testing.T) { trie.Hash() trie.Commit(nil) root := trie.Hash() - exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6") + exp := common.HexToHash("f0c0681648c93b347479cd58c61995557f01294425bd031ce1943c2799bbd4ec") if exp != root { t.Errorf("got %x, exp %x", root, exp) } @@ -650,7 +650,6 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { var ( nonce = uint64(random.Int63()) root = emptyRoot - code = codehash.EmptyCodeHash.Bytes() ) // The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, // and will consume different amount of data from the rand source. @@ -660,7 +659,16 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { balanceBytes := make([]byte, numBytes) random.Read(balanceBytes) balance := new(big.Int).SetBytes(balanceBytes) - data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code}) + + data, _ := rlp.EncodeToBytes(&types.StateAccount{ + Nonce: nonce, + Balance: balance, + Root: root, + KeccakCodeHash: codehash.EmptyKeccakCodeHash.Bytes(), + PoseidonCodeHash: codehash.EmptyPoseidonCodeHash.Bytes(), + CodeSize: 0, + }) + accounts[i] = data } return addresses, accounts @@ -717,12 +725,12 @@ func TestCommitSequence(t *testing.T) { expWriteSeqHash []byte expCallbackSeqHash []byte }{ - {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066"), - common.FromHex("ff00f91ac05df53b82d7f178d77ada54fd0dca64526f537034a5dbe41b17df2a")}, - {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e"), - common.FromHex("f3cd509064c8d319bbdd1c68f511850a902ad275e6ed5bea11547e23d492a926")}, - {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7"), - common.FromHex("ff795ea898ba1e4cfed4a33b4cf5535a347a02cf931f88d88719faf810f9a1c9")}, + {20, common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"), + common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e")}, + {200, common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"), + common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f")}, + {2000, common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"), + common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d")}, } { addresses, accounts := makeAccounts(tc.count) // This spongeDb is used to check the sequence of disk-db-writes diff --git a/trie/zk_trie_impl_test.go b/trie/zk_trie_impl_test.go index 47ebdfad57..c42ab8c053 100644 --- a/trie/zk_trie_impl_test.go +++ b/trie/zk_trie_impl_test.go @@ -222,20 +222,24 @@ func TestMerkleTree_UpdateAccount(t *testing.T) { mt := newTestingMerkle(t, 10) acc1 := &types.StateAccount{ - Nonce: 1, - Balance: big.NewInt(10000000), - Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"), - CodeHash: common.HexToHash("cc0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab").Bytes(), + 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"), - CodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), + 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) @@ -248,7 +252,9 @@ func TestMerkleTree_UpdateAccount(t *testing.T) { 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.CodeHash, acc.CodeHash) + 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) @@ -258,7 +264,9 @@ func TestMerkleTree_UpdateAccount(t *testing.T) { 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.CodeHash, acc.CodeHash) + 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) diff --git a/trie/zkproof/orderer.go b/trie/zkproof/orderer.go index 27c985230f..3aac0cbce4 100644 --- a/trie/zkproof/orderer.go +++ b/trie/zkproof/orderer.go @@ -126,10 +126,12 @@ func NewRWTblOrderer(inited map[common.Address]*types.StateAccount) *rwTblOrdere } initedAcc[addr] = &types.AccountWrapper{ - Address: addr, - Nonce: data.Nonce, - Balance: (*hexutil.Big)(bl), - CodeHash: common.BytesToHash(data.CodeHash), + Address: addr, + Nonce: data.Nonce, + Balance: (*hexutil.Big)(bl), + KeccakCodeHash: common.BytesToHash(data.KeccakCodeHash), + PoseidonCodeHash: common.BytesToHash(data.PoseidonCodeHash), + CodeSize: data.CodeSize, } } @@ -251,7 +253,9 @@ func (od *rwTblOrderer) absorb(st *types.AccountWrapper) { if traced, existed := od.opAccNonce[addrStr]; !existed { traced = copyAccountState(st) traced.Balance = initedRef.Balance - traced.CodeHash = initedRef.CodeHash + traced.KeccakCodeHash = initedRef.KeccakCodeHash + traced.PoseidonCodeHash = initedRef.PoseidonCodeHash + traced.CodeSize = initedRef.CodeSize traced.Storage = nil od.opAccNonce[addrStr] = traced } else { @@ -260,7 +264,9 @@ func (od *rwTblOrderer) absorb(st *types.AccountWrapper) { if traced, existed := od.opAccBalance[addrStr]; !existed { traced = copyAccountState(st) - traced.CodeHash = initedRef.CodeHash + traced.KeccakCodeHash = initedRef.KeccakCodeHash + traced.PoseidonCodeHash = initedRef.PoseidonCodeHash + traced.CodeSize = initedRef.CodeSize traced.Storage = nil od.opAccBalance[addrStr] = traced } else { @@ -275,7 +281,9 @@ func (od *rwTblOrderer) absorb(st *types.AccountWrapper) { } else { traced.Nonce = st.Nonce traced.Balance = st.Balance - traced.CodeHash = st.CodeHash + traced.KeccakCodeHash = st.KeccakCodeHash + traced.PoseidonCodeHash = st.PoseidonCodeHash + traced.CodeSize = st.CodeSize } } diff --git a/trie/zkproof/types.go b/trie/zkproof/types.go index 89aa98ac07..178c9912d8 100644 --- a/trie/zkproof/types.go +++ b/trie/zkproof/types.go @@ -30,9 +30,11 @@ type SMTPath struct { // 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) - CodeHash hexutil.Bytes `json:"codeHash,omitempty"` + 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 diff --git a/trie/zkproof/writer.go b/trie/zkproof/writer.go index a49592dbf1..193690d72a 100644 --- a/trie/zkproof/writer.go +++ b/trie/zkproof/writer.go @@ -263,16 +263,18 @@ func copyAccountState(st *types.AccountWrapper) *types.AccountWrapper { } return &types.AccountWrapper{ - Nonce: st.Nonce, - Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())), - CodeHash: st.CodeHash, - Address: st.Address, - Storage: stg, + 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.CodeHash.Bytes(), common.Hash{}.Bytes()) + return state.Nonce == 0 && bytes.Equal(state.KeccakCodeHash.Bytes(), common.Hash{}.Bytes()) } func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount { @@ -282,9 +284,12 @@ func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount } return &types.StateAccount{ - Nonce: state.Nonce, - Balance: (*big.Int)(state.Balance), - CodeHash: state.CodeHash.Bytes(), + Nonce: state.Nonce, + Balance: (*big.Int)(state.Balance), + KeccakCodeHash: state.KeccakCodeHash.Bytes(), + PoseidonCodeHash: state.PoseidonCodeHash.Bytes(), + CodeSize: state.CodeSize, + // Root omitted intentionally } } @@ -305,13 +310,14 @@ func verifyAccount(addr common.Address, data *types.StateAccount, leaf *SMTPathN return fmt.Errorf("unmatch leaf node in address: %s", addr) } } else if data != nil { - h, err := data.Hash() + arr, flag := data.MarshalFields() + h, err := zkt.PreHandlingElems(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(zkt.NewHashFromBigInt(h)[:], leaf.Value) { + if !bytes.Equal(h[:], leaf.Value) { return fmt.Errorf("unmatch data in leaf for address %s", addr) } } @@ -386,18 +392,22 @@ func (w *zktrieProofWriter) traceAccountUpdate(addr common.Address, updateAccDat // 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)), - CodeHash: accDataBefore.CodeHash, + 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)), - CodeHash: accData.CodeHash, + Nonce: int(accData.Nonce), + Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)), + KeccakCodeHash: accData.KeccakCodeHash, + PoseidonCodeHash: accData.PoseidonCodeHash, + CodeSize: accData.CodeSize, } } @@ -515,10 +525,12 @@ func (w *zktrieProofWriter) traceStorageUpdate(addr common.Address, key, value [ panic(fmt.Errorf("unexpected storage root before: [%s] vs [%x]", acc.Root, accRootFromState)) } return &types.StateAccount{ - Nonce: acc.Nonce, - Balance: acc.Balance, - CodeHash: acc.CodeHash, - Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)), + 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 {