Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/state: add code to state reader #30808

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
}
// Set up the trie reader, which is expected to always be available
// as the gatekeeper unless the state is corrupted.
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
tr, err := newTrieReader(stateRoot, db.triedb, db, db.pointCache)
if err != nil {
return nil, err
}
Expand Down
85 changes: 72 additions & 13 deletions core/state/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ type Reader interface {
// - The returned storage slot is safe to modify after the call
Storage(addr common.Address, slot common.Hash) (common.Hash, error)

// ContractCode returns the code associated with a particular account.
//
// - Returns an empty code if it does not exist
// - It can return an error to indicate code doesn't exist
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks consistency to the rest of the interface. I have done this to avoid any semantics change in this PR.

I am a bit puzzled why the statedb is recording "not found" errors for code but not for storage or account.

// - The returned code is safe to modify after the call
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)

// ContractCodeSize returns the size of the code associated with a particular account.
//
// - Returns 0 if the code does not exist
// - It can return an error to indicate code doesn't exist
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)

// Copy returns a deep-copied state reader.
Copy() Reader
}
Expand Down Expand Up @@ -123,6 +136,16 @@ func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash
return value, nil
}

// ContractCode implements Reader, retrieving the code associated with a particular account.
func (r *stateReader) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
return nil, nil
}

// ContractCodeSize implements Reader, returning the size of the code associated with a particular account.
func (r *stateReader) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
return 0, nil
}

// Copy implements Reader, returning a deep-copied snap reader.
func (r *stateReader) Copy() Reader {
return &stateReader{
Expand All @@ -134,17 +157,18 @@ func (r *stateReader) Copy() Reader {
// trieReader implements the Reader interface, providing functions to access
// state from the referenced trie.
type trieReader struct {
root common.Hash // State root which uniquely represent a state
db *triedb.Database // Database for loading trie
buff crypto.KeccakState // Buffer for keccak256 hashing
mainTrie Trie // Main trie, resolved in constructor
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
root common.Hash // State root which uniquely represent a state
db *triedb.Database // Database for loading trie
contractDB Database // Database for loading code
buff crypto.KeccakState // Buffer for keccak256 hashing
mainTrie Trie // Main trie, resolved in constructor
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
}

// trieReader constructs a trie reader of the specific state. An error will be
// returned if the associated trie specified by root is not existent.
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
func newTrieReader(root common.Hash, db *triedb.Database, contractDB Database, cache *utils.PointCache) (*trieReader, error) {
var (
tr Trie
err error
Expand All @@ -158,12 +182,13 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
return nil, err
}
return &trieReader{
root: root,
db: db,
buff: crypto.NewKeccakState(),
mainTrie: tr,
subRoots: make(map[common.Address]common.Hash),
subTries: make(map[common.Address]Trie),
root: root,
db: db,
contractDB: contractDB,
buff: crypto.NewKeccakState(),
mainTrie: tr,
subRoots: make(map[common.Address]common.Hash),
subTries: make(map[common.Address]Trie),
}, nil
}

Expand Down Expand Up @@ -227,6 +252,16 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil
}

// ContractCode implements Reader, retrieving the code associated with a particular account.
func (r *trieReader) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
return r.contractDB.ContractCode(addr, codeHash)
}

// ContractCodeSize implements Reader, returning the size of the code associated with a particular account.
func (r *trieReader) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
return r.contractDB.ContractCodeSize(addr, codeHash)
}

// Copy implements Reader, returning a deep-copied trie reader.
func (r *trieReader) Copy() Reader {
tries := make(map[common.Address]Trie)
Expand Down Expand Up @@ -298,6 +333,30 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has
return common.Hash{}, errors.Join(errs...)
}

// ContractCode implements Reader, retrieving the code associated with a particular account.
func (r *multiReader) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
var errs []error
for _, reader := range r.readers {
code, err := reader.ContractCode(addr, codeHash)
if err == nil {
return code, nil
}
}
return nil, errors.Join(errs...)
}

// ContractCodeSize implements Reader, returning the size of the code associated with a particular account.
func (r *multiReader) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
var errs []error
for _, reader := range r.readers {
size, err := reader.ContractCodeSize(addr, codeHash)
if err == nil {
return size, nil
}
}
return 0, errors.Join(errs...)
}

// Copy implementing Reader interface, returning a deep-copied state reader.
func (r *multiReader) Copy() Reader {
var readers []Reader
Expand Down
4 changes: 2 additions & 2 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ func (s *stateObject) Code() []byte {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return nil
}
code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
code, err := s.db.reader.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
if err != nil {
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
}
Expand All @@ -528,7 +528,7 @@ func (s *stateObject) CodeSize() int {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return 0
}
size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
size, err := s.db.reader.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
if err != nil {
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
}
Expand Down
Loading