mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
all: implement codedb
This commit is contained in:
parent
eaaa5b716d
commit
336030d35a
31 changed files with 392 additions and 283 deletions
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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 {
|
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool) *state.StateDB {
|
||||||
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsVerkle: isBintrie})
|
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
|
root := types.EmptyRootHash
|
||||||
if isBintrie {
|
if isBintrie {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -226,8 +227,9 @@ func runCmd(ctx *cli.Context) error {
|
||||||
HashDB: hashdb.Defaults,
|
HashDB: hashdb.Defaults,
|
||||||
})
|
})
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
genesis := genesisConfig.MustCommit(db, triedb)
|
genesis := genesisConfig.MustCommit(db, triedb)
|
||||||
sdb := state.NewDatabase(triedb, nil)
|
sdb := state.NewDatabase(triedb, codedb.New(db))
|
||||||
prestate, _ = state.New(genesis.Root(), sdb)
|
prestate, _ = state.New(genesis.Root(), sdb)
|
||||||
chainConfig = genesisConfig.Config
|
chainConfig = genesisConfig.Config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/history"
|
"github.com/ethereum/go-ethereum/core/history"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"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
|
triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup
|
||||||
defer triedb.Close()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -220,7 +221,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
||||||
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||||
cacheConfig.SnapshotLimit = 0
|
cacheConfig.SnapshotLimit = 0
|
||||||
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
|
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)
|
checkBlockHashes(statedb, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/history"
|
"github.com/ethereum/go-ethereum/core/history"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -72,10 +73,11 @@ var (
|
||||||
accountUpdateTimer = metrics.NewRegisteredResettingTimer("chain/account/updates", nil)
|
accountUpdateTimer = metrics.NewRegisteredResettingTimer("chain/account/updates", nil)
|
||||||
accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil)
|
accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil)
|
||||||
|
|
||||||
storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil)
|
storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil)
|
||||||
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
||||||
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
||||||
codeReadTimer = metrics.NewRegisteredResettingTimer("chain/code/reads", 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)
|
accountCacheHitMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/hit", nil)
|
||||||
accountCacheMissMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/miss", nil)
|
accountCacheMissMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/miss", nil)
|
||||||
|
|
@ -91,9 +93,6 @@ var (
|
||||||
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
||||||
codeReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/code/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)
|
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||||
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
||||||
blockCrossValidationTimer = metrics.NewRegisteredResettingTimer("chain/crossvalidation", nil)
|
blockCrossValidationTimer = metrics.NewRegisteredResettingTimer("chain/crossvalidation", nil)
|
||||||
|
|
@ -302,7 +301,7 @@ type BlockChain struct {
|
||||||
lastWrite uint64 // Last block when the state was flushed
|
lastWrite uint64 // Last block when the state was flushed
|
||||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||||
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
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
|
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||||
|
|
||||||
hc *HeaderChain
|
hc *HeaderChain
|
||||||
|
|
@ -384,6 +383,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
db: db,
|
db: db,
|
||||||
triedb: triedb,
|
triedb: triedb,
|
||||||
|
codedb: codedb.New(db),
|
||||||
triegc: prque.New[int64, common.Hash](nil),
|
triegc: prque.New[int64, common.Hash](nil),
|
||||||
chainmu: syncx.NewClosableMutex(),
|
chainmu: syncx.NewClosableMutex(),
|
||||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
bc.flushInterval.Store(int64(cfg.TrieTimeLimit))
|
bc.flushInterval.Store(int64(cfg.TrieTimeLimit))
|
||||||
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
|
||||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||||
bc.processor = NewStateProcessor(bc.hc)
|
bc.processor = NewStateProcessor(bc.hc)
|
||||||
|
|
@ -577,9 +576,6 @@ func (bc *BlockChain) setupSnapshot() {
|
||||||
AsyncBuild: !bc.cfg.SnapshotWait,
|
AsyncBuild: !bc.cfg.SnapshotWait,
|
||||||
}
|
}
|
||||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
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()
|
startTime = time.Now()
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
interrupt atomic.Bool
|
interrupt atomic.Bool
|
||||||
|
sdb = state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
|
||||||
)
|
)
|
||||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
if bc.cfg.NoPrefetch {
|
if bc.cfg.NoPrefetch {
|
||||||
statedb, err = state.New(parentRoot, bc.statedb)
|
statedb, err = state.New(parentRoot, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Note: the main processor and prefetcher share the same reader with a local
|
||||||
// cache for mitigating the overhead of state access.
|
// cache for mitigating the overhead of state access.
|
||||||
prefetch, process, err := bc.statedb.ReadersWithCacheStats(parentRoot)
|
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, prefetch)
|
throwaway, err := state.NewWithReader(parentRoot, sdb, prefetch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, err = state.NewWithReader(parentRoot, bc.statedb, process)
|
statedb, err = state.NewWithReader(parentRoot, sdb, process)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -2241,11 +2238,10 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the metrics touched during block commit
|
// Update the metrics touched during block commit
|
||||||
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
|
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.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
|
||||||
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
|
stats.DBCommit = statedb.DatabaseCommits // Trie database 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.DatabaseCommits
|
||||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
|
|
||||||
|
|
||||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||||
stats.TotalTime = elapsed
|
stats.TotalTime = elapsed
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||||
_, err := bc.statedb.OpenTrie(hash)
|
_, err := bc.triedb.NodeReader(hash)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,7 +404,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
||||||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
|
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
|
||||||
// TODO(rjl493456442) The associated account address is also required
|
// TODO(rjl493456442) The associated account address is also required
|
||||||
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
// 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.
|
// 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.
|
// StateAt returns a new mutable state based on a particular point in time.
|
||||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
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.
|
// HistoricState returns a historic state specified by the given root.
|
||||||
// Live states are not available and won't be served, please use `State`
|
// Live states are not available and won't be served, please use `State`
|
||||||
// or `StateAt` instead.
|
// or `StateAt` instead.
|
||||||
func (bc *BlockChain) HistoricState(root common.Hash) (*state.StateDB, error) {
|
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.
|
// Config retrieves the chain's fork configuration.
|
||||||
|
|
@ -444,11 +445,6 @@ func (bc *BlockChain) Processor() Processor {
|
||||||
return bc.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.
|
// GasLimit returns the gas limit of the current HEAD block.
|
||||||
func (bc *BlockChain) GasLimit() uint64 {
|
func (bc *BlockChain) GasLimit() uint64 {
|
||||||
return bc.CurrentBlock().GasLimit
|
return bc.CurrentBlock().GasLimit
|
||||||
|
|
@ -492,6 +488,11 @@ func (bc *BlockChain) TrieDB() *triedb.Database {
|
||||||
return bc.triedb
|
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.
|
// HeaderChain returns the underlying header chain.
|
||||||
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
||||||
return bc.hc
|
return bc.hc
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -2041,7 +2040,6 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
dbconfig.HashDB = hashdb.Defaults
|
dbconfig.HashDB = hashdb.Defaults
|
||||||
}
|
}
|
||||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||||
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
|
||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ type ExecuteStats struct {
|
||||||
Execution time.Duration // Time spent on the EVM execution
|
Execution time.Duration // Time spent on the EVM execution
|
||||||
Validation time.Duration // Time spent on the block validation
|
Validation time.Duration // Time spent on the block validation
|
||||||
CrossValidation time.Duration // Optional, time spent on the block cross validation
|
CrossValidation time.Duration // Optional, time spent on the block cross validation
|
||||||
SnapshotCommit time.Duration // Time spent on snapshot commit
|
DBCommit time.Duration // Time spent on database commit
|
||||||
TrieDBCommit time.Duration // Time spent on database commit
|
|
||||||
BlockWrite time.Duration // Time spent on block write
|
BlockWrite time.Duration // Time spent on block write
|
||||||
TotalTime time.Duration // The total time spent on block execution
|
TotalTime time.Duration // The total time spent on block execution
|
||||||
MgasPerSecond float64 // The million gas processed per second
|
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
|
blockExecutionTimer.Update(s.Execution) // The time spent on EVM processing
|
||||||
blockValidationTimer.Update(s.Validation) // The time spent on block validation
|
blockValidationTimer.Update(s.Validation) // The time spent on block validation
|
||||||
blockCrossValidationTimer.Update(s.CrossValidation) // The time spent on stateless cross validation
|
blockCrossValidationTimer.Update(s.CrossValidation) // The time spent on stateless cross validation
|
||||||
snapshotCommitTimer.Update(s.SnapshotCommit) // Snapshot commits are complete, we can mark them
|
databaseCommitTimer.Update(s.DBCommit) // Trie database commits are complete, we can mark them
|
||||||
triedbCommitTimer.Update(s.TrieDBCommit) // Trie database commits are complete, we can mark them
|
|
||||||
blockWriteTimer.Update(s.BlockWrite) // The time spent on block write
|
blockWriteTimer.Update(s.BlockWrite) // The time spent on block write
|
||||||
blockInsertTimer.Update(s.TotalTime) // The total time spent on block execution
|
blockInsertTimer.Update(s.TotalTime) // The total time spent on block execution
|
||||||
chainMgaspsMeter.Update(time.Duration(s.MgasPerSecond)) // TODO(rjl493456442) generalize the ResettingTimer
|
chainMgaspsMeter.Update(time.Duration(s.MgasPerSecond)) // TODO(rjl493456442) generalize the ResettingTimer
|
||||||
|
|
@ -148,9 +146,9 @@ State write: %v
|
||||||
common.PrettyDuration(s.CodeReads), s.CodeLoaded,
|
common.PrettyDuration(s.CodeReads), s.CodeLoaded,
|
||||||
|
|
||||||
// State write
|
// 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(max(s.AccountCommits, s.StorageCommits)),
|
||||||
common.PrettyDuration(s.TrieDBCommit+s.SnapshotCommit),
|
common.PrettyDuration(s.DBCommit),
|
||||||
common.PrettyDuration(s.BlockWrite),
|
common.PrettyDuration(s.BlockWrite),
|
||||||
|
|
||||||
// cache statistics
|
// cache statistics
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
}
|
}
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"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)
|
triedb := triedb.NewDatabase(db, triedbConfig)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
codedb := codedb.New(db)
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -145,7 +146,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
emptyRoot = types.EmptyVerkleHash
|
emptyRoot = types.EmptyVerkleHash
|
||||||
}
|
}
|
||||||
db := rawdb.NewMemoryDatabase()
|
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 {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
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
|
// flushAlloc is very similar with hash, but the main difference is all the
|
||||||
// generated states will be persisted into the given database.
|
// 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
|
emptyRoot := types.EmptyRootHash
|
||||||
if triedb.IsVerkle() {
|
if triedb.IsVerkle() {
|
||||||
emptyRoot = types.EmptyVerkleHash
|
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 {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
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")
|
return nil, errors.New("can't start clique chain without signers")
|
||||||
}
|
}
|
||||||
// flush the data to disk and compute the state root
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
189
core/state/codedb/database.go
Normal file
189
core/state/codedb/database.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -23,10 +23,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/core/overlay"
|
"github.com/ethereum/go-ethereum/core/overlay"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||||
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
||||||
|
|
@ -34,14 +36,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"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.
|
// Database wraps access to tries and contract code.
|
||||||
type Database interface {
|
type Database interface {
|
||||||
// Reader returns a state reader associated with the specified state root.
|
// 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 returns the underlying state snapshot.
|
||||||
Snapshot() *snapshot.Tree
|
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.
|
// Trie is a Ethereum Merkle Patricia trie.
|
||||||
|
|
@ -145,28 +144,22 @@ type Trie interface {
|
||||||
IsVerkle() bool
|
IsVerkle() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CachingDB is an implementation of Database interface. It leverages both trie and
|
// CachingDB is an implementation of Database interface, providing functionalities
|
||||||
// state snapshot to provide functionalities for state access. It's meant to be a
|
// for state access and update by leveraging configured data storage layers.
|
||||||
// long-live object and has a few caches inside for sharing between blocks.
|
|
||||||
type CachingDB struct {
|
type CachingDB struct {
|
||||||
disk ethdb.KeyValueStore
|
triedb *triedb.Database
|
||||||
triedb *triedb.Database
|
codedb *codedb.Database
|
||||||
snap *snapshot.Tree
|
snap *snapshot.Tree
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
|
||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
|
||||||
|
|
||||||
// Transition-specific fields
|
// Transition-specific fields
|
||||||
TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState]
|
TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a state database with the provided data sources.
|
// 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{
|
return &CachingDB{
|
||||||
disk: triedb.Disk(),
|
|
||||||
triedb: triedb,
|
triedb: triedb,
|
||||||
snap: snap,
|
codedb: codedb,
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
||||||
TransitionStatePerRoot: lru.NewCache[common.Hash, *overlay.TransitionState](1000),
|
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
|
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||||
// db by using an ephemeral memory db with default config for testing.
|
// db by using an ephemeral memory db with default config for testing.
|
||||||
func NewDatabaseForTesting() *CachingDB {
|
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.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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
|
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.
|
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||||
func (db *CachingDB) TrieDB() *triedb.Database {
|
func (db *CachingDB) TrieDB() *triedb.Database {
|
||||||
return db.triedb
|
return db.triedb
|
||||||
|
|
@ -285,6 +270,40 @@ func (db *CachingDB) Snapshot() *snapshot.Tree {
|
||||||
return db.snap
|
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.
|
// mustCopyTrie returns a deep-copied trie.
|
||||||
func mustCopyTrie(t Trie) Trie {
|
func mustCopyTrie(t Trie) Trie {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
"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
|
// HistoricDB is the implementation of Database interface, with the ability to
|
||||||
// access historical state.
|
// access historical state.
|
||||||
type HistoricDB struct {
|
type HistoricDB struct {
|
||||||
disk ethdb.KeyValueStore
|
triedb *triedb.Database
|
||||||
triedb *triedb.Database
|
codedb *codedb.Database
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
|
||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHistoricDatabase creates a historic state 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{
|
return &HistoricDB{
|
||||||
disk: disk,
|
triedb: triedb,
|
||||||
triedb: triedb,
|
codedb: codedb,
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +117,7 @@ func (db *HistoricDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// 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 {
|
func (db *HistoricDB) Snapshot() *snapshot.Tree {
|
||||||
return nil
|
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")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,9 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/overlay"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||||
|
|
@ -43,19 +40,13 @@ type ContractCodeReader interface {
|
||||||
// specified address and hash exists or not.
|
// specified address and hash exists or not.
|
||||||
Has(addr common.Address, codeHash common.Hash) bool
|
Has(addr common.Address, codeHash common.Hash) bool
|
||||||
|
|
||||||
// Code retrieves a particular contract's code.
|
// Code retrieves a particular contract's code. Returns nil code if the
|
||||||
//
|
// requested contract code doesn't exist
|
||||||
// - Returns nil code along with nil error if the requested contract code
|
Code(addr common.Address, codeHash common.Hash) []byte
|
||||||
// doesn't exist
|
|
||||||
// - Returns an error only if an unexpected issue occurs
|
|
||||||
Code(addr common.Address, codeHash common.Hash) ([]byte, error)
|
|
||||||
|
|
||||||
// CodeSize retrieves a particular contracts code's size.
|
// CodeSize retrieves a particular contracts code's size. Returns zero
|
||||||
//
|
// code size if the requested contract code doesn't exist
|
||||||
// - Returns zero code size along with nil error if the requested contract code
|
CodeSize(addr common.Address, codeHash common.Hash) int
|
||||||
// doesn't exist
|
|
||||||
// - Returns an error only if an unexpected issue occurs
|
|
||||||
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateReader defines the interface for accessing accounts and storage slots
|
// StateReader defines the interface for accessing accounts and storage slots
|
||||||
|
|
@ -123,63 +114,6 @@ type ReaderWithStats interface {
|
||||||
GetStats() ReaderStats
|
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.
|
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||||
type flatReader struct {
|
type flatReader struct {
|
||||||
reader database.StateReader
|
reader database.StateReader
|
||||||
|
|
@ -144,10 +144,7 @@ func (it *nodeIterator) step() error {
|
||||||
}
|
}
|
||||||
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||||
it.codeHash = common.BytesToHash(account.CodeHash)
|
it.codeHash = common.BytesToHash(account.CodeHash)
|
||||||
it.code, err = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
it.code = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
|
||||||
}
|
|
||||||
if len(it.code) == 0 {
|
if len(it.code) == 0 {
|
||||||
return fmt.Errorf("code is not found: %x", account.CodeHash)
|
return fmt.Errorf("code is not found: %x", account.CodeHash)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -537,10 +537,7 @@ func (s *stateObject) Code() []byte {
|
||||||
s.db.CodeReads += time.Since(start)
|
s.db.CodeReads += time.Since(start)
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
code, err := s.db.reader.Code(s.address, common.BytesToHash(s.CodeHash()))
|
code := 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))
|
|
||||||
}
|
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
|
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)
|
s.db.CodeReads += time.Since(start)
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
size, err := s.db.reader.CodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
size := 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))
|
|
||||||
}
|
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
|
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
|
@ -34,7 +35,7 @@ func TestSizeTracker(t *testing.T) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
tdb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
|
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
|
// Generate 50 blocks to establish a baseline
|
||||||
baselineBlockNum := uint64(50)
|
baselineBlockNum := uint64(50)
|
||||||
|
|
@ -100,7 +101,7 @@ func TestSizeTracker(t *testing.T) {
|
||||||
t.Fatalf("Failed to close triedb before baseline measurement: %v", err)
|
t.Fatalf("Failed to close triedb before baseline measurement: %v", err)
|
||||||
}
|
}
|
||||||
tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
|
tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
|
||||||
sdb = NewDatabase(tdb, nil)
|
sdb = NewDatabase(tdb, codedb.New(db))
|
||||||
|
|
||||||
// Wait for snapshot completion
|
// Wait for snapshot completion
|
||||||
for !tdb.SnapshotCompleted() {
|
for !tdb.SnapshotCompleted() {
|
||||||
|
|
@ -154,7 +155,7 @@ func TestSizeTracker(t *testing.T) {
|
||||||
if i%3 == 0 {
|
if i%3 == 0 {
|
||||||
newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Failed to commit state at block %d: %v", blockNum, err)
|
t.Fatalf("Failed to commit state at block %d: %v", blockNum, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
|
@ -41,7 +42,7 @@ func newStateEnv() *stateEnv {
|
||||||
func TestDump(t *testing.T) {
|
func TestDump(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||||
tdb := NewDatabase(triedb, nil)
|
tdb := NewDatabase(triedb, codedb.New(db))
|
||||||
sdb, _ := New(types.EmptyRootHash, tdb)
|
sdb, _ := New(types.EmptyRootHash, tdb)
|
||||||
s := &stateEnv{state: sdb}
|
s := &stateEnv{state: sdb}
|
||||||
|
|
||||||
|
|
@ -97,7 +98,7 @@ func TestDump(t *testing.T) {
|
||||||
func TestIterativeDump(t *testing.T) {
|
func TestIterativeDump(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||||
tdb := NewDatabase(triedb, nil)
|
tdb := NewDatabase(triedb, codedb.New(db))
|
||||||
sdb, _ := New(types.EmptyRootHash, tdb)
|
sdb, _ := New(types.EmptyRootHash, tdb)
|
||||||
s := &stateEnv{state: sdb}
|
s := &stateEnv{state: sdb}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -148,9 +147,8 @@ type StateDB struct {
|
||||||
StorageReads time.Duration
|
StorageReads time.Duration
|
||||||
StorageUpdates time.Duration
|
StorageUpdates time.Duration
|
||||||
StorageCommits time.Duration
|
StorageCommits time.Duration
|
||||||
SnapshotCommits time.Duration
|
|
||||||
TrieDBCommits time.Duration
|
|
||||||
CodeReads time.Duration
|
CodeReads time.Duration
|
||||||
|
DatabaseCommits time.Duration
|
||||||
|
|
||||||
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||||
AccountUpdated int // Number of accounts updated 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
|
// commit gathers the state mutations accumulated along with the associated
|
||||||
// trie changes, resetting all internal flags with the new state as the base.
|
// 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.
|
// Short circuit in case any database failure occurred earlier.
|
||||||
if s.dbErr != nil {
|
if s.dbErr != nil {
|
||||||
return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
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
|
// the same block, account deletions must be processed first. This ensures
|
||||||
// that the storage trie nodes deleted during destruction and recreated
|
// that the storage trie nodes deleted during destruction and recreated
|
||||||
// during subsequent resurrection can be combined correctly.
|
// during subsequent resurrection can be combined correctly.
|
||||||
deletes, delNodes, err := s.handleDestruction(noStorageWiping)
|
deletes, delNodes, err := s.handleDestruction(rawStorageKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1313,58 +1311,20 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
|
||||||
origin := s.originalRoot
|
origin := s.originalRoot
|
||||||
s.originalRoot = root
|
s.originalRoot = root
|
||||||
|
|
||||||
return newStateUpdate(noStorageWiping, origin, root, blockNumber, deletes, updates, nodes), nil
|
start = time.Now()
|
||||||
}
|
update := newStateUpdate(rawStorageKey, origin, root, blockNumber, deletes, updates, nodes)
|
||||||
|
if dedupCode {
|
||||||
// commitAndFlush is a wrapper of commit which also commits the state mutations
|
update.markCodeExistence(s.reader)
|
||||||
// to the configured data stores.
|
}
|
||||||
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool, dedupCode bool) (*stateUpdate, error) {
|
if err := s.db.Commit(update); err != nil {
|
||||||
ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
s.DatabaseCommits = time.Since(start)
|
||||||
|
|
||||||
if dedupCode {
|
// The reader update must be performed as the final step, otherwise,
|
||||||
ret.markCodeExistence(s.reader)
|
// the new state would not be visible before db.commit.
|
||||||
}
|
s.reader, _ = s.db.Reader(root)
|
||||||
|
return update, nil
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit writes the state mutations into the configured data stores.
|
// 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
|
// 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
|
// no empty accounts left that could be deleted by EIP-158, storage wiping
|
||||||
// should not occur.
|
// should not occur.
|
||||||
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (common.Hash, error) {
|
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, rawStorageKey bool) (common.Hash, error) {
|
||||||
ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping, false)
|
ret, err := s.commit(deleteEmptyObjects, rawStorageKey, block, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
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.
|
// 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) {
|
func (s *StateDB) CommitAndTrack(block uint64, deleteEmptyObjects bool, rawStorageKey bool, sizer *SizeTracker) (common.Hash, error) {
|
||||||
ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping, true)
|
ret, err := s.commit(deleteEmptyObjects, rawStorageKey, block, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -209,7 +210,7 @@ func (test *stateTest) run() bool {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
root = roots[len(roots)-1]
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +229,7 @@ func (test *stateTest) run() bool {
|
||||||
} else {
|
} else {
|
||||||
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -52,7 +53,7 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(db, nil)
|
tdb = triedb.NewDatabase(db, nil)
|
||||||
sdb = NewDatabase(tdb, nil)
|
sdb = NewDatabase(tdb, codedb.New(db))
|
||||||
)
|
)
|
||||||
state, _ := New(types.EmptyRootHash, sdb)
|
state, _ := New(types.EmptyRootHash, sdb)
|
||||||
|
|
||||||
|
|
@ -90,8 +91,8 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
finalDb := rawdb.NewMemoryDatabase()
|
finalDb := rawdb.NewMemoryDatabase()
|
||||||
transNdb := triedb.NewDatabase(transDb, nil)
|
transNdb := triedb.NewDatabase(transDb, nil)
|
||||||
finalNdb := triedb.NewDatabase(finalDb, nil)
|
finalNdb := triedb.NewDatabase(finalDb, nil)
|
||||||
transState, _ := New(types.EmptyRootHash, NewDatabase(transNdb, nil))
|
transState, _ := New(types.EmptyRootHash, NewDatabase(transNdb, codedb.New(transDb)))
|
||||||
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalNdb, nil))
|
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalNdb, codedb.New(finalDb)))
|
||||||
|
|
||||||
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||||
state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)), tracing.BalanceChangeUnspecified)
|
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,
|
CleanCacheSize: 0,
|
||||||
}}) // disable caching
|
}}) // disable caching
|
||||||
}
|
}
|
||||||
db := NewDatabase(tdb, nil)
|
db := NewDatabase(tdb, codedb.New(memDb))
|
||||||
|
|
||||||
var root common.Hash
|
var root common.Hash
|
||||||
state, _ := New(types.EmptyRootHash, db)
|
state, _ := New(types.EmptyRootHash, db)
|
||||||
|
|
@ -1204,7 +1205,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
memdb = rawdb.NewMemoryDatabase()
|
memdb = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(memdb, triedb.HashDefaults)
|
tdb = triedb.NewDatabase(memdb, triedb.HashDefaults)
|
||||||
statedb = NewDatabase(tdb, nil)
|
statedb = NewDatabase(tdb, codedb.New(memdb))
|
||||||
state, _ = New(types.EmptyRootHash, statedb)
|
state, _ = New(types.EmptyRootHash, statedb)
|
||||||
)
|
)
|
||||||
for a := byte(0); a < 10; a++ {
|
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)
|
t.Fatalf("failed to commit state trie: %v", err)
|
||||||
}
|
}
|
||||||
// Reopen the state trie from flushed disk and verify it
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to reopen state trie: %v", err)
|
t.Fatalf("failed to reopen state trie: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1276,7 +1277,7 @@ func TestDeleteStorage(t *testing.T) {
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, nil)
|
tdb = triedb.NewDatabase(disk, nil)
|
||||||
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
|
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)
|
state, _ = New(types.EmptyRootHash, db)
|
||||||
addr = common.HexToAddress("0x1")
|
addr = common.HexToAddress("0x1")
|
||||||
)
|
)
|
||||||
|
|
@ -1289,9 +1290,10 @@ func TestDeleteStorage(t *testing.T) {
|
||||||
state.SetState(addr, slot, value)
|
state.SetState(addr, slot, value)
|
||||||
}
|
}
|
||||||
root, _ := state.Commit(0, true, false)
|
root, _ := state.Commit(0, true, false)
|
||||||
|
|
||||||
// Init phase done, create two states, one with snap and one without
|
// Init phase done, create two states, one with snap and one without
|
||||||
fastState, _ := New(root, NewDatabase(tdb, snaps))
|
fastState, _ := New(root, NewDatabase(tdb, codedb.New(disk)).WithSnapshot(snaps))
|
||||||
slowState, _ := New(root, NewDatabase(tdb, nil))
|
slowState, _ := New(root, NewDatabase(tdb, codedb.New(disk)))
|
||||||
|
|
||||||
obj := fastState.getOrNewStateObject(addr)
|
obj := fastState.getOrNewStateObject(addr)
|
||||||
storageRoot := obj.data.Root
|
storageRoot := obj.data.Root
|
||||||
|
|
@ -1329,7 +1331,7 @@ func TestStorageDirtiness(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, nil)
|
tdb = triedb.NewDatabase(disk, nil)
|
||||||
db = NewDatabase(tdb, nil)
|
db = NewDatabase(tdb, codedb.New(disk))
|
||||||
state, _ = New(types.EmptyRootHash, db)
|
state, _ = New(types.EmptyRootHash, db)
|
||||||
addr = common.HexToAddress("0x1")
|
addr = common.HexToAddress("0x1")
|
||||||
checkDirty = func(key common.Hash, value common.Hash, dirty bool) {
|
checkDirty = func(key common.Hash, value common.Hash, dirty bool) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -54,7 +55,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c
|
||||||
}
|
}
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
nodeDb := triedb.NewDatabase(db, config)
|
nodeDb := triedb.NewDatabase(db, config)
|
||||||
sdb := NewDatabase(nodeDb, nil)
|
sdb := NewDatabase(nodeDb, codedb.New(db))
|
||||||
state, _ := New(types.EmptyRootHash, sdb)
|
state, _ := New(types.EmptyRootHash, sdb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// 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
|
config.PathDB = pathdb.Defaults
|
||||||
}
|
}
|
||||||
// Check root availability and state contents
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
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 {
|
if scheme == rawdb.PathScheme {
|
||||||
config.PathDB = pathdb.Defaults
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -222,8 +223,8 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s
|
||||||
codeResults = make([]trie.CodeSyncResult, len(codeElements))
|
codeResults = make([]trie.CodeSyncResult, len(codeElements))
|
||||||
)
|
)
|
||||||
for i, element := range codeElements {
|
for i, element := range codeElements {
|
||||||
data, err := cReader.Code(common.Address{}, element.code)
|
data := cReader.Code(common.Address{}, element.code)
|
||||||
if err != nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
|
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
|
||||||
}
|
}
|
||||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
||||||
|
|
@ -346,8 +347,8 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) {
|
||||||
if len(codeElements) > 0 {
|
if len(codeElements) > 0 {
|
||||||
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
|
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
|
||||||
for i, element := range codeElements[:len(codeResults)] {
|
for i, element := range codeElements[:len(codeResults)] {
|
||||||
data, err := cReader.Code(common.Address{}, element.code)
|
data := cReader.Code(common.Address{}, element.code)
|
||||||
if err != nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
|
t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
|
||||||
}
|
}
|
||||||
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
|
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 {
|
if len(codeQueue) > 0 {
|
||||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||||
for hash := range codeQueue {
|
for hash := range codeQueue {
|
||||||
data, err := cReader.Code(common.Address{}, hash)
|
data := cReader.Code(common.Address{}, hash)
|
||||||
if err != nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||||
|
|
@ -551,8 +552,8 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
|
||||||
for hash := range codeQueue {
|
for hash := range codeQueue {
|
||||||
delete(codeQueue, hash)
|
delete(codeQueue, hash)
|
||||||
|
|
||||||
data, err := cReader.Code(common.Address{}, hash)
|
data := cReader.Code(common.Address{}, hash)
|
||||||
if err != nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||||
|
|
@ -671,8 +672,8 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
|
||||||
if len(codeQueue) > 0 {
|
if len(codeQueue) > 0 {
|
||||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||||
for hash := range codeQueue {
|
for hash := range codeQueue {
|
||||||
data, err := cReader.Code(common.Address{}, hash)
|
data := cReader.Code(common.Address{}, hash)
|
||||||
if err != nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||||
}
|
}
|
||||||
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -69,7 +70,7 @@ func TestUseAfterTerminate(t *testing.T) {
|
||||||
func TestVerklePrefetcher(t *testing.T) {
|
func TestVerklePrefetcher(t *testing.T) {
|
||||||
disk := rawdb.NewMemoryDatabase()
|
disk := rawdb.NewMemoryDatabase()
|
||||||
db := triedb.NewDatabase(disk, triedb.VerkleDefaults)
|
db := triedb.NewDatabase(disk, triedb.VerkleDefaults)
|
||||||
sdb := NewDatabase(db, nil)
|
sdb := NewDatabase(db, codedb.New(disk))
|
||||||
|
|
||||||
state, err := New(types.EmptyRootHash, sdb)
|
state, err := New(types.EmptyRootHash, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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
|
// Create and populate the state database to serve as the stateless backend
|
||||||
memdb := witness.MakeHashDB()
|
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 {
|
if err != nil {
|
||||||
return common.Hash{}, common.Hash{}, err
|
return common.Hash{}, common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -113,7 +114,7 @@ func TestAccountRange(t *testing.T) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mdb = rawdb.NewMemoryDatabase()
|
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)
|
sdb, _ = state.New(types.EmptyRootHash, statedb)
|
||||||
addrs = [AccountRangeMaxResults * 2]common.Address{}
|
addrs = [AccountRangeMaxResults * 2]common.Address{}
|
||||||
m = map[common.Address]bool{}
|
m = map[common.Address]bool{}
|
||||||
|
|
@ -212,7 +213,7 @@ func TestStorageRangeAt(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
mdb = rawdb.NewMemoryDatabase()
|
mdb = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(mdb, &triedb.Config{Preimages: true})
|
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)
|
sdb, _ = state.New(types.EmptyRootHash, db)
|
||||||
addr = common.Address{0x01}
|
addr = common.Address{0x01}
|
||||||
keys = []common.Hash{ // hashes of Keys of storage
|
keys = []common.Hash{ // hashes of Keys of storage
|
||||||
|
|
|
||||||
|
|
@ -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,
|
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
|
||||||
// please re-enable it for better performance.
|
// please re-enable it for better performance.
|
||||||
tdb := triedb.NewDatabase(eth.chainDb, triedb.HashDefaults)
|
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 {
|
if statedb, err = state.New(block.Root(), database); err == nil {
|
||||||
log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
|
log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
|
||||||
return statedb, noopReleaser, nil
|
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,
|
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
|
||||||
// please re-enable it for better performance.
|
// please re-enable it for better performance.
|
||||||
tdb = triedb.NewDatabase(eth.chainDb, triedb.HashDefaults)
|
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,
|
// 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
|
// otherwise we would rewind past a persisted block (specific corner case is
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,9 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type precompileContract struct{}
|
type precompileContract struct{}
|
||||||
|
|
@ -40,7 +38,7 @@ func (p *precompileContract) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateOverrideMovePrecompile(t *testing.T) {
|
func TestStateOverrideMovePrecompile(t *testing.T) {
|
||||||
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
db := state.NewDatabaseForTesting()
|
||||||
statedb, err := state.New(types.EmptyRootHash, db)
|
statedb, err := state.New(types.EmptyRootHash, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create statedb: %v", err)
|
t.Fatalf("failed to create statedb: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,8 @@ func createMiner(t *testing.T) *Miner {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can't create new chain %v", err)
|
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)}
|
blockchain := &testBlockChain{bc.Genesis().Root(), chainConfig, statedb, 10000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := legacypool.New(testTxPoolConfig, blockchain)
|
pool := legacypool.New(testTxPoolConfig, blockchain)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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
|
tconf.PathDB = pathdb.Defaults
|
||||||
}
|
}
|
||||||
triedb := triedb.NewDatabase(db, tconf)
|
triedb := triedb.NewDatabase(db, tconf)
|
||||||
sdb := state.NewDatabase(triedb, nil)
|
sdb := state.NewDatabase(triedb, codedb.New(db))
|
||||||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
statedb.SetCode(addr, a.Code, tracing.CodeChangeUnspecified)
|
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)
|
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)
|
statedb, _ = state.New(root, sdb)
|
||||||
return StateTestState{statedb, triedb, snaps}
|
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.
|
// NodeReader returns a reader for accessing trie nodes within the specified state.
|
||||||
// An error will be returned if the specified state is not available.
|
// An error will be returned if the specified state is not available.
|
||||||
func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) {
|
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 {
|
if _, err := db.node(root); err != nil {
|
||||||
return nil, fmt.Errorf("state %#x is not available, %v", root, err)
|
return nil, fmt.Errorf("state %#x is not available, %v", root, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue