mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
core/state: cleanup
This commit is contained in:
parent
d649d93323
commit
890b02fd98
6 changed files with 221 additions and 177 deletions
|
|
@ -2116,11 +2116,29 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
sdb = state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
|
||||
sdb state.Database
|
||||
)
|
||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||
|
||||
if bc.cfg.NoPrefetch {
|
||||
if bc.chainConfig.IsUBT(block.Number(), block.Time()) {
|
||||
sdb = state.NewUBTDB(bc.triedb, bc.codedb)
|
||||
} else {
|
||||
sdb = state.NewMerkleDB(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
|
||||
}
|
||||
// If prefetching is enabled, run that against the current state to pre-cache
|
||||
// transactions and probabilistically some of the account/storage trie nodes.
|
||||
//
|
||||
// Note: the main processor and prefetcher share the same reader with a local
|
||||
// cache for mitigating the overhead of state access.
|
||||
type prewarmReader interface {
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the
|
||||
// same underlying state reader and internal state cache, while maintaining
|
||||
// separate statistics respectively.
|
||||
ReadersWithCacheStats(stateRoot common.Hash) (state.Reader, state.Reader, error)
|
||||
}
|
||||
warmer, ok := sdb.(prewarmReader)
|
||||
|
||||
if bc.cfg.NoPrefetch || !ok {
|
||||
statedb, err = state.New(parentRoot, sdb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -2131,7 +2149,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
//
|
||||
// Note: the main processor and prefetcher share the same reader with a local
|
||||
// cache for mitigating the overhead of state access.
|
||||
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
|
||||
prefetch, process, err := warmer.ReadersWithCacheStats(parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -421,7 +421,8 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
|||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
|
||||
// TODO(rjl493456442) support ubt later
|
||||
return state.New(root, state.NewMerkleDB(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
|
||||
}
|
||||
|
||||
// HistoricState returns a historic state specified by the given root.
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
|
|
@ -55,20 +53,8 @@ type Database interface {
|
|||
// if the commit fails.
|
||||
Commit(update *stateUpdate) error
|
||||
|
||||
// WithSnapshot configures the snapshot tree. This registration must be
|
||||
// performed before the database is used.
|
||||
WithSnapshot(snap *snapshot.Tree) Database
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
Snapshot() *snapshot.Tree
|
||||
|
||||
// StateReader returns a state reader associated with the specified state root.
|
||||
StateReader(stateRoot common.Hash) (StateReader, error)
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
// underlying state reader and internal state cache, while maintaining separate
|
||||
// statistics respectively.
|
||||
ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error)
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Patricia trie.
|
||||
|
|
@ -156,31 +142,14 @@ type Trie interface {
|
|||
IsUBT() bool
|
||||
}
|
||||
|
||||
// MerkleDB is an implementation of Database interface for Merkle Patricia Tries.
|
||||
// It leverages both trie and state snapshot to provide functionalities for state
|
||||
// access. It's meant to be a long-live object and has a few caches inside for
|
||||
// sharing between blocks.
|
||||
type MerkleDB struct {
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
// NewDatabase creates a state database with the provided data sources.
|
||||
//
|
||||
// Deprecated, please use NewMerkleDB or NewUBTDB directly.
|
||||
func NewDatabase(tdb *triedb.Database, codedb *CodeDB) Database {
|
||||
if codedb == nil {
|
||||
codedb = NewCodeDB(tdb.Disk())
|
||||
}
|
||||
if tdb.IsUBT() {
|
||||
return &UBTDB{
|
||||
triedb: tdb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
return &MerkleDB{
|
||||
triedb: tdb,
|
||||
codedb: codedb,
|
||||
return NewUBTDB(tdb, codedb)
|
||||
}
|
||||
return NewMerkleDB(tdb, codedb)
|
||||
}
|
||||
|
||||
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||
|
|
@ -190,139 +159,6 @@ func NewDatabaseForTesting() Database {
|
|||
return NewDatabase(triedb.NewDatabase(db, nil), NewCodeDB(db))
|
||||
}
|
||||
|
||||
// WithSnapshot configures the provided contract code cache. Note that this
|
||||
// registration must be performed before the MPTDB is used.
|
||||
func (db *MerkleDB) WithSnapshot(snapshot *snapshot.Tree) Database {
|
||||
db.snap = snapshot
|
||||
return db
|
||||
}
|
||||
|
||||
// StateReader returns a state reader associated with the specified state root.
|
||||
func (db *MerkleDB) StateReader(stateRoot common.Hash) (StateReader, error) {
|
||||
var readers []StateReader
|
||||
|
||||
// Configure the state reader using the standalone snapshot in hash mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot is not fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
|
||||
snap := db.snap.Snapshot(stateRoot)
|
||||
if snap != nil {
|
||||
readers = append(readers, newFlatReader(snap))
|
||||
}
|
||||
}
|
||||
// Configure the state reader using the path database in path mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot data in path database is not
|
||||
// fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.PathScheme {
|
||||
reader, err := db.triedb.StateReader(stateRoot)
|
||||
if err == nil {
|
||||
readers = append(readers, newFlatReader(reader))
|
||||
}
|
||||
}
|
||||
// Configure the trie reader, which is expected to be available as the
|
||||
// gatekeeper unless the state is corrupted.
|
||||
tr, err := newTrieReader(stateRoot, db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readers = append(readers, tr)
|
||||
|
||||
return newMultiStateReader(readers...)
|
||||
}
|
||||
|
||||
// Reader implements Database, returning a reader associated with the specified
|
||||
// state root.
|
||||
func (db *MerkleDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||
sr, err := db.StateReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(db.codedb.Reader(), sr), nil
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
// underlying state reader and internal state cache, while maintaining separate
|
||||
// statistics respectively.
|
||||
func (db *MerkleDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error) {
|
||||
r, err := db.StateReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sr := newStateReaderWithCache(r)
|
||||
ra := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
rb := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
return ra, rb, nil
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *MerkleDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *MerkleDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *MerkleDB) TrieDB() *triedb.Database {
|
||||
return db.triedb
|
||||
}
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
func (db *MerkleDB) Snapshot() *snapshot.Tree {
|
||||
return db.snap
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *MerkleDB) Commit(update *stateUpdate) error {
|
||||
// Short circuit if nothing to commit
|
||||
if update.empty() {
|
||||
return nil
|
||||
}
|
||||
// Commit dirty contract code if any exists
|
||||
if len(update.codes) > 0 {
|
||||
batch := db.codedb.NewBatchWithSize(len(update.codes))
|
||||
for _, code := range update.codes {
|
||||
batch.Put(code.hash, code.blob)
|
||||
}
|
||||
if err := batch.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if db.snap != nil && db.snap.Snapshot(update.originRoot) != nil {
|
||||
if err := db.snap.Update(update.root, update.originRoot, update.accounts, update.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", update.originRoot, "to", update.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
// - head layer is paired with HEAD state
|
||||
// - head-1 layer is paired with HEAD-1 state
|
||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||
if err := db.snap.Cap(update.root, TriesInMemory); err != nil {
|
||||
log.Warn("Failed to cap snapshot tree", "root", update.root, "layers", TriesInMemory, "err", err)
|
||||
}
|
||||
}
|
||||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
|
||||
}
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
// through which the account iterator and storage iterator can be created.
|
||||
func (db *MerkleDB) Iteratee(root common.Hash) (Iteratee, error) {
|
||||
return newStateIteratee(true, root, db.triedb, db.snap)
|
||||
}
|
||||
|
||||
// mustCopyTrie returns a deep-copied trie.
|
||||
func mustCopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
|
|
|
|||
|
|
@ -316,8 +316,3 @@ func (db *HistoricDB) Snapshot() *snapshot.Tree {
|
|||
func (db *HistoricDB) StateReader(stateRoot common.Hash) (StateReader, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats is not supported by historic database.
|
||||
func (db *HistoricDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error) {
|
||||
return nil, nil, errors.New("not implemented")
|
||||
}
|
||||
|
|
|
|||
181
core/state/database_merkle.go
Normal file
181
core/state/database_merkle.go
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"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/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
// MerkleDB is an implementation of Database interface for Merkle Patricia Tries.
|
||||
// It leverages both trie and state snapshot to provide functionalities for state
|
||||
// access. It's meant to be a long-live object and has a few caches inside for
|
||||
// sharing between blocks.
|
||||
type MerkleDB struct {
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
// NewMerkleDB creates a state database with the Merkle Patricia Trie manner.
|
||||
func NewMerkleDB(tdb *triedb.Database, codedb *CodeDB) *MerkleDB {
|
||||
if codedb == nil {
|
||||
codedb = NewCodeDB(tdb.Disk())
|
||||
}
|
||||
return &MerkleDB{
|
||||
triedb: tdb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
// WithSnapshot configures the provided contract code cache. Note that this
|
||||
// registration must be performed before the MerkleDB is used.
|
||||
func (db *MerkleDB) WithSnapshot(snapshot *snapshot.Tree) Database {
|
||||
db.snap = snapshot
|
||||
return db
|
||||
}
|
||||
|
||||
// StateReader returns a state reader associated with the specified state root.
|
||||
func (db *MerkleDB) StateReader(stateRoot common.Hash) (StateReader, error) {
|
||||
var readers []StateReader
|
||||
|
||||
// Configure the state reader using the standalone snapshot in hash mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot is not fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
|
||||
snap := db.snap.Snapshot(stateRoot)
|
||||
if snap != nil {
|
||||
readers = append(readers, newFlatReader(snap))
|
||||
}
|
||||
}
|
||||
// Configure the state reader using the path database in path mode.
|
||||
// This reader offers improved performance but is optional and only
|
||||
// partially useful if the snapshot data in path database is not
|
||||
// fully generated.
|
||||
if db.TrieDB().Scheme() == rawdb.PathScheme {
|
||||
reader, err := db.triedb.StateReader(stateRoot)
|
||||
if err == nil {
|
||||
readers = append(readers, newFlatReader(reader))
|
||||
}
|
||||
}
|
||||
// Configure the trie reader, which is expected to be available as the
|
||||
// gatekeeper unless the state is corrupted.
|
||||
tr, err := newTrieReader(stateRoot, db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readers = append(readers, tr)
|
||||
|
||||
return newMultiStateReader(readers...)
|
||||
}
|
||||
|
||||
// Reader implements Database, returning a reader associated with the specified
|
||||
// state root.
|
||||
func (db *MerkleDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||
sr, err := db.StateReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(db.codedb.Reader(), sr), nil
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
// underlying state reader and internal state cache, while maintaining separate
|
||||
// statistics respectively.
|
||||
func (db *MerkleDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error) {
|
||||
r, err := db.StateReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sr := newStateReaderWithCache(r)
|
||||
ra := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
rb := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
return ra, rb, nil
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *MerkleDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *MerkleDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *MerkleDB) TrieDB() *triedb.Database {
|
||||
return db.triedb
|
||||
}
|
||||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
func (db *MerkleDB) Snapshot() *snapshot.Tree {
|
||||
return db.snap
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *MerkleDB) Commit(update *stateUpdate) error {
|
||||
// Short circuit if nothing to commit
|
||||
if update.empty() {
|
||||
return nil
|
||||
}
|
||||
// Commit dirty contract code if any exists
|
||||
if len(update.codes) > 0 {
|
||||
batch := db.codedb.NewBatchWithSize(len(update.codes))
|
||||
for _, code := range update.codes {
|
||||
batch.Put(code.hash, code.blob)
|
||||
}
|
||||
if err := batch.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if db.snap != nil && db.snap.Snapshot(update.originRoot) != nil {
|
||||
if err := db.snap.Update(update.root, update.originRoot, update.accounts, update.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", update.originRoot, "to", update.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
// - head layer is paired with HEAD state
|
||||
// - head-1 layer is paired with HEAD-1 state
|
||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||
if err := db.snap.Cap(update.root, TriesInMemory); err != nil {
|
||||
log.Warn("Failed to cap snapshot tree", "root", update.root, "layers", TriesInMemory, "err", err)
|
||||
}
|
||||
}
|
||||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
|
||||
}
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
// through which the account iterator and storage iterator can be created.
|
||||
func (db *MerkleDB) Iteratee(root common.Hash) (Iteratee, error) {
|
||||
return newStateIteratee(true, root, db.triedb, db.snap)
|
||||
}
|
||||
|
|
@ -26,12 +26,25 @@ import (
|
|||
)
|
||||
|
||||
// UBTDB is an implementation of Database interface for Universal Binary Tries.
|
||||
// It provides the same functionality as MerkleDB but uses binary tries for state
|
||||
// storage instead of Merkle Patricia Tries.
|
||||
type UBTDB struct {
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
// NewUBTDB creates a state database with the Unified binary trie manner.
|
||||
func NewUBTDB(triedb *triedb.Database, codedb *CodeDB) *UBTDB {
|
||||
if codedb == nil {
|
||||
codedb = NewCodeDB(triedb.Disk())
|
||||
}
|
||||
return &UBTDB{
|
||||
triedb: triedb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
// WithSnapshot configures the snapshot tree. Note that this registration must
|
||||
// be performed before the UBTDB is used.
|
||||
func (db *UBTDB) WithSnapshot(snapshot *snapshot.Tree) Database {
|
||||
|
|
|
|||
Loading…
Reference in a new issue