core: calculate changeset inside stateupdate

Signed-off-by: jsvisa <delweng@gmail.com>
This commit is contained in:
jsvisa 2025-05-28 13:45:49 +00:00
parent a6f31d86c2
commit 5ab49e9342
2 changed files with 116 additions and 94 deletions

View file

@ -45,7 +45,6 @@ import (
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/syncx" "github.com/ethereum/go-ethereum/internal/syncx"
@ -1589,7 +1588,22 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
return err return err
} }
// Tracing the state changes if the logger is enabled. // 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 // If node is running in path mode, skip explicit gc operation
// which is unnecessary in this mode. // which is unnecessary in this mode.
if bc.triedb.Scheme() == rawdb.PathScheme { if bc.triedb.Scheme() == rawdb.PathScheme {
@ -1651,98 +1665,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
return nil 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. // writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead.
// This function expects the chain mutex to be held. // 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) { func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {

View file

@ -20,6 +20,8 @@ import (
"maps" "maps"
"github.com/ethereum/go-ethereum/common" "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/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
) )
@ -192,3 +194,101 @@ func (sc *StateUpdate) stateSet() *triedb.StateSet {
RawStorageKey: sc.RawStorageKey, 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,
}
}