mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
core/state: implement merkle hasher
This commit is contained in:
parent
38c7021c73
commit
282cece030
12 changed files with 740 additions and 531 deletions
|
|
@ -2265,7 +2265,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
stats.CodeReads = statedb.CodeReads
|
||||
|
||||
stats.AccountLoaded = statedb.AccountLoaded
|
||||
stats.AccountUpdated = statedb.AccountUpdated
|
||||
//stats.AccountUpdated = statedb.AccountUpdated
|
||||
stats.AccountDeleted = statedb.AccountDeleted
|
||||
stats.StorageLoaded = statedb.StorageLoaded
|
||||
stats.StorageUpdated = int(statedb.StorageUpdated.Load())
|
||||
|
|
@ -2273,8 +2273,8 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
|||
|
||||
stats.CodeLoaded = statedb.CodeLoaded
|
||||
stats.CodeLoadBytes = statedb.CodeLoadBytes
|
||||
stats.CodeUpdated = statedb.CodeUpdated
|
||||
stats.CodeUpdateBytes = statedb.CodeUpdateBytes
|
||||
//stats.CodeUpdated = statedb.CodeUpdated
|
||||
//stats.CodeUpdateBytes = statedb.CodeUpdateBytes
|
||||
|
||||
stats.Execution = ptime - (statedb.AccountReads + statedb.StorageReads + statedb.CodeReads) // The time spent on EVM processing
|
||||
stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
|
|
@ -220,8 +221,10 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
|||
return newReader(db.codedb.Reader(), sr), nil
|
||||
}
|
||||
|
||||
// Hasher implements Database, returning a hasher associated with the specified
|
||||
// state root.
|
||||
func (db *CachingDB) Hasher(stateRoot common.Hash) (Hasher, error) {
|
||||
return &noopHasher{}, nil
|
||||
return newMerkleHasher(stateRoot, db.triedb)
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
|
|
@ -300,18 +303,26 @@ func (db *CachingDB) Commit(update *stateUpdate) error {
|
|||
}
|
||||
// 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)
|
||||
//}
|
||||
accounts, _, storages, _, err := update.encodeMerkle()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.snap.Update(update.root, update.originRoot, accounts, 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())
|
||||
stateSet, err := update.stateSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, stateSet)
|
||||
}
|
||||
|
||||
// Iteratee returns a state iteratee associated with the specified state root,
|
||||
|
|
|
|||
|
|
@ -17,25 +17,38 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
// AccountMutation describes a state transition for a single account.
|
||||
type AccountMutation struct {
|
||||
Account *Account // Null for deletion
|
||||
DirtyCode bool // Flag whether the code is changed
|
||||
Code []byte // Null for deletion
|
||||
// CodeMut represents a mutation to contract code.
|
||||
type CodeMut struct {
|
||||
Code []byte // Null for deletion
|
||||
}
|
||||
|
||||
// SecondaryHash encapsulates the secondary hash of storage tries.
|
||||
// It is only relevant in the context of the Merkle Patricia Trie and
|
||||
// includes both the post-transition root and the original root.
|
||||
type SecondaryHash struct {
|
||||
Hash common.Hash
|
||||
Prev common.Hash
|
||||
// AccountMut represents a mutation to an account.
|
||||
// Semantics:
|
||||
// - Account == nil: delete the account
|
||||
// - Code == nil: leave code unchanged
|
||||
// - Code != nil: apply the given code mutation
|
||||
type AccountMut struct {
|
||||
Account *Account // Null for deletion
|
||||
Code *CodeMut // Null for unchanged
|
||||
}
|
||||
|
||||
// Hashes encapsulates a trie root together with its original (pre-update) root.
|
||||
type Hashes struct {
|
||||
Hash common.Hash // Post-mutation root
|
||||
Prev common.Hash // Pre-mutation root
|
||||
}
|
||||
|
||||
// Hasher defines the minimal interface for computing state root hashes.
|
||||
|
|
@ -54,11 +67,9 @@ type SecondaryHash struct {
|
|||
// compatibility with pre-Byzantium semantics.
|
||||
type Hasher interface {
|
||||
// UpdateAccount writes a list of accounts into the state.
|
||||
UpdateAccount(addresses []common.Address, accounts []AccountMutation) error
|
||||
UpdateAccount(addresses []common.Address, accounts []AccountMut) error
|
||||
|
||||
// UpdateStorage writes a list of storage slot value. The hasher handles
|
||||
// encoding internally. Note, the value with empty data should be
|
||||
// interpreted as deletion.
|
||||
// UpdateStorage writes a list of storage slot value.
|
||||
UpdateStorage(address common.Address, keys []common.Hash, values []common.Hash) error
|
||||
|
||||
// Hash computes and returns the state root hash without committing.
|
||||
|
|
@ -70,14 +81,14 @@ type Hasher interface {
|
|||
// Additionally, if the hasher uses a two-layer structure, the roots of the
|
||||
// secondary tries together with their original hashes will also be returned
|
||||
// for all mutated accounts, regardless of whether their storage was modified.
|
||||
Commit() (common.Hash, *trienode.MergedNodeSet, map[common.Address]SecondaryHash, error)
|
||||
Commit() (common.Hash, *trienode.MergedNodeSet, map[common.Address]Hashes, error)
|
||||
|
||||
// Copy returns a deep-copied hasher instance.
|
||||
Copy() Hasher
|
||||
}
|
||||
|
||||
// Prefetcher is an optional extension implemented by hashers that can
|
||||
// asynchronously warm up trie/state data ahead of mutations or hashing.
|
||||
// asynchronously warm up trie/state data ahead of hashing.
|
||||
type Prefetcher interface {
|
||||
// PrefetchAccount schedules the account for prefetching.
|
||||
PrefetchAccount(addresses []common.Address, read bool)
|
||||
|
|
@ -87,9 +98,9 @@ type Prefetcher interface {
|
|||
}
|
||||
|
||||
// WitnessCollector is an optional extension implemented by hashers that can
|
||||
// construct a stateless witness for the most recent committed state transition.
|
||||
// construct a state witness for the most recent committed state transition.
|
||||
type WitnessCollector interface {
|
||||
// Witness returns the stateless witness corresponding to the most recent
|
||||
// Witness returns the state witness corresponding to the most recent
|
||||
// committed state transition.
|
||||
Witness() (*stateless.Witness, error)
|
||||
}
|
||||
|
|
@ -120,29 +131,207 @@ type Prover interface {
|
|||
ProveStorage(addr common.Address, key common.Hash, proofDb ethdb.KeyValueWriter) error
|
||||
}
|
||||
|
||||
// noopHasher is a Hasher implementation that performs no work and always
|
||||
// returns an empty state root.
|
||||
type noopHasher struct{}
|
||||
|
||||
func (n noopHasher) UpdateAccount(addresses []common.Address, accounts []AccountMutation) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
func (n *noopHasher) UpdateAccount([]common.Address, []AccountMut) error { return nil }
|
||||
func (n *noopHasher) UpdateStorage(common.Address, []common.Hash, []common.Hash) error {
|
||||
return nil
|
||||
}
|
||||
func (n *noopHasher) Hash() common.Hash { return common.Hash{} }
|
||||
func (n *noopHasher) Commit() (common.Hash, *trienode.MergedNodeSet, map[common.Address]Hashes, error) {
|
||||
return common.Hash{}, trienode.NewMergedNodeSet(), make(map[common.Address]Hashes), nil
|
||||
}
|
||||
func (n *noopHasher) Copy() Hasher { return &noopHasher{} }
|
||||
|
||||
// merkleHasher is a Hasher implementation backed by the traditional two-layer
|
||||
// Merkle Patricia Trie (separate account trie and per-account storage tries).
|
||||
type merkleHasher struct {
|
||||
db *triedb.Database
|
||||
root common.Hash
|
||||
|
||||
accountTrie *trie.StateTrie
|
||||
storageTries map[common.Address]*trie.StateTrie // lazily opened
|
||||
|
||||
// storageRoots tracks the storage root transition for every mutated
|
||||
// account. Prev is recorded once (first touch) and Hash is updated
|
||||
// on each UpdateAccount call.
|
||||
storageRoots map[common.Address]Hashes
|
||||
|
||||
lock sync.Mutex // guards storageTries (concurrent updateTrie)
|
||||
}
|
||||
|
||||
func (n noopHasher) UpdateStorage(address common.Address, keys []common.Hash, values []common.Hash) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
func newMerkleHasher(root common.Hash, db *triedb.Database) (*merkleHasher, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &merkleHasher{
|
||||
db: db,
|
||||
root: root,
|
||||
accountTrie: tr,
|
||||
storageTries: make(map[common.Address]*trie.StateTrie),
|
||||
storageRoots: make(map[common.Address]Hashes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n noopHasher) Hash() common.Hash {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
// accountStorageRoot reads the storage root of account from the account trie.
|
||||
func (h *merkleHasher) accountStorageRoot(addr common.Address) common.Hash {
|
||||
if acc, _ := h.accountTrie.GetAccount(addr); acc != nil {
|
||||
return acc.Root
|
||||
}
|
||||
return types.EmptyRootHash
|
||||
}
|
||||
|
||||
func (n noopHasher) Commit() (common.Hash, *trienode.MergedNodeSet, map[common.Address]SecondaryHash, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
// recordOrigin records the original (pre-mutation) storage root for addr.
|
||||
// Only the first call per address has any effect.
|
||||
func (h *merkleHasher) recordOrigin(addr common.Address) {
|
||||
if _, ok := h.storageRoots[addr]; !ok {
|
||||
root := h.accountStorageRoot(addr)
|
||||
h.storageRoots[addr] = Hashes{
|
||||
Prev: root,
|
||||
Hash: root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n noopHasher) Copy() Hasher {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
// openStorageTrie returns the cached storage trie for the given address,
|
||||
// or opens one from the database if not already cached.
|
||||
func (h *merkleHasher) openStorageTrie(address common.Address) (*trie.StateTrie, error) {
|
||||
if st, ok := h.storageTries[address]; ok {
|
||||
return st, nil
|
||||
}
|
||||
// Record the original storage trie root if it has not already been tracked
|
||||
// when the storage trie is loaded.
|
||||
h.recordOrigin(address)
|
||||
|
||||
id := trie.StorageTrieID(h.root, crypto.Keccak256Hash(address.Bytes()), h.accountStorageRoot(address))
|
||||
st, err := trie.NewStateTrie(id, h.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.storageTries[address] = st
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (h *merkleHasher) UpdateStorage(address common.Address, keys []common.Hash, values []common.Hash) error {
|
||||
h.lock.Lock()
|
||||
st, err := h.openStorageTrie(address)
|
||||
if err != nil {
|
||||
h.lock.Unlock()
|
||||
return err
|
||||
}
|
||||
h.lock.Unlock()
|
||||
|
||||
for i, key := range keys {
|
||||
if values[i] == (common.Hash{}) {
|
||||
if err := st.DeleteStorage(address, key[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := st.UpdateStorage(address, key[:], common.TrimLeftZeroes(values[i][:])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *merkleHasher) UpdateAccount(addresses []common.Address, accounts []AccountMut) error {
|
||||
for i, addr := range addresses {
|
||||
h.recordOrigin(addr)
|
||||
acct := accounts[i]
|
||||
|
||||
// Deletion: remove from account trie and evict any cached
|
||||
// storage trie so a re-created account starts fresh.
|
||||
if acct.Account == nil {
|
||||
if err := h.accountTrie.DeleteAccount(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(h.storageTries, addr)
|
||||
|
||||
h.storageRoots[addr] = Hashes{
|
||||
Prev: h.storageRoots[addr].Prev,
|
||||
Hash: types.EmptyRootHash,
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Determine storage root from the cached trie (if storage was
|
||||
// modified) or from the account trie (unchanged storage).
|
||||
storageRoot := h.accountStorageRoot(addr)
|
||||
if st, ok := h.storageTries[addr]; ok {
|
||||
storageRoot = st.Hash()
|
||||
}
|
||||
sa := &types.StateAccount{
|
||||
Nonce: acct.Account.Nonce,
|
||||
Balance: acct.Account.Balance,
|
||||
Root: storageRoot,
|
||||
CodeHash: acct.Account.CodeHash,
|
||||
}
|
||||
if err := h.accountTrie.UpdateAccount(addr, sa, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
h.storageRoots[addr] = Hashes{
|
||||
Prev: h.storageRoots[addr].Prev,
|
||||
Hash: storageRoot,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *merkleHasher) Hash() common.Hash {
|
||||
return h.accountTrie.Hash()
|
||||
}
|
||||
|
||||
func (h *merkleHasher) Commit() (common.Hash, *trienode.MergedNodeSet, map[common.Address]Hashes, error) {
|
||||
nodes := trienode.NewMergedNodeSet()
|
||||
|
||||
// Commit all dirty storage tries.
|
||||
for _, st := range h.storageTries {
|
||||
if _, set := st.Commit(false); set != nil {
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
return common.Hash{}, nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// Commit the account trie. collectLeaf must be true so that hashdb
|
||||
// can link account trie leaves to their storage trie roots.
|
||||
root, set := h.accountTrie.Commit(true)
|
||||
if set != nil {
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
return common.Hash{}, nil, nil, err
|
||||
}
|
||||
}
|
||||
return root, nodes, h.storageRoots, nil
|
||||
}
|
||||
|
||||
func (h *merkleHasher) Copy() Hasher {
|
||||
cpy := &merkleHasher{
|
||||
db: h.db,
|
||||
root: h.root,
|
||||
accountTrie: h.accountTrie.Copy(),
|
||||
storageTries: make(map[common.Address]*trie.StateTrie, len(h.storageTries)),
|
||||
storageRoots: maps.Clone(h.storageRoots),
|
||||
}
|
||||
for addr, st := range h.storageTries {
|
||||
cpy.storageTries[addr] = st.Copy()
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// ProveAccount implements Prover by constructing a Merkle proof for the
|
||||
// given account against the current account trie.
|
||||
func (h *merkleHasher) ProveAccount(addr common.Address, proofDb ethdb.KeyValueWriter) error {
|
||||
return h.accountTrie.Prove(crypto.Keccak256(addr.Bytes()), proofDb)
|
||||
}
|
||||
|
||||
// ProveStorage implements Prover by constructing a Merkle proof for the given
|
||||
// storage slot. The storage trie is opened lazily if not already cached.
|
||||
func (h *merkleHasher) ProveStorage(addr common.Address, key common.Hash, proofDb ethdb.KeyValueWriter) error {
|
||||
st, err := h.openStorageTrie(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return st.Prove(crypto.Keccak256(key.Bytes()), proofDb)
|
||||
}
|
||||
|
|
|
|||
75
core/state/state_mut.go
Normal file
75
core/state/state_mut.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// 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"
|
||||
|
||||
type mutationType int
|
||||
|
||||
const (
|
||||
update mutationType = iota
|
||||
deletion
|
||||
)
|
||||
|
||||
type mutation struct {
|
||||
typ mutationType
|
||||
applied bool
|
||||
|
||||
// precedingDelete indicates that a previously unapplied deletion was
|
||||
// overwritten by an update (account deleted then re-created within
|
||||
// the same block). IntermediateRoot uses this to notify the hasher
|
||||
// of the deletion before the update so that any cached storage trie
|
||||
// is evicted and the re-created account starts with a fresh trie.
|
||||
precedingDelete bool
|
||||
}
|
||||
|
||||
func (m *mutation) copy() *mutation {
|
||||
return &mutation{typ: m.typ, applied: m.applied, precedingDelete: m.precedingDelete}
|
||||
}
|
||||
|
||||
func (m *mutation) isDelete() bool {
|
||||
return m.typ == deletion
|
||||
}
|
||||
|
||||
// markDelete is invoked when an account is deleted but the deletion is
|
||||
// not yet committed. The pending mutation is cached and will be applied
|
||||
// all together.
|
||||
func (s *StateDB) markDelete(addr common.Address) {
|
||||
if _, ok := s.mutations[addr]; !ok {
|
||||
s.mutations[addr] = &mutation{}
|
||||
}
|
||||
s.mutations[addr].applied = false
|
||||
s.mutations[addr].typ = deletion
|
||||
s.mutations[addr].precedingDelete = false
|
||||
}
|
||||
|
||||
func (s *StateDB) markUpdate(addr common.Address) {
|
||||
m, ok := s.mutations[addr]
|
||||
if !ok {
|
||||
s.mutations[addr] = &mutation{}
|
||||
m = s.mutations[addr]
|
||||
}
|
||||
// If this update overwrites a pending (unapplied) deletion, record it
|
||||
// so that IntermediateRoot can notify the hasher of the deletion first.
|
||||
// Do not reset precedingDelete otherwise: a subsequent markUpdate must
|
||||
// preserve the flag set by an earlier markDelete→markUpdate sequence.
|
||||
if !m.applied && m.typ == deletion {
|
||||
m.precedingDelete = true
|
||||
}
|
||||
m.applied = false
|
||||
m.typ = update
|
||||
}
|
||||
|
|
@ -121,15 +121,6 @@ func (s *stateObject) touch() {
|
|||
s.db.journal.touchChange(s.address)
|
||||
}
|
||||
|
||||
// getTrie returns the associated storage trie. The trie will be opened if it's
|
||||
// not loaded previously. An error will be returned if trie can't be loaded.
|
||||
//
|
||||
// If a new trie is opened, it will be cached within the state object to allow
|
||||
// subsequent reads to expand the same trie instead of reloading from disk.
|
||||
func (s *stateObject) getTrie() (Trie, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetState retrieves a value associated with the given storage key.
|
||||
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
||||
value, _ := s.getState(key)
|
||||
|
|
@ -294,6 +285,7 @@ func (s *stateObject) updateTrie() error {
|
|||
vals = append(vals, value)
|
||||
}
|
||||
s.uncommittedStorage = make(Storage) // empties the commit markers
|
||||
|
||||
return s.db.hasher.UpdateStorage(s.address, keys, vals)
|
||||
}
|
||||
|
||||
|
|
@ -344,12 +336,12 @@ func (s *stateObject) commit() (*accountUpdate, error) {
|
|||
}
|
||||
// commit the contract code if it's modified
|
||||
if s.dirtyCode {
|
||||
s.dirtyCode = false // reset the dirty flag
|
||||
|
||||
op.code = &contractCode{
|
||||
hash: common.BytesToHash(s.CodeHash()),
|
||||
blob: s.code,
|
||||
}
|
||||
s.dirtyCode = false // reset the dirty flag
|
||||
|
||||
if s.origin == nil {
|
||||
op.code.originHash = types.EmptyCodeHash
|
||||
} else {
|
||||
|
|
@ -494,7 +486,3 @@ func (s *stateObject) Balance() *uint256.Int {
|
|||
func (s *stateObject) Nonce() uint64 {
|
||||
return s.data.Nonce
|
||||
}
|
||||
|
||||
func (s *stateObject) Root() common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
|
@ -126,134 +127,137 @@ func (s SizeStats) add(diff SizeStats) SizeStats {
|
|||
|
||||
// calSizeStats measures the state size changes of the provided state update.
|
||||
func calSizeStats(update *stateUpdate) (SizeStats, error) {
|
||||
return SizeStats{}, nil
|
||||
//stats := SizeStats{
|
||||
// BlockNumber: update.blockNumber,
|
||||
// StateRoot: update.root,
|
||||
//}
|
||||
//
|
||||
//// Measure the account changes
|
||||
//for addr, oldValue := range update.accountsOrigin {
|
||||
// addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
// newValue, exists := update.accounts[addrHash]
|
||||
// if !exists {
|
||||
// return SizeStats{}, fmt.Errorf("account %x not found", addr)
|
||||
// }
|
||||
// oldLen, newLen := len(oldValue), len(newValue)
|
||||
//
|
||||
// switch {
|
||||
// case oldLen > 0 && newLen == 0:
|
||||
// // Account deletion
|
||||
// stats.Accounts -= 1
|
||||
// stats.AccountBytes -= accountKeySize + int64(oldLen)
|
||||
// case oldLen == 0 && newLen > 0:
|
||||
// // Account creation
|
||||
// stats.Accounts += 1
|
||||
// stats.AccountBytes += accountKeySize + int64(newLen)
|
||||
// default:
|
||||
// // Account update
|
||||
// stats.AccountBytes += int64(newLen - oldLen)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Measure storage changes
|
||||
//for addr, slots := range update.storagesOrigin {
|
||||
// addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
// subset, exists := update.storages[addrHash]
|
||||
// if !exists {
|
||||
// return SizeStats{}, fmt.Errorf("storage %x not found", addr)
|
||||
// }
|
||||
// for key, oldValue := range slots {
|
||||
// var (
|
||||
// exists bool
|
||||
// newValue []byte
|
||||
// )
|
||||
// if update.rawStorageKey {
|
||||
// newValue, exists = subset[crypto.Keccak256Hash(key.Bytes())]
|
||||
// } else {
|
||||
// newValue, exists = subset[key]
|
||||
// }
|
||||
// if !exists {
|
||||
// return SizeStats{}, fmt.Errorf("storage slot %x-%x not found", addr, key)
|
||||
// }
|
||||
// oldLen, newLen := len(oldValue), len(newValue)
|
||||
//
|
||||
// switch {
|
||||
// case oldLen > 0 && newLen == 0:
|
||||
// // Storage deletion
|
||||
// stats.Storages -= 1
|
||||
// stats.StorageBytes -= storageKeySize + int64(oldLen)
|
||||
// case oldLen == 0 && newLen > 0:
|
||||
// // Storage creation
|
||||
// stats.Storages += 1
|
||||
// stats.StorageBytes += storageKeySize + int64(newLen)
|
||||
// default:
|
||||
// // Storage update
|
||||
// stats.StorageBytes += int64(newLen - oldLen)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Measure trienode changes
|
||||
//for owner, subset := range update.nodes.Sets {
|
||||
// var (
|
||||
// keyPrefix int64
|
||||
// isAccount = owner == (common.Hash{})
|
||||
// )
|
||||
// if isAccount {
|
||||
// keyPrefix = accountTrienodePrefixSize
|
||||
// } else {
|
||||
// keyPrefix = storageTrienodePrefixSize
|
||||
// }
|
||||
//
|
||||
// // Iterate over Origins since every modified node has an origin entry
|
||||
// for path, oldNode := range subset.Origins {
|
||||
// newNode, exists := subset.Nodes[path]
|
||||
// if !exists {
|
||||
// return SizeStats{}, fmt.Errorf("node %x-%v not found", owner, path)
|
||||
// }
|
||||
// keySize := keyPrefix + int64(len(path))
|
||||
//
|
||||
// switch {
|
||||
// case len(oldNode) > 0 && len(newNode.Blob) == 0:
|
||||
// // Node deletion
|
||||
// if isAccount {
|
||||
// stats.AccountTrienodes -= 1
|
||||
// stats.AccountTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
// } else {
|
||||
// stats.StorageTrienodes -= 1
|
||||
// stats.StorageTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
// }
|
||||
// case len(oldNode) == 0 && len(newNode.Blob) > 0:
|
||||
// // Node creation
|
||||
// if isAccount {
|
||||
// stats.AccountTrienodes += 1
|
||||
// stats.AccountTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
// } else {
|
||||
// stats.StorageTrienodes += 1
|
||||
// stats.StorageTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
// }
|
||||
// default:
|
||||
// // Node update
|
||||
// if isAccount {
|
||||
// stats.AccountTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
// } else {
|
||||
// stats.StorageTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//codeExists := make(map[common.Hash]struct{})
|
||||
//for _, code := range update.codes {
|
||||
// if _, ok := codeExists[code.hash]; ok || code.duplicate {
|
||||
// continue
|
||||
// }
|
||||
// stats.ContractCodes += 1
|
||||
// stats.ContractCodeBytes += codeKeySize + int64(len(code.blob))
|
||||
// codeExists[code.hash] = struct{}{}
|
||||
//}
|
||||
//return stats, nil
|
||||
stats := SizeStats{
|
||||
BlockNumber: update.blockNumber,
|
||||
StateRoot: update.root,
|
||||
}
|
||||
accounts, accountOrigin, storages, storageOrigin, err := update.encodeMerkle()
|
||||
if err != nil {
|
||||
return SizeStats{}, err
|
||||
}
|
||||
|
||||
// Measure the account changes
|
||||
for addr, oldValue := range accountOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
newValue, exists := accounts[addrHash]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("account %x not found", addr)
|
||||
}
|
||||
oldLen, newLen := len(oldValue), len(newValue)
|
||||
|
||||
switch {
|
||||
case oldLen > 0 && newLen == 0:
|
||||
// Account deletion
|
||||
stats.Accounts -= 1
|
||||
stats.AccountBytes -= accountKeySize + int64(oldLen)
|
||||
case oldLen == 0 && newLen > 0:
|
||||
// Account creation
|
||||
stats.Accounts += 1
|
||||
stats.AccountBytes += accountKeySize + int64(newLen)
|
||||
default:
|
||||
// Account update
|
||||
stats.AccountBytes += int64(newLen - oldLen)
|
||||
}
|
||||
}
|
||||
|
||||
// Measure storage changes
|
||||
for addr, slots := range storageOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
subset, exists := storages[addrHash]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("storage %x not found", addr)
|
||||
}
|
||||
for key, oldValue := range slots {
|
||||
var (
|
||||
exists bool
|
||||
newValue []byte
|
||||
)
|
||||
if update.rawStorageKey {
|
||||
newValue, exists = subset[crypto.Keccak256Hash(key.Bytes())]
|
||||
} else {
|
||||
newValue, exists = subset[key]
|
||||
}
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("storage slot %x-%x not found", addr, key)
|
||||
}
|
||||
oldLen, newLen := len(oldValue), len(newValue)
|
||||
|
||||
switch {
|
||||
case oldLen > 0 && newLen == 0:
|
||||
// Storage deletion
|
||||
stats.Storages -= 1
|
||||
stats.StorageBytes -= storageKeySize + int64(oldLen)
|
||||
case oldLen == 0 && newLen > 0:
|
||||
// Storage creation
|
||||
stats.Storages += 1
|
||||
stats.StorageBytes += storageKeySize + int64(newLen)
|
||||
default:
|
||||
// Storage update
|
||||
stats.StorageBytes += int64(newLen - oldLen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure trienode changes
|
||||
for owner, subset := range update.nodes.Sets {
|
||||
var (
|
||||
keyPrefix int64
|
||||
isAccount = owner == (common.Hash{})
|
||||
)
|
||||
if isAccount {
|
||||
keyPrefix = accountTrienodePrefixSize
|
||||
} else {
|
||||
keyPrefix = storageTrienodePrefixSize
|
||||
}
|
||||
|
||||
// Iterate over Origins since every modified node has an origin entry
|
||||
for path, oldNode := range subset.Origins {
|
||||
newNode, exists := subset.Nodes[path]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("node %x-%v not found", owner, path)
|
||||
}
|
||||
keySize := keyPrefix + int64(len(path))
|
||||
|
||||
switch {
|
||||
case len(oldNode) > 0 && len(newNode.Blob) == 0:
|
||||
// Node deletion
|
||||
if isAccount {
|
||||
stats.AccountTrienodes -= 1
|
||||
stats.AccountTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
} else {
|
||||
stats.StorageTrienodes -= 1
|
||||
stats.StorageTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
}
|
||||
case len(oldNode) == 0 && len(newNode.Blob) > 0:
|
||||
// Node creation
|
||||
if isAccount {
|
||||
stats.AccountTrienodes += 1
|
||||
stats.AccountTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
} else {
|
||||
stats.StorageTrienodes += 1
|
||||
stats.StorageTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
}
|
||||
default:
|
||||
// Node update
|
||||
if isAccount {
|
||||
stats.AccountTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
} else {
|
||||
stats.StorageTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
codeExists := make(map[common.Hash]struct{})
|
||||
for _, code := range update.codes {
|
||||
if _, ok := codeExists[code.hash]; ok || code.duplicate {
|
||||
continue
|
||||
}
|
||||
stats.ContractCodes += 1
|
||||
stats.ContractCodeBytes += codeKeySize + int64(len(code.blob))
|
||||
codeExists[code.hash] = struct{}{}
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
type stateSizeQuery struct {
|
||||
|
|
|
|||
|
|
@ -42,26 +42,6 @@ import (
|
|||
// TriesInMemory represents the number of layers that are kept in RAM.
|
||||
const TriesInMemory = 128
|
||||
|
||||
type mutationType int
|
||||
|
||||
const (
|
||||
update mutationType = iota
|
||||
deletion
|
||||
)
|
||||
|
||||
type mutation struct {
|
||||
typ mutationType
|
||||
applied bool
|
||||
}
|
||||
|
||||
func (m *mutation) copy() *mutation {
|
||||
return &mutation{typ: m.typ, applied: m.applied}
|
||||
}
|
||||
|
||||
func (m *mutation) isDelete() bool {
|
||||
return m.typ == deletion
|
||||
}
|
||||
|
||||
// StateDB structs within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. StateDBs take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
|
|
@ -144,7 +124,6 @@ type StateDB struct {
|
|||
CodeReads time.Duration
|
||||
|
||||
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||
AccountUpdated int // Number of accounts updated during the state transition
|
||||
AccountDeleted int // Number of accounts deleted during the state transition
|
||||
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
|
||||
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
|
||||
|
|
@ -154,10 +133,8 @@ type StateDB struct {
|
|||
// This value may be smaller than the actual number of bytes read, since
|
||||
// some APIs (e.g. CodeSize) may load the entire code from either the
|
||||
// cache or the database when the size is not available in the cache.
|
||||
CodeLoaded int // Number of contract code loaded during the state transition
|
||||
CodeLoadBytes int // Total bytes of resolved code
|
||||
CodeUpdated int // Number of contracts with code changes that persisted
|
||||
CodeUpdateBytes int // Total bytes of persisted code written
|
||||
CodeLoaded int // Number of contract code loaded during the state transition
|
||||
CodeLoadBytes int // Total bytes of resolved code
|
||||
}
|
||||
|
||||
// New creates a new state from a given trie.
|
||||
|
|
@ -311,19 +288,6 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
// GetStorageRoot retrieves the storage root from the given address or empty
|
||||
// if object not found.
|
||||
//
|
||||
// Note: the storage root returned corresponds to the trie since last Intermediate
|
||||
// operation, some recent in-memory changes are excluded.
|
||||
func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Root()
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// TxIndex returns the current transaction index set by SetTxContext.
|
||||
func (s *StateDB) TxIndex() int {
|
||||
return s.txIndex
|
||||
|
|
@ -777,64 +741,73 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
// Finalise all the dirty storage states and write them into the tries
|
||||
s.Finalise(deleteEmptyObjects)
|
||||
|
||||
// Process all storage updates concurrently. The state object update root
|
||||
// method will internally call a blocking trie fetch from the prefetcher,
|
||||
// so there's no need to explicitly wait for the prefetchers to finish.
|
||||
// Pre-process mutations whose preceding deletion has not yet been
|
||||
// applied. This happens when an account is deleted and then re-created
|
||||
// within the same block and the deletion was overwritten by the update.
|
||||
// Notify the hasher of the deletion first so that any cached storage
|
||||
// trie is evicted and the re-created account starts with a fresh trie.
|
||||
var (
|
||||
start = time.Now()
|
||||
workers errgroup.Group
|
||||
delAddrs []common.Address
|
||||
delAccts []AccountMut
|
||||
start = time.Now()
|
||||
)
|
||||
for addr, op := range s.mutations {
|
||||
if !op.precedingDelete {
|
||||
continue
|
||||
}
|
||||
op.precedingDelete = false
|
||||
delAddrs = append(delAddrs, addr)
|
||||
delAccts = append(delAccts, AccountMut{Account: nil})
|
||||
}
|
||||
if len(delAddrs) > 0 {
|
||||
if err := s.hasher.UpdateAccount(delAddrs, delAccts); err != nil {
|
||||
s.setError(err)
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
s.AccountUpdates += time.Since(start)
|
||||
|
||||
// Process all storage updates concurrently, flushing them to hasher.
|
||||
start = time.Now()
|
||||
var workers errgroup.Group
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr] // closure for the task runner below
|
||||
obj := s.stateObjects[addr]
|
||||
workers.Go(obj.updateTrie)
|
||||
}
|
||||
workers.Wait()
|
||||
if err := workers.Wait(); err != nil {
|
||||
s.setError(err)
|
||||
}
|
||||
s.StorageUpdates += time.Since(start)
|
||||
|
||||
// Now we're about to start to write changes to the trie. The trie is so far
|
||||
// _untouched_. We can check with the prefetcher, if it can give us a trie
|
||||
// which has the same root, but also has some content loaded into it.
|
||||
//
|
||||
// Don't check prefetcher if verkle trie has been used. In the context of verkle,
|
||||
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
||||
// here could result in losing uncommitted changes from storage.
|
||||
start = time.Now()
|
||||
|
||||
// Process all account updates
|
||||
var (
|
||||
addresses []common.Address
|
||||
accounts []AccountMutation
|
||||
accounts []AccountMut
|
||||
)
|
||||
start = time.Now()
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied {
|
||||
continue
|
||||
}
|
||||
op.applied = true
|
||||
|
||||
addresses = append(addresses, addr)
|
||||
|
||||
if op.isDelete() {
|
||||
accounts = append(accounts, AccountMutation{Account: nil})
|
||||
} else {
|
||||
obj := s.stateObjects[addr]
|
||||
mut := AccountMutation{
|
||||
Account: &obj.data,
|
||||
DirtyCode: obj.dirtyCode,
|
||||
Code: obj.code,
|
||||
}
|
||||
accounts = append(accounts, mut)
|
||||
|
||||
s.AccountUpdated += 1
|
||||
|
||||
// Count code writes post-Finalise so reverted CREATEs are excluded.
|
||||
if obj.dirtyCode {
|
||||
s.CodeUpdated += 1
|
||||
s.CodeUpdateBytes += len(obj.code)
|
||||
}
|
||||
accounts = append(accounts, AccountMut{Account: nil})
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr]
|
||||
mut := AccountMut{Account: &obj.data}
|
||||
if obj.dirtyCode {
|
||||
mut.Code = &CodeMut{Code: obj.code}
|
||||
}
|
||||
accounts = append(accounts, mut)
|
||||
}
|
||||
if err := s.hasher.UpdateAccount(addresses, accounts); err != nil {
|
||||
s.setError(err)
|
||||
return common.Hash{}
|
||||
}
|
||||
s.AccountUpdates += time.Since(start)
|
||||
|
|
@ -1028,7 +1001,6 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
|
|||
}
|
||||
accountReadMeters.Mark(int64(s.AccountLoaded))
|
||||
storageReadMeters.Mark(int64(s.StorageLoaded))
|
||||
accountUpdatedMeter.Mark(int64(s.AccountUpdated))
|
||||
storageUpdatedMeter.Mark(s.StorageUpdated.Load())
|
||||
accountDeletedMeter.Mark(int64(s.AccountDeleted))
|
||||
storageDeletedMeter.Mark(s.StorageDeleted.Load())
|
||||
|
|
@ -1038,7 +1010,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
|
|||
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
|
||||
|
||||
// Clear the metric markers
|
||||
s.AccountLoaded, s.AccountUpdated, s.AccountDeleted = 0, 0, 0
|
||||
s.AccountLoaded, s.AccountDeleted = 0, 0
|
||||
s.StorageLoaded = 0
|
||||
s.StorageUpdated.Store(0)
|
||||
s.StorageDeleted.Store(0)
|
||||
|
|
@ -1186,25 +1158,6 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre
|
|||
return s.accessList.Contains(addr, slot)
|
||||
}
|
||||
|
||||
// markDelete is invoked when an account is deleted but the deletion is
|
||||
// not yet committed. The pending mutation is cached and will be applied
|
||||
// all together
|
||||
func (s *StateDB) markDelete(addr common.Address) {
|
||||
if _, ok := s.mutations[addr]; !ok {
|
||||
s.mutations[addr] = &mutation{}
|
||||
}
|
||||
s.mutations[addr].applied = false
|
||||
s.mutations[addr].typ = deletion
|
||||
}
|
||||
|
||||
func (s *StateDB) markUpdate(addr common.Address) {
|
||||
if _, ok := s.mutations[addr]; !ok {
|
||||
s.mutations[addr] = &mutation{}
|
||||
}
|
||||
s.mutations[addr].applied = false
|
||||
s.mutations[addr].typ = update
|
||||
}
|
||||
|
||||
// Witness retrieves the current state witness being collected.
|
||||
func (s *StateDB) Witness() *stateless.Witness {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
|
|
@ -182,10 +183,11 @@ func (test *stateTest) run() bool {
|
|||
storages []map[common.Hash]map[common.Hash][]byte
|
||||
storageOrigin []map[common.Address]map[common.Hash][]byte
|
||||
copyUpdate = func(update *stateUpdate) {
|
||||
//accounts = append(accounts, maps.Clone(update.accounts))
|
||||
//accountOrigin = append(accountOrigin, maps.Clone(update.accountsOrigin))
|
||||
//storages = append(storages, maps.Clone(update.storages))
|
||||
//storageOrigin = append(storageOrigin, maps.Clone(update.storagesOrigin))
|
||||
encoded, _ := update.stateSet()
|
||||
accounts = append(accounts, maps.Clone(encoded.Accounts))
|
||||
accountOrigin = append(accountOrigin, maps.Clone(encoded.AccountsOrigin))
|
||||
storages = append(storages, maps.Clone(encoded.Storages))
|
||||
storageOrigin = append(storageOrigin, maps.Clone(encoded.StoragesOrigin))
|
||||
}
|
||||
disk = rawdb.NewMemoryDatabase()
|
||||
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
|
|
@ -540,47 +538,6 @@ func (test *snapshotTest) run() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
so := s.getStateObject(addr)
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
tr, err := so.getTrie()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trieIt, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
it = trie.NewIterator(trieIt)
|
||||
visited = make(map[common.Hash]bool)
|
||||
)
|
||||
|
||||
for it.Next() {
|
||||
key := common.BytesToHash(tr.GetKey(it.Key))
|
||||
visited[key] = true
|
||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
||||
if !cb(key, value) {
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(it.Value) > 0 {
|
||||
_, content, _, err := rlp.Split(it.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cb(key, common.BytesToHash(content)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkEqual checks that methods of state and checkstate return the same values.
|
||||
func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
for _, addr := range test.addrs {
|
||||
|
|
@ -606,12 +563,6 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
|||
}
|
||||
// Check storage.
|
||||
if obj := state.getStateObject(addr); obj != nil {
|
||||
forEachStorage(state, addr, func(key, value common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
|
||||
})
|
||||
forEachStorage(checkstate, addr, func(key, value common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
|
||||
})
|
||||
other := checkstate.getStateObject(addr)
|
||||
// Check dirty storage which is not in trie
|
||||
if !maps.Equal(obj.dirtyStorage, other.dirtyStorage) {
|
||||
|
|
@ -770,8 +721,14 @@ func TestCopyCommitCopy(t *testing.T) {
|
|||
t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, common.Hash{})
|
||||
}
|
||||
// Commit state, ensure states can be loaded from disk
|
||||
root, _ := state.Commit(0, false, false)
|
||||
state, _ = New(root, tdb)
|
||||
root, err := state.Commit(0, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("commit fail: %v", err)
|
||||
}
|
||||
state, err = New(root, tdb)
|
||||
if err != nil {
|
||||
t.Fatalf("New fail: %v", err)
|
||||
}
|
||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
|
@ -30,8 +33,8 @@ import (
|
|||
// contractCode encapsulates contract bytecode and its associated metadata.
|
||||
type contractCode struct {
|
||||
hash common.Hash // hash is the cryptographic hash of the current contract code.
|
||||
blob []byte // blob is the raw byte representation of the current contract code.
|
||||
originHash common.Hash // originHash is the cryptographic hash of the code prior to mutation.
|
||||
blob []byte // blob is the raw byte representation of the current contract code.
|
||||
|
||||
// Derived fields, populated only when state tracking is enabled.
|
||||
duplicate bool // duplicate indicates whether the updated code already exists.
|
||||
|
|
@ -87,7 +90,7 @@ type stateUpdate struct {
|
|||
|
||||
codes map[common.Address]*contractCode // codes contains mutated contract codes, keyed by address.
|
||||
nodes *trienode.MergedNodeSet // nodes aggregates all dirty trie nodes produced by the update.
|
||||
secondaryHashes map[common.Address]SecondaryHash // hashes of secondary tries
|
||||
secondaryHashes map[common.Address]Hashes // hashes of secondary tries
|
||||
}
|
||||
|
||||
// empty returns a flag indicating the state transition is empty or not.
|
||||
|
|
@ -101,7 +104,7 @@ func (sc *stateUpdate) empty() bool {
|
|||
//
|
||||
// rawStorageKey is a flag indicating whether to use the raw storage slot key or
|
||||
// the hash of the slot key for constructing state update object.
|
||||
func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, blockNumber uint64, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet, secondaryHashes map[common.Address]SecondaryHash) *stateUpdate {
|
||||
func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, blockNumber uint64, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet, secondaryHashes map[common.Address]Hashes) *stateUpdate {
|
||||
var (
|
||||
accounts = make(map[common.Hash]*Account)
|
||||
accountsOrigin = make(map[common.Address]*Account)
|
||||
|
|
@ -182,19 +185,87 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
|
|||
}
|
||||
}
|
||||
|
||||
func encodeSlot(val common.Hash) []byte {
|
||||
if val == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
blob, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(val[:]))
|
||||
return blob
|
||||
}
|
||||
|
||||
func (sc *stateUpdate) encodeMerkle() (map[common.Hash][]byte, map[common.Address][]byte, map[common.Hash]map[common.Hash][]byte, map[common.Address]map[common.Hash][]byte, error) {
|
||||
var (
|
||||
accounts = make(map[common.Hash][]byte)
|
||||
storages = make(map[common.Hash]map[common.Hash][]byte)
|
||||
accountOrigin = make(map[common.Address][]byte)
|
||||
storageOrigin = make(map[common.Address]map[common.Hash][]byte)
|
||||
)
|
||||
for addr, prev := range sc.accountsOrigin {
|
||||
if prev == nil {
|
||||
accountOrigin[addr] = nil
|
||||
} else {
|
||||
pair, ok := sc.secondaryHashes[addr]
|
||||
if !ok {
|
||||
return nil, nil, nil, nil, errors.New("no secondary hash")
|
||||
}
|
||||
accountOrigin[addr] = types.SlimAccountRLP(types.StateAccount{
|
||||
Balance: prev.Balance,
|
||||
Nonce: prev.Nonce,
|
||||
CodeHash: prev.CodeHash,
|
||||
Root: pair.Prev,
|
||||
})
|
||||
}
|
||||
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
data := sc.accounts[addrHash]
|
||||
if data == nil {
|
||||
accounts[addrHash] = nil
|
||||
} else {
|
||||
pair, ok := sc.secondaryHashes[addr]
|
||||
if !ok {
|
||||
return nil, nil, nil, nil, errors.New("no secondary hash")
|
||||
}
|
||||
accounts[addrHash] = types.SlimAccountRLP(types.StateAccount{
|
||||
Balance: data.Balance,
|
||||
Nonce: data.Nonce,
|
||||
CodeHash: data.CodeHash,
|
||||
Root: pair.Hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
for addr, slots := range sc.storagesOrigin {
|
||||
subset := make(map[common.Hash][]byte)
|
||||
for key, val := range slots {
|
||||
subset[key] = encodeSlot(val)
|
||||
}
|
||||
storageOrigin[addr] = subset
|
||||
}
|
||||
for addrHash, slots := range sc.storages {
|
||||
subset := make(map[common.Hash][]byte)
|
||||
for key, val := range slots {
|
||||
subset[key] = encodeSlot(val)
|
||||
}
|
||||
storages[addrHash] = subset
|
||||
}
|
||||
return accounts, accountOrigin, storages, storageOrigin, nil
|
||||
}
|
||||
|
||||
// stateSet converts the current stateUpdate object into a triedb.StateSet
|
||||
// object. This function extracts the necessary data from the stateUpdate
|
||||
// struct and formats it into the StateSet structure consumed by the triedb
|
||||
// package.
|
||||
func (sc *stateUpdate) stateSet() *triedb.StateSet {
|
||||
return nil
|
||||
//return &triedb.StateSet{
|
||||
// Accounts: sc.accounts,
|
||||
// AccountsOrigin: sc.accountsOrigin,
|
||||
// Storages: sc.storages,
|
||||
// StoragesOrigin: sc.storagesOrigin,
|
||||
// RawStorageKey: sc.rawStorageKey,
|
||||
//}
|
||||
func (sc *stateUpdate) stateSet() (*triedb.StateSet, error) {
|
||||
accounts, accountOrigin, storages, storageOrigin, err := sc.encodeMerkle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &triedb.StateSet{
|
||||
Accounts: accounts,
|
||||
AccountsOrigin: accountOrigin,
|
||||
Storages: storages,
|
||||
StoragesOrigin: storageOrigin,
|
||||
RawStorageKey: sc.rawStorageKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// deriveCodeFields derives the missing fields of contract code changes
|
||||
|
|
@ -226,141 +297,117 @@ func (sc *stateUpdate) deriveCodeFields(reader ContractCodeReader) error {
|
|||
|
||||
// ToTracingUpdate converts the internal stateUpdate to an exported tracing.StateUpdate.
|
||||
func (sc *stateUpdate) ToTracingUpdate() (*tracing.StateUpdate, error) {
|
||||
return nil, nil
|
||||
//update := &tracing.StateUpdate{
|
||||
// OriginRoot: sc.originRoot,
|
||||
// Root: sc.root,
|
||||
// BlockNumber: sc.blockNumber,
|
||||
// AccountChanges: make(map[common.Address]*tracing.AccountChange, len(sc.accountsOrigin)),
|
||||
// StorageChanges: make(map[common.Address]map[common.Hash]*tracing.StorageChange),
|
||||
// CodeChanges: make(map[common.Address]*tracing.CodeChange, len(sc.codes)),
|
||||
// TrieChanges: make(map[common.Hash]map[string]*tracing.TrieNodeChange),
|
||||
//}
|
||||
//// Gather all account changes
|
||||
//for addr, oldData := range sc.accountsOrigin {
|
||||
// addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
// newData, exists := sc.accounts[addrHash]
|
||||
// if !exists {
|
||||
// return nil, fmt.Errorf("account %x not found", addr)
|
||||
// }
|
||||
// change := &tracing.AccountChange{}
|
||||
//
|
||||
// if len(oldData) > 0 {
|
||||
// acct, err := types.FullAccount(oldData)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// change.Prev = &types.StateAccount{
|
||||
// Nonce: acct.Nonce,
|
||||
// Balance: acct.Balance,
|
||||
// Root: acct.Root,
|
||||
// CodeHash: acct.CodeHash,
|
||||
// }
|
||||
// }
|
||||
// if len(newData) > 0 {
|
||||
// acct, err := types.FullAccount(newData)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// change.New = &types.StateAccount{
|
||||
// Nonce: acct.Nonce,
|
||||
// Balance: acct.Balance,
|
||||
// Root: acct.Root,
|
||||
// CodeHash: acct.CodeHash,
|
||||
// }
|
||||
// }
|
||||
// update.AccountChanges[addr] = change
|
||||
//}
|
||||
//
|
||||
//// Gather all storage slot changes
|
||||
//for addr, slots := range sc.storagesOrigin {
|
||||
// addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
// subset, exists := sc.storages[addrHash]
|
||||
// if !exists {
|
||||
// return nil, fmt.Errorf("storage %x not found", addr)
|
||||
// }
|
||||
// storageChanges := make(map[common.Hash]*tracing.StorageChange, len(slots))
|
||||
//
|
||||
// for key, encPrev := range slots {
|
||||
// // Get new value - handle both raw and hashed key formats
|
||||
// var (
|
||||
// exists bool
|
||||
// encNew []byte
|
||||
// decPrev []byte
|
||||
// decNew []byte
|
||||
// err error
|
||||
// )
|
||||
// if sc.rawStorageKey {
|
||||
// encNew, exists = subset[crypto.Keccak256Hash(key.Bytes())]
|
||||
// } else {
|
||||
// encNew, exists = subset[key]
|
||||
// }
|
||||
// if !exists {
|
||||
// return nil, fmt.Errorf("storage slot %x-%x not found", addr, key)
|
||||
// }
|
||||
//
|
||||
// // Decode the prev and new values
|
||||
// if len(encPrev) > 0 {
|
||||
// _, decPrev, _, err = rlp.Split(encPrev)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("failed to decode prevValue: %v", err)
|
||||
// }
|
||||
// }
|
||||
// if len(encNew) > 0 {
|
||||
// _, decNew, _, err = rlp.Split(encNew)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("failed to decode newValue: %v", err)
|
||||
// }
|
||||
// }
|
||||
// storageChanges[key] = &tracing.StorageChange{
|
||||
// Prev: common.BytesToHash(decPrev),
|
||||
// New: common.BytesToHash(decNew),
|
||||
// }
|
||||
// }
|
||||
// update.StorageChanges[addr] = storageChanges
|
||||
//}
|
||||
//
|
||||
//// Gather all contract code changes
|
||||
//for addr, code := range sc.codes {
|
||||
// change := &tracing.CodeChange{
|
||||
// New: &tracing.ContractCode{
|
||||
// Hash: code.hash,
|
||||
// Code: code.blob,
|
||||
// Exists: code.duplicate,
|
||||
// },
|
||||
// }
|
||||
// if code.originHash != types.EmptyCodeHash {
|
||||
// change.Prev = &tracing.ContractCode{
|
||||
// Hash: code.originHash,
|
||||
// Code: code.originBlob,
|
||||
// Exists: true,
|
||||
// }
|
||||
// }
|
||||
// update.CodeChanges[addr] = change
|
||||
//}
|
||||
//
|
||||
//// Gather all trie node changes
|
||||
//if sc.nodes != nil {
|
||||
// for owner, subset := range sc.nodes.Sets {
|
||||
// nodeChanges := make(map[string]*tracing.TrieNodeChange, len(subset.Origins))
|
||||
// for path, oldNode := range subset.Origins {
|
||||
// newNode, exists := subset.Nodes[path]
|
||||
// if !exists {
|
||||
// return nil, fmt.Errorf("node %x-%v not found", owner, path)
|
||||
// }
|
||||
// nodeChanges[path] = &tracing.TrieNodeChange{
|
||||
// Prev: &trienode.Node{
|
||||
// Hash: crypto.Keccak256Hash(oldNode),
|
||||
// Blob: oldNode,
|
||||
// },
|
||||
// New: &trienode.Node{
|
||||
// Hash: newNode.Hash,
|
||||
// Blob: newNode.Blob,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// update.TrieChanges[owner] = nodeChanges
|
||||
// }
|
||||
//}
|
||||
//return update, nil
|
||||
update := &tracing.StateUpdate{
|
||||
OriginRoot: sc.originRoot,
|
||||
Root: sc.root,
|
||||
BlockNumber: sc.blockNumber,
|
||||
AccountChanges: make(map[common.Address]*tracing.AccountChange, len(sc.accountsOrigin)),
|
||||
StorageChanges: make(map[common.Address]map[common.Hash]*tracing.StorageChange),
|
||||
CodeChanges: make(map[common.Address]*tracing.CodeChange, len(sc.codes)),
|
||||
TrieChanges: make(map[common.Hash]map[string]*tracing.TrieNodeChange),
|
||||
}
|
||||
// Gather all account changes
|
||||
for addr, oldData := range sc.accountsOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
newData, exists := sc.accounts[addrHash]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("account %x not found", addr)
|
||||
}
|
||||
hashes := sc.secondaryHashes[addr]
|
||||
change := &tracing.AccountChange{}
|
||||
|
||||
if oldData != nil {
|
||||
change.Prev = &types.StateAccount{
|
||||
Nonce: oldData.Nonce,
|
||||
Balance: oldData.Balance,
|
||||
Root: hashes.Prev,
|
||||
CodeHash: oldData.CodeHash,
|
||||
}
|
||||
}
|
||||
if newData != nil {
|
||||
change.New = &types.StateAccount{
|
||||
Nonce: newData.Nonce,
|
||||
Balance: newData.Balance,
|
||||
Root: hashes.Hash,
|
||||
CodeHash: newData.CodeHash,
|
||||
}
|
||||
}
|
||||
update.AccountChanges[addr] = change
|
||||
}
|
||||
|
||||
// Gather all storage slot changes
|
||||
for addr, slots := range sc.storagesOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
subset, exists := sc.storages[addrHash]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("storage %x not found", addr)
|
||||
}
|
||||
storageChanges := make(map[common.Hash]*tracing.StorageChange, len(slots))
|
||||
|
||||
for key, prev := range slots {
|
||||
// Get new value - handle both raw and hashed key formats
|
||||
var (
|
||||
exists bool
|
||||
current common.Hash
|
||||
)
|
||||
if sc.rawStorageKey {
|
||||
current, exists = subset[crypto.Keccak256Hash(key.Bytes())]
|
||||
} else {
|
||||
current, exists = subset[key]
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("storage slot %x-%x not found", addr, key)
|
||||
}
|
||||
|
||||
storageChanges[key] = &tracing.StorageChange{
|
||||
Prev: prev,
|
||||
New: current,
|
||||
}
|
||||
}
|
||||
update.StorageChanges[addr] = storageChanges
|
||||
}
|
||||
|
||||
// Gather all contract code changes
|
||||
for addr, code := range sc.codes {
|
||||
change := &tracing.CodeChange{
|
||||
New: &tracing.ContractCode{
|
||||
Hash: code.hash,
|
||||
Code: code.blob,
|
||||
Exists: code.duplicate,
|
||||
},
|
||||
}
|
||||
if code.originHash != types.EmptyCodeHash {
|
||||
change.Prev = &tracing.ContractCode{
|
||||
Hash: code.originHash,
|
||||
Code: code.originBlob,
|
||||
Exists: true,
|
||||
}
|
||||
}
|
||||
update.CodeChanges[addr] = change
|
||||
}
|
||||
|
||||
// Gather all trie node changes
|
||||
if sc.nodes != nil {
|
||||
for owner, subset := range sc.nodes.Sets {
|
||||
nodeChanges := make(map[string]*tracing.TrieNodeChange, len(subset.Origins))
|
||||
for path, oldNode := range subset.Origins {
|
||||
newNode, exists := subset.Nodes[path]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("node %x-%v not found", owner, path)
|
||||
}
|
||||
nodeChanges[path] = &tracing.TrieNodeChange{
|
||||
Prev: &trienode.Node{
|
||||
Hash: crypto.Keccak256Hash(oldNode),
|
||||
Blob: oldNode,
|
||||
},
|
||||
New: &trienode.Node{
|
||||
Hash: newNode.Hash,
|
||||
Blob: newNode.Blob,
|
||||
},
|
||||
}
|
||||
}
|
||||
update.TrieChanges[owner] = nodeChanges
|
||||
}
|
||||
}
|
||||
return update, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,38 +232,31 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.Block
|
|||
}
|
||||
|
||||
func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Address, start []byte, maxResult int) (StorageRangeResult, error) {
|
||||
storageRoot := statedb.GetStorageRoot(address)
|
||||
if storageRoot == types.EmptyRootHash || storageRoot == (common.Hash{}) {
|
||||
return StorageRangeResult{}, nil // empty storage
|
||||
it, err := statedb.Database().Iteratee(root)
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
storageIt, err := it.NewStorageIterator(crypto.Keccak256Hash(address.Bytes()), common.BytesToHash(start))
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
// TODO(rjl493456442) it's problematic for traversing the state with in-memory
|
||||
// state mutations, specifically txIndex != 0.
|
||||
id := trie.StorageTrieID(root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
||||
tr, err := trie.NewStateTrie(id, statedb.Database().TrieDB())
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
trieIt, err := tr.NodeIterator(start)
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
result := StorageRangeResult{Storage: storageMap{}}
|
||||
for i := 0; i < maxResult && it.Next(); i++ {
|
||||
_, content, _, err := rlp.Split(it.Value)
|
||||
for i := 0; i < maxResult && storageIt.Next(); i++ {
|
||||
_, content, _, err := rlp.Split(storageIt.Slot())
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
e := storageEntry{Value: common.BytesToHash(content)}
|
||||
if preimage := tr.GetKey(it.Key); preimage != nil {
|
||||
preimage := common.BytesToHash(preimage)
|
||||
if preimage, err := storageIt.Key(); err == nil {
|
||||
e.Key = &preimage
|
||||
}
|
||||
result.Storage[common.BytesToHash(it.Key)] = e
|
||||
result.Storage[storageIt.Hash()] = e
|
||||
}
|
||||
// Add the 'next key' so clients can continue downloading.
|
||||
if it.Next() {
|
||||
next := common.BytesToHash(it.Key)
|
||||
if storageIt.Next() {
|
||||
next := storageIt.Hash()
|
||||
result.NextKey = &next
|
||||
}
|
||||
return result, nil
|
||||
|
|
|
|||
|
|
@ -388,17 +388,15 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
|
|||
return nil, err
|
||||
}
|
||||
codeHash := statedb.GetCodeHash(address)
|
||||
storageRoot := statedb.GetStorageRoot(address)
|
||||
|
||||
hasher, err := statedb.Database().Hasher(header.Root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prover, ok := hasher.(state.Prover)
|
||||
if !ok {
|
||||
return nil, errors.New("state proving is not supported")
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
var storageTrie state.Trie
|
||||
if storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) {
|
||||
st, err := statedb.Database().OpenStorageTrie(header.Root, address, storageRoot, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageTrie = st
|
||||
}
|
||||
// Create the proofs for the storageKeys.
|
||||
for i, key := range keys {
|
||||
if err := ctx.Err(); err != nil {
|
||||
|
|
@ -414,12 +412,8 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
|
|||
} else {
|
||||
outputKey = hexutil.Encode(key[:])
|
||||
}
|
||||
if storageTrie == nil {
|
||||
storageProof[i] = StorageResult{outputKey, &hexutil.Big{}, []string{}}
|
||||
continue
|
||||
}
|
||||
var proof proofList
|
||||
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), &proof); err != nil {
|
||||
if err := prover.ProveStorage(address, crypto.Keccak256Hash(key.Bytes()), &proof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value := (*hexutil.Big)(statedb.GetState(address, key).Big())
|
||||
|
|
@ -427,12 +421,8 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
|
|||
}
|
||||
}
|
||||
// Create the accountProof.
|
||||
tr, err := statedb.Database().OpenTrie(header.Root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var accountProof proofList
|
||||
if err := tr.Prove(crypto.Keccak256(address.Bytes()), &accountProof); err != nil {
|
||||
if err := prover.ProveAccount(address, &accountProof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balance := statedb.GetBalance(address).ToBig()
|
||||
|
|
@ -442,7 +432,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
|
|||
Balance: (*hexutil.Big)(balance),
|
||||
CodeHash: codeHash,
|
||||
Nonce: hexutil.Uint64(statedb.GetNonce(address)),
|
||||
StorageHash: storageRoot,
|
||||
//StorageHash: storageRoot, // TODO(rjl493456442)
|
||||
StorageProof: storageProof,
|
||||
}, statedb.Error()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue