mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +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 {
|
if err != nil {
|
||||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
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()
|
it.accountHash = it.stateIt.Parent()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,46 @@ import (
|
||||||
// CodeReader defines the interface for accessing contract code.
|
// CodeReader defines the interface for accessing contract code.
|
||||||
type CodeReader interface {
|
type CodeReader interface {
|
||||||
// ContractCode retrieves a particular contract's code.
|
// 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)
|
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||||
|
|
||||||
// ContractCodeSize retrieves a particular contracts code's size.
|
// 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)
|
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
|
// cachingCodeReader implements CodeReader, accessing contract code either in
|
||||||
// local key-value store or the shared code cache.
|
// local key-value store or the shared code cache.
|
||||||
type cachingCodeReader struct {
|
type cachingCodeReader struct {
|
||||||
|
|
@ -62,6 +96,7 @@ func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstraine
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCode implements CodeReader, retrieving a particular contract's code.
|
// 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) {
|
func (r *cachingCodeReader) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
||||||
code, _ := r.codeCache.Get(codeHash)
|
code, _ := r.codeCache.Get(codeHash)
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
|
|
@ -71,37 +106,21 @@ func (r *cachingCodeReader) ContractCode(addr common.Address, codeHash common.Ha
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
r.codeCache.Add(codeHash, code)
|
r.codeCache.Add(codeHash, code)
|
||||||
r.codeSizeCache.Add(codeHash, len(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.
|
// 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) {
|
func (r *cachingCodeReader) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||||
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
code, err := r.ContractCode(addr, codeHash)
|
code, err := r.ContractCode(addr, codeHash)
|
||||||
return len(code), err
|
if err != nil {
|
||||||
}
|
return 0, err
|
||||||
|
}
|
||||||
// StateReader defines the interface for accessing accounts and storage slots
|
return len(code), nil
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatReader wraps a database state reader.
|
// 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
|
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.
|
// singleReader is the wrapper of CodeReader and StateReader interface.
|
||||||
type singleReader struct {
|
type singleReader struct {
|
||||||
CodeReader
|
CodeReader
|
||||||
|
|
|
||||||
|
|
@ -514,6 +514,9 @@ func (s *stateObject) Code() []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
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
|
s.code = code
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
@ -532,6 +535,9 @@ func (s *stateObject) CodeSize() int {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
|
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
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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 {
|
for i, element := range codeElements {
|
||||||
data, err := cReader.ContractCode(common.Address{}, element.code)
|
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)
|
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
|
||||||
}
|
}
|
||||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
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)
|
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
|
||||||
for i, element := range codeElements[:len(codeResults)] {
|
for i, element := range codeElements[:len(codeResults)] {
|
||||||
data, err := cReader.ContractCode(common.Address{}, element.code)
|
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)
|
t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
|
||||||
}
|
}
|
||||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
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))
|
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||||
for hash := range codeQueue {
|
for hash := range codeQueue {
|
||||||
data, err := cReader.ContractCode(common.Address{}, 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)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||||
|
|
@ -550,7 +551,7 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
|
||||||
delete(codeQueue, hash)
|
delete(codeQueue, hash)
|
||||||
|
|
||||||
data, err := cReader.ContractCode(common.Address{}, 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)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
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))
|
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||||
for hash := range codeQueue {
|
for hash := range codeQueue {
|
||||||
data, err := cReader.ContractCode(common.Address{}, 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)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
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
|
// Sanity check that removing any node from the database is detected
|
||||||
for _, node := range addedCodes {
|
for _, node := range addedCodes {
|
||||||
val := rawdb.ReadCode(dstDb, node)
|
val := rawdb.ReadCode(dstDb, node)
|
||||||
|
if len(val) == 0 {
|
||||||
|
fmt.Println("node code")
|
||||||
|
} else {
|
||||||
|
fmt.Println("has code")
|
||||||
|
}
|
||||||
rawdb.DeleteCode(dstDb, node)
|
rawdb.DeleteCode(dstDb, node)
|
||||||
if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil {
|
if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil {
|
||||||
t.Errorf("trie inconsistency not caught, missing: %x", node)
|
t.Errorf("trie inconsistency not caught, missing: %x", node)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue