core/state: pipe code loading through reader

This commit is contained in:
Sina Mahmoodi 2024-11-25 22:03:08 +01:00
parent 23800122b3
commit e4a794aeb1
3 changed files with 75 additions and 16 deletions

View file

@ -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 // Set up the trie reader, which is expected to always be available
// as the gatekeeper unless the state is corrupted. // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -48,6 +48,19 @@ type Reader interface {
// - The returned storage slot is safe to modify after the call // - The returned storage slot is safe to modify after the call
Storage(addr common.Address, slot common.Hash) (common.Hash, error) 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
// - 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 returns a deep-copied state reader.
Copy() Reader Copy() Reader
} }
@ -123,6 +136,16 @@ func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash
return value, nil 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. // Copy implements Reader, returning a deep-copied snap reader.
func (r *stateReader) Copy() Reader { func (r *stateReader) Copy() Reader {
return &stateReader{ return &stateReader{
@ -134,17 +157,18 @@ func (r *stateReader) Copy() Reader {
// trieReader implements the Reader interface, providing functions to access // trieReader implements the Reader interface, providing functions to access
// state from the referenced trie. // state from the referenced trie.
type trieReader struct { type trieReader struct {
root common.Hash // State root which uniquely represent a state root common.Hash // State root which uniquely represent a state
db *triedb.Database // Database for loading trie db *triedb.Database // Database for loading trie
buff crypto.KeccakState // Buffer for keccak256 hashing contractDB Database // Database for loading code
mainTrie Trie // Main trie, resolved in constructor buff crypto.KeccakState // Buffer for keccak256 hashing
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved mainTrie Trie // Main trie, resolved in constructor
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved 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 // trieReader constructs a trie reader of the specific state. An error will be
// returned if the associated trie specified by root is not existent. // 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 ( var (
tr Trie tr Trie
err error err error
@ -158,12 +182,13 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
return nil, err return nil, err
} }
return &trieReader{ return &trieReader{
root: root, root: root,
db: db, db: db,
buff: crypto.NewKeccakState(), contractDB: contractDB,
mainTrie: tr, buff: crypto.NewKeccakState(),
subRoots: make(map[common.Address]common.Hash), mainTrie: tr,
subTries: make(map[common.Address]Trie), subRoots: make(map[common.Address]common.Hash),
subTries: make(map[common.Address]Trie),
}, nil }, nil
} }
@ -227,6 +252,16 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil 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. // Copy implements Reader, returning a deep-copied trie reader.
func (r *trieReader) Copy() Reader { func (r *trieReader) Copy() Reader {
tries := make(map[common.Address]Trie) tries := make(map[common.Address]Trie)
@ -298,6 +333,30 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has
return common.Hash{}, errors.Join(errs...) 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. // Copy implementing Reader interface, returning a deep-copied state reader.
func (r *multiReader) Copy() Reader { func (r *multiReader) Copy() Reader {
var readers []Reader var readers []Reader

View file

@ -510,7 +510,7 @@ func (s *stateObject) Code() []byte {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return nil 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 { 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))
} }
@ -528,7 +528,7 @@ func (s *stateObject) CodeSize() int {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return 0 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 { 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))
} }