From e049615dc61af22f1a776556158f557f3e1458f5 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 28 Nov 2024 14:29:08 +0800 Subject: [PATCH] core/state, eth/protocols/snap: rework code interface --- core/blockchain_reader.go | 5 +-- core/state/database.go | 24 +++++------ core/state/reader.go | 76 ++++++++++++----------------------- eth/protocols/snap/handler.go | 2 +- 4 files changed, 38 insertions(+), 69 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 8f1da82a2b..cf72e98cb2 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -344,10 +344,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool { // ContractCodeWithPrefix retrieves a blob of data associated with a contract // hash either from ephemeral in-memory cache, or from persistent storage. -// -// If the code doesn't exist in the in-memory cache, check the storage with -// new code scheme. -func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { +func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte { // TODO(rjl493456442) The associated account address is also required // in Verkle scheme. Fix it once snap-sync is supported for Verkle. return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash) diff --git a/core/state/database.go b/core/state/database.go index d8f0b98489..fff7f1519f 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,7 +17,6 @@ package state import ( - "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -174,11 +173,7 @@ func NewDatabaseForTesting() *CachingDB { // Reader returns a state reader associated with the specified state root. func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { - var readers []Reader - - // Construct the code reader with retained caches. These caches are - // thread safe. - cReader := newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache) + var readers []StateReader // Set up the state snapshot reader if available. This feature // is optional and may be partially useful if it's not fully @@ -186,7 +181,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { if db.snap != nil { snap := db.snap.Snapshot(stateRoot) if snap != nil { - readers = append(readers, newSingleReader(cReader, newFlatReader(snap))) + readers = append(readers, newFlatReader(snap)) } } // Set up the trie reader, which is expected to always be available @@ -195,9 +190,13 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { if err != nil { return nil, err } - readers = append(readers, newSingleReader(cReader, tr)) + readers = append(readers, tr) - return newMultiReader(readers...) + combined, err := newMultiStateReader(readers...) + if err != nil { + return nil, err + } + return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil } // OpenTrie opens the main account trie at a specific root hash. @@ -230,18 +229,17 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre // ContractCodeWithPrefix retrieves a particular contract's code. If the // code can't be found in the cache, then check the existence with **new** // db scheme. -func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) { +func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) []byte { code, _ := db.codeCache.Get(codeHash) if len(code) > 0 { - return code, nil + return code } code = rawdb.ReadCodeWithPrefix(db.disk, codeHash) if len(code) > 0 { db.codeCache.Add(codeHash, code) db.codeSizeCache.Add(codeHash, len(code)) - return code, nil } - return nil, errors.New("not found") + return code } // TrieDB retrieves any intermediate trie-node caching layer. diff --git a/core/state/reader.go b/core/state/reader.go index 2156e6c099..a0f15dfcc8 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -290,46 +290,32 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, return value, nil } -// singleReader is the wrapper of ContractCodeReader and StateReader interface. -type singleReader struct { - ContractCodeReader - StateReader +// multiStateReader is the aggregation of a list of StateReader interface, +// providing state access by leveraging all readers. The checking priority +// is determined by the position in the reader list. +type multiStateReader struct { + readers []StateReader // List of state readers, sorted by checking priority } -// newSingleReader constructs a reader with the supplied code reader and state reader. -func newSingleReader(codeReader ContractCodeReader, stateReader StateReader) *singleReader { - return &singleReader{ - ContractCodeReader: codeReader, - StateReader: stateReader, - } -} - -// multiReader is the aggregation of a list of Reader interface, providing state -// access by leveraging all readers. The checking priority is determined by the -// position in the reader list. -type multiReader struct { - readers []Reader // List of readers, sorted by checking priority -} - -// newMultiReader constructs a multiReader instance with the given readers. The -// priority among readers is assumed to be sorted. Note, it must contain at least -// one reader for constructing a multiReader. -func newMultiReader(readers ...Reader) (*multiReader, error) { +// newMultiStateReader constructs a multiStateReader instance with the given +// readers. The priority among readers is assumed to be sorted. Note, it must +// contain at least one reader for constructing a multiStateReader. +func newMultiStateReader(readers ...StateReader) (*multiStateReader, error) { if len(readers) == 0 { return nil, errors.New("empty reader set") } - return &multiReader{ + return &multiStateReader{ readers: readers, }, nil } -// Account implementing Reader interface, retrieving the account associated with -// a particular address. +// Account implementing StateReader interface, retrieving 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 -func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) { +func (r *multiStateReader) Account(addr common.Address) (*types.StateAccount, error) { var errs []error for _, reader := range r.readers { acct, err := reader.Account(addr) @@ -341,13 +327,13 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) return nil, errors.Join(errs...) } -// Storage implementing Reader interface, retrieving the storage slot associated -// with a particular account address and slot key. +// Storage implementing StateReader interface, retrieving 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 -func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { +func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { var errs []error for _, reader := range r.readers { slot, err := reader.Storage(addr, slot) @@ -359,28 +345,16 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has return common.Hash{}, errors.Join(errs...) } -// ContractCode implements Reader, retrieving a particular contract's code. -func (r *multiReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) { - var errs []error - for _, reader := range r.readers { - code, err := reader.Code(addr, codeHash) - if err == nil { - return code, nil - } - errs = append(errs, err) - } - return nil, errors.Join(errs...) +// reader is the wrapper of ContractCodeReader and StateReader interface. +type reader struct { + ContractCodeReader + StateReader } -// ContractCodeSize implements Reader, retrieving a particular contracts code's size. -func (r *multiReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) { - var errs []error - for _, reader := range r.readers { - size, err := reader.CodeSize(addr, codeHash) - if err == nil { - return size, nil - } - errs = append(errs, err) +// newReader constructs a reader with the supplied code reader and state reader. +func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader { + return &reader{ + ContractCodeReader: codeReader, + StateReader: stateReader, } - return 0, errors.Join(errs...) } diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index d36f9621b1..8164973c20 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -454,7 +454,7 @@ func ServiceGetByteCodesQuery(chain *core.BlockChain, req *GetByteCodesPacket) [ // Peers should not request the empty code, but if they do, at // least sent them back a correct response without db lookups codes = append(codes, []byte{}) - } else if blob, err := chain.ContractCodeWithPrefix(hash); err == nil { + } else if blob := chain.ContractCodeWithPrefix(hash); len(blob) > 0 { codes = append(codes, blob) bytes += uint64(len(blob)) }