mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-10 01:56:37 +00:00
In the recent refactoring, the state commit logic has been abstracted, making it more flexible to design state databases for various use cases. For example, execution-only modes where state mutation is disabled. As part of this change, the database interface was extended with a Commit function. However, it currently accepts an unexported struct `stateUpdate`, which prevents downstream projects from customizing the state commit behavior. To address this limitation, the stateUpdate type is now exported.
463 lines
15 KiB
Go
463 lines
15 KiB
Go
// 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 state
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/ethereum/go-ethereum/trie"
|
|
"github.com/ethereum/go-ethereum/triedb"
|
|
)
|
|
|
|
// Iterator is an iterator to step over all the accounts or the specific
|
|
// storage in the specific state.
|
|
type Iterator interface {
|
|
// Next steps the iterator forward one element. It returns false if the iterator
|
|
// is exhausted or if an error occurs. Any error encountered is retained and
|
|
// can be retrieved via Error().
|
|
Next() bool
|
|
|
|
// Error returns any failure that occurred during iteration, which might have
|
|
// caused a premature iteration exit.
|
|
Error() error
|
|
|
|
// Hash returns the hash of the account or storage slot the iterator is
|
|
// currently at.
|
|
Hash() common.Hash
|
|
|
|
// Release releases associated resources. Release should always succeed and
|
|
// can be called multiple times without causing error.
|
|
Release()
|
|
}
|
|
|
|
// AccountIterator is an iterator to step over all the accounts in the
|
|
// specific state.
|
|
type AccountIterator interface {
|
|
Iterator
|
|
|
|
// Address returns the raw account address the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
Address() (common.Address, error)
|
|
|
|
// Account returns the account the iterator is currently at.
|
|
// An error will be retained if the iterator becomes invalid.
|
|
Account() *types.StateAccount
|
|
}
|
|
|
|
// StorageIterator is an iterator to step over the specific storage in the
|
|
// specific state.
|
|
type StorageIterator interface {
|
|
Iterator
|
|
|
|
// Key returns the raw storage slot key the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
Key() (common.Hash, error)
|
|
|
|
// Slot returns the storage slot the iterator is currently at. An error will
|
|
// be retained if the iterator becomes invalid.
|
|
Slot() common.Hash
|
|
}
|
|
|
|
// Iteratee wraps the NewIterator methods for traversing the accounts and
|
|
// storages of the specific state.
|
|
type Iteratee interface {
|
|
// NewAccountIterator creates an account iterator for the state specified by
|
|
// the given root. It begins at a specified starting position, corresponding
|
|
// to a particular initial key (or the next key if the specified one does
|
|
// not exist).
|
|
//
|
|
// The starting position here refers to the hash of the account address.
|
|
NewAccountIterator(start common.Hash) (AccountIterator, error)
|
|
|
|
// NewStorageIterator creates a storage iterator for the state specified by
|
|
// the address hash. It begins at a specified starting position, corresponding
|
|
// to a particular initial key (or the next key if the specified one does
|
|
// not exist).
|
|
//
|
|
// The starting position here refers to the hash of the slot key.
|
|
NewStorageIterator(addressHash common.Hash, start common.Hash) (StorageIterator, error)
|
|
}
|
|
|
|
// PreimageReader wraps the function Preimage for accessing the preimage of
|
|
// a given hash.
|
|
type PreimageReader interface {
|
|
// Preimage returns the preimage of associated hash.
|
|
Preimage(hash common.Hash) []byte
|
|
}
|
|
|
|
// flatAccountIterator is a wrapper around the underlying flat state iterator.
|
|
// Before returning data from the iterator, it performs an additional conversion
|
|
// to bridge the slim encoding with the full encoding format.
|
|
type flatAccountIterator struct {
|
|
err error
|
|
it snapshot.AccountIterator
|
|
preimage PreimageReader
|
|
}
|
|
|
|
// newFlatAccountIterator constructs the account iterator with the provided
|
|
// flat state iterator.
|
|
func newFlatAccountIterator(it snapshot.AccountIterator, preimage PreimageReader) *flatAccountIterator {
|
|
return &flatAccountIterator{it: it, preimage: preimage}
|
|
}
|
|
|
|
// Next steps the iterator forward one element. It returns false if the iterator
|
|
// is exhausted or if an error occurs. Any error encountered is retained and
|
|
// can be retrieved via Error().
|
|
func (ai *flatAccountIterator) Next() bool {
|
|
if ai.err != nil {
|
|
return false
|
|
}
|
|
return ai.it.Next()
|
|
}
|
|
|
|
// Error returns any failure that occurred during iteration, which might have
|
|
// caused a premature iteration exit.
|
|
func (ai *flatAccountIterator) Error() error {
|
|
return errors.Join(ai.err, ai.it.Error())
|
|
}
|
|
|
|
// Hash returns the hash of the account or storage slot the iterator is
|
|
// currently at.
|
|
func (ai *flatAccountIterator) Hash() common.Hash {
|
|
return ai.it.Hash()
|
|
}
|
|
|
|
// Release releases associated resources. Release should always succeed and
|
|
// can be called multiple times without causing error.
|
|
func (ai *flatAccountIterator) Release() {
|
|
ai.it.Release()
|
|
}
|
|
|
|
// Address returns the raw account address the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
func (ai *flatAccountIterator) Address() (common.Address, error) {
|
|
if ai.preimage == nil {
|
|
return common.Address{}, errors.New("account address is not available")
|
|
}
|
|
preimage := ai.preimage.Preimage(ai.Hash())
|
|
if preimage == nil {
|
|
return common.Address{}, errors.New("account address is not available")
|
|
}
|
|
return common.BytesToAddress(preimage), nil
|
|
}
|
|
|
|
// Account returns the account data the iterator is currently at. The account
|
|
// data is encoded as slim format from the underlying iterator, the conversion
|
|
// is required.
|
|
func (ai *flatAccountIterator) Account() *types.StateAccount {
|
|
data, err := types.FullAccount(ai.it.Account())
|
|
if err != nil {
|
|
ai.err = err
|
|
return nil
|
|
}
|
|
return data
|
|
}
|
|
|
|
// flatStorageIterator is a wrapper around the underlying flat state iterator.
|
|
type flatStorageIterator struct {
|
|
err error
|
|
it snapshot.StorageIterator
|
|
preimage PreimageReader
|
|
}
|
|
|
|
// newFlatStorageIterator constructs the storage iterator with the provided
|
|
// flat state iterator.
|
|
func newFlatStorageIterator(it snapshot.StorageIterator, preimage PreimageReader) *flatStorageIterator {
|
|
return &flatStorageIterator{it: it, preimage: preimage}
|
|
}
|
|
|
|
// Next steps the iterator forward one element. It returns false if the iterator
|
|
// is exhausted or if an error occurs. Any error encountered is retained and
|
|
// can be retrieved via Error().
|
|
func (si *flatStorageIterator) Next() bool {
|
|
if si.err != nil {
|
|
return false
|
|
}
|
|
return si.it.Next()
|
|
}
|
|
|
|
// Error returns any failure that occurred during iteration, which might have
|
|
// caused a premature iteration exit.
|
|
func (si *flatStorageIterator) Error() error {
|
|
return errors.Join(si.err, si.it.Error())
|
|
}
|
|
|
|
// Hash returns the hash of the account or storage slot the iterator is
|
|
// currently at.
|
|
func (si *flatStorageIterator) Hash() common.Hash {
|
|
return si.it.Hash()
|
|
}
|
|
|
|
// Release releases associated resources. Release should always succeed and
|
|
// can be called multiple times without causing error.
|
|
func (si *flatStorageIterator) Release() {
|
|
si.it.Release()
|
|
}
|
|
|
|
// Key returns the raw storage slot key the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
func (si *flatStorageIterator) Key() (common.Hash, error) {
|
|
if si.preimage == nil {
|
|
return common.Hash{}, errors.New("slot key is not available")
|
|
}
|
|
preimage := si.preimage.Preimage(si.Hash())
|
|
if preimage == nil {
|
|
return common.Hash{}, errors.New("slot key is not available")
|
|
}
|
|
return common.BytesToHash(preimage), nil
|
|
}
|
|
|
|
// Slot returns the storage slot data the iterator is currently at.
|
|
func (si *flatStorageIterator) Slot() common.Hash {
|
|
// Perform the rlp-decode as the slot value is RLP-encoded
|
|
blob := si.it.Slot()
|
|
_, content, _, err := rlp.Split(blob)
|
|
if err != nil {
|
|
si.err = err
|
|
return common.Hash{}
|
|
}
|
|
var value common.Hash
|
|
value.SetBytes(content)
|
|
return value
|
|
}
|
|
|
|
// merkleIterator implements the Iterator interface, providing functions to traverse
|
|
// the accounts or storages with the manner of Merkle-Patricia-Trie.
|
|
type merkleIterator struct {
|
|
tr Trie
|
|
err error
|
|
it *trie.Iterator
|
|
account bool
|
|
}
|
|
|
|
// newMerkleTrieIterator constructs the iterator with the given trie and starting position.
|
|
func newMerkleTrieIterator(tr Trie, start common.Hash, account bool) (*merkleIterator, error) {
|
|
it, err := tr.NodeIterator(start.Bytes())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &merkleIterator{
|
|
tr: tr,
|
|
it: trie.NewIterator(it),
|
|
account: account,
|
|
}, nil
|
|
}
|
|
|
|
// Next steps the iterator forward one element. It returns false if the iterator
|
|
// is exhausted or if an error occurs. Any error encountered is retained and
|
|
// can be retrieved via Error().
|
|
func (ti *merkleIterator) Next() bool {
|
|
if ti.err != nil {
|
|
return false
|
|
}
|
|
return ti.it.Next()
|
|
}
|
|
|
|
// Error returns any failure that occurred during iteration, which might have
|
|
// caused a premature iteration exit.
|
|
func (ti *merkleIterator) Error() error {
|
|
return errors.Join(ti.err, ti.it.Err)
|
|
}
|
|
|
|
// Hash returns the hash of the account or storage slot the iterator is
|
|
// currently at.
|
|
func (ti *merkleIterator) Hash() common.Hash {
|
|
return common.BytesToHash(ti.it.Key)
|
|
}
|
|
|
|
// Release releases associated resources. Release should always succeed and
|
|
// can be called multiple times without causing error.
|
|
func (ti *merkleIterator) Release() {}
|
|
|
|
// Address returns the raw account address the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
func (ti *merkleIterator) Address() (common.Address, error) {
|
|
if !ti.account {
|
|
return common.Address{}, errors.New("account address is not available")
|
|
}
|
|
preimage := ti.tr.GetKey(ti.it.Key)
|
|
if preimage == nil {
|
|
return common.Address{}, errors.New("account address is not available")
|
|
}
|
|
return common.BytesToAddress(preimage), nil
|
|
}
|
|
|
|
// Account returns the account data the iterator is currently at.
|
|
func (ti *merkleIterator) Account() *types.StateAccount {
|
|
if !ti.account {
|
|
return nil
|
|
}
|
|
var account types.StateAccount
|
|
if err := rlp.DecodeBytes(ti.it.Value, &account); err != nil {
|
|
ti.err = err
|
|
return nil
|
|
}
|
|
return &account
|
|
}
|
|
|
|
// Key returns the raw storage slot key the iterator is currently at.
|
|
// An error will be returned if the preimage is not available.
|
|
func (ti *merkleIterator) Key() (common.Hash, error) {
|
|
if ti.account {
|
|
return common.Hash{}, errors.New("slot key is not available")
|
|
}
|
|
preimage := ti.tr.GetKey(ti.it.Key)
|
|
if preimage == nil {
|
|
return common.Hash{}, errors.New("slot key is not available")
|
|
}
|
|
return common.BytesToHash(preimage), nil
|
|
}
|
|
|
|
// Slot returns the storage slot the iterator is currently at.
|
|
func (ti *merkleIterator) Slot() common.Hash {
|
|
if ti.account {
|
|
return common.Hash{}
|
|
}
|
|
// Perform the rlp-decode as the slot value is RLP-encoded
|
|
_, content, _, err := rlp.Split(ti.it.Value)
|
|
if err != nil {
|
|
ti.err = err
|
|
return common.Hash{}
|
|
}
|
|
var value common.Hash
|
|
value.SetBytes(content)
|
|
return value
|
|
}
|
|
|
|
// stateIteratee implements Iteratee interface, providing the state traversal
|
|
// functionalities of a specific state.
|
|
type stateIteratee struct {
|
|
merkle bool
|
|
root common.Hash
|
|
triedb *triedb.Database
|
|
snap *snapshot.Tree
|
|
}
|
|
|
|
func newStateIteratee(merkle bool, root common.Hash, triedb *triedb.Database, snap *snapshot.Tree) (*stateIteratee, error) {
|
|
return &stateIteratee{
|
|
merkle: merkle,
|
|
root: root,
|
|
triedb: triedb,
|
|
snap: snap,
|
|
}, nil
|
|
}
|
|
|
|
// NewAccountIterator creates an account iterator for the state specified by
|
|
// the given root. It begins at a specified starting position, corresponding
|
|
// to a particular initial key (or the next key if the specified one does
|
|
// not exist).
|
|
//
|
|
// The starting position here refers to the hash of the account address.
|
|
func (si *stateIteratee) NewAccountIterator(start common.Hash) (AccountIterator, error) {
|
|
// If the external snapshot is available (hash scheme), try to initialize
|
|
// the account iterator from there first.
|
|
if si.snap != nil {
|
|
it, err := si.snap.AccountIterator(si.root, start)
|
|
if err == nil {
|
|
return newFlatAccountIterator(it, si.triedb), nil
|
|
}
|
|
}
|
|
// If the external snapshot is not available, try to initialize the
|
|
// account iterator from the trie database (path scheme)
|
|
it, err := si.triedb.AccountIterator(si.root, start)
|
|
if err == nil {
|
|
return newFlatAccountIterator(it, si.triedb), nil
|
|
}
|
|
if !si.merkle {
|
|
return nil, fmt.Errorf("state %x is not available for account traversal", si.root)
|
|
}
|
|
// The snapshot is not usable so far, construct the account iterator from
|
|
// the trie as the fallback. It's not as efficient as the flat state iterator.
|
|
tr, err := trie.NewStateTrie(trie.StateTrieID(si.root), si.triedb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newMerkleTrieIterator(tr, start, true)
|
|
}
|
|
|
|
// NewStorageIterator creates a storage iterator for the state specified by
|
|
// the address hash. It begins at a specified starting position, corresponding
|
|
// to a particular initial key (or the next key if the specified one does not exist).
|
|
//
|
|
// The starting position here refers to the hash of the slot key.
|
|
func (si *stateIteratee) NewStorageIterator(addressHash common.Hash, start common.Hash) (StorageIterator, error) {
|
|
// If the external snapshot is available (hash scheme), try to initialize
|
|
// the storage iterator from there first.
|
|
if si.snap != nil {
|
|
it, err := si.snap.StorageIterator(si.root, addressHash, start)
|
|
if err == nil {
|
|
return newFlatStorageIterator(it, si.triedb), nil
|
|
}
|
|
}
|
|
// If the external snapshot is not available, try to initialize the
|
|
// storage iterator from the trie database (path scheme)
|
|
it, err := si.triedb.StorageIterator(si.root, addressHash, start)
|
|
if err == nil {
|
|
return newFlatStorageIterator(it, si.triedb), nil
|
|
}
|
|
if !si.merkle {
|
|
return nil, fmt.Errorf("state %x is not available for storage traversal", si.root)
|
|
}
|
|
// The snapshot is not usable so far, construct the storage iterator from
|
|
// the trie as the fallback. It's not as efficient as the flat state iterator.
|
|
tr, err := trie.NewStateTrie(trie.StateTrieID(si.root), si.triedb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
acct, err := tr.GetAccountByHash(addressHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if acct == nil || acct.Root == types.EmptyRootHash {
|
|
return &exhaustedIterator{}, nil
|
|
}
|
|
storageTr, err := trie.NewStateTrie(trie.StorageTrieID(si.root, addressHash, acct.Root), si.triedb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newMerkleTrieIterator(storageTr, start, false)
|
|
}
|
|
|
|
type exhaustedIterator struct{}
|
|
|
|
func (e exhaustedIterator) Next() bool {
|
|
return false
|
|
}
|
|
|
|
func (e exhaustedIterator) Error() error {
|
|
return nil
|
|
}
|
|
|
|
func (e exhaustedIterator) Hash() common.Hash {
|
|
return common.Hash{}
|
|
}
|
|
|
|
func (e exhaustedIterator) Release() {
|
|
}
|
|
|
|
func (e exhaustedIterator) Key() (common.Hash, error) {
|
|
return common.Hash{}, nil
|
|
}
|
|
|
|
func (e exhaustedIterator) Slot() common.Hash {
|
|
return common.Hash{}
|
|
}
|