all: implement codedb

This commit is contained in:
Gary Rong 2025-11-04 13:49:37 +08:00
parent eaaa5b716d
commit 336030d35a
31 changed files with 392 additions and 283 deletions

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -379,7 +380,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool) *state.StateDB {
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsVerkle: isBintrie})
sdb := state.NewDatabase(tdb, nil)
sdb := state.NewDatabase(tdb, codedb.New(db))
root := types.EmptyRootHash
if isBintrie {

View file

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -226,8 +227,9 @@ func runCmd(ctx *cli.Context) error {
HashDB: hashdb.Defaults,
})
defer triedb.Close()
genesis := genesisConfig.MustCommit(db, triedb)
sdb := state.NewDatabase(triedb, nil)
sdb := state.NewDatabase(triedb, codedb.New(db))
prestate, _ = state.New(genesis.Root(), sdb)
chainConfig = genesisConfig.Config

View file

@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@ -650,7 +651,7 @@ func dump(ctx *cli.Context) error {
triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup
defer triedb.Close()
state, err := state.New(root, state.NewDatabase(triedb, nil))
state, err := state.New(root, state.NewDatabase(triedb, codedb.New(db)))
if err != nil {
return err
}

View file

@ -27,6 +27,7 @@ import (
"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/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -220,7 +221,7 @@ func TestProcessParentBlockHash(t *testing.T) {
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
cacheConfig.SnapshotLimit = 0
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, codedb.New(db)))
checkBlockHashes(statedb, true)
})
}

View file

@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/tracing"
@ -76,6 +77,7 @@ var (
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
codeReadTimer = metrics.NewRegisteredResettingTimer("chain/code/reads", nil)
databaseCommitTimer = metrics.NewRegisteredResettingTimer("chain/database/commits", nil)
accountCacheHitMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/hit", nil)
accountCacheMissMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/miss", nil)
@ -91,9 +93,6 @@ var (
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)
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
blockCrossValidationTimer = metrics.NewRegisteredResettingTimer("chain/crossvalidation", nil)
@ -302,7 +301,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 *codedb.Database // The database handler for maintaining contract codes
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
hc *HeaderChain
@ -384,6 +383,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
cfg: cfg,
db: db,
triedb: triedb,
codedb: codedb.New(db),
triegc: prque.New[int64, common.Hash](nil),
chainmu: syncx.NewClosableMutex(),
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
@ -400,7 +400,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)
@ -577,9 +576,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)
}
}
@ -2055,11 +2051,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
}
@ -2069,15 +2066,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
}
@ -2243,9 +2240,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.DBCommit = statedb.DatabaseCommits // Trie 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

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -371,7 +372,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 +404,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 +414,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 +445,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 +488,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() *codedb.Database {
return bc.codedb
}
// HeaderChain returns the underlying header chain.
func (bc *BlockChain) HeaderChain() *HeaderChain {
return bc.hc

View file

@ -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 {

View file

@ -50,8 +50,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
DBCommit 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
@ -84,8 +83,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
databaseCommitTimer.Update(s.DBCommit) // 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
@ -148,9 +146,9 @@ State write: %v
common.PrettyDuration(s.CodeReads), s.CodeLoaded,
// State write
common.PrettyDuration(max(s.AccountCommits, s.StorageCommits)+s.TrieDBCommit+s.SnapshotCommit+s.BlockWrite),
common.PrettyDuration(max(s.AccountCommits, s.StorageCommits)+s.DBCommit+s.BlockWrite),
common.PrettyDuration(max(s.AccountCommits, s.StorageCommits)),
common.PrettyDuration(s.TrieDBCommit+s.SnapshotCommit),
common.PrettyDuration(s.DBCommit),
common.PrettyDuration(s.BlockWrite),
// cache statistics

View file

@ -156,7 +156,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
}
return err
}
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
sdb := state.NewDatabase(blockchain.triedb, blockchain.codedb)
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), sdb)
if err != nil {
return err
}

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
@ -432,9 +433,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
triedb := triedb.NewDatabase(db, triedbConfig)
defer triedb.Close()
codedb := codedb.New(db)
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), state.NewDatabase(triedb, nil))
statedb, err := state.New(parent.Root(), state.NewDatabase(triedb, codedb))
if err != nil {
panic(err)
}

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@ -145,7 +146,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
emptyRoot = types.EmptyVerkleHash
}
db := rawdb.NewMemoryDatabase()
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil))
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), codedb.New(db)))
if err != nil {
return common.Hash{}, err
}
@ -164,12 +165,12 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
// flushAlloc is very similar with hash, but the main difference is all the
// generated states will be persisted into the given database.
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, error) {
func flushAlloc(ga *types.GenesisAlloc, db ethdb.KeyValueStore, triedb *triedb.Database) (common.Hash, error) {
emptyRoot := types.EmptyRootHash
if triedb.IsVerkle() {
emptyRoot = types.EmptyVerkleHash
}
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, nil))
statedb, err := state.New(emptyRoot, state.NewDatabase(triedb, codedb.New(db)))
if err != nil {
return common.Hash{}, err
}
@ -552,7 +553,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
return nil, errors.New("can't start clique chain without signers")
}
// flush the data to disk and compute the state root
root, err := flushAlloc(&g.Alloc, triedb)
root, err := flushAlloc(&g.Alloc, db, triedb)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,189 @@
// Copyright 2025 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 codedb
import (
"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
)
// Cache 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 Cache struct {
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
codeSizeCache *lru.Cache[common.Hash, int]
}
// NewCache initializes the contract code cache with the predefined capacity.
func NewCache() *Cache {
return &Cache{
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 *Cache) 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 *Cache) 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 *Cache) Put(hash common.Hash, code []byte) {
c.codeCache.Add(hash, code)
c.codeSizeCache.Add(hash, len(code))
}
// Reader implements state.ContractCodeReader, accessing contract code either in
// local key-value store or the shared code cache.
//
// Reader is safe for concurrent access.
type Reader struct {
db ethdb.KeyValueReader
cache *Cache
}
// newReader constructs the code reader with provided key value store and the cache.
func newReader(db ethdb.KeyValueReader, cache *Cache) *Reader {
return &Reader{
db: db,
cache: cache,
}
}
// Has returns the flag indicating whether the contract code with
// specified address and hash exists or not.
func (r *Reader) 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 *Reader) Code(addr common.Address, codeHash common.Hash) []byte {
code, _ := r.cache.Get(codeHash)
if len(code) > 0 {
return code
}
code = rawdb.ReadCode(r.db, codeHash)
if len(code) > 0 {
r.cache.Put(codeHash, 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 *Reader) CodeSize(addr common.Address, codeHash common.Hash) int {
if cached, ok := r.cache.GetSize(codeHash); ok {
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 *Reader) CodeWithPrefix(addr common.Address, codeHash common.Hash) []byte {
code, _ := r.cache.Get(codeHash)
if len(code) > 0 {
return code
}
code = rawdb.ReadCodeWithPrefix(r.db, codeHash)
if len(code) > 0 {
r.cache.Put(codeHash, code)
}
return code
}
// Writer implements the state.ContractCodeWriter for committing the dirty contract
// code into database.
type Writer struct {
db *Database
codes [][]byte
codeHashes []common.Hash
}
// newWriter constructs the code writer.
func newWriter(db *Database) *Writer {
return &Writer{
db: db,
}
}
// Put inserts the given contract code into the writer, waiting for commit.
func (w *Writer) Put(codeHash common.Hash, code []byte) {
w.codes = append(w.codes, code)
w.codeHashes = append(w.codeHashes, codeHash)
}
// Commit flushes the accumulated dirty contract code into the database and
// also place them in the cache.
func (w *Writer) Commit() error {
batch := w.db.db.NewBatch()
for i, code := range w.codes {
rawdb.WriteCode(batch, w.codeHashes[i], code)
w.db.cache.Put(w.codeHashes[i], code)
}
if err := batch.Write(); err != nil {
return err
}
w.codes = w.codes[:0]
w.codeHashes = w.codeHashes[:0]
return nil
}
// Database 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 Database struct {
db ethdb.KeyValueStore
cache *Cache
}
// New constructs the contract code database with the provided key value store.
func New(db ethdb.KeyValueStore) *Database {
return &Database{
db: db,
cache: NewCache(),
}
}
// Reader returns the contract code reader.
func (s *Database) Reader() *Reader {
return newReader(s.db, s.cache)
}
// Writer returns the contract code writer.
func (s *Database) Writer() *Writer {
return newWriter(s)
}

View file

@ -23,10 +23,12 @@ import (
"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/codedb"
"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 +36,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 +52,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.
@ -145,28 +144,22 @@ type Trie interface {
IsVerkle() bool
}
// CachingDB is an implementation of Database interface. It leverages both trie and
// 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.
// CachingDB is an implementation of Database interface, providing functionalities
// for state access and update by leveraging configured data storage layers.
type CachingDB struct {
disk ethdb.KeyValueStore
triedb *triedb.Database
codedb *codedb.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]
}
// 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.Database) *CachingDB {
return &CachingDB{
disk: triedb.Disk(),
triedb: triedb,
snap: snap,
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
codedb: codedb,
TransitionStatePerRoot: lru.NewCache[common.Hash, *overlay.TransitionState](1000),
}
}
@ -174,7 +167,15 @@ func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
// 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), codedb.New(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
}
// Reader returns a state reader associated with the specified state root.
@ -212,7 +213,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), combined), nil
return newReader(db.codedb.Reader(), combined), nil
}
// ReadersWithCacheStats creates a pair of state readers sharing the same internal cache and
@ -259,22 +260,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
@ -285,6 +270,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 {
writer := db.codedb.Writer()
for _, code := range update.codes {
writer.Put(code.hash, code.blob)
}
if err := writer.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) {

View file

@ -20,10 +20,9 @@ import (
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
@ -100,19 +99,15 @@ func (r *historicReader) Storage(addr common.Address, key common.Hash) (common.H
// 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]
codedb *codedb.Database
}
// NewHistoricDatabase creates a historic state database.
func NewHistoricDatabase(disk ethdb.KeyValueStore, triedb *triedb.Database) *HistoricDB {
func NewHistoricDatabase(triedb *triedb.Database, codedb *codedb.Database) *HistoricDB {
return &HistoricDB{
disk: disk,
triedb: triedb,
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
codedb: codedb,
}
}
@ -122,7 +117,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), newHistoricReader(hr)), nil
return newReader(db.codedb.Reader(), newHistoricReader(hr)), nil
}
// OpenTrie opens the main account trie. It's not supported by historic database.
@ -145,3 +140,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")
}

View file

@ -23,12 +23,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"
@ -43,19 +40,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
@ -123,63 +114,6 @@ type ReaderWithStats interface {
GetStats() ReaderStats
}
// 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]
}
// 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 {
return code, nil
}
code = rawdb.ReadCode(r.db, codeHash)
if len(code) > 0 {
r.codeCache.Add(codeHash, code)
r.codeSizeCache.Add(codeHash, 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 {
return cached, nil
}
code, err := r.Code(addr, codeHash)
if err != nil {
return 0, err
}
return len(code), nil
}
// Has returns 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
}
// flatReader wraps a database state reader and is safe for concurrent access.
type flatReader struct {
reader database.StateReader

View file

@ -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)
}

View file

@ -537,10 +537,7 @@ func (s *stateObject) Code() []byte {
s.db.CodeReads += time.Since(start)
}(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()))
}
@ -563,10 +560,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()))
}

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/triedb"
@ -34,7 +35,7 @@ func TestSizeTracker(t *testing.T) {
defer db.Close()
tdb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
sdb := NewDatabase(tdb, nil)
sdb := NewDatabase(tdb, codedb.New(db))
// Generate 50 blocks to establish a baseline
baselineBlockNum := uint64(50)
@ -100,7 +101,7 @@ func TestSizeTracker(t *testing.T) {
t.Fatalf("Failed to close triedb before baseline measurement: %v", err)
}
tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
sdb = NewDatabase(tdb, nil)
sdb = NewDatabase(tdb, codedb.New(db))
// Wait for snapshot completion
for !tdb.SnapshotCompleted() {
@ -154,7 +155,7 @@ func TestSizeTracker(t *testing.T) {
if i%3 == 0 {
newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified)
}
ret, err := newState.commitAndFlush(blockNum, true, false, true)
ret, err := newState.commit(true, false, blockNum, true)
if err != nil {
t.Fatalf("Failed to commit state at block %d: %v", blockNum, err)
}

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/triedb"
@ -41,7 +42,7 @@ func newStateEnv() *stateEnv {
func TestDump(t *testing.T) {
db := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
tdb := NewDatabase(triedb, nil)
tdb := NewDatabase(triedb, codedb.New(db))
sdb, _ := New(types.EmptyRootHash, tdb)
s := &stateEnv{state: sdb}
@ -97,7 +98,7 @@ func TestDump(t *testing.T) {
func TestIterativeDump(t *testing.T) {
db := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
tdb := NewDatabase(triedb, nil)
tdb := NewDatabase(triedb, codedb.New(db))
sdb, _ := New(types.EmptyRootHash, tdb)
s := &stateEnv{state: sdb}

View file

@ -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,9 +147,8 @@ type StateDB struct {
StorageReads time.Duration
StorageUpdates time.Duration
StorageCommits time.Duration
SnapshotCommits time.Duration
TrieDBCommits time.Duration
CodeReads time.Duration
DatabaseCommits time.Duration
AccountLoaded int // Number of accounts retrieved from the database during the state transition
AccountUpdated int // Number of accounts updated during the state transition
@ -1161,7 +1159,7 @@ func (s *StateDB) GetTrie() Trie {
// commit gathers the state mutations accumulated along with the associated
// trie changes, resetting all internal flags with the new state as the base.
func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNumber uint64) (*stateUpdate, error) {
func (s *StateDB) commit(deleteEmptyObjects bool, rawStorageKey bool, blockNumber uint64, dedupCode bool) (*stateUpdate, error) {
// Short circuit in case any database failure occurred earlier.
if s.dbErr != nil {
return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
@ -1215,7 +1213,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
// the same block, account deletions must be processed first. This ensures
// that the storage trie nodes deleted during destruction and recreated
// during subsequent resurrection can be combined correctly.
deletes, delNodes, err := s.handleDestruction(noStorageWiping)
deletes, delNodes, err := s.handleDestruction(rawStorageKey)
if err != nil {
return nil, err
}
@ -1313,58 +1311,20 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
origin := s.originalRoot
s.originalRoot = root
return newStateUpdate(noStorageWiping, origin, root, blockNumber, deletes, updates, nodes), nil
}
// commitAndFlush is a wrapper of commit which also commits the state mutations
// to the configured data stores.
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool, dedupCode bool) (*stateUpdate, error) {
ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block)
if err != nil {
return nil, err
}
start = time.Now()
update := newStateUpdate(rawStorageKey, origin, root, blockNumber, deletes, updates, nodes)
if dedupCode {
ret.markCodeExistence(s.reader)
update.markCodeExistence(s.reader)
}
if err := s.db.Commit(update); err != nil {
return nil, err
}
s.DatabaseCommits = time.Since(start)
// 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)
}
}
s.reader, _ = s.db.Reader(s.originalRoot)
return ret, err
// 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(root)
return update, nil
}
// Commit writes the state mutations into the configured data stores.
@ -1381,8 +1341,8 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag
// Since self-destruction was deprecated with the Cancun fork and there are
// no empty accounts left that could be deleted by EIP-158, storage wiping
// should not occur.
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (common.Hash, error) {
ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping, false)
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, rawStorageKey bool) (common.Hash, error) {
ret, err := s.commit(deleteEmptyObjects, rawStorageKey, block, false)
if err != nil {
return common.Hash{}, err
}
@ -1390,8 +1350,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping
}
// CommitAndTrack writes the state mutations and notifies the size tracker of the state changes.
func (s *StateDB) CommitAndTrack(block uint64, deleteEmptyObjects bool, noStorageWiping bool, sizer *SizeTracker) (common.Hash, error) {
ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping, true)
func (s *StateDB) CommitAndTrack(block uint64, deleteEmptyObjects bool, rawStorageKey bool, sizer *SizeTracker) (common.Hash, error) {
ret, err := s.commit(deleteEmptyObjects, rawStorageKey, block, true)
if err != nil {
return common.Hash{}, err
}

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
@ -209,7 +210,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, codedb.New(disk)).WithSnapshot(snaps))
if err != nil {
panic(err)
}
@ -228,7 +229,7 @@ func (test *stateTest) run() bool {
} else {
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
}
ret, err := state.commitAndFlush(0, true, false, false) // call commit at the block boundary
ret, err := state.commit(true, false, 0, false) // call commit at the block boundary
if err != nil {
panic(err)
}

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
@ -52,7 +53,7 @@ func TestUpdateLeaks(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(db, nil)
sdb = NewDatabase(tdb, nil)
sdb = NewDatabase(tdb, codedb.New(db))
)
state, _ := New(types.EmptyRootHash, sdb)
@ -90,8 +91,8 @@ func TestIntermediateLeaks(t *testing.T) {
finalDb := rawdb.NewMemoryDatabase()
transNdb := triedb.NewDatabase(transDb, nil)
finalNdb := triedb.NewDatabase(finalDb, nil)
transState, _ := New(types.EmptyRootHash, NewDatabase(transNdb, nil))
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalNdb, nil))
transState, _ := New(types.EmptyRootHash, NewDatabase(transNdb, codedb.New(transDb)))
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalNdb, codedb.New(finalDb)))
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)), tracing.BalanceChangeUnspecified)
@ -980,7 +981,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
CleanCacheSize: 0,
}}) // disable caching
}
db := NewDatabase(tdb, nil)
db := NewDatabase(tdb, codedb.New(memDb))
var root common.Hash
state, _ := New(types.EmptyRootHash, db)
@ -1204,7 +1205,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
var (
memdb = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(memdb, triedb.HashDefaults)
statedb = NewDatabase(tdb, nil)
statedb = NewDatabase(tdb, codedb.New(memdb))
state, _ = New(types.EmptyRootHash, statedb)
)
for a := byte(0); a < 10; a++ {
@ -1225,7 +1226,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
t.Fatalf("failed to commit state trie: %v", err)
}
// Reopen the state trie from flushed disk and verify it
state, err = New(root, NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil))
state, err = New(root, NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), codedb.New(memdb)))
if err != nil {
t.Fatalf("failed to reopen state trie: %v", err)
}
@ -1276,7 +1277,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, codedb.New(disk)).WithSnapshot(snaps)
state, _ = New(types.EmptyRootHash, db)
addr = common.HexToAddress("0x1")
)
@ -1289,9 +1290,10 @@ func TestDeleteStorage(t *testing.T) {
state.SetState(addr, slot, value)
}
root, _ := state.Commit(0, true, false)
// Init phase done, create two states, one with snap and one without
fastState, _ := New(root, NewDatabase(tdb, snaps))
slowState, _ := New(root, NewDatabase(tdb, nil))
fastState, _ := New(root, NewDatabase(tdb, codedb.New(disk)).WithSnapshot(snaps))
slowState, _ := New(root, NewDatabase(tdb, codedb.New(disk)))
obj := fastState.getOrNewStateObject(addr)
storageRoot := obj.data.Root
@ -1329,7 +1331,7 @@ func TestStorageDirtiness(t *testing.T) {
var (
disk = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(disk, nil)
db = NewDatabase(tdb, nil)
db = NewDatabase(tdb, codedb.New(disk))
state, _ = New(types.EmptyRootHash, db)
addr = common.HexToAddress("0x1")
checkDirty = func(key common.Hash, value common.Hash, dirty bool) {

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@ -54,7 +55,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c
}
db := rawdb.NewMemoryDatabase()
nodeDb := triedb.NewDatabase(db, config)
sdb := NewDatabase(nodeDb, nil)
sdb := NewDatabase(nodeDb, codedb.New(db))
state, _ := New(types.EmptyRootHash, sdb)
// Fill it with some arbitrary data
@ -95,7 +96,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root com
config.PathDB = pathdb.Defaults
}
// Check root availability and state contents
state, err := New(root, NewDatabase(triedb.NewDatabase(db, &config), nil))
state, err := New(root, NewDatabase(triedb.NewDatabase(db, &config), codedb.New(db)))
if err != nil {
t.Fatalf("failed to create state trie at %x: %v", root, err)
}
@ -121,7 +122,7 @@ func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) e
if scheme == rawdb.PathScheme {
config.PathDB = pathdb.Defaults
}
state, err := New(root, NewDatabase(triedb.NewDatabase(db, config), nil))
state, err := New(root, NewDatabase(triedb.NewDatabase(db, config), codedb.New(db)))
if err != nil {
return err
}
@ -222,8 +223,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 +347,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 +453,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 +552,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 +672,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})

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@ -69,7 +70,7 @@ func TestUseAfterTerminate(t *testing.T) {
func TestVerklePrefetcher(t *testing.T) {
disk := rawdb.NewMemoryDatabase()
db := triedb.NewDatabase(disk, triedb.VerkleDefaults)
sdb := NewDatabase(db, nil)
sdb := NewDatabase(db, codedb.New(disk))
state, err := New(types.EmptyRootHash, sdb)
if err != nil {

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -51,7 +52,7 @@ func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *typ
}
// Create and populate the state database to serve as the stateless backend
memdb := witness.MakeHashDB()
db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil))
db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), codedb.New(memdb)))
if err != nil {
return common.Hash{}, common.Hash{}, err
}

View file

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@ -113,7 +114,7 @@ func TestAccountRange(t *testing.T) {
var (
mdb = rawdb.NewMemoryDatabase()
statedb = state.NewDatabase(triedb.NewDatabase(mdb, &triedb.Config{Preimages: true}), nil)
statedb = state.NewDatabase(triedb.NewDatabase(mdb, &triedb.Config{Preimages: true}), codedb.New(mdb))
sdb, _ = state.New(types.EmptyRootHash, statedb)
addrs = [AccountRangeMaxResults * 2]common.Address{}
m = map[common.Address]bool{}
@ -212,7 +213,7 @@ func TestStorageRangeAt(t *testing.T) {
var (
mdb = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(mdb, &triedb.Config{Preimages: true})
db = state.NewDatabase(tdb, nil)
db = state.NewDatabase(tdb, codedb.New(mdb))
sdb, _ = state.New(types.EmptyRootHash, db)
addr = common.Address{0x01}
keys = []common.Hash{ // hashes of Keys of storage

View file

@ -69,7 +69,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance.
tdb := triedb.NewDatabase(eth.chainDb, triedb.HashDefaults)
database = state.NewDatabase(tdb, nil)
database = state.NewDatabase(tdb, eth.blockchain.CodeDB())
if statedb, err = state.New(block.Root(), database); err == nil {
log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
return statedb, noopReleaser, nil
@ -87,7 +87,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance.
tdb = triedb.NewDatabase(eth.chainDb, triedb.HashDefaults)
database = state.NewDatabase(tdb, nil)
database = state.NewDatabase(tdb, eth.blockchain.CodeDB())
// If we didn't check the live database, do check state over ephemeral database,
// otherwise we would rewind past a persisted block (specific corner case is

View file

@ -22,11 +22,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"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/core/vm"
"github.com/ethereum/go-ethereum/triedb"
)
type precompileContract struct{}
@ -40,7 +38,7 @@ func (p *precompileContract) Name() string {
}
func TestStateOverrideMovePrecompile(t *testing.T) {
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
db := state.NewDatabaseForTesting()
statedb, err := state.New(types.EmptyRootHash, db)
if err != nil {
t.Fatalf("failed to create statedb: %v", err)

View file

@ -155,7 +155,8 @@ 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())
sdb := state.NewDatabase(bc.TrieDB(), bc.CodeDB())
statedb, _ := state.New(bc.Genesis().Root(), sdb)
blockchain := &testBlockChain{bc.Genesis().Root(), chainConfig, statedb, 10000000, new(event.Feed)}
pool := legacypool.New(testTxPoolConfig, blockchain)

View file

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/codedb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
@ -508,7 +509,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo
tconf.PathDB = pathdb.Defaults
}
triedb := triedb.NewDatabase(db, tconf)
sdb := state.NewDatabase(triedb, nil)
sdb := state.NewDatabase(triedb, codedb.New(db))
statedb, _ := state.New(types.EmptyRootHash, sdb)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code, tracing.CodeChangeUnspecified)
@ -532,7 +533,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, codedb.New(db)).WithSnapshot(snaps)
statedb, _ = state.New(root, sdb)
return StateTestState{statedb, triedb, snaps}
}

View file

@ -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)
}