From c3e12a3dfbc6e2b8efaa7c1bbc9b2d7b22929bd1 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 28 Nov 2024 11:11:53 +0800 Subject: [PATCH] core/state: rework the code reader --- core/state/iterator.go | 3 ++ core/state/reader.go | 70 ++++++++++++++++++++++---------------- core/state/state_object.go | 6 ++++ core/state/sync_test.go | 16 ++++++--- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/core/state/iterator.go b/core/state/iterator.go index 523992b907..78ffb65c6c 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -140,6 +140,9 @@ func (it *nodeIterator) step() error { if err != nil { return fmt.Errorf("code %x: %v", account.CodeHash, err) } + if len(it.code) == 0 { + return fmt.Errorf("code is not found: %x", account.CodeHash) + } } it.accountHash = it.stateIt.Parent() return nil diff --git a/core/state/reader.go b/core/state/reader.go index cabc3c3437..a50ccde060 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -35,12 +35,46 @@ import ( // CodeReader defines the interface for accessing contract code. type CodeReader interface { // ContractCode retrieves a particular contract's code. + // + // - Returns nil code along with nil error if the requested contract code + // doesn't exist + // - Returns an error only if an unexpected issue occurs ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) // ContractCodeSize retrieves a particular contracts code's size. + // + // - Returns zero code size along with nil error if the requested contract code + // doesn't exist + // - Returns an error only if an unexpected issue occurs ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) } +// StateReader defines the interface for accessing accounts and storage slots +// associated with a specific state. +type StateReader interface { + // Account retrieves the account associated with a particular address. + // + // - Returns a nil account if it does not exist + // - Returns an error only if an unexpected issue occurs + // - The returned account is safe to modify after the call + Account(addr common.Address) (*types.StateAccount, error) + + // Storage retrieves the storage slot associated with a particular account + // address and slot key. + // + // - Returns an empty slot if it does not exist + // - Returns an error only if an unexpected issue occurs + // - The returned storage slot is safe to modify after the call + Storage(addr common.Address, slot common.Hash) (common.Hash, error) +} + +// Reader defines the interface for accessing accounts, storage slots and contract +// code associated with a specific state. +type Reader interface { + CodeReader + StateReader +} + // cachingCodeReader implements CodeReader, accessing contract code either in // local key-value store or the shared code cache. type cachingCodeReader struct { @@ -62,6 +96,7 @@ func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstraine } // ContractCode implements CodeReader, retrieving a particular contract's code. +// If the contract code doesn't exist, no error will be returned. func (r *cachingCodeReader) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) { code, _ := r.codeCache.Get(codeHash) if len(code) > 0 { @@ -71,37 +106,21 @@ func (r *cachingCodeReader) ContractCode(addr common.Address, codeHash common.Ha if len(code) > 0 { r.codeCache.Add(codeHash, code) r.codeSizeCache.Add(codeHash, len(code)) - return code, nil } - return nil, errors.New("not found") + return code, nil } // ContractCodeSize implements CodeReader, retrieving a particular contracts code's size. +// If the contract code doesn't exist, no error will be returned. func (r *cachingCodeReader) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) { if cached, ok := r.codeSizeCache.Get(codeHash); ok { return cached, nil } code, err := r.ContractCode(addr, codeHash) - return len(code), err -} - -// StateReader defines the interface for accessing accounts and storage slots -// associated with a specific state. -type StateReader interface { - // Account retrieves the account associated with a particular address. - // - // - Returns a nil account if it does not exist - // - Returns an error only if an unexpected issue occurs - // - The returned account is safe to modify after the call - Account(addr common.Address) (*types.StateAccount, error) - - // Storage retrieves the storage slot associated with a particular account - // address and slot key. - // - // - Returns an empty slot if it does not exist - // - Returns an error only if an unexpected issue occurs - // - The returned storage slot is safe to modify after the call - Storage(addr common.Address, slot common.Hash) (common.Hash, error) + if err != nil { + return 0, err + } + return len(code), nil } // flatReader wraps a database state reader. @@ -271,13 +290,6 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, return value, nil } -// Reader defines the interface for accessing accounts, storage slots and contract -// code associated with a specific state. -type Reader interface { - CodeReader - StateReader -} - // singleReader is the wrapper of CodeReader and StateReader interface. type singleReader struct { CodeReader diff --git a/core/state/state_object.go b/core/state/state_object.go index 172d6c5f5e..bda57fdb4e 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -514,6 +514,9 @@ func (s *stateObject) Code() []byte { if err != nil { s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) } + if len(code) == 0 { + s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash())) + } s.code = code return code } @@ -532,6 +535,9 @@ func (s *stateObject) CodeSize() int { if err != nil { s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) } + if size == 0 { + s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash())) + } return size } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 2fa83f2eee..2ebe40b5f5 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -18,6 +18,7 @@ package state import ( "bytes" + "fmt" "testing" "github.com/ethereum/go-ethereum/common" @@ -221,7 +222,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s ) for i, element := range codeElements { data, err := cReader.ContractCode(common.Address{}, element.code) - if err != nil { + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code) } codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} @@ -345,7 +346,7 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) { codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1) for i, element := range codeElements[:len(codeResults)] { data, err := cReader.ContractCode(common.Address{}, element.code) - if err != nil { + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve contract bytecode for %x", element.code) } codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} @@ -451,7 +452,7 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) { results := make([]trie.CodeSyncResult, 0, len(codeQueue)) for hash := range codeQueue { data, err := cReader.ContractCode(common.Address{}, hash) - if err != nil { + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -550,7 +551,7 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) { delete(codeQueue, hash) data, err := cReader.ContractCode(common.Address{}, hash) - if err != nil { + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -670,7 +671,7 @@ func testIncompleteStateSync(t *testing.T, scheme string) { results := make([]trie.CodeSyncResult, 0, len(codeQueue)) for hash := range codeQueue { data, err := cReader.ContractCode(common.Address{}, hash) - if err != nil { + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -733,6 +734,11 @@ func testIncompleteStateSync(t *testing.T, scheme string) { // Sanity check that removing any node from the database is detected for _, node := range addedCodes { val := rawdb.ReadCode(dstDb, node) + if len(val) == 0 { + fmt.Println("node code") + } else { + fmt.Println("has code") + } rawdb.DeleteCode(dstDb, node) if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil { t.Errorf("trie inconsistency not caught, missing: %x", node)