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 <noelwei@gmail.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
This commit is contained in:
Péter Garamvölgyi 2023-02-09 01:12:41 +01:00 committed by GitHub
parent 1fe2d22e29
commit 81e7775aa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 891 additions and 587 deletions

View file

@ -42,8 +42,8 @@ var (
// emptyRoot is the known root hash of an empty trie. // emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode. // emptyKeccakCodeHash is the known hash of the empty EVM bytecode.
emptyCode = codehash.EmptyCodeHash.Bytes() emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash.Bytes()
) )
var ( var (
@ -314,10 +314,10 @@ func traverseState(ctx *cli.Context) error {
return storageIter.Err return storageIter.Err
} }
} }
if !bytes.Equal(acc.CodeHash, emptyCode) { if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) {
code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.KeccakCodeHash))
if len(code) == 0 { 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") return errors.New("missing code")
} }
codes += 1 codes += 1
@ -435,8 +435,8 @@ func traverseRawState(ctx *cli.Context) error {
return storageIter.Error() return storageIter.Error()
} }
} }
if !bytes.Equal(acc.CodeHash, emptyCode) { if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) {
code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.KeccakCodeHash))
if len(code) == 0 { if len(code) == 0 {
log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
return errors.New("missing code") return errors.New("missing code")
@ -499,14 +499,16 @@ func dumpState(ctx *cli.Context) error {
return err return err
} }
da := &state.DumpAccount{ da := &state.DumpAccount{
Balance: account.Balance.String(), Balance: account.Balance.String(),
Nonce: account.Nonce, Nonce: account.Nonce,
Root: account.Root, Root: account.Root,
CodeHash: account.CodeHash, KeccakCodeHash: account.KeccakCodeHash,
SecureKey: accIt.Hash().Bytes(), PoseidonCodeHash: account.PoseidonCodeHash,
CodeSize: account.CodeSize,
SecureKey: accIt.Hash().Bytes(),
} }
if !conf.SkipCode && !bytes.Equal(account.CodeHash, emptyCode) { if !conf.SkipCode && !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash) {
da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) da.Code = rawdb.ReadCode(db, common.BytesToHash(account.KeccakCodeHash))
} }
if !conf.SkipStorage { if !conf.SkipStorage {
da.Storage = make(map[common.Hash]string) da.Storage = make(map[common.Hash]string)

View file

@ -3043,8 +3043,11 @@ func TestPoseidonCodeHash(t *testing.T) {
// check empty code hash // check empty code hash
state, _ := blockchain.State() state, _ := blockchain.State()
codeHash := state.GetCodeHash(addr1) poseidonCodeHash := state.GetPoseidonCodeHash(addr1)
assert.Equal(t, codeHash, common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), "code hash mismatch") 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 // deploy contract through transaction
chain, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 1, func(i int, gen *BlockGen) { 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") assert.Equal(t, common.HexToAddress("0x3A220f351252089D385b29beca14e27F204c296A"), contractAddress, "address mismatch")
state, _ = blockchain.State() state, _ = blockchain.State()
codeHash = state.GetCodeHash(contractAddress) poseidonCodeHash = state.GetPoseidonCodeHash(contractAddress)
keccakCodeHash = state.GetKeccakCodeHash(contractAddress)
// keccak: 0x089bfd332dfa6117cbc20756f31801ce4f5a175eb258e46bf8123317da54cd96 assert.Equal(t, common.HexToHash("0x0df04366a061c969e08137570a59536df95672ec21d00cb738fb90cac8e78bcc"), poseidonCodeHash, "code hash mismatch")
assert.Equal(t, codeHash, common.HexToHash("0x28ec09723b285e17caabc4a8d52dbd097feddf408aee115cbb57c3c9c814d2b2"), "code hash mismatch") assert.Equal(t, common.HexToHash("0x089bfd332dfa6117cbc20756f31801ce4f5a175eb258e46bf8123317da54cd96"), keccakCodeHash, "code hash mismatch")
// deploy contract through another contract (CREATE and CREATE2) // deploy contract through another contract (CREATE and CREATE2)
chain, receipts = GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), engine, db, 1, func(i int, gen *BlockGen) { 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") assert.Equal(t, common.HexToAddress("0x4099734c88B7D091E744da0E849df0e818e7E208"), address2, "address mismatch")
state, _ = blockchain.State() state, _ = blockchain.State()
codeHash1 := state.GetCodeHash(address1) poseidonCodeHash1 := state.GetPoseidonCodeHash(address1)
codeHash2 := state.GetCodeHash(address2) poseidonCodeHash2 := state.GetPoseidonCodeHash(address2)
keccakCodeHash1 := state.GetKeccakCodeHash(address1)
keccakCodeHash2 := state.GetKeccakCodeHash(address2)
// keccak: 0xfb5cd93a70ce47f91d33fac3afdb7b54680a6b0683506646a108ef4dfc047583 assert.Equal(t, common.HexToHash("0x12eb18061d12f883c4d4c2041925cc2916c86fcfcb9458c8b9a3fb32257215d0"), poseidonCodeHash1, "code hash mismatch")
assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash1, "code hash mismatch") assert.Equal(t, common.HexToHash("0x12eb18061d12f883c4d4c2041925cc2916c86fcfcb9458c8b9a3fb32257215d0"), poseidonCodeHash2, "code hash mismatch")
assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash2, "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. // TestFeeVault tests that the fee vault receives all tx fees correctly.

View file

@ -41,7 +41,7 @@ func TestInvalidCliqueConfig(t *testing.T) {
func TestSetupGenesis(t *testing.T) { func TestSetupGenesis(t *testing.T) {
var ( var (
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") customghash = common.HexToHash("0x700380ab70d789c462c4e8f0db082842095321f390d0a3f25f400f0746db32bc")
customg = Genesis{ customg = Genesis{
Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)}, Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)},
Alloc: GenesisAlloc{ Alloc: GenesisAlloc{
@ -66,23 +66,23 @@ func TestSetupGenesis(t *testing.T) {
wantErr: errGenesisNoConfig, wantErr: errGenesisNoConfig,
wantConfig: params.AllEthashProtocolChanges, wantConfig: params.AllEthashProtocolChanges,
}, },
{ // {
name: "no block in DB, genesis == nil", // name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, nil) // return SetupGenesisBlock(db, nil)
}, // },
wantHash: params.MainnetGenesisHash, // wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig, // wantConfig: params.MainnetChainConfig,
}, // },
{ // {
name: "mainnet block in DB, genesis == nil", // name: "mainnet block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
DefaultGenesisBlock().MustCommit(db) // DefaultGenesisBlock().MustCommit(db)
return SetupGenesisBlock(db, nil) // return SetupGenesisBlock(db, nil)
}, // },
wantHash: params.MainnetGenesisHash, // wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig, // wantConfig: params.MainnetChainConfig,
}, // },
{ {
name: "custom block in DB, genesis == nil", name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
@ -92,16 +92,16 @@ func TestSetupGenesis(t *testing.T) {
wantHash: customghash, wantHash: customghash,
wantConfig: customg.Config, wantConfig: customg.Config,
}, },
{ // {
name: "custom block in DB, genesis == ropsten", // name: "custom block in DB, genesis == ropsten",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { // fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db) // customg.MustCommit(db)
return SetupGenesisBlock(db, DefaultRopstenGenesisBlock()) // return SetupGenesisBlock(db, DefaultRopstenGenesisBlock())
}, // },
wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash}, // wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash},
wantHash: params.RopstenGenesisHash, // wantHash: params.RopstenGenesisHash,
wantConfig: params.RopstenChainConfig, // wantConfig: params.RopstenChainConfig,
}, // },
{ {
name: "compatible config in DB", name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 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 // // TestGenesisHashes checks the congruity of default genesis data to
// corresponding hardcoded genesis hash values. // // corresponding hardcoded genesis hash values.
func TestGenesisHashes(t *testing.T) { // func TestGenesisHashes(t *testing.T) {
for i, c := range []struct { // for i, c := range []struct {
genesis *Genesis // genesis *Genesis
want common.Hash // want common.Hash
}{ // }{
{DefaultGenesisBlock(), params.MainnetGenesisHash}, // {DefaultGenesisBlock(), params.MainnetGenesisHash},
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash}, // {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
{DefaultRopstenGenesisBlock(), params.RopstenGenesisHash}, // {DefaultRopstenGenesisBlock(), params.RopstenGenesisHash},
{DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash}, // {DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash},
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, // {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
} { // } {
// Test via MustCommit // // Test via MustCommit
if have := c.genesis.MustCommit(rawdb.NewMemoryDatabase()).Hash(); have != c.want { // 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()) // t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
} // }
// Test via ToBlock // // Test via ToBlock
if have := c.genesis.ToBlock(nil).Hash(); have != c.want { // 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()) // t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
} // }
} // }
} // }
func TestGenesis_Commit(t *testing.T) { func TestGenesis_Commit(t *testing.T) {
genesis := &Genesis{ genesis := &Genesis{

View file

@ -49,15 +49,16 @@ type DumpCollector interface {
// DumpAccount represents an account in the state. // DumpAccount represents an account in the state.
type DumpAccount struct { type DumpAccount struct {
Balance string `json:"balance"` Balance string `json:"balance"`
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
Root hexutil.Bytes `json:"root"` Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"` KeccakCodeHash hexutil.Bytes `json:"keccakCodeHash"`
Code hexutil.Bytes `json:"code,omitempty"` PoseidonCodeHash hexutil.Bytes `json:"poseidonCodeHash"`
Storage map[common.Hash]string `json:"storage,omitempty"` CodeSize uint64 `json:"codeSize"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode Code hexutil.Bytes `json:"code,omitempty"`
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key 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. // Dump represents the full dump in a collected format, as one large map.
@ -101,14 +102,15 @@ type iterativeDump struct {
// OnAccount implements DumpCollector interface // OnAccount implements DumpCollector interface
func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) { func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{ dumpAccount := &DumpAccount{
Balance: account.Balance, Balance: account.Balance,
Nonce: account.Nonce, Nonce: account.Nonce,
Root: account.Root, Root: account.Root,
CodeHash: account.CodeHash, KeccakCodeHash: account.KeccakCodeHash,
Code: account.Code, PoseidonCodeHash: account.PoseidonCodeHash,
Storage: account.Storage, Code: account.Code,
SecureKey: account.SecureKey, Storage: account.Storage,
Address: nil, SecureKey: account.SecureKey,
Address: nil,
} }
if addr != (common.Address{}) { if addr != (common.Address{}) {
dumpAccount.Address = &addr dumpAccount.Address = &addr
@ -146,11 +148,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
panic(err) panic(err)
} }
account := DumpAccount{ account := DumpAccount{
Balance: data.Balance.String(), Balance: data.Balance.String(),
Nonce: data.Nonce, Nonce: data.Nonce,
Root: data.Root[:], Root: data.Root[:],
CodeHash: data.CodeHash, KeccakCodeHash: data.KeccakCodeHash,
SecureKey: it.Key, PoseidonCodeHash: data.PoseidonCodeHash,
CodeSize: data.CodeSize,
SecureKey: it.Key,
} }
addrBytes := s.trie.GetKey(it.Key) addrBytes := s.trie.GetKey(it.Key)
if addrBytes == nil { if addrBytes == nil {

View file

@ -117,12 +117,12 @@ func (it *NodeIterator) step() error {
if !it.dataIt.Next(true) { if !it.dataIt.Next(true) {
it.dataIt = nil it.dataIt = nil
} }
if !bytes.Equal(account.CodeHash, emptyCodeHash) { if !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash) {
it.codeHash = common.BytesToHash(account.CodeHash) it.codeHash = common.BytesToHash(account.KeccakCodeHash)
addrHash := common.BytesToHash(it.stateIt.LeafKey()) 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 { 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() it.accountHash = it.stateIt.Parent()

View file

@ -113,8 +113,8 @@ type (
key, prevalue common.Hash key, prevalue common.Hash
} }
codeChange struct { codeChange struct {
account *common.Address account *common.Address
prevcode, prevhash []byte prevcode []byte
} }
// Changes to other state values. // Changes to other state values.
@ -198,7 +198,7 @@ func (ch nonceChange) dirtied() *common.Address {
} }
func (ch codeChange) revert(s *StateDB) { 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 { func (ch codeChange) dirtied() *common.Address {

View file

@ -59,8 +59,8 @@ var (
// emptyRoot is the known root hash of an empty trie. // emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode. // emptyKeccakCodeHash is the known hash of the empty EVM bytecode.
emptyCode = codehash.EmptyCodeHash.Bytes() emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash.Bytes()
) )
// Pruner is an offline tool to prune the stale state with the // 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() return storageIter.Error()
} }
} }
if !bytes.Equal(acc.CodeHash, emptyCode) { if !bytes.Equal(acc.KeccakCodeHash, emptyKeccakCodeHash) {
stateBloom.Put(acc.CodeHash, nil) stateBloom.Put(acc.KeccakCodeHash, nil)
} }
} }
} }

View file

@ -29,14 +29,16 @@ import (
// or slim-snapshot format which replaces the empty root and code hash as nil // or slim-snapshot format which replaces the empty root and code hash as nil
// byte slice. // byte slice.
type Account struct { type Account struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
Root []byte Root []byte
CodeHash []byte KeccakCodeHash []byte
PoseidonCodeHash []byte
CodeSize uint64
} }
// SlimAccount converts a state.Account content into a slim snapshot account // 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{ slim := Account{
Nonce: nonce, Nonce: nonce,
Balance: balance, Balance: balance,
@ -44,16 +46,18 @@ func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []by
if root != emptyRoot { if root != emptyRoot {
slim.Root = root[:] slim.Root = root[:]
} }
if !bytes.Equal(codehash, emptyCode[:]) { if !bytes.Equal(keccakcodehash, emptyKeccakCode[:]) {
slim.CodeHash = codehash slim.KeccakCodeHash = keccakcodehash
slim.PoseidonCodeHash = poseidoncodehash
slim.CodeSize = codesize
} }
return slim return slim
} }
// SlimAccountRLP converts a state.Account content into a slim snapshot // SlimAccountRLP converts a state.Account content into a slim snapshot
// version RLP encoded. // version RLP encoded.
func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) []byte { 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, codehash)) data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, keccakcodehash, poseidoncodehash, codesize))
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -70,8 +74,10 @@ func FullAccount(data []byte) (Account, error) {
if len(account.Root) == 0 { if len(account.Root) == 0 {
account.Root = emptyRoot[:] account.Root = emptyRoot[:]
} }
if len(account.CodeHash) == 0 { if len(account.KeccakCodeHash) == 0 {
account.CodeHash = emptyCode[:] account.KeccakCodeHash = emptyKeccakCode[:]
account.PoseidonCodeHash = emptyPoseidonCode[:]
account.CodeSize = 0
} }
return account, nil return account, nil
} }

View file

@ -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) { 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. // Migrate the code first, commit the contract code into the tmp db.
if codeHash != emptyCode { if codeHash != emptyKeccakCode {
code := rawdb.ReadCode(src, codeHash) code := rawdb.ReadCode(src, codeHash)
if len(code) == 0 { if len(code) == 0 {
return common.Hash{}, errors.New("failed to read contract code") 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) return stop(err)
} }
go func(hash common.Hash) { 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 { if err != nil {
results <- err results <- err
return return

View file

@ -43,8 +43,9 @@ var (
// emptyRoot is the known root hash of an empty trie. // emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode. // emptyPoseidonCode is the known hash of the empty EVM bytecode.
emptyCode = codehash.EmptyCodeHash emptyPoseidonCode = codehash.EmptyPoseidonCodeHash
emptyKeccakCode = codehash.EmptyKeccakCodeHash
// accountCheckRange is the upper limit of the number of accounts involved in // 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 // 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 // Retrieve the current account and flatten it into the internal format
var acc struct { var acc struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
Root common.Hash Root common.Hash
CodeHash []byte KeccakCodeHash []byte
PoseidonCodeHash []byte
CodeSize uint64
} }
if err := rlp.DecodeBytes(val, &acc); err != nil { if err := rlp.DecodeBytes(val, &acc); err != nil {
log.Crit("Invalid account encountered during snapshot creation", "err", err) 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) { if accMarker == nil || !bytes.Equal(accountHash[:], accMarker) {
dataLen := len(val) // Approximate size, saves us a round of RLP-encoding dataLen := len(val) // Approximate size, saves us a round of RLP-encoding
if !write { if !write {
if bytes.Equal(acc.CodeHash, emptyCode[:]) { if bytes.Equal(acc.KeccakCodeHash, emptyKeccakCode[:]) {
dataLen -= 32 // account for keccakCodeHash, poseidonCodeHash, and CodeSize
dataLen = dataLen - 32 - 32 - 8
} }
if acc.Root == emptyRoot { if acc.Root == emptyRoot {
dataLen -= 32 dataLen -= 32
} }
snapRecoveredAccountMeter.Mark(1) snapRecoveredAccountMeter.Mark(1)
} else { } 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) dataLen = len(data)
rawdb.WriteAccountSnapshot(batch, accountHash, data) rawdb.WriteAccountSnapshot(batch, accountHash, data)
snapGeneratedAccountMeter.Mark(1) snapGeneratedAccountMeter.Mark(1)

View file

@ -50,21 +50,21 @@ func TestGeneration(t *testing.T) {
stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root, _, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd root, _, _ := accTrie.Commit(nil) // Root: 0x0bc6b6959d2589404dd3e4b25783a829b58625f6b673f095e9a97391b474c3f9
triedb.Commit(root, false, nil) 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) t.Fatalf("have %#x want %#x", have, want)
} }
snap := generateSnapshot(diskdb, triedb, 16, root) snap := generateSnapshot(diskdb, triedb, 16, root)
@ -107,7 +107,7 @@ func TestGenerateExistentState(t *testing.T) {
stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-1")), val) 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-2")), []byte("val-2"))
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-3")), []byte("val-3")) 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
diskdb.Put(hashData([]byte("acc-2")).Bytes(), val) diskdb.Put(hashData([]byte("acc-2")).Bytes(), val)
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-2")), 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-3")), val) 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"}) 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 // 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"}) 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 // 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 // Miss slots
{ {
// Account three, non empty root but misses slots in the beginning // 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"}) 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 // 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"}) 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 // 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"}) helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"})
} }
// Wrong storage slots // Wrong storage slots
{ {
// Account six, non empty root but wrong slots in the beginning // 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"}) 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 // 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"}) 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 // 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"}) 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 // 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"}) helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"})
} }
// Extra storage slots // Extra storage slots
{ {
// Account 10, non empty root but extra slots in the beginning // 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"}) 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 // 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"}) 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 // 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"}) 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 // Missing accounts, only in the trie
{ {
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Beginning 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, CodeHash: emptyCode.Bytes()}) // Middle 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, CodeHash: emptyCode.Bytes()}) // End helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}) // End
} }
// Wrong accounts // Wrong accounts
{ {
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 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, CodeHash: common.Hex2Bytes("0x1234")}) 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.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(), CodeHash: emptyCode.Bytes()}) 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 // 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-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(), CodeHash: common.Hex2Bytes("0x1234")}) // Middle 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(), CodeHash: emptyRoot.Bytes()}) // after the end 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() root, snap := helper.Generate()
@ -384,15 +384,15 @@ func TestGenerateCorruptAccountTrie(t *testing.T) {
triedb = trie.NewDatabase(diskdb) triedb = trie.NewDatabase(diskdb)
) )
tr, _ := trie.NewSecure(common.Hash{}, triedb) 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) val, _ := rlp.EncodeToBytes(acc)
tr.Update([]byte("acc-1"), val) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 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) val, _ = rlp.EncodeToBytes(acc)
tr.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 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) val, _ = rlp.EncodeToBytes(acc)
tr.Update([]byte("acc-3"), val) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 tr.Update([]byte("acc-3"), val) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
tr.Commit(nil) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 tr.Commit(nil) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
@ -434,27 +434,27 @@ func TestGenerateMissingStorageTrie(t *testing.T) {
stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) 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) 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) 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 accTrie.Update([]byte("acc-3"), val) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
// We can only corrupt the disk database, so flush the tries out // We can only corrupt the disk database, so flush the tries out
triedb.Reference( triedb.Reference(
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), common.HexToHash("0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f"),
) )
triedb.Reference( triedb.Reference(
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), common.HexToHash("0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b"),
) )
triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil)
@ -493,27 +493,27 @@ func TestGenerateCorruptStorageTrie(t *testing.T) {
stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) 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) 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) 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) val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 accTrie.Update([]byte("acc-3"), val) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
// We can only corrupt the disk database, so flush the tries out // We can only corrupt the disk database, so flush the tries out
triedb.Reference( triedb.Reference(
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), common.HexToHash("0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f"),
) )
triedb.Reference( triedb.Reference(
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), common.HexToHash("0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b"),
) )
triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil)
@ -555,7 +555,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
) )
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
{ // Account one in the trie { // 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
// Identical in the snap // Identical in the snap
@ -568,7 +568,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-5")), []byte("val-5")) rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-5")), []byte("val-5"))
} }
{ // Account two exists only in the snapshot { // 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) val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte("acc-2")) key := hashData([]byte("acc-2"))
rawdb.WriteAccountSnapshot(diskdb, key, val) rawdb.WriteAccountSnapshot(diskdb, key, val)
@ -619,7 +619,7 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
) )
accTrie, _ := trie.NewSecure(common.Hash{}, triedb) accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
{ // Account one in the trie { // 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
// Identical in the snap // Identical in the snap
@ -631,8 +631,8 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
} }
{ // 100 accounts exist only in snapshot { // 100 accounts exist only in snapshot
for i := 0; i < 1000; i++ { 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: stTrie.Hash().Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes()}
acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), KeccakCodeHash: emptyKeccakCode.Bytes(), PoseidonCodeHash: emptyPoseidonCode.Bytes(), CodeSize: 0}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte(fmt.Sprintf("acc-%d", i))) key := hashData([]byte(fmt.Sprintf("acc-%d", i)))
rawdb.WriteAccountSnapshot(diskdb, key, val) rawdb.WriteAccountSnapshot(diskdb, key, val)
@ -677,7 +677,7 @@ func TestGenerateWithExtraBeforeAndAfter(t *testing.T) {
) )
accTrie, _ := trie.New(common.Hash{}, triedb) 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update(common.HexToHash("0x03").Bytes(), val) accTrie.Update(common.HexToHash("0x03").Bytes(), val)
accTrie.Update(common.HexToHash("0x07").Bytes(), val) accTrie.Update(common.HexToHash("0x07").Bytes(), val)
@ -723,7 +723,7 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
) )
accTrie, _ := trie.New(common.Hash{}, triedb) 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) val, _ := rlp.EncodeToBytes(acc)
accTrie.Update(common.HexToHash("0x03").Bytes(), val) accTrie.Update(common.HexToHash("0x03").Bytes(), val)
@ -767,7 +767,7 @@ func TestGenerateFromEmptySnap(t *testing.T) {
// Add 1K accounts to the trie // Add 1K accounts to the trie
for i := 0; i < 400; i++ { for i := 0; i < 400; i++ {
helper.addTrieAccount(fmt.Sprintf("acc-%d", 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() root, snap := helper.Generate()
t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4
@ -804,7 +804,7 @@ func TestGenerateWithIncompleteStorage(t *testing.T) {
// on the sensitive spots at the boundaries // on the sensitive spots at the boundaries
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
accKey := fmt.Sprintf("acc-%d", 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 moddedKeys []string
var moddedVals []string var moddedVals []string
for ii := 0; ii < 8; ii++ { for ii := 0; ii < 8; ii++ {

View file

@ -44,10 +44,12 @@ func randomHash() common.Hash {
func randomAccount() []byte { func randomAccount() []byte {
root := randomHash() root := randomHash()
a := Account{ a := Account{
Balance: big.NewInt(rand.Int63()), Balance: big.NewInt(rand.Int63()),
Nonce: rand.Uint64(), Nonce: rand.Uint64(),
Root: root[:], Root: root[:],
CodeHash: emptyCode[:], KeccakCodeHash: emptyKeccakCode[:],
PoseidonCodeHash: emptyPoseidonCode[:],
CodeSize: 0,
} }
data, _ := rlp.EncodeToBytes(a) data, _ := rlp.EncodeToBytes(a)
return data return data

View file

@ -31,7 +31,8 @@ import (
"github.com/scroll-tech/go-ethereum/rlp" "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 type Code []byte
@ -96,7 +97,8 @@ type stateObject struct {
// empty returns whether the account is considered empty. // empty returns whether the account is considered empty.
func (s *stateObject) empty() bool { 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. // newObject creates a state object.
@ -104,8 +106,10 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st
if data.Balance == nil { if data.Balance == nil {
data.Balance = new(big.Int) data.Balance = new(big.Int)
} }
if data.CodeHash == nil { if data.KeccakCodeHash == nil {
data.CodeHash = emptyCodeHash data.KeccakCodeHash = emptyKeccakCodeHash
data.PoseidonCodeHash = emptyPoseidonCodeHash
data.CodeSize = 0
} }
if data.Root == (common.Hash{}) { if data.Root == (common.Hash{}) {
data.Root = db.db.TrieDB().EmptyRoot() data.Root = db.db.TrieDB().EmptyRoot()
@ -482,12 +486,12 @@ func (s *stateObject) Code(db Database) []byte {
if s.code != nil { if s.code != nil {
return s.code return s.code
} }
if bytes.Equal(s.CodeHash(), emptyCodeHash) { if bytes.Equal(s.KeccakCodeHash(), emptyKeccakCodeHash) {
return nil 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 { 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 s.code = code
return 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, // 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 // 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. // inside the database to avoid loading codes seen recently.
func (s *stateObject) CodeSize(db Database) int { func (s *stateObject) CodeSize() uint64 {
if s.code != nil { return s.data.CodeSize
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) SetCode(codeHash common.Hash, code []byte) { func (s *stateObject) SetCode(code []byte) {
prevcode := s.Code(s.db.db) prevcode := s.Code(s.db.db)
s.db.journal.append(codeChange{ s.db.journal.append(codeChange{
account: &s.address, account: &s.address,
prevhash: s.CodeHash(),
prevcode: prevcode, 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.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 s.dirtyCode = true
} }
@ -538,8 +533,12 @@ func (s *stateObject) setNonce(nonce uint64) {
s.data.Nonce = nonce s.data.Nonce = nonce
} }
func (s *stateObject) CodeHash() []byte { func (s *stateObject) PoseidonCodeHash() []byte {
return s.data.CodeHash return s.data.PoseidonCodeHash
}
func (s *stateObject) KeccakCodeHash() []byte {
return s.data.KeccakCodeHash
} }
func (s *stateObject) Balance() *big.Int { func (s *stateObject) Balance() *big.Int {

View file

@ -23,7 +23,6 @@ import (
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/crypto/codehash"
"github.com/scroll-tech/go-ethereum/ethdb" "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 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(big.NewInt(22)) obj1.AddBalance(big.NewInt(22))
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) 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 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(big.NewInt(44)) 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 // check that DumpToCollector contains the state objects that are in trie
got := string(s.state.Dump(nil)) got := string(s.state.Dump(nil))
want := `{ want := `{
"root": "bfd17853c56ac63be7327da432ef90a9b04376d71efede2a60765d2c94704cfe", "root": "789955993afb9d2a04b957a91be5d7b139aabb60fb7af63df6405021211c13c4",
"accounts": { "accounts": {
"0x0000000000000000000000000000000000000001": { "0x0000000000000000000000000000000000000001": {
"balance": "22", "balance": "22",
"nonce": 0, "nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
"codeSize": 0,
"key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d" "key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
}, },
"0x0000000000000000000000000000000000000002": { "0x0000000000000000000000000000000000000002": {
"balance": "44", "balance": "44",
"nonce": 0, "nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
"codeSize": 0,
"key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62" "key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
}, },
"0x0000000000000000000000000000000000000102": { "0x0000000000000000000000000000000000000102": {
"balance": "0", "balance": "0",
"nonce": 0, "nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0x2263940ad476ca23bf01a9b033d65cd6b8bf9b7224a7ef2a4f10b61a2c039083", "keccakCodeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
"poseidonCodeHash": "0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c",
"codeSize": 7,
"code": "0x03030303030303", "code": "0x03030303030303",
"key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1" "key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"
} }
@ -165,7 +170,7 @@ func TestSnapshot2(t *testing.T) {
so0 := state.getStateObject(stateobjaddr0) so0 := state.getStateObject(stateobjaddr0)
so0.SetBalance(big.NewInt(42)) so0.SetBalance(big.NewInt(42))
so0.SetNonce(43) 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.suicided = false
so0.deleted = false so0.deleted = false
state.setStateObject(so0) state.setStateObject(so0)
@ -177,7 +182,7 @@ func TestSnapshot2(t *testing.T) {
so1 := state.getStateObject(stateobjaddr1) so1 := state.getStateObject(stateobjaddr1)
so1.SetBalance(big.NewInt(52)) so1.SetBalance(big.NewInt(52))
so1.SetNonce(53) 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.suicided = true
so1.deleted = true so1.deleted = true
state.setStateObject(so1) state.setStateObject(so1)
@ -217,8 +222,14 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
if so0.data.Root != so1.data.Root { if so0.data.Root != so1.data.Root {
t.Errorf("Root mismatch: have %x, want %x", 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()) { if !bytes.Equal(so0.KeccakCodeHash(), so1.KeccakCodeHash()) {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) 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) { if !bytes.Equal(so0.code, so1.code) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)

View file

@ -31,7 +31,6 @@ import (
"github.com/scroll-tech/go-ethereum/core/state/snapshot" "github.com/scroll-tech/go-ethereum/core/state/snapshot"
"github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto" "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/log"
"github.com/scroll-tech/go-ethereum/metrics" "github.com/scroll-tech/go-ethereum/metrics"
"github.com/scroll-tech/go-ethereum/rlp" "github.com/scroll-tech/go-ethereum/rlp"
@ -290,20 +289,28 @@ func (s *StateDB) GetCode(addr common.Address) []byte {
return nil return nil
} }
func (s *StateDB) GetCodeSize(addr common.Address) int { func (s *StateDB) GetCodeSize(addr common.Address) uint64 {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.CodeSize(s.db) return stateObject.CodeSize()
} }
return 0 return 0
} }
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { func (s *StateDB) GetPoseidonCodeHash(addr common.Address) common.Hash {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
if stateObject == nil { if stateObject == nil {
return common.Hash{} 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. // 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) { func (s *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil { 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 // enough to track account updates at commit time, deletions need tracking
// at transaction boundary level to ensure we capture state clearing. // at transaction boundary level to ensure we capture state clearing.
if s.snap != nil { 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 return nil
} }
data = &types.StateAccount{ data = &types.StateAccount{
Nonce: acc.Nonce, Nonce: acc.Nonce,
Balance: acc.Balance, Balance: acc.Balance,
CodeHash: acc.CodeHash, Root: common.BytesToHash(acc.Root),
Root: common.BytesToHash(acc.Root), KeccakCodeHash: acc.KeccakCodeHash,
PoseidonCodeHash: acc.PoseidonCodeHash,
CodeSize: acc.CodeSize,
} }
if len(data.CodeHash) == 0 { if len(data.KeccakCodeHash) == 0 {
data.CodeHash = emptyCodeHash data.KeccakCodeHash = emptyKeccakCodeHash
data.PoseidonCodeHash = emptyPoseidonCodeHash
data.CodeSize = 0
} }
if data.Root == (common.Hash{}) { if data.Root == (common.Hash{}) {
data.Root = s.db.TrieDB().EmptyRoot() 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 { if obj := s.stateObjects[addr]; !obj.deleted {
// Write any contract code associated with the state object // Write any contract code associated with the state object
if obj.code != nil && obj.dirtyCode { 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 obj.dirtyCode = false
} }
// Write any storage changes in the state object to its storage trie // Write any storage changes in the state object to its storage trie

View file

@ -443,7 +443,8 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(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)) checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage. // Check storage.
if obj := state.getStateObject(addr); obj != nil { if obj := state.getStateObject(addr); obj != nil {

View file

@ -49,7 +49,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S
return err return err
} }
syncer.AddSubTrie(obj.Root, hexpath, parent, onSlot) 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 return nil
} }
syncer = trie.NewSync(root, database, onAccount, bloom) syncer = trie.NewSync(root, database, onAccount, bloom)

View file

@ -59,12 +59,12 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
acc.nonce = uint64(42 * i) acc.nonce = uint64(42 * i)
if i%3 == 0 { 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} acc.code = []byte{i, i, i, i, i}
} }
if i%5 == 0 { if i%5 == 0 {
for j := byte(0); j < 5; j++ { 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) obj.SetState(db, hash, hash)
} }
} }
@ -408,10 +408,10 @@ func TestIncompleteStateSync(t *testing.T) {
var isCode = make(map[common.Hash]struct{}) var isCode = make(map[common.Hash]struct{})
for _, acc := range srcAccounts { for _, acc := range srcAccounts {
if len(acc.code) > 0 { 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) checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler

View file

@ -285,7 +285,7 @@ func TestStateProcessorErrors(t *testing.T) {
txs: []*types.Transaction{ txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), 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) block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)

View file

@ -29,7 +29,7 @@ import (
"github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/params"
) )
var emptyCodeHash = codehash.EmptyCodeHash var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash
/* /*
The State Transitioning Model The State Transitioning Model
@ -227,7 +227,7 @@ func (st *StateTransition) preCheck() error {
st.msg.From().Hex(), stNonce) st.msg.From().Hex(), stNonce)
} }
// Make sure the sender is an EOA // 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, return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
st.msg.From().Hex(), codeHash) st.msg.From().Hex(), codeHash)
} }

View file

@ -66,8 +66,8 @@ type ExecutionResult struct {
// currently they are just `from` and `to` account // currently they are just `from` and `to` account
AccountsAfter []*AccountWrapper `json:"accountAfter"` AccountsAfter []*AccountWrapper `json:"accountAfter"`
// `CodeHash` only exists when tx is a contract call. // `PoseidonCodeHash` only exists when tx is a contract call.
CodeHash *common.Hash `json:"codeHash,omitempty"` PoseidonCodeHash *common.Hash `json:"poseidonCodeHash,omitempty"`
// If it is a contract call, the contract code is returned. // If it is a contract call, the contract code is returned.
ByteCode string `json:"byteCode,omitempty"` ByteCode string `json:"byteCode,omitempty"`
StructLogs []*StructLogRes `json:"structLogs"` StructLogs []*StructLogRes `json:"structLogs"`
@ -134,11 +134,13 @@ type ExtraData struct {
} }
type AccountWrapper struct { type AccountWrapper struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
Balance *hexutil.Big `json:"balance"` Balance *hexutil.Big `json:"balance"`
CodeHash common.Hash `json:"codeHash,omitempty"` KeccakCodeHash common.Hash `json:"keccakCodeHash,omitempty"`
Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation 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, // while key & value can also be retrieved from StructLogRes.Storage,

View file

@ -25,8 +25,12 @@ import (
// StateAccount is the Ethereum consensus representation of accounts. // StateAccount is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie. // These objects are stored in the main account trie.
type StateAccount struct { type StateAccount struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
Root common.Hash // merkle root of the storage trie Root common.Hash // merkle root of the storage trie
CodeHash []byte KeccakCodeHash []byte
// StateAccount Scroll extensions
PoseidonCodeHash []byte
CodeSize uint64
} }

View file

@ -21,7 +21,6 @@ import (
"errors" "errors"
"math/big" "math/big"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/iden3/go-iden3-crypto/utils" "github.com/iden3/go-iden3-crypto/utils"
zkt "github.com/scroll-tech/zktrie/types" zkt "github.com/scroll-tech/zktrie/types"
@ -33,73 +32,74 @@ var (
ErrInvalidLength = errors.New("StateAccount: invalid input length") ErrInvalidLength = errors.New("StateAccount: invalid input length")
) )
// Hash of StateAccount // MarshalFields marshalls a StateAccount into a sequence of bytes. The bytes scheme is:
// AccountHash = Hash( // [0:32] (bytes in big-endian)
// Hash(nonce, balance), // [0:16] Reserved with all 0
// Hash( // [16:24] CodeSize, uint64 in big-endian
// Root, // [24:32] Nonce, uint64 in big-endian
// 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
// [32:64] Balance // [32:64] Balance
// [64:96] Root // [64:96] StorageRoot
// [96:128] CodeHash // [96:128] KeccakCodeHash
// [128:160] PoseidonCodehash
// (total 160 bytes)
func (s *StateAccount) MarshalFields() ([]zkt.Byte32, uint32) { func (s *StateAccount) MarshalFields() ([]zkt.Byte32, uint32) {
fields := make([]zkt.Byte32, 4) fields := make([]zkt.Byte32, 5)
binary.BigEndian.PutUint64(fields[0][24:], s.Nonce)
if s.Balance == nil {
panic("StateAccount balance nil")
}
if !utils.CheckBigIntInField(s.Balance) { if !utils.CheckBigIntInField(s.Balance) {
panic("balance overflow") panic("StateAccount balance overflow")
} }
s.Balance.FillBytes(fields[1][:])
copy(fields[2][:], s.CodeHash) if !utils.CheckBigIntInField(s.Root.Big()) {
copy(fields[3][:], s.Root.Bytes()) panic("StateAccount root overflow")
return fields, 4 }
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) { func UnmarshalStateAccount(bytes []byte) (*StateAccount, error) {
if len(bytes) != 128 { if len(bytes) != 160 {
return nil, ErrInvalidLength return nil, ErrInvalidLength
} }
acc := new(StateAccount) 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.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 = 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 return acc, nil
} }

View file

@ -1,45 +1,189 @@
package types package types
import ( import (
"math"
"math/big" "math/big"
"testing" "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/common"
"github.com/scroll-tech/go-ethereum/crypto/codehash"
) )
func TestAccountMarshalling(t *testing.T) { func assertAccountsEqual(t *testing.T, expected *StateAccount, actual *StateAccount) {
//ensure the hash scheme consistent with designation assert.Equal(t, expected.Nonce, actual.Nonce)
example1 := &StateAccount{ assert.Zero(t, expected.Balance.Cmp(actual.Balance))
Nonce: 5, assert.Equal(t, expected.Root, actual.Root)
Balance: big.NewInt(0).SetBytes(common.Hex2Bytes("01fffffffffffffffffffffffffffffffffffffffffffffffff9c8672c6bc7b3")), assert.Equal(t, expected.KeccakCodeHash, actual.KeccakCodeHash)
CodeHash: common.Hex2Bytes("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), assert.Equal(t, expected.PoseidonCodeHash, actual.PoseidonCodeHash)
} assert.Equal(t, expected.CodeSize, actual.CodeSize)
}
example2 := &StateAccount{
Nonce: 2, func TestMarshalUnmarshalEmptyAccount(t *testing.T) {
Balance: big.NewInt(0), acc := StateAccount{
CodeHash: common.Hex2Bytes("cc0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab"), Nonce: 0,
Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"), Balance: big.NewInt(0),
} Root: common.Hash{},
KeccakCodeHash: codehash.EmptyKeccakCodeHash.Bytes(),
for i, example := range []*StateAccount{example1, example2} { PoseidonCodeHash: codehash.EmptyPoseidonCodeHash.Bytes(),
fields, flag := example.MarshalFields() CodeSize: 0,
}
h1, err := zktrie.PreHandlingElems(flag, fields)
if err != nil { // marshal account
t.Fatal(err)
} bytes, flag := acc.MarshalFields()
h2, err := example.Hash() assert.Equal(t, 5, len(bytes))
if err != nil { assert.Equal(t, uint32(8), flag)
t.Fatal(err)
} assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[0].Bytes())
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[1].Bytes())
if h1.BigInt().Cmp(h2) != 0 { assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[2].Bytes())
t.Errorf("hash <%d> unmatched, expected [%x], get [%x]", i, h2.Bytes(), h1.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()
})
} }

View file

@ -55,7 +55,7 @@ type Contract struct {
analysis bitvec // Locally cached result of JUMPDEST analysis analysis bitvec // Locally cached result of JUMPDEST analysis
Code []byte Code []byte
CodeHash common.Hash CodeHash common.Hash // Keccak code hash
CodeAddr *common.Address CodeAddr *common.Address
Input []byte Input []byte

View file

@ -29,9 +29,9 @@ import (
"github.com/scroll-tech/go-ethereum/params" "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). // deployed contract addresses (relevant after the account abstraction).
var emptyCodeHash = codehash.EmptyCodeHash var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash
type ( type (
// CanTransferFunc is the signature of a transfer guard function // 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 // If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above // The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas) 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) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas 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. // 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. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas) 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) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -332,7 +332,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
addrCopy := addr addrCopy := addr
// Initialise a new contract and make initialise the delegate values // Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() 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) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas 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. // 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. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) 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 // 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 // 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. // 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) evm.StateDB.AddAddressToAccessList(address)
} }
// Ensure there's no existing contract already at the designated address // Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address) contractHash := evm.StateDB.GetKeccakCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyKeccakCodeHash) {
return nil, common.Address{}, 0, ErrContractAddressCollision return nil, common.Address{}, 0, ErrContractAddressCollision
} }
// Create a new account on the state // Create a new account on the state

View file

@ -342,7 +342,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek() slot := scope.Stack.peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) slot.SetUint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))
return nil, nil return nil, nil
} }
@ -420,7 +420,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
if interpreter.evm.StateDB.Empty(address) { if interpreter.evm.StateDB.Empty(address) {
slot.Clear() slot.Clear()
} else { } else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) slot.SetBytes(interpreter.evm.StateDB.GetKeccakCodeHash(address).Bytes())
} }
return nil, nil return nil, nil
} }

View file

@ -34,10 +34,11 @@ type StateDB interface {
GetNonce(common.Address) uint64 GetNonce(common.Address) uint64
SetNonce(common.Address, uint64) SetNonce(common.Address, uint64)
GetCodeHash(common.Address) common.Hash GetKeccakCodeHash(common.Address) common.Hash
GetCode(common.Address) []byte GetCode(common.Address) []byte
SetCode(common.Address, []byte) SetCode(common.Address, []byte)
GetCodeSize(common.Address) int GetPoseidonCodeHash(common.Address) common.Hash
GetCodeSize(common.Address) uint64
AddRefund(uint64) AddRefund(uint64)
SubRefund(uint64) SubRefund(uint64)

View file

@ -104,19 +104,23 @@ func traceLastNAddressAccount(n int) traceFunc {
// StorageWrapper will be empty // StorageWrapper will be empty
func getWrappedAccountForAddr(l *StructLogger, address common.Address) *types.AccountWrapper { func getWrappedAccountForAddr(l *StructLogger, address common.Address) *types.AccountWrapper {
return &types.AccountWrapper{ return &types.AccountWrapper{
Address: address, Address: address,
Nonce: l.env.StateDB.GetNonce(address), Nonce: l.env.StateDB.GetNonce(address),
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
CodeHash: l.env.StateDB.GetCodeHash(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 { func getWrappedAccountForStorage(l *StructLogger, address common.Address, key common.Hash) *types.AccountWrapper {
return &types.AccountWrapper{ return &types.AccountWrapper{
Address: address, Address: address,
Nonce: l.env.StateDB.GetNonce(address), Nonce: l.env.StateDB.GetNonce(address),
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
CodeHash: l.env.StateDB.GetCodeHash(address), KeccakCodeHash: l.env.StateDB.GetKeccakCodeHash(address),
PoseidonCodeHash: l.env.StateDB.GetPoseidonCodeHash(address),
CodeSize: l.env.StateDB.GetCodeSize(address),
Storage: &types.StorageWrapper{ Storage: &types.StorageWrapper{
Key: key.String(), Key: key.String(),
Value: l.env.StateDB.GetState(address, key).String(), Value: l.env.StateDB.GetState(address, key).String(),

View file

@ -2,15 +2,22 @@ package codehash
import ( import (
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/crypto/poseidon" "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) return poseidon.CodeHash(code)
} }
func init() { func KeccakCodeHash(code []byte) (h common.Hash) {
EmptyCodeHash = poseidon.CodeHash(nil) return crypto.Keccak256Hash(code)
}
func init() {
EmptyPoseidonCodeHash = poseidon.CodeHash(nil)
EmptyKeccakCodeHash = crypto.Keccak256Hash(nil)
} }

View file

@ -4,44 +4,40 @@ import (
"math/big" "math/big"
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/crypto"
) )
const defaultPoseidonChunk = 3 const defaultPoseidonChunk = 3
const nBytesToFieldElement = 31
func CodeHash(code []byte) (h common.Hash) { func CodeHash(code []byte) (h common.Hash) {
// special case for nil hash nBytes := int64(len(code))
if len(code) == 0 {
return crypto.Keccak256Hash(nil)
}
cap := int64(len(code)) // 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
// step 1: pad code with 0x0 (STOP) so that len(code) % 16 == 0 var length = (len(code) + nBytesToFieldElement - 1) / nBytesToFieldElement
// step 2: for every 16 bytes, convert to Fr, so that we get a Fr array
var length = (len(code) + 15) / 16
Frs := make([]*big.Int, length) Frs := make([]*big.Int, length)
ii := 0 ii := 0
for ii < length-1 { for ii < length-1 {
Frs[ii] = big.NewInt(0) Frs[ii] = big.NewInt(0)
Frs[ii].SetBytes(code[ii*16 : (ii+1)*16]) Frs[ii].SetBytes(code[ii*nBytesToFieldElement : (ii+1)*nBytesToFieldElement])
ii++ ii++
} }
Frs[ii] = big.NewInt(0) if length > 0 {
bytes := make([]byte, 16) Frs[ii] = big.NewInt(0)
copy(bytes, code[ii*16:]) bytes := make([]byte, nBytesToFieldElement)
Frs[ii].SetBytes(bytes) copy(bytes, code[ii*nBytesToFieldElement:])
Frs[ii].SetBytes(bytes)
}
// step 3: apply the array onto a sponge process with the current poseidon scheme // 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) // (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) // step 4: convert final root Fr to u256 (big-endian representation)
hash, _ := HashWithCap(Frs, defaultPoseidonChunk, cap) hash, err := HashWithCap(Frs, defaultPoseidonChunk, nBytes)
if err != nil {
codeHash := common.Hash{} return common.Hash{}
hash.FillBytes(codeHash[:]) }
return common.BigToHash(hash)
return codeHash
} }

View file

@ -8,7 +8,7 @@ import (
func TestPoseidonCodeHash(t *testing.T) { func TestPoseidonCodeHash(t *testing.T) {
// nil // nil
got := fmt.Sprintf("%s", CodeHash(nil)) got := fmt.Sprintf("%s", CodeHash(nil))
want := "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" want := "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864"
if got != want { if got != want {
t.Errorf("got %q, wanted %q", got, want) t.Errorf("got %q, wanted %q", got, want)
@ -16,9 +16,27 @@ func TestPoseidonCodeHash(t *testing.T) {
// single byte // single byte
got = fmt.Sprintf("%s", CodeHash([]byte{0})) got = fmt.Sprintf("%s", CodeHash([]byte{0}))
want = "0x0ee069e6aa796ef0e46cbd51d10468393d443a00f5affe72898d9ab62e335e16" want = "0x29f94b67ee4e78b2bb08da025f9943c1201a7af025a27600c2dd0a2e71c7cf8b"
if got != want { if got != want {
t.Errorf("got %q, wanted %q", 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)
}
} }

View file

@ -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 // 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 ) // (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 { if width < 2 {
return nil, fmt.Errorf("width must be ranged from 2 to 16") 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 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 := make([]*ff.Element, width)
state[0] = capflag state[0] = capflag
for i := 1; i < width; i++ { 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 rate := width - 1
var absorb []*ff.Element i := 0
for len(inpBI) > 0 { // always perform one round of permutation even when input is empty
// sponge for fully absorb for {
if l := len(absorb); l != 0 { // each round absorb at most `rate` elements from `inpBI`
//sanity check for j := 0; j < rate && i < len(inpBI); i, j = i+1, j+1 {
if l != rate { state[j+1].Add(state[j+1], ff.NewElement().SetBigInt(inpBI[i]))
panic("unexpected absorption size")
}
for i, elm := range absorb {
state[i+1].Add(state[i+1], elm)
}
state = permute(state, width)
} }
state = permute(state, width)
// absorb if i == len(inpBI) {
if len(inpBI) < rate { break
// padding zero() equal to no action on state
absorb = utils.BigIntArrayToElementArray(inpBI)
inpBI = nil
} else {
absorb = utils.BigIntArrayToElementArray(inpBI[:rate])
inpBI = inpBI[rate:]
} }
} }
//last time sponge (padding with unabsorb items) // squeeze
for i, elm := range absorb {
state[i+1].Add(state[i+1], elm)
}
state = permute(state, width)
//squeeze
rE := state[0] rE := state[0]
r := big.NewInt(0) r := big.NewInt(0)
rE.ToBigIntRegular(r) rE.ToBigIntRegular(r)

View file

@ -390,7 +390,7 @@ func handleMessage(backend Backend, peer *Peer) error {
bytes uint64 bytes uint64
) )
for _, hash := range req.Hashes { for _, hash := range req.Hashes {
if hash == emptyCode { if hash == emptyKeccakCodeHash {
// Peers should not request the empty code, but if they do, at // Peers should not request the empty code, but if they do, at
// least sent them back a correct response without db lookups // least sent them back a correct response without db lookups
codes = append(codes, []byte{}) codes = append(codes, []byte{})

View file

@ -50,8 +50,11 @@ var (
// emptyRoot is the known root hash of an empty trie. // emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode. // emptyKeccakCodeHash is the known keccak hash of the empty EVM bytecode.
emptyCode = codehash.EmptyCodeHash emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash
// emptyPoseidonCodeHash is the known poseidon hash of the empty EVM bytecode.
emptyPoseidonCodeHash = codehash.EmptyPoseidonCodeHash
) )
const ( const (
@ -1754,9 +1757,9 @@ func (s *Syncer) processAccountResponse(res *accountResponse) {
res.task.pend = 0 res.task.pend = 0
for i, account := range res.accounts { for i, account := range res.accounts {
// Check if the account is a contract with an unknown code // Check if the account is a contract with an unknown code
if !bytes.Equal(account.CodeHash, emptyCode[:]) { if !bytes.Equal(account.KeccakCodeHash, emptyKeccakCodeHash[:]) {
if code := rawdb.ReadCodeWithPrefix(s.db, common.BytesToHash(account.CodeHash)); code == nil { if code := rawdb.ReadCodeWithPrefix(s.db, common.BytesToHash(account.KeccakCodeHash)); code == nil {
res.task.codeTasks[common.BytesToHash(account.CodeHash)] = struct{}{} res.task.codeTasks[common.BytesToHash(account.KeccakCodeHash)] = struct{}{}
res.task.needCode[i] = true res.task.needCode[i] = true
res.task.pend++ res.task.pend++
} }
@ -1820,7 +1823,7 @@ func (s *Syncer) processBytecodeResponse(res *bytecodeResponse) {
} }
// Code was delivered, mark it not needed any more // Code was delivered, mark it not needed any more
for j, account := range res.task.res.accounts { 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.needCode[j] = false
res.task.pend-- res.task.pend--
} }
@ -2150,7 +2153,7 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
if task.needCode[i] || task.needState[i] { if task.needCode[i] || task.needState[i] {
break 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) rawdb.WriteAccountSnapshot(batch, hash, slim)
// If the task is complete, drop it into the stack trie to generate // 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 { if err := rlp.DecodeBytes(value, &account); err != nil {
return 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) rawdb.WriteAccountSnapshot(s.stateWriter, common.BytesToHash(paths[0]), blob)
s.accountHealed += 1 s.accountHealed += 1
s.accountHealedBytes += common.StorageSize(1 + common.HashLength + len(blob)) s.accountHealedBytes += common.StorageSize(1 + common.HashLength + len(blob))

View file

@ -33,6 +33,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto" "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/ethdb"
"github.com/scroll-tech/go-ethereum/light" "github.com/scroll-tech/go-ethereum/light"
"github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/log"
@ -1313,7 +1314,7 @@ func key32(i uint64) []byte {
} }
var ( var (
codehashes = []common.Hash{ keccakCodehashes = []common.Hash{
crypto.Keccak256Hash([]byte{0}), crypto.Keccak256Hash([]byte{0}),
crypto.Keccak256Hash([]byte{1}), crypto.Keccak256Hash([]byte{1}),
crypto.Keccak256Hash([]byte{2}), crypto.Keccak256Hash([]byte{2}),
@ -1323,20 +1324,37 @@ var (
crypto.Keccak256Hash([]byte{6}), crypto.Keccak256Hash([]byte{6}),
crypto.Keccak256Hash([]byte{7}), 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 // getKeccakCodeHash returns a pseudo-random code hash
func getCodeHash(i uint64) []byte { func getKeccakCodeHash(i uint64) []byte {
h := codehashes[int(i)%len(codehashes)] 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[:]) return common.CopyBytes(h[:])
} }
// getCodeByHash convenience function to lookup the code from the code hash // getCodeByHash convenience function to lookup the code from the code hash
func getCodeByHash(hash common.Hash) []byte { func getCodeByHash(hash common.Hash) []byte {
if hash == emptyCode { if hash == emptyKeccakCodeHash {
return nil return nil
} }
for i, h := range codehashes { for i, h := range keccakCodehashes {
if h == hash { if h == hash {
return []byte{byte(i)} return []byte{byte(i)}
} }
@ -1351,10 +1369,12 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
var entries entrySlice var entries entrySlice
for i := uint64(1); i <= uint64(n); i++ { for i := uint64(1); i <= uint64(n); i++ {
value, _ := rlp.EncodeToBytes(types.StateAccount{ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: big.NewInt(int64(i)),
Root: emptyRoot, Root: emptyRoot,
CodeHash: getCodeHash(i), KeccakCodeHash: getKeccakCodeHash(i),
PoseidonCodeHash: getPoseidonCodeHash(i),
CodeSize: 1,
}) })
key := key32(i) key := key32(i)
elem := &kv{key, value} elem := &kv{key, value}
@ -1396,10 +1416,12 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
// Fill boundary accounts // Fill boundary accounts
for i := 0; i < len(boundaries); i++ { for i := 0; i < len(boundaries); i++ {
value, _ := rlp.EncodeToBytes(types.StateAccount{ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: uint64(0), Nonce: uint64(0),
Balance: big.NewInt(int64(i)), Balance: big.NewInt(int64(i)),
Root: emptyRoot, Root: emptyRoot,
CodeHash: getCodeHash(uint64(i)), KeccakCodeHash: getKeccakCodeHash(uint64(i)),
PoseidonCodeHash: getPoseidonCodeHash(uint64(i)),
CodeSize: 1,
}) })
elem := &kv{boundaries[i].Bytes(), value} elem := &kv{boundaries[i].Bytes(), value}
trie.Update(elem.k, elem.v) trie.Update(elem.k, elem.v)
@ -1408,10 +1430,12 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
// Fill other accounts if required // Fill other accounts if required
for i := uint64(1); i <= uint64(n); i++ { for i := uint64(1); i <= uint64(n); i++ {
value, _ := rlp.EncodeToBytes(types.StateAccount{ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: big.NewInt(int64(i)),
Root: emptyRoot, Root: emptyRoot,
CodeHash: getCodeHash(i), KeccakCodeHash: getKeccakCodeHash(i),
PoseidonCodeHash: getPoseidonCodeHash(i),
CodeSize: 1,
}) })
elem := &kv{key32(i), value} elem := &kv{key32(i), value}
trie.Update(elem.k, elem.v) trie.Update(elem.k, elem.v)
@ -1435,19 +1459,23 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
// Create n accounts in the trie // Create n accounts in the trie
for i := uint64(1); i <= uint64(accounts); i++ { for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i) key := key32(i)
codehash := emptyCode[:] keccakCodehash := emptyKeccakCodeHash[:]
poseidonCodeHash := emptyPoseidonCodeHash[:]
if code { if code {
codehash = getCodeHash(i) keccakCodehash = getKeccakCodeHash(i)
poseidonCodeHash = getPoseidonCodeHash(i)
} }
// Create a storage trie // Create a storage trie
stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db) stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db)
stRoot := stTrie.Hash() stRoot := stTrie.Hash()
stTrie.Commit(nil) stTrie.Commit(nil)
value, _ := rlp.EncodeToBytes(types.StateAccount{ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: big.NewInt(int64(i)),
Root: stRoot, Root: stRoot,
CodeHash: codehash, KeccakCodeHash: keccakCodehash,
PoseidonCodeHash: poseidonCodeHash,
CodeSize: 1,
}) })
elem := &kv{key, value} elem := &kv{key, value}
accTrie.Update(elem.k, elem.v) 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 // Create n accounts in the trie
for i := uint64(1); i <= uint64(accounts); i++ { for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i) key := key32(i)
codehash := emptyCode[:] keccakCodehash := emptyKeccakCodeHash[:]
poseidonCodeHash := emptyPoseidonCodeHash[:]
if code { if code {
codehash = getCodeHash(i) keccakCodehash = getKeccakCodeHash(i)
poseidonCodeHash = getPoseidonCodeHash(i)
} }
value, _ := rlp.EncodeToBytes(types.StateAccount{ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: big.NewInt(int64(i)),
Root: stRoot, Root: stRoot,
CodeHash: codehash, KeccakCodeHash: keccakCodehash,
PoseidonCodeHash: poseidonCodeHash,
CodeSize: 1,
}) })
elem := &kv{key, value} elem := &kv{key, value}
accTrie.Update(elem.k, elem.v) 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)) accIt := trie.NewIterator(accTrie.NodeIterator(nil))
for accIt.Next() { for accIt.Next() {
var acc struct { var acc struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
Root common.Hash Root common.Hash
CodeHash []byte KeccakCodeHash []byte
PoseidonCodeHash []byte
CodeSize uint64
} }
if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil { if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil {
log.Crit("Invalid account encountered during snapshot creation", "err", err) log.Crit("Invalid account encountered during snapshot creation", "err", err)

View file

@ -206,18 +206,22 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
} }
sender := &types.AccountWrapper{ sender := &types.AccountWrapper{
Address: from, Address: from,
Nonce: state.GetNonce(from), Nonce: state.GetNonce(from),
Balance: (*hexutil.Big)(state.GetBalance(from)), Balance: (*hexutil.Big)(state.GetBalance(from)),
CodeHash: state.GetCodeHash(from), KeccakCodeHash: state.GetKeccakCodeHash(from),
PoseidonCodeHash: state.GetPoseidonCodeHash(from),
CodeSize: state.GetCodeSize(from),
} }
var receiver *types.AccountWrapper var receiver *types.AccountWrapper
if to != nil { if to != nil {
receiver = &types.AccountWrapper{ receiver = &types.AccountWrapper{
Address: *to, Address: *to,
Nonce: state.GetNonce(*to), Nonce: state.GetNonce(*to),
Balance: (*hexutil.Big)(state.GetBalance(*to)), Balance: (*hexutil.Big)(state.GetBalance(*to)),
CodeHash: state.GetCodeHash(*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 // collect affected account after tx being applied
for _, acc := range []common.Address{from, *to, env.coinbase} { for _, acc := range []common.Address{from, *to, env.coinbase} {
after = append(after, &types.AccountWrapper{ after = append(after, &types.AccountWrapper{
Address: acc, Address: acc,
Nonce: state.GetNonce(acc), Nonce: state.GetNonce(acc),
Balance: (*hexutil.Big)(state.GetBalance(acc)), Balance: (*hexutil.Big)(state.GetBalance(acc)),
CodeHash: state.GetCodeHash(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{ blockTrace := &types.BlockTrace{
Coinbase: &types.AccountWrapper{ Coinbase: &types.AccountWrapper{
Address: env.coinbase, Address: env.coinbase,
Nonce: statedb.GetNonce(env.coinbase), Nonce: statedb.GetNonce(env.coinbase),
Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)), Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)),
CodeHash: statedb.GetCodeHash(env.coinbase), KeccakCodeHash: statedb.GetKeccakCodeHash(env.coinbase),
PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase),
CodeSize: statedb.GetCodeSize(env.coinbase),
}, },
Header: block.Header(), Header: block.Header(),
StorageTrace: env.StorageTrace, 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 { if len(tx.Data()) != 0 && tx.To() != nil {
evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To())) evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To()))
// Get tx.to address's code hash. // Get tx.to address's code hash.
codeHash := statedb.GetCodeHash(*tx.To()) codeHash := statedb.GetPoseidonCodeHash(*tx.To())
evmTrace.CodeHash = &codeHash evmTrace.PoseidonCodeHash = &codeHash
} else if tx.To() == nil { // Contract is created. } else if tx.To() == nil { // Contract is created.
evmTrace.ByteCode = hexutil.Encode(tx.Data()) evmTrace.ByteCode = hexutil.Encode(tx.Data())
} }

View file

@ -60,13 +60,15 @@ func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*
// AccountResult is the result of a GetProof operation. // AccountResult is the result of a GetProof operation.
type AccountResult struct { type AccountResult struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"` AccountProof []string `json:"accountProof"`
Balance *big.Int `json:"balance"` Balance *big.Int `json:"balance"`
CodeHash common.Hash `json:"codeHash"` KeccakCodeHash common.Hash `json:"keccakCodeHash"`
Nonce uint64 `json:"nonce"` PoseidonCodeHash common.Hash `json:"poseidonCodeHash"`
StorageHash common.Hash `json:"storageHash"` CodeSize common.Hash `json:"codeSize"`
StorageProof []StorageResult `json:"storageProof"` Nonce uint64 `json:"nonce"`
StorageHash common.Hash `json:"storageHash"`
StorageProof []StorageResult `json:"storageProof"`
} }
// StorageResult provides a proof for a key-value pair. // 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 { type accountResult struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"` AccountProof []string `json:"accountProof"`
Balance *hexutil.Big `json:"balance"` Balance *hexutil.Big `json:"balance"`
CodeHash common.Hash `json:"codeHash"` KeccakCodeHash common.Hash `json:"keccakodeHash"`
Nonce hexutil.Uint64 `json:"nonce"` PoseidonCodeHash common.Hash `json:"poseidonCodeHash"`
StorageHash common.Hash `json:"storageHash"` CodeSize common.Hash `json:"codeSize"`
StorageProof []storageResult `json:"storageProof"` Nonce hexutil.Uint64 `json:"nonce"`
StorageHash common.Hash `json:"storageHash"`
StorageProof []storageResult `json:"storageProof"`
} }
var res accountResult var res accountResult
@ -108,12 +112,14 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s
}) })
} }
result := AccountResult{ result := AccountResult{
Address: res.Address, Address: res.Address,
AccountProof: res.AccountProof, AccountProof: res.AccountProof,
Balance: res.Balance.ToInt(), Balance: res.Balance.ToInt(),
Nonce: uint64(res.Nonce), Nonce: uint64(res.Nonce),
CodeHash: res.CodeHash, KeccakCodeHash: res.KeccakCodeHash,
StorageHash: res.StorageHash, PoseidonCodeHash: res.PoseidonCodeHash,
CodeSize: res.CodeSize,
StorageHash: res.StorageHash,
} }
return &result, err return &result, err
} }

2
go.mod
View file

@ -49,7 +49,7 @@ require (
github.com/prometheus/tsdb v0.7.1 github.com/prometheus/tsdb v0.7.1
github.com/rjeczalik/notify v0.9.1 github.com/rjeczalik/notify v0.9.1
github.com/rs/cors v1.7.0 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/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0

4
go.sum
View file

@ -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 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 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/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.4.3 h1:RyhusIu8F8u5ITmzqZjkAwlL6jdC9TK9i6tfuJoZcpk=
github.com/scroll-tech/zktrie v0.3.0/go.mod h1:CuJFlG1/soTJJBAySxCZgTF7oPvd5qF6utHOEciC43Q= 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.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.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= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=

View file

@ -636,13 +636,15 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add
// Result structs for GetProof // Result structs for GetProof
type AccountResult struct { type AccountResult struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"` AccountProof []string `json:"accountProof"`
Balance *hexutil.Big `json:"balance"` Balance *hexutil.Big `json:"balance"`
CodeHash common.Hash `json:"codeHash"` PoseidonCodeHash common.Hash `json:"poseidonCodeHash"`
Nonce hexutil.Uint64 `json:"nonce"` KeccakCodeHash common.Hash `json:"keccakCodeHash"`
StorageHash common.Hash `json:"storageHash"` CodeSize hexutil.Uint64 `json:"codeSize"`
StorageProof []StorageResult `json:"storageProof"` Nonce hexutil.Uint64 `json:"nonce"`
StorageHash common.Hash `json:"storageHash"`
StorageProof []StorageResult `json:"storageProof"`
} }
type StorageResult struct { type StorageResult struct {
@ -665,7 +667,8 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre
if !zktrie { if !zktrie {
storageHash = types.EmptyRootHash storageHash = types.EmptyRootHash
} }
codeHash := state.GetCodeHash(address) keccakCodeHash := state.GetKeccakCodeHash(address)
poseidonCodeHash := state.GetPoseidonCodeHash(address)
storageProof := make([]StorageResult, len(storageKeys)) storageProof := make([]StorageResult, len(storageKeys))
// if we have a storageTrie, (which means the account exists), we can update the storagehash // 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() storageHash = storageTrie.Hash()
} else { } else {
// no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray. // 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 // create the proof for the storageKeys
@ -696,13 +700,15 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre
} }
return &AccountResult{ return &AccountResult{
Address: address, Address: address,
AccountProof: toHexSlice(accountProof), AccountProof: toHexSlice(accountProof),
Balance: (*hexutil.Big)(state.GetBalance(address)), Balance: (*hexutil.Big)(state.GetBalance(address)),
CodeHash: codeHash, KeccakCodeHash: keccakCodeHash,
Nonce: hexutil.Uint64(state.GetNonce(address)), PoseidonCodeHash: poseidonCodeHash,
StorageHash: storageHash, CodeSize: hexutil.Uint64(state.GetCodeSize(address)),
StorageProof: storageProof, Nonce: hexutil.Uint64(state.GetNonce(address)),
StorageHash: storageHash,
StorageProof: storageProof,
}, state.Error() }, state.Error()
} }

View file

@ -310,9 +310,9 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
p.bumpInvalid() p.bumpInvalid()
continue 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 { 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 continue
} }
// Accumulate the code and abort if enough data was retrieved // Accumulate the code and abort if enough data was retrieved

View file

@ -70,7 +70,7 @@ func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
} }
func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
if codeHash == codehash.EmptyCodeHash { if codeHash == codehash.EmptyKeccakCodeHash {
return nil, nil return nil, nil
} }
code := rawdb.ReadCode(db.backend.Database(), codeHash) code := rawdb.ReadCode(db.backend.Database(), codeHash)

View file

@ -190,7 +190,7 @@ func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, cal
// as is. // as is.
func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash) { func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash) {
// Short circuit if the entry is empty or already known // Short circuit if the entry is empty or already known
if hash == codehash.EmptyCodeHash { if hash == codehash.EmptyKeccakCodeHash {
return return
} }
if s.membatch.hasCode(hash) { if s.membatch.hasCode(hash) {

View file

@ -593,15 +593,15 @@ func TestTinyTrie(t *testing.T) {
_, accounts := makeAccounts(5) _, accounts := makeAccounts(5)
trie := newEmpty() trie := newEmpty()
trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3]) 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) t.Errorf("1: got %x, exp %x", root, exp)
} }
trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001338"), accounts[4]) 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) t.Errorf("2: got %x, exp %x", root, exp)
} }
trie.Update(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001339"), accounts[4]) 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) t.Errorf("3: got %x, exp %x", root, exp)
} }
checktr, _ := New(common.Hash{}, trie.db) checktr, _ := New(common.Hash{}, trie.db)
@ -625,7 +625,7 @@ func TestCommitAfterHash(t *testing.T) {
trie.Hash() trie.Hash()
trie.Commit(nil) trie.Commit(nil)
root := trie.Hash() root := trie.Hash()
exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6") exp := common.HexToHash("f0c0681648c93b347479cd58c61995557f01294425bd031ce1943c2799bbd4ec")
if exp != root { if exp != root {
t.Errorf("got %x, exp %x", root, exp) t.Errorf("got %x, exp %x", root, exp)
} }
@ -650,7 +650,6 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) {
var ( var (
nonce = uint64(random.Int63()) nonce = uint64(random.Int63())
root = emptyRoot root = emptyRoot
code = codehash.EmptyCodeHash.Bytes()
) )
// The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, // 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. // 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) balanceBytes := make([]byte, numBytes)
random.Read(balanceBytes) random.Read(balanceBytes)
balance := new(big.Int).SetBytes(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 accounts[i] = data
} }
return addresses, accounts return addresses, accounts
@ -717,12 +725,12 @@ func TestCommitSequence(t *testing.T) {
expWriteSeqHash []byte expWriteSeqHash []byte
expCallbackSeqHash []byte expCallbackSeqHash []byte
}{ }{
{20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066"), {20, common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"),
common.FromHex("ff00f91ac05df53b82d7f178d77ada54fd0dca64526f537034a5dbe41b17df2a")}, common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e")},
{200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e"), {200, common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"),
common.FromHex("f3cd509064c8d319bbdd1c68f511850a902ad275e6ed5bea11547e23d492a926")}, common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f")},
{2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7"), {2000, common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"),
common.FromHex("ff795ea898ba1e4cfed4a33b4cf5535a347a02cf931f88d88719faf810f9a1c9")}, common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d")},
} { } {
addresses, accounts := makeAccounts(tc.count) addresses, accounts := makeAccounts(tc.count)
// This spongeDb is used to check the sequence of disk-db-writes // This spongeDb is used to check the sequence of disk-db-writes

View file

@ -222,20 +222,24 @@ func TestMerkleTree_UpdateAccount(t *testing.T) {
mt := newTestingMerkle(t, 10) mt := newTestingMerkle(t, 10)
acc1 := &types.StateAccount{ acc1 := &types.StateAccount{
Nonce: 1, Nonce: 1,
Balance: big.NewInt(10000000), Balance: big.NewInt(10000000),
Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"), Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"),
CodeHash: common.HexToHash("cc0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab").Bytes(), KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
PoseidonCodeHash: common.HexToHash("0c0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab").Bytes(),
CodeSize: 0,
} }
err := mt.TryUpdateAccount(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes(), acc1) err := mt.TryUpdateAccount(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes(), acc1)
assert.Nil(t, err) assert.Nil(t, err)
acc2 := &types.StateAccount{ acc2 := &types.StateAccount{
Nonce: 5, Nonce: 5,
Balance: big.NewInt(50000000), Balance: big.NewInt(50000000),
Root: common.HexToHash("0"), Root: common.HexToHash("0"),
CodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(), KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
PoseidonCodeHash: common.HexToHash("05d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
CodeSize: 5,
} }
err = mt.TryUpdateAccount(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes(), acc2) err = mt.TryUpdateAccount(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes(), acc2)
assert.Nil(t, err) 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.Nonce, acc.Nonce)
assert.Equal(t, acc1.Balance.Uint64(), acc.Balance.Uint64()) assert.Equal(t, acc1.Balance.Uint64(), acc.Balance.Uint64())
assert.Equal(t, acc1.Root.Bytes(), acc.Root.Bytes()) 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()) bt, err = mt.TryGet(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
assert.Nil(t, err) 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.Nonce, acc.Nonce)
assert.Equal(t, acc2.Balance.Uint64(), acc.Balance.Uint64()) assert.Equal(t, acc2.Balance.Uint64(), acc.Balance.Uint64())
assert.Equal(t, acc2.Root.Bytes(), acc.Root.Bytes()) 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()) bt, err = mt.TryGet(common.HexToAddress("0x8dE13967F19410A7991D63c2c0179feBFDA0c261").Bytes())
assert.Nil(t, err) assert.Nil(t, err)

View file

@ -126,10 +126,12 @@ func NewRWTblOrderer(inited map[common.Address]*types.StateAccount) *rwTblOrdere
} }
initedAcc[addr] = &types.AccountWrapper{ initedAcc[addr] = &types.AccountWrapper{
Address: addr, Address: addr,
Nonce: data.Nonce, Nonce: data.Nonce,
Balance: (*hexutil.Big)(bl), Balance: (*hexutil.Big)(bl),
CodeHash: common.BytesToHash(data.CodeHash), 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 { if traced, existed := od.opAccNonce[addrStr]; !existed {
traced = copyAccountState(st) traced = copyAccountState(st)
traced.Balance = initedRef.Balance traced.Balance = initedRef.Balance
traced.CodeHash = initedRef.CodeHash traced.KeccakCodeHash = initedRef.KeccakCodeHash
traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
traced.CodeSize = initedRef.CodeSize
traced.Storage = nil traced.Storage = nil
od.opAccNonce[addrStr] = traced od.opAccNonce[addrStr] = traced
} else { } else {
@ -260,7 +264,9 @@ func (od *rwTblOrderer) absorb(st *types.AccountWrapper) {
if traced, existed := od.opAccBalance[addrStr]; !existed { if traced, existed := od.opAccBalance[addrStr]; !existed {
traced = copyAccountState(st) traced = copyAccountState(st)
traced.CodeHash = initedRef.CodeHash traced.KeccakCodeHash = initedRef.KeccakCodeHash
traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
traced.CodeSize = initedRef.CodeSize
traced.Storage = nil traced.Storage = nil
od.opAccBalance[addrStr] = traced od.opAccBalance[addrStr] = traced
} else { } else {
@ -275,7 +281,9 @@ func (od *rwTblOrderer) absorb(st *types.AccountWrapper) {
} else { } else {
traced.Nonce = st.Nonce traced.Nonce = st.Nonce
traced.Balance = st.Balance traced.Balance = st.Balance
traced.CodeHash = st.CodeHash traced.KeccakCodeHash = st.KeccakCodeHash
traced.PoseidonCodeHash = st.PoseidonCodeHash
traced.CodeSize = st.CodeSize
} }
} }

View file

@ -30,9 +30,11 @@ type SMTPath struct {
// StateAccount is the represent of StateAccount in L2 circuit // StateAccount is the represent of StateAccount in L2 circuit
// Notice in L2 we have different hash scheme against StateAccount.MarshalByte // Notice in L2 we have different hash scheme against StateAccount.MarshalByte
type StateAccount struct { type StateAccount struct {
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
Balance *hexutil.Big `json:"balance"` //just the common hex expression of integer (big-endian) Balance *hexutil.Big `json:"balance"` //just the common hex expression of integer (big-endian)
CodeHash hexutil.Bytes `json:"codeHash,omitempty"` 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 // StateStorage is the represent of a stored key-value pair for specified account

View file

@ -263,16 +263,18 @@ func copyAccountState(st *types.AccountWrapper) *types.AccountWrapper {
} }
return &types.AccountWrapper{ return &types.AccountWrapper{
Nonce: st.Nonce, Nonce: st.Nonce,
Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())), Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())),
CodeHash: st.CodeHash, KeccakCodeHash: st.KeccakCodeHash,
Address: st.Address, PoseidonCodeHash: st.PoseidonCodeHash,
Storage: stg, CodeSize: st.CodeSize,
Address: st.Address,
Storage: stg,
} }
} }
func isDeletedAccount(state *types.AccountWrapper) bool { 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 { func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount {
@ -282,9 +284,12 @@ func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount
} }
return &types.StateAccount{ return &types.StateAccount{
Nonce: state.Nonce, Nonce: state.Nonce,
Balance: (*big.Int)(state.Balance), Balance: (*big.Int)(state.Balance),
CodeHash: state.CodeHash.Bytes(), 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) return fmt.Errorf("unmatch leaf node in address: %s", addr)
} }
} else if data != nil { } 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)) //log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16))
if err != nil { if err != nil {
return fmt.Errorf("fail to hash account: %v", err) 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) 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 // we have ensured the nBefore has a key corresponding to the query one
out.AccountKey = out.AccountPath[0].Leaf.Sibling out.AccountKey = out.AccountPath[0].Leaf.Sibling
out.AccountUpdate[0] = &StateAccount{ out.AccountUpdate[0] = &StateAccount{
Nonce: int(accDataBefore.Nonce), Nonce: int(accDataBefore.Nonce),
Balance: (*hexutil.Big)(big.NewInt(0).Set(accDataBefore.Balance)), Balance: (*hexutil.Big)(big.NewInt(0).Set(accDataBefore.Balance)),
CodeHash: accDataBefore.CodeHash, KeccakCodeHash: accDataBefore.KeccakCodeHash,
PoseidonCodeHash: accDataBefore.PoseidonCodeHash,
CodeSize: accDataBefore.CodeSize,
} }
} }
accData := updateAccData(accDataBefore) accData := updateAccData(accDataBefore)
if accData != nil { if accData != nil {
out.AccountUpdate[1] = &StateAccount{ out.AccountUpdate[1] = &StateAccount{
Nonce: int(accData.Nonce), Nonce: int(accData.Nonce),
Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)), Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)),
CodeHash: accData.CodeHash, 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)) panic(fmt.Errorf("unexpected storage root before: [%s] vs [%x]", acc.Root, accRootFromState))
} }
return &types.StateAccount{ return &types.StateAccount{
Nonce: acc.Nonce, Nonce: acc.Nonce,
Balance: acc.Balance, Balance: acc.Balance,
CodeHash: acc.CodeHash, Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)),
Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)), KeccakCodeHash: acc.KeccakCodeHash,
PoseidonCodeHash: acc.PoseidonCodeHash,
CodeSize: acc.CodeSize,
} }
}) })
if err != nil { if err != nil {