From 5ab49e93423776a72b665a169152171cfd4f66ac Mon Sep 17 00:00:00 2001 From: jsvisa Date: Wed, 28 May 2025 13:45:49 +0000 Subject: [PATCH] core: calculate changeset inside stateupdate Signed-off-by: jsvisa --- core/blockchain.go | 110 ++++++-------------------------------- core/state/stateupdate.go | 100 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 94 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index c449408ce3..bf4194e882 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -45,7 +45,6 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/syncx" @@ -1589,7 +1588,22 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return err } // Tracing the state changes if the logger is enabled. - bc.handleStateUpdates(block, update) + if bc.logger != nil && bc.logger.OnStateCommit != nil { + sc := update.IntoChangeset() + bc.logger.OnStateCommit(&tracing.StateUpdate{ + Number: block.NumberU64(), + Hash: block.Hash(), + Time: block.Time(), + Accounts: int64(sc.Accounts), + AccountSize: int64(sc.AccountSize), + Storages: int64(sc.Storages), + StorageSize: int64(sc.StorageSize), + Trienodes: int64(sc.Trienodes), + TrienodeSize: int64(sc.TrienodeSize), + Codes: int64(sc.Codes), + CodeSize: int64(sc.CodeSize), + }) + } // If node is running in path mode, skip explicit gc operation // which is unnecessary in this mode. if bc.triedb.Scheme() == rawdb.PathScheme { @@ -1651,98 +1665,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return nil } -func (bc *BlockChain) handleStateUpdates(block *types.Block, update *state.StateUpdate) { - if bc.logger == nil || bc.logger.OnStateCommit == nil { - return - } - - var ( - accountSize, storageSize, nodeSize, codeSize int - accounts, storages, nodes, codes int - ) - - for addr, oldValue := range update.AccountsOrigin { - addrHash := crypto.Keccak256Hash(addr.Bytes()) - newValue, exists := update.Accounts[addrHash] - if !exists { - log.Warn("State update missing account", "address", addr) - continue - } - if len(newValue) == 0 { - accounts -= 1 - accountSize -= common.AddressLength - } - if len(oldValue) == 0 { - accounts += 1 - accountSize += common.AddressLength - } - accountSize += len(newValue) - len(oldValue) - } - for addr, slots := range update.StoragesOrigin { - addrHash := crypto.Keccak256Hash(addr.Bytes()) - subset, exists := update.Storages[addrHash] - if !exists { - log.Warn("State update missing storage", "address", addr) - continue - } - 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 { - log.Warn("State update missing storage slot", "address", addr, "key", key) - continue - } - if len(newValue) == 0 { - storages -= 1 - storageSize -= common.HashLength - } - if len(oldValue) == 0 { - storages += 1 - storageSize += common.HashLength - } - storageSize += len(newValue) - len(oldValue) - } - } - for _, subset := range update.Nodes.Sets { - for path, n := range subset.Nodes { - if len(n.Blob) == 0 { - nodes -= 1 - nodeSize -= len(path) + common.HashLength - } - if n.OriginLen() == 0 { - nodes += 1 - nodeSize += len(path) + common.HashLength - } - nodeSize += len(n.Blob) - n.OriginLen() - } - } - for _, code := range update.Codes { - codes += 1 - codeSize += code.CodeLen() + common.HashLength // no deduplication - } - - bc.logger.OnStateCommit(&tracing.StateUpdate{ - Number: block.NumberU64(), - Hash: block.Hash(), - Time: block.Time(), - Accounts: int64(accounts), - AccountSize: int64(accountSize), - Storages: int64(storages), - StorageSize: int64(storageSize), - Trienodes: int64(nodes), - TrienodeSize: int64(nodeSize), - Codes: int64(codes), - CodeSize: int64(codeSize), - }) -} - // writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead. // This function expects the chain mutex to be held. func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { diff --git a/core/state/stateupdate.go b/core/state/stateupdate.go index 3c290c17b6..a787626024 100644 --- a/core/state/stateupdate.go +++ b/core/state/stateupdate.go @@ -20,6 +20,8 @@ import ( "maps" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" ) @@ -192,3 +194,101 @@ func (sc *StateUpdate) stateSet() *triedb.StateSet { RawStorageKey: sc.RawStorageKey, } } + +// StateChangeset represents a state mutations that occurred during the execution of a block. +type StateChangeset struct { + Accounts int // Total number of accounts present in the state at this block + Storages int // Total number of storage entries across all accounts in the state at this block + Trienodes int // Total number of trie nodes present in the state at this block + Codes int // Total number of contract codes present in the state at this block, with 32 bytes hash as the identifier + AccountSize int // Combined size of all accounts in the state, with 20 bytes address as the identifier + StorageSize int // Combined size of all storage entries, with 32 bytes key as the identifier + TrienodeSize int // Combined size of all trie nodes, with varying size node path as the identifier (up to 64 bytes) + CodeSize int // Combined size of all contract codes in the state, with 20 bytes address as the identifier +} + +// IntoChangeset converts the current StateUpdate into a StateChangeset. +func (sc *StateUpdate) IntoChangeset() *StateChangeset { + var ( + accountSize, storageSize, nodeSize, codeSize int + accounts, storages, nodes, codes int + ) + + for addr, oldValue := range sc.AccountsOrigin { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + newValue, exists := sc.Accounts[addrHash] + if !exists { + log.Warn("State update missing account", "address", addr) + continue + } + if len(newValue) == 0 { + accounts -= 1 + accountSize -= common.AddressLength + } + if len(oldValue) == 0 { + accounts += 1 + accountSize += common.AddressLength + } + accountSize += len(newValue) - len(oldValue) + } + for addr, slots := range sc.StoragesOrigin { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + subset, exists := sc.Storages[addrHash] + if !exists { + log.Warn("State update missing storage", "address", addr) + continue + } + for key, oldValue := range slots { + var ( + exists bool + newValue []byte + ) + if sc.RawStorageKey { + newValue, exists = subset[crypto.Keccak256Hash(key.Bytes())] + } else { + newValue, exists = subset[key] + } + if !exists { + log.Warn("State update missing storage slot", "address", addr, "key", key) + continue + } + if len(newValue) == 0 { + storages -= 1 + storageSize -= common.HashLength + } + if len(oldValue) == 0 { + storages += 1 + storageSize += common.HashLength + } + storageSize += len(newValue) - len(oldValue) + } + } + for _, subset := range sc.Nodes.Sets { + for path, n := range subset.Nodes { + if len(n.Blob) == 0 { + nodes -= 1 + nodeSize -= len(path) + common.HashLength + } + if n.OriginLen() == 0 { + nodes += 1 + nodeSize += len(path) + common.HashLength + } + nodeSize += len(n.Blob) - n.OriginLen() + } + } + for _, code := range sc.Codes { + codes += 1 + codeSize += code.CodeLen() + common.HashLength // no deduplication + } + + return &StateChangeset{ + Accounts: accounts, + AccountSize: accountSize, + Storages: storages, + StorageSize: storageSize, + Trienodes: nodes, + TrienodeSize: nodeSize, + Codes: codes, + CodeSize: codeSize, + } +}