mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
core: impelement state database
This commit is contained in:
parent
b62c0c67fa
commit
006ad0b23c
9 changed files with 176 additions and 60 deletions
|
|
@ -288,7 +288,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)
|
stateDB *stateDatabase // State database to reuse between imports (contains state cache)
|
||||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||||
|
|
||||||
hc *HeaderChain
|
hc *HeaderChain
|
||||||
|
|
@ -338,19 +338,18 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
cfg = DefaultConfig()
|
cfg = DefaultConfig()
|
||||||
}
|
}
|
||||||
|
// Open state database with provided config
|
||||||
// Open trie database with provided config
|
|
||||||
enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
|
enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
triedb := triedb.NewDatabase(db, cfg.triedbConfig(enableVerkle))
|
stateDB := newStateDatabase(db, cfg)
|
||||||
|
|
||||||
// Write the supplied genesis to the database if it has not been initialized
|
// Write the supplied genesis to the database if it has not been initialized
|
||||||
// yet. The corresponding chain config will be returned, either from the
|
// yet. The corresponding chain config will be returned, either from the
|
||||||
// provided genesis or from the locally stored configuration if the genesis
|
// provided genesis or from the locally stored configuration if the genesis
|
||||||
// has already been initialized.
|
// has already been initialized.
|
||||||
chainConfig, genesisHash, compatErr, err := SetupGenesisBlockWithOverride(db, triedb, genesis, cfg.Overrides)
|
chainConfig, genesisHash, compatErr, err := SetupGenesisBlockWithOverride(db, stateDB.triedb(enableVerkle), genesis, cfg.Overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +365,8 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
db: db,
|
db: db,
|
||||||
triedb: triedb,
|
stateDB: stateDB,
|
||||||
|
triedb: stateDB.triedb(false), // TODO(rjl493456442) support verkle
|
||||||
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),
|
||||||
|
|
@ -382,7 +382,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(chainConfig, bc.hc)
|
bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
||||||
|
|
@ -548,9 +547,7 @@ 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)
|
||||||
|
bc.stateDB.setSnapshot(bc.snaps)
|
||||||
// Re-initialize the state database with snapshot
|
|
||||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -941,7 +938,7 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
|
||||||
// then block number zero is returned, indicating that snapshot recovery is disabled
|
// then block number zero is returned, indicating that snapshot recovery is disabled
|
||||||
// and the whole snapshot should be auto-generated in case of head mismatch.
|
// and the whole snapshot should be auto-generated in case of head mismatch.
|
||||||
func (bc *BlockChain) rewindHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
func (bc *BlockChain) rewindHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.cfg.StateScheme == rawdb.PathScheme {
|
||||||
return bc.rewindPathHead(head, root)
|
return bc.rewindPathHead(head, root)
|
||||||
}
|
}
|
||||||
return bc.rewindHashHead(head, root)
|
return bc.rewindHashHead(head, root)
|
||||||
|
|
@ -1097,7 +1094,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
|
||||||
}
|
}
|
||||||
// Reset the trie database with the fresh snap synced state.
|
// Reset the trie database with the fresh snap synced state.
|
||||||
root := block.Root()
|
root := block.Root()
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.cfg.StateScheme == rawdb.PathScheme {
|
||||||
if err := bc.triedb.Enable(root); err != nil {
|
if err := bc.triedb.Enable(root); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1269,7 +1266,7 @@ func (bc *BlockChain) Stop() {
|
||||||
}
|
}
|
||||||
bc.snaps.Release()
|
bc.snaps.Release()
|
||||||
}
|
}
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.cfg.StateScheme == rawdb.PathScheme {
|
||||||
// Ensure that the in-memory trie nodes are journaled to disk properly.
|
// Ensure that the in-memory trie nodes are journaled to disk properly.
|
||||||
if err := bc.triedb.Journal(bc.CurrentBlock().Root); err != nil {
|
if err := bc.triedb.Journal(bc.CurrentBlock().Root); err != nil {
|
||||||
log.Info("Failed to journal in-memory trie nodes", "err", err)
|
log.Info("Failed to journal in-memory trie nodes", "err", err)
|
||||||
|
|
@ -1312,8 +1309,8 @@ func (bc *BlockChain) Stop() {
|
||||||
bc.logger.OnClose()
|
bc.logger.OnClose()
|
||||||
}
|
}
|
||||||
// Close the trie database, release all the held resources as the last step.
|
// Close the trie database, release all the held resources as the last step.
|
||||||
if err := bc.triedb.Close(); err != nil {
|
if err := bc.stateDB.close(); err != nil {
|
||||||
log.Error("Failed to close trie database", "err", err)
|
log.Error("Failed to close state database", "err", err)
|
||||||
}
|
}
|
||||||
log.Info("Blockchain stopped")
|
log.Info("Blockchain stopped")
|
||||||
}
|
}
|
||||||
|
|
@ -1586,7 +1583,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
// If node is running in path mode, skip explicit gc operation
|
// If node is running in path mode, skip explicit gc operation
|
||||||
// which is unnecessary in this mode.
|
// which is unnecessary in this mode.
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.cfg.StateScheme == rawdb.PathScheme {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// If we're running an archive node, always flush
|
// If we're running an archive node, always flush
|
||||||
|
|
@ -1956,7 +1953,8 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
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)
|
sdb := bc.stateDB.stateDB(bc.chainConfig.IsVerkle(block.Number(), block.Time()))
|
||||||
|
statedb, err = state.New(parentRoot, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1966,15 +1964,16 @@ 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)
|
sdb := bc.stateDB.stateDB(bc.chainConfig.IsVerkle(block.Number(), block.Time()))
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,8 +325,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)
|
return bc.stateDB.hasState(hash)
|
||||||
return err == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasBlockAndState checks if a block and associated state trie is fully present
|
// HasBlockAndState checks if a block and associated state trie is fully present
|
||||||
|
|
@ -345,7 +344,7 @@ func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
|
||||||
// state is not treated as recoverable if it's available, thus
|
// state is not treated as recoverable if it's available, thus
|
||||||
// false will be returned in this case.
|
// false will be returned in this case.
|
||||||
func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
||||||
if bc.triedb.Scheme() == rawdb.HashScheme {
|
if bc.cfg.StateScheme == rawdb.HashScheme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
result, _ := bc.triedb.Recoverable(root)
|
result, _ := bc.triedb.Recoverable(root)
|
||||||
|
|
@ -357,7 +356,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.stateDB.contractCode(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.
|
||||||
|
|
@ -367,7 +366,8 @@ 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)
|
// TODO(rjl493456442) support verkle state accessing
|
||||||
|
return state.New(root, bc.stateDB.stateDB(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config retrieves the chain's fork configuration.
|
// Config retrieves the chain's fork configuration.
|
||||||
|
|
@ -393,7 +393,8 @@ func (bc *BlockChain) Processor() Processor {
|
||||||
|
|
||||||
// StateCache returns the caching database underpinning the blockchain instance.
|
// StateCache returns the caching database underpinning the blockchain instance.
|
||||||
func (bc *BlockChain) StateCache() state.Database {
|
func (bc *BlockChain) StateCache() state.Database {
|
||||||
return bc.statedb
|
// TODO(rjl493456442) support verkle database
|
||||||
|
return bc.stateDB.stateDB(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GasLimit returns the gas limit of the current HEAD block.
|
// GasLimit returns the gas limit of the current HEAD block.
|
||||||
|
|
|
||||||
|
|
@ -1845,7 +1845,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
|
rawdb.WriteLastPivotNumber(db, *tt.pivotBlock)
|
||||||
}
|
}
|
||||||
// Pull the plug on the database, simulating a hard crash
|
// Pull the plug on the database, simulating a hard crash
|
||||||
chain.triedb.Close()
|
chain.stateDB.close()
|
||||||
db.Close()
|
db.Close()
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
|
|
@ -1971,7 +1971,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull the plug on the database, simulating a hard crash
|
// Pull the plug on the database, simulating a hard crash
|
||||||
chain.triedb.Close()
|
chain.stateDB.close()
|
||||||
db.Close()
|
db.Close()
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -2033,15 +2032,14 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
}
|
}
|
||||||
// Reopen the trie database without persisting in-memory dirty nodes.
|
// Reopen the trie database without persisting in-memory dirty nodes.
|
||||||
chain.triedb.Close()
|
chain.stateDB.close()
|
||||||
dbconfig := &triedb.Config{}
|
dbconfig := &triedb.Config{}
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
dbconfig.PathDB = pathdb.Defaults
|
dbconfig.PathDB = pathdb.Defaults
|
||||||
} else {
|
} else {
|
||||||
dbconfig.HashDB = hashdb.Defaults
|
dbconfig.HashDB = hashdb.Defaults
|
||||||
}
|
}
|
||||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
chain.stateDB = newStateDatabase(chain.db, chain.cfg)
|
||||||
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
|
||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
db := chain.db
|
db := chain.db
|
||||||
db.Close()
|
db.Close()
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
chain.triedb.Close()
|
chain.stateDB.close()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
|
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
|
||||||
|
|
@ -416,7 +416,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulate the blockchain crash.
|
// Simulate the blockchain crash.
|
||||||
tmp.triedb.Close()
|
tmp.stateDB.close()
|
||||||
tmp.stopWithoutSaving()
|
tmp.stopWithoutSaving()
|
||||||
|
|
||||||
newchain, err = NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme))
|
newchain, err = NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme))
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,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 := blockchain.stateDB.stateDB(false)
|
||||||
|
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,14 +33,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of codehash->size associations to keep.
|
// CodeSizeCacheSize is the number of codehash->size associations to keep.
|
||||||
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
CodeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||||
|
|
||||||
// Cache size granted for caching clean code.
|
// CodeCacheSize is the cache size granted for caching clean code.
|
||||||
codeCacheSize = 256 * 1024 * 1024
|
CodeCacheSize = 256 * 1024 * 1024
|
||||||
|
|
||||||
// Number of address->curve point associations to keep.
|
// PointCacheSize is the number of address->curve point associations to keep.
|
||||||
pointCacheSize = 4096
|
PointCacheSize = 4096
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database wraps access to tries and contract code.
|
// Database wraps access to tries and contract code.
|
||||||
|
|
@ -159,9 +159,22 @@ func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
|
||||||
disk: triedb.Disk(),
|
disk: triedb.Disk(),
|
||||||
triedb: triedb,
|
triedb: triedb,
|
||||||
snap: snap,
|
snap: snap,
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](CodeCacheSize),
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
codeSizeCache: lru.NewCache[common.Hash, int](CodeSizeCacheSize),
|
||||||
pointCache: utils.NewPointCache(pointCacheSize),
|
pointCache: utils.NewPointCache(PointCacheSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDatabaseWithCache creates a state database with the provided database
|
||||||
|
// and relative caches.
|
||||||
|
func NewDatabaseWithCache(disk ethdb.KeyValueStore, triedb *triedb.Database, snap *snapshot.Tree, codeCache *lru.SizeConstrainedCache[common.Hash, []byte], codeSizeCache *lru.Cache[common.Hash, int], pointCache *utils.PointCache) *CachingDB {
|
||||||
|
return &CachingDB{
|
||||||
|
disk: disk,
|
||||||
|
snap: snap,
|
||||||
|
triedb: triedb,
|
||||||
|
codeCache: codeCache,
|
||||||
|
codeSizeCache: codeSizeCache,
|
||||||
|
pointCache: pointCache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,22 +261,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
|
||||||
|
|
|
||||||
117
core/state_database.go
Normal file
117
core/state_database.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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 core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"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/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// stateDatabase is the central structure for managing state data,
|
||||||
|
// including both the Merkle-Patricia Trie and the Verkle tree (post-transition).
|
||||||
|
type stateDatabase struct {
|
||||||
|
disk ethdb.Database
|
||||||
|
merkle *triedb.Database
|
||||||
|
verkle *triedb.Database
|
||||||
|
snap *snapshot.Tree
|
||||||
|
|
||||||
|
// Various caches
|
||||||
|
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||||
|
codeSizeCache *lru.Cache[common.Hash, int]
|
||||||
|
pointCache *utils.PointCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStateDatabase initializes a new stateDatabase instance, creating
|
||||||
|
// both Merkle and Verkle trie databases.
|
||||||
|
func newStateDatabase(disk ethdb.Database, cfg *BlockChainConfig) *stateDatabase {
|
||||||
|
return &stateDatabase{
|
||||||
|
disk: disk,
|
||||||
|
merkle: triedb.NewDatabase(disk, cfg.triedbConfig(false)),
|
||||||
|
verkle: triedb.NewDatabase(disk, cfg.triedbConfig(true)),
|
||||||
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](state.CodeCacheSize),
|
||||||
|
codeSizeCache: lru.NewCache[common.Hash, int](state.CodeSizeCacheSize),
|
||||||
|
pointCache: utils.NewPointCache(state.PointCacheSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSnapshot assigns a snapshot tree to the state database.
|
||||||
|
func (s *stateDatabase) setSnapshot(snap *snapshot.Tree) {
|
||||||
|
s.snap = snap
|
||||||
|
}
|
||||||
|
|
||||||
|
// triedb returns the appropriate trie database based on the given flag.
|
||||||
|
func (s *stateDatabase) triedb(isVerkle bool) *triedb.Database {
|
||||||
|
if isVerkle {
|
||||||
|
return s.verkle
|
||||||
|
}
|
||||||
|
return s.merkle
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateDB returns the appropriate state database based on the given flag.
|
||||||
|
func (s *stateDatabase) stateDB(isVerkle bool) *state.CachingDB {
|
||||||
|
if isVerkle {
|
||||||
|
// Verkle is compatible only with path mode; snapshot is integrated natively.
|
||||||
|
return state.NewDatabaseWithCache(s.disk, s.verkle, nil, s.codeCache, s.codeSizeCache, s.pointCache)
|
||||||
|
}
|
||||||
|
return state.NewDatabaseWithCache(s.disk, s.merkle, s.snap, s.codeCache, s.codeSizeCache, s.pointCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// contractCode retrieves the contract code by its hash. If not present in the
|
||||||
|
// cache, it falls back to reading from disk using the contract database prefix.
|
||||||
|
func (s *stateDatabase) contractCode(address common.Address, codeHash common.Hash) []byte {
|
||||||
|
code, _ := s.codeCache.Get(codeHash)
|
||||||
|
if len(code) > 0 {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
code = rawdb.ReadCodeWithPrefix(s.disk, codeHash)
|
||||||
|
if len(code) > 0 {
|
||||||
|
s.codeCache.Add(codeHash, code)
|
||||||
|
s.codeSizeCache.Add(codeHash, len(code))
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasState checks whether a trie state exists for the given root hash
|
||||||
|
// in either the Merkle or Verkle database.
|
||||||
|
func (s *stateDatabase) hasState(root common.Hash) bool {
|
||||||
|
_, err := s.merkle.NodeReader(root)
|
||||||
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err = s.verkle.NodeReader(root)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// close terminates the state database and closes two internal trie databases.
|
||||||
|
func (s *stateDatabase) close() error {
|
||||||
|
var errs []error
|
||||||
|
if err := s.merkle.Close(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
if err := s.verkle.Close(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
@ -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