mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
core/state: rework the code reader
This commit is contained in:
parent
04bdd12ff7
commit
c3e12a3dfb
4 changed files with 61 additions and 34 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue