mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
core: implement code database
This commit is contained in:
parent
4f59baa7d1
commit
a362a10779
19 changed files with 367 additions and 250 deletions
|
|
@ -91,9 +91,7 @@ var (
|
|||
accountReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/account/single/reads", nil)
|
||||
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
||||
codeReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/code/single/reads", nil)
|
||||
|
||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
|
||||
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
||||
|
|
@ -320,7 +318,7 @@ type BlockChain struct {
|
|||
lastWrite uint64 // Last block when the state was flushed
|
||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
||||
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
|
||||
codedb *state.CodeDB // The database handler for maintaining contract codes.
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
|
|
@ -402,6 +400,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
|||
cfg: cfg,
|
||||
db: db,
|
||||
triedb: triedb,
|
||||
codedb: state.NewCodeDB(db),
|
||||
triegc: prque.New[int64, common.Hash](nil),
|
||||
chainmu: syncx.NewClosableMutex(),
|
||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||
|
|
@ -418,7 +417,6 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
|||
return nil, err
|
||||
}
|
||||
bc.flushInterval.Store(int64(cfg.TrieTimeLimit))
|
||||
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||
bc.processor = NewStateProcessor(bc.hc)
|
||||
|
|
@ -595,9 +593,6 @@ func (bc *BlockChain) setupSnapshot() {
|
|||
AsyncBuild: !bc.cfg.SnapshotWait,
|
||||
}
|
||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
||||
|
||||
// Re-initialize the state database with snapshot
|
||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2079,11 +2074,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
sdb = state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
|
||||
)
|
||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||
|
||||
if bc.cfg.NoPrefetch {
|
||||
statedb, err = state.New(parentRoot, bc.statedb)
|
||||
statedb, err = state.New(parentRoot, sdb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -2093,15 +2089,15 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
//
|
||||
// Note: the main processor and prefetcher share the same reader with a local
|
||||
// cache for mitigating the overhead of state access.
|
||||
prefetch, process, err := bc.statedb.ReadersWithCacheStats(parentRoot)
|
||||
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, prefetch)
|
||||
throwaway, err := state.NewWithReader(parentRoot, sdb, prefetch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statedb, err = state.NewWithReader(parentRoot, bc.statedb, process)
|
||||
statedb, err = state.NewWithReader(parentRoot, sdb, process)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -2262,9 +2258,8 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
// Update the metrics touched during block commit
|
||||
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
|
||||
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
|
||||
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
|
||||
stats.TrieDBCommit = statedb.TrieDBCommits // Trie database commits are complete, we can mark them
|
||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
|
||||
stats.DatabaseCommit = statedb.DatabaseCommits // Database commits are complete, we can mark them
|
||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
||||
|
||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||
stats.TotalTime = elapsed
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ func (bc *BlockChain) TxIndexDone() bool {
|
|||
|
||||
// HasState checks if state trie is fully present in the database or not.
|
||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||
_, err := bc.statedb.OpenTrie(hash)
|
||||
_, err := bc.triedb.NodeReader(hash)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +403,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
|||
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)
|
||||
return bc.codedb.Reader().CodeWithPrefix(common.Address{}, hash)
|
||||
}
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
|
|
@ -413,14 +413,14 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
|||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, bc.statedb)
|
||||
return state.New(root, state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
|
||||
}
|
||||
|
||||
// HistoricState returns a historic state specified by the given root.
|
||||
// Live states are not available and won't be served, please use `State`
|
||||
// or `StateAt` instead.
|
||||
func (bc *BlockChain) HistoricState(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, state.NewHistoricDatabase(bc.db, bc.triedb))
|
||||
return state.New(root, state.NewHistoricDatabase(bc.triedb, bc.codedb))
|
||||
}
|
||||
|
||||
// Config retrieves the chain's fork configuration.
|
||||
|
|
@ -444,11 +444,6 @@ func (bc *BlockChain) Processor() Processor {
|
|||
return bc.processor
|
||||
}
|
||||
|
||||
// StateCache returns the caching database underpinning the blockchain instance.
|
||||
func (bc *BlockChain) StateCache() state.Database {
|
||||
return bc.statedb
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
func (bc *BlockChain) GasLimit() uint64 {
|
||||
return bc.CurrentBlock().GasLimit
|
||||
|
|
@ -492,6 +487,11 @@ func (bc *BlockChain) TrieDB() *triedb.Database {
|
|||
return bc.triedb
|
||||
}
|
||||
|
||||
// CodeDB retrieves the low level contract code database used for data storage.
|
||||
func (bc *BlockChain) CodeDB() *state.CodeDB {
|
||||
return bc.codedb
|
||||
}
|
||||
|
||||
// HeaderChain returns the underlying header chain.
|
||||
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
||||
return bc.hc
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -2041,7 +2040,6 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
dbconfig.HashDB = hashdb.Defaults
|
||||
}
|
||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
||||
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ type ExecuteStats struct {
|
|||
Execution time.Duration // Time spent on the EVM execution
|
||||
Validation time.Duration // Time spent on the block validation
|
||||
CrossValidation time.Duration // Optional, time spent on the block cross validation
|
||||
SnapshotCommit time.Duration // Time spent on snapshot commit
|
||||
TrieDBCommit time.Duration // Time spent on database commit
|
||||
DatabaseCommit time.Duration // Time spent on database commit
|
||||
BlockWrite time.Duration // Time spent on block write
|
||||
TotalTime time.Duration // The total time spent on block execution
|
||||
MgasPerSecond float64 // The million gas processed per second
|
||||
|
|
@ -87,8 +86,7 @@ func (s *ExecuteStats) reportMetrics() {
|
|||
blockExecutionTimer.Update(s.Execution) // The time spent on EVM processing
|
||||
blockValidationTimer.Update(s.Validation) // The time spent on block validation
|
||||
blockCrossValidationTimer.Update(s.CrossValidation) // The time spent on stateless cross validation
|
||||
snapshotCommitTimer.Update(s.SnapshotCommit) // Snapshot commits are complete, we can mark them
|
||||
triedbCommitTimer.Update(s.TrieDBCommit) // Trie database commits are complete, we can mark them
|
||||
triedbCommitTimer.Update(s.DatabaseCommit) // Trie database commits are complete, we can mark them
|
||||
blockWriteTimer.Update(s.BlockWrite) // The time spent on block write
|
||||
blockInsertTimer.Update(s.TotalTime) // The total time spent on block execution
|
||||
chainMgaspsMeter.Update(time.Duration(s.MgasPerSecond)) // TODO(rjl493456442) generalize the ResettingTimer
|
||||
|
|
@ -208,7 +206,7 @@ func (s *ExecuteStats) logSlow(block *types.Block, slowBlockThreshold time.Durat
|
|||
ExecutionMs: durationToMs(s.Execution),
|
||||
StateReadMs: durationToMs(s.AccountReads + s.StorageReads + s.CodeReads),
|
||||
StateHashMs: durationToMs(s.AccountHashes + s.AccountUpdates + s.StorageUpdates),
|
||||
CommitMs: durationToMs(max(s.AccountCommits, s.StorageCommits) + s.TrieDBCommit + s.SnapshotCommit + s.BlockWrite),
|
||||
CommitMs: durationToMs(max(s.AccountCommits, s.StorageCommits) + s.DatabaseCommit + s.BlockWrite),
|
||||
TotalMs: durationToMs(s.TotalTime),
|
||||
},
|
||||
Throughput: slowBlockThru{
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), state.NewDatabase(blockchain.triedb, blockchain.codedb))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/overlay"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
||||
|
|
@ -34,14 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
const (
|
||||
// Number of codehash->size associations to keep.
|
||||
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||
|
||||
// Cache size granted for caching clean code.
|
||||
codeCacheSize = 256 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Database wraps access to tries and contract code.
|
||||
type Database interface {
|
||||
// Reader returns a state reader associated with the specified state root.
|
||||
|
|
@ -58,6 +50,11 @@ type Database interface {
|
|||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
Snapshot() *snapshot.Tree
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
Commit(update *stateUpdate) error
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Patricia trie.
|
||||
|
|
@ -149,32 +146,34 @@ type Trie interface {
|
|||
// state snapshot to provide functionalities for state access. It's meant to be a
|
||||
// long-live object and has a few caches inside for sharing between blocks.
|
||||
type CachingDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
snap *snapshot.Tree
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
|
||||
// Transition-specific fields
|
||||
TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState]
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
// NewDatabase creates a state database with the provided data sources.
|
||||
func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
|
||||
func NewDatabase(triedb *triedb.Database, codedb *CodeDB) *CachingDB {
|
||||
if codedb == nil {
|
||||
codedb = NewCodeDB(triedb.Disk())
|
||||
}
|
||||
return &CachingDB{
|
||||
disk: triedb.Disk(),
|
||||
triedb: triedb,
|
||||
snap: snap,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
TransitionStatePerRoot: lru.NewCache[common.Hash, *overlay.TransitionState](1000),
|
||||
triedb: triedb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||
// db by using an ephemeral memory db with default config for testing.
|
||||
func NewDatabaseForTesting() *CachingDB {
|
||||
return NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
return NewDatabase(triedb.NewDatabase(db, nil), NewCodeDB(db))
|
||||
}
|
||||
|
||||
// WithSnapshot configures the provided contract code cache. Note that this
|
||||
// registration must be performed before the cachingDB is used.
|
||||
func (db *CachingDB) WithSnapshot(snapshot *snapshot.Tree) *CachingDB {
|
||||
db.snap = snapshot
|
||||
return db
|
||||
}
|
||||
|
||||
// StateReader returns a state reader associated with the specified state root.
|
||||
|
|
@ -218,7 +217,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), sr), nil
|
||||
return newReader(db.codedb.Reader(), sr), nil
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
|
|
@ -230,8 +229,8 @@ func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reade
|
|||
return nil, nil, err
|
||||
}
|
||||
sr := newStateReaderWithCache(r)
|
||||
ra := newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), newStateReaderWithStats(sr))
|
||||
rb := newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), newStateReaderWithStats(sr))
|
||||
ra := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
rb := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
return ra, rb, nil
|
||||
}
|
||||
|
||||
|
|
@ -267,22 +266,6 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
|
|||
return tr, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
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
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *CachingDB) TrieDB() *triedb.Database {
|
||||
return db.triedb
|
||||
|
|
@ -293,6 +276,40 @@ func (db *CachingDB) Snapshot() *snapshot.Tree {
|
|||
return db.snap
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *CachingDB) Commit(update *stateUpdate) error {
|
||||
// Short circuit if nothing to commit
|
||||
if update.empty() {
|
||||
return nil
|
||||
}
|
||||
// Commit dirty contract code if any exists
|
||||
if len(update.codes) > 0 {
|
||||
batch := db.codedb.NewBatchWithSize(len(update.codes))
|
||||
for _, code := range update.codes {
|
||||
batch.Put(code.hash, code.blob)
|
||||
}
|
||||
if err := batch.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if db.snap != nil && db.snap.Snapshot(update.originRoot) != nil {
|
||||
if err := db.snap.Update(update.root, update.originRoot, update.accounts, update.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", update.originRoot, "to", update.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
// - head layer is paired with HEAD state
|
||||
// - head-1 layer is paired with HEAD-1 state
|
||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||
if err := db.snap.Cap(update.root, TriesInMemory); err != nil {
|
||||
log.Warn("Failed to cap snapshot tree", "root", update.root, "layers", TriesInMemory, "err", err)
|
||||
}
|
||||
}
|
||||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
|
||||
}
|
||||
|
||||
// mustCopyTrie returns a deep-copied trie.
|
||||
func mustCopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
|
|
|
|||
231
core/state/database_code.go
Normal file
231
core/state/database_code.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
const (
|
||||
// Number of codeHash->size associations to keep.
|
||||
codeSizeCacheSize = 1_000_000
|
||||
|
||||
// Cache size granted for caching clean code.
|
||||
codeCacheSize = 256 * 1024 * 1024
|
||||
)
|
||||
|
||||
// CodeCache maintains cached contract code that is shared across blocks, enabling
|
||||
// fast access for external calls such as RPCs and state transitions.
|
||||
//
|
||||
// It is thread-safe and has a bounded size.
|
||||
type codeCache struct {
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
}
|
||||
|
||||
// newCodeCache initializes the contract code cache with the predefined capacity.
|
||||
func newCodeCache() *codeCache {
|
||||
return &codeCache{
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the contract code associated with the provided code hash.
|
||||
func (c *codeCache) Get(hash common.Hash) ([]byte, bool) {
|
||||
return c.codeCache.Get(hash)
|
||||
}
|
||||
|
||||
// GetSize returns the contract code size associated with the provided code hash.
|
||||
func (c *codeCache) GetSize(hash common.Hash) (int, bool) {
|
||||
return c.codeSizeCache.Get(hash)
|
||||
}
|
||||
|
||||
// Put adds the provided contract code along with its size information into the cache.
|
||||
func (c *codeCache) Put(hash common.Hash, code []byte) {
|
||||
c.codeCache.Add(hash, code)
|
||||
c.codeSizeCache.Add(hash, len(code))
|
||||
}
|
||||
|
||||
// CodeReader implements state.ContractCodeReader, accessing contract code either in
|
||||
// local key-value store or the shared code cache.
|
||||
//
|
||||
// Reader is safe for concurrent access.
|
||||
type CodeReader struct {
|
||||
db ethdb.KeyValueReader
|
||||
cache *codeCache
|
||||
|
||||
// Cache statistics
|
||||
hit atomic.Int64 // Number of code lookups found in the cache
|
||||
miss atomic.Int64 // Number of code lookups not found in the cache
|
||||
hitBytes atomic.Int64 // Total number of bytes read from cache
|
||||
missBytes atomic.Int64 // Total number of bytes read from database
|
||||
}
|
||||
|
||||
// newCodeReader constructs the code reader with provided key value store and the cache.
|
||||
func newCodeReader(db ethdb.KeyValueReader, cache *codeCache) *CodeReader {
|
||||
return &CodeReader{
|
||||
db: db,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns the flag indicating whether the contract code with
|
||||
// specified address and hash exists or not.
|
||||
func (r *CodeReader) Has(addr common.Address, codeHash common.Hash) bool {
|
||||
return len(r.Code(addr, codeHash)) > 0
|
||||
}
|
||||
|
||||
// Code implements state.ContractCodeReader, retrieving a particular contract's code.
|
||||
// Null is returned if the contract code is not present.
|
||||
func (r *CodeReader) Code(addr common.Address, codeHash common.Hash) []byte {
|
||||
code, _ := r.cache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
r.hitBytes.Add(int64(len(code)))
|
||||
return code
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCode(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.cache.Put(codeHash, code)
|
||||
r.missBytes.Add(int64(len(code)))
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// CodeSize implements state.ContractCodeReader, retrieving a particular contract
|
||||
// code's size. Zero is returned if the contract code is not present.
|
||||
func (r *CodeReader) CodeSize(addr common.Address, codeHash common.Hash) int {
|
||||
if cached, ok := r.cache.GetSize(codeHash); ok {
|
||||
r.hit.Add(1)
|
||||
return cached
|
||||
}
|
||||
return len(r.Code(addr, codeHash))
|
||||
}
|
||||
|
||||
// CodeWithPrefix retrieves the contract code for the specified account address
|
||||
// and code hash. It is almost identical to Code, but uses rawdb.ReadCodeWithPrefix
|
||||
// for database lookups. The intention is to gradually deprecate the old
|
||||
// contract code scheme.
|
||||
func (r *CodeReader) CodeWithPrefix(addr common.Address, codeHash common.Hash) []byte {
|
||||
code, _ := r.cache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
r.hitBytes.Add(int64(len(code)))
|
||||
return code
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCodeWithPrefix(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.cache.Put(codeHash, code)
|
||||
r.missBytes.Add(int64(len(code)))
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// GetCodeStats implements ContractCodeReaderStater, returning the statistics
|
||||
// of the code reader.
|
||||
func (r *CodeReader) GetCodeStats() ContractCodeReaderStats {
|
||||
return ContractCodeReaderStats{
|
||||
CacheHit: r.hit.Load(),
|
||||
CacheMiss: r.miss.Load(),
|
||||
CacheHitBytes: r.hitBytes.Load(),
|
||||
CacheMissBytes: r.missBytes.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
type CodeBatch struct {
|
||||
db *CodeDB
|
||||
codes [][]byte
|
||||
codeHashes []common.Hash
|
||||
}
|
||||
|
||||
// newCodeBatch constructs the batch for writing contract code.
|
||||
func newCodeBatch(db *CodeDB) *CodeBatch {
|
||||
return &CodeBatch{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// newCodeBatchWithSize constructs the batch with a pre-allocated capacity.
|
||||
func newCodeBatchWithSize(db *CodeDB, size int) *CodeBatch {
|
||||
return &CodeBatch{
|
||||
db: db,
|
||||
codes: make([][]byte, 0, size),
|
||||
codeHashes: make([]common.Hash, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts the given contract code into the writer, waiting for commit.
|
||||
func (b *CodeBatch) Put(codeHash common.Hash, code []byte) {
|
||||
b.codes = append(b.codes, code)
|
||||
b.codeHashes = append(b.codeHashes, codeHash)
|
||||
}
|
||||
|
||||
// Commit flushes the accumulated dirty contract code into the database and
|
||||
// also place them in the cache.
|
||||
func (b *CodeBatch) Commit() error {
|
||||
batch := b.db.db.NewBatch()
|
||||
for i, code := range b.codes {
|
||||
rawdb.WriteCode(batch, b.codeHashes[i], code)
|
||||
b.db.cache.Put(b.codeHashes[i], code)
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
b.codes = b.codes[:0]
|
||||
b.codeHashes = b.codeHashes[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// CodeDB is responsible for managing the contract code and provides the access
|
||||
// to it. It can be used as a global object, sharing it between multiple entities.
|
||||
type CodeDB struct {
|
||||
db ethdb.KeyValueStore
|
||||
cache *codeCache
|
||||
}
|
||||
|
||||
// NewCodeDB constructs the contract code database with the provided key value store.
|
||||
func NewCodeDB(db ethdb.KeyValueStore) *CodeDB {
|
||||
return &CodeDB{
|
||||
db: db,
|
||||
cache: newCodeCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader returns the contract code reader.
|
||||
func (d *CodeDB) Reader() *CodeReader {
|
||||
return newCodeReader(d.db, d.cache)
|
||||
}
|
||||
|
||||
// NewBatch returns the batch for flushing contract codes.
|
||||
func (d *CodeDB) NewBatch() *CodeBatch {
|
||||
return newCodeBatch(d)
|
||||
}
|
||||
|
||||
// NewBatchWithSize returns the batch with pre-allocated capacity.
|
||||
func (d *CodeDB) NewBatchWithSize(size int) *CodeBatch {
|
||||
return newCodeBatchWithSize(d, size)
|
||||
}
|
||||
|
|
@ -17,15 +17,14 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
|
|
@ -221,19 +220,15 @@ func (r *historicalTrieReader) Storage(addr common.Address, key common.Hash) (co
|
|||
// HistoricDB is the implementation of Database interface, with the ability to
|
||||
// access historical state.
|
||||
type HistoricDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
}
|
||||
|
||||
// NewHistoricDatabase creates a historic state database.
|
||||
func NewHistoricDatabase(disk ethdb.KeyValueStore, triedb *triedb.Database) *HistoricDB {
|
||||
func NewHistoricDatabase(triedb *triedb.Database, codedb *CodeDB) *HistoricDB {
|
||||
return &HistoricDB{
|
||||
disk: disk,
|
||||
triedb: triedb,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
triedb: triedb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +253,7 @@ func (db *HistoricDB) Reader(stateRoot common.Hash) (Reader, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
||||
return newReader(db.codedb.Reader(), combined), nil
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie. It's not supported by historic database.
|
||||
|
|
@ -298,3 +293,10 @@ func (db *HistoricDB) TrieDB() *triedb.Database {
|
|||
func (db *HistoricDB) Snapshot() *snapshot.Tree {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *HistoricDB) Commit(update *stateUpdate) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,10 +144,7 @@ func (it *nodeIterator) step() error {
|
|||
}
|
||||
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
it.codeHash = common.BytesToHash(account.CodeHash)
|
||||
it.code, err = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
||||
if err != nil {
|
||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
||||
}
|
||||
it.code = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
||||
if len(it.code) == 0 {
|
||||
return fmt.Errorf("code is not found: %x", account.CodeHash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,9 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/overlay"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
|
|
@ -44,19 +41,13 @@ type ContractCodeReader interface {
|
|||
// specified address and hash exists or not.
|
||||
Has(addr common.Address, codeHash common.Hash) bool
|
||||
|
||||
// Code 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
|
||||
Code(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||
// Code retrieves a particular contract's code. Returns nil code if the
|
||||
// requested contract code doesn't exist.
|
||||
Code(addr common.Address, codeHash common.Hash) []byte
|
||||
|
||||
// CodeSize 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
|
||||
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||
// CodeSize retrieves a particular contracts code's size. Returns zero code
|
||||
// size if the requested contract code doesn't exist.
|
||||
CodeSize(addr common.Address, codeHash common.Hash) int
|
||||
}
|
||||
|
||||
// StateReader defines the interface for accessing accounts and storage slots
|
||||
|
|
@ -90,86 +81,6 @@ type Reader interface {
|
|||
StateReader
|
||||
}
|
||||
|
||||
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
||||
// local key-value store or the shared code cache.
|
||||
//
|
||||
// cachingCodeReader is safe for concurrent access.
|
||||
type cachingCodeReader struct {
|
||||
db ethdb.KeyValueReader
|
||||
|
||||
// These caches could be shared by multiple code reader instances,
|
||||
// they are natively thread-safe.
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
|
||||
// Cache statistics
|
||||
hit atomic.Int64 // Number of code lookups found in the cache
|
||||
miss atomic.Int64 // Number of code lookups not found in the cache
|
||||
hitBytes atomic.Int64 // Total number of bytes read from cache
|
||||
missBytes atomic.Int64 // Total number of bytes read from database
|
||||
}
|
||||
|
||||
// newCachingCodeReader constructs the code reader.
|
||||
func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstrainedCache[common.Hash, []byte], codeSizeCache *lru.Cache[common.Hash, int]) *cachingCodeReader {
|
||||
return &cachingCodeReader{
|
||||
db: db,
|
||||
codeCache: codeCache,
|
||||
codeSizeCache: codeSizeCache,
|
||||
}
|
||||
}
|
||||
|
||||
// Code implements ContractCodeReader, retrieving a particular contract's code.
|
||||
// If the contract code doesn't exist, no error will be returned.
|
||||
func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := r.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
r.hitBytes.Add(int64(len(code)))
|
||||
return code, nil
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCode(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.codeCache.Add(codeHash, code)
|
||||
r.codeSizeCache.Add(codeHash, len(code))
|
||||
r.missBytes.Add(int64(len(code)))
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// CodeSize implements ContractCodeReader, retrieving a particular contracts code's size.
|
||||
// If the contract code doesn't exist, no error will be returned.
|
||||
func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
||||
r.hit.Add(1)
|
||||
return cached, nil
|
||||
}
|
||||
code, err := r.Code(addr, codeHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(code), nil
|
||||
}
|
||||
|
||||
// Has implements ContractCodeReader, returning the flag indicating whether
|
||||
// the contract code with specified address and hash exists or not.
|
||||
func (r *cachingCodeReader) Has(addr common.Address, codeHash common.Hash) bool {
|
||||
code, _ := r.Code(addr, codeHash)
|
||||
return len(code) > 0
|
||||
}
|
||||
|
||||
// GetCodeStats implements ContractCodeReaderStater, returning the statistics
|
||||
// of the code reader.
|
||||
func (r *cachingCodeReader) GetCodeStats() ContractCodeReaderStats {
|
||||
return ContractCodeReaderStats{
|
||||
CacheHit: r.hit.Load(),
|
||||
CacheMiss: r.miss.Load(),
|
||||
CacheHitBytes: r.hitBytes.Load(),
|
||||
CacheMissBytes: r.missBytes.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||
type flatReader struct {
|
||||
reader database.StateReader
|
||||
|
|
|
|||
|
|
@ -551,10 +551,7 @@ func (s *stateObject) Code() []byte {
|
|||
s.db.CodeLoadBytes += len(s.code)
|
||||
}(time.Now())
|
||||
|
||||
code, err := s.db.reader.Code(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
code := s.db.reader.Code(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if len(code) == 0 {
|
||||
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
|
||||
}
|
||||
|
|
@ -577,10 +574,7 @@ func (s *stateObject) CodeSize() int {
|
|||
s.db.CodeReads += time.Since(start)
|
||||
}(time.Now())
|
||||
|
||||
size, err := s.db.reader.CodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
size := s.db.reader.CodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if size == 0 {
|
||||
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
|
|
@ -148,8 +147,7 @@ type StateDB struct {
|
|||
StorageReads time.Duration
|
||||
StorageUpdates time.Duration
|
||||
StorageCommits time.Duration
|
||||
SnapshotCommits time.Duration
|
||||
TrieDBCommits time.Duration
|
||||
DatabaseCommits time.Duration
|
||||
CodeReads time.Duration
|
||||
|
||||
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||
|
|
@ -1333,41 +1331,14 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
// Commit dirty contract code if any exists
|
||||
if db := s.db.TrieDB().Disk(); db != nil && len(ret.codes) > 0 {
|
||||
batch := db.NewBatch()
|
||||
for _, code := range ret.codes {
|
||||
rawdb.WriteCode(batch, code.hash, code.blob)
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !ret.empty() {
|
||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil {
|
||||
start := time.Now()
|
||||
if err := snap.Update(ret.root, ret.originRoot, ret.accounts, ret.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
// - head layer is paired with HEAD state
|
||||
// - head-1 layer is paired with HEAD-1 state
|
||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||
if err := snap.Cap(ret.root, TriesInMemory); err != nil {
|
||||
log.Warn("Failed to cap snapshot tree", "root", ret.root, "layers", TriesInMemory, "err", err)
|
||||
}
|
||||
s.SnapshotCommits += time.Since(start)
|
||||
}
|
||||
// If trie database is enabled, commit the state update as a new layer
|
||||
if db := s.db.TrieDB(); db != nil {
|
||||
start := time.Now()
|
||||
if err := db.Update(ret.root, ret.originRoot, block, ret.nodes, ret.stateSet()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.TrieDBCommits += time.Since(start)
|
||||
}
|
||||
start := time.Now()
|
||||
if err := s.db.Commit(ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.DatabaseCommits = time.Since(start)
|
||||
|
||||
// The reader update must be performed as the final step, otherwise,
|
||||
// the new state would not be visible before db.commit.
|
||||
s.reader, _ = s.db.Reader(s.originalRoot)
|
||||
return ret, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func (test *stateTest) run() bool {
|
|||
if i != 0 {
|
||||
root = roots[len(roots)-1]
|
||||
}
|
||||
state, err := New(root, NewDatabase(tdb, snaps))
|
||||
state, err := New(root, NewDatabase(tdb, nil).WithSnapshot(snaps))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1276,7 +1276,7 @@ func TestDeleteStorage(t *testing.T) {
|
|||
disk = rawdb.NewMemoryDatabase()
|
||||
tdb = triedb.NewDatabase(disk, nil)
|
||||
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
|
||||
db = NewDatabase(tdb, snaps)
|
||||
db = NewDatabase(tdb, nil).WithSnapshot(snaps)
|
||||
state, _ = New(types.EmptyRootHash, db)
|
||||
addr = common.HexToAddress("0x1")
|
||||
)
|
||||
|
|
@ -1290,7 +1290,7 @@ func TestDeleteStorage(t *testing.T) {
|
|||
}
|
||||
root, _ := state.Commit(0, true, false)
|
||||
// Init phase done, create two states, one with snap and one without
|
||||
fastState, _ := New(root, NewDatabase(tdb, snaps))
|
||||
fastState, _ := New(root, NewDatabase(tdb, nil).WithSnapshot(snaps))
|
||||
slowState, _ := New(root, NewDatabase(tdb, nil))
|
||||
|
||||
obj := fastState.getOrNewStateObject(addr)
|
||||
|
|
|
|||
|
|
@ -211,9 +211,9 @@ func (sc *stateUpdate) deriveCodeFields(reader ContractCodeReader) error {
|
|||
cache := make(map[common.Hash]bool)
|
||||
for addr, code := range sc.codes {
|
||||
if code.originHash != types.EmptyCodeHash {
|
||||
blob, err := reader.Code(addr, code.originHash)
|
||||
if err != nil {
|
||||
return err
|
||||
blob := reader.Code(addr, code.originHash)
|
||||
if len(blob) == 0 {
|
||||
return fmt.Errorf("original code of %x is empty", addr)
|
||||
}
|
||||
code.originBlob = blob
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s
|
|||
codeResults = make([]trie.CodeSyncResult, len(codeElements))
|
||||
)
|
||||
for i, element := range codeElements {
|
||||
data, err := cReader.Code(common.Address{}, element.code)
|
||||
if err != nil || len(data) == 0 {
|
||||
data := cReader.Code(common.Address{}, element.code)
|
||||
if len(data) == 0 {
|
||||
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
|
||||
}
|
||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
||||
|
|
@ -346,8 +346,8 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) {
|
|||
if len(codeElements) > 0 {
|
||||
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
|
||||
for i, element := range codeElements[:len(codeResults)] {
|
||||
data, err := cReader.Code(common.Address{}, element.code)
|
||||
if err != nil || len(data) == 0 {
|
||||
data := cReader.Code(common.Address{}, element.code)
|
||||
if len(data) == 0 {
|
||||
t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
|
||||
}
|
||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
||||
|
|
@ -452,8 +452,8 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) {
|
|||
if len(codeQueue) > 0 {
|
||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||
for hash := range codeQueue {
|
||||
data, err := cReader.Code(common.Address{}, hash)
|
||||
if err != nil || len(data) == 0 {
|
||||
data := cReader.Code(common.Address{}, hash)
|
||||
if len(data) == 0 {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||
|
|
@ -551,8 +551,8 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
|
|||
for hash := range codeQueue {
|
||||
delete(codeQueue, hash)
|
||||
|
||||
data, err := cReader.Code(common.Address{}, hash)
|
||||
if err != nil || len(data) == 0 {
|
||||
data := cReader.Code(common.Address{}, hash)
|
||||
if len(data) == 0 {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||
|
|
@ -671,8 +671,8 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
|
|||
if len(codeQueue) > 0 {
|
||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||
for hash := range codeQueue {
|
||||
data, err := cReader.Code(common.Address{}, hash)
|
||||
if err != nil || len(data) == 0 {
|
||||
data := cReader.Code(common.Address{}, hash)
|
||||
if len(data) == 0 {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ func createMiner(t *testing.T) *Miner {
|
|||
if err != nil {
|
||||
t.Fatalf("can't create new chain %v", err)
|
||||
}
|
||||
statedb, _ := state.New(bc.Genesis().Root(), bc.StateCache())
|
||||
statedb, _ := state.New(bc.Genesis().Root(), state.NewDatabase(bc.TrieDB(), bc.CodeDB()))
|
||||
blockchain := &testBlockChain{bc.Genesis().Root(), chainConfig, statedb, 10000000, new(event.Feed)}
|
||||
|
||||
pool := legacypool.New(testTxPoolConfig, blockchain)
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo
|
|||
}
|
||||
snaps, _ = snapshot.New(snapconfig, db, triedb, root)
|
||||
}
|
||||
sdb = state.NewDatabase(triedb, snaps)
|
||||
sdb = state.NewDatabase(triedb, nil).WithSnapshot(snaps)
|
||||
statedb, _ = state.New(root, sdb)
|
||||
return StateTestState{statedb, triedb, snaps}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -612,6 +612,9 @@ func (db *Database) Close() error {
|
|||
// NodeReader returns a reader for accessing trie nodes within the specified state.
|
||||
// An error will be returned if the specified state is not available.
|
||||
func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) {
|
||||
if root == types.EmptyRootHash {
|
||||
return &reader{db: db}, nil
|
||||
}
|
||||
if _, err := db.node(root); err != nil {
|
||||
return nil, fmt.Errorf("state %#x is not available, %v", root, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue