Skip to content
37 changes: 20 additions & 17 deletions consensus/misc/eip4844/eip4844.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ func (bc *BlobConfig) blobPrice(excessBlobGas uint64) *big.Int {
return new(big.Int).Mul(f, big.NewInt(params.BlobTxBlobGasPerBlob))
}

func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
func latestBlobConfig(cfg *params.ChainConfig, time uint64) (BlobConfig, error) {
if cfg.BlobScheduleConfig == nil {
return nil
return BlobConfig{}, errors.New("no blob config")
}
var (
london = cfg.LondonBlock
Expand All @@ -80,14 +80,14 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
case cfg.IsCancun(london, time) && s.Cancun != nil:
bc = s.Cancun
default:
return nil
return BlobConfig{}, errors.New("no blob config")
}

return &BlobConfig{
return BlobConfig{
Target: bc.Target,
Max: bc.Max,
UpdateFraction: bc.UpdateFraction,
}
}, nil
}

// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
Expand All @@ -98,8 +98,8 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
panic("bad header pair")
}

bcfg := latestBlobConfig(config, header.Time)
if bcfg == nil {
bcfg, err := latestBlobConfig(config, header.Time)
if err != nil {
panic("called before EIP-4844 is active")
}

Expand Down Expand Up @@ -130,11 +130,14 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
// blobs on top of the excess blob gas.
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 {
isOsaka := config.IsOsaka(config.LondonBlock, headTimestamp)
bcfg := latestBlobConfig(config, headTimestamp)
bcfg, err := latestBlobConfig(config, headTimestamp)
if err != nil {
panic("calculating excess blob gas on nil blob config")
}
return calcExcessBlobGas(isOsaka, bcfg, parent)
}

func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uint64 {
func calcExcessBlobGas(isOsaka bool, bcfg BlobConfig, parent *types.Header) uint64 {
var parentExcessBlobGas, parentBlobGasUsed uint64
if parent.ExcessBlobGas != nil {
parentExcessBlobGas = *parent.ExcessBlobGas
Expand Down Expand Up @@ -169,17 +172,17 @@ func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uin

// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
blobConfig := latestBlobConfig(config, header.Time)
if blobConfig == nil {
blobConfig, err := latestBlobConfig(config, header.Time)
if err != nil {
panic("calculating blob fee on unsupported fork")
}
return blobConfig.blobBaseFee(*header.ExcessBlobGas)
}

// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
blobConfig := latestBlobConfig(cfg, time)
if blobConfig == nil {
blobConfig, err := latestBlobConfig(cfg, time)
if err != nil {
return 0
}
return blobConfig.Max
Expand All @@ -193,17 +196,17 @@ func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 {
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
// configuration, regardless of the currently active fork.
func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
bcfg := latestBlobConfig(cfg, math.MaxUint64)
if bcfg == nil {
bcfg, err := latestBlobConfig(cfg, math.MaxUint64)
if err != nil {
return 0
}
return bcfg.Max
}

// TargetBlobsPerBlock returns the target blobs per block for a block at the given timestamp.
func TargetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
blobConfig := latestBlobConfig(cfg, time)
if blobConfig == nil {
blobConfig, err := latestBlobConfig(cfg, time)
if err != nil {
return 0
}
return blobConfig.Target
Expand Down
5 changes: 1 addition & 4 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
func NewEVMTxContext(msg *Message) vm.TxContext {
ctx := vm.TxContext{
Origin: msg.From,
GasPrice: new(big.Int).Set(msg.GasPrice),
GasPrice: uint256.MustFromBig(msg.GasPrice),
BlobHashes: msg.BlobHashes,
}
if msg.BlobGasFeeCap != nil {
ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
}
return ctx
}

Expand Down
65 changes: 33 additions & 32 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ type journalEntry interface {
revert(*StateDB)

// dirtied returns the Ethereum address modified by this journal entry.
dirtied() *common.Address
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it introdice a noticeable impact on the allocation? Just out of curiosity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ROUTINE ======================== github.com/ethereum/go-ethereum/core/state.(*journal).append in github.com/ethereum/go-ethereum/core/state/journal.go
3735047495 3735047495 (flat, cum)  0.48% of Total
         .          .    101:func (j *journal) append(entry journalEntry) {
 594360687  594360687    102:	j.entries = append(j.entries, entry)
3005545312 3005545312    103:	if addr := entry.dirtied(); addr != nil {
 135141496  135141496    104:		j.dirties[*addr]++
         .          .    105:	}

Almost half a percent, but its also in the critical path, so it might make a small impact

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure allocation can be reduced by adopting the new approach? The address object is returned as the copy alongside the boolean flag.

Copy link
Copy Markdown
Contributor

@jrhea jrhea Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

escape analysis shows "moved to heap: ch" in journal.go on master for lines 297/328/343/357/372/387/402 due to return &ch.account from dirtied() *Address. Those escapes go away with (Address,bool).

$ go test ./core/state -run TestNonExistent -gcflags='-m=2' 2>&1 | rg 'journal\.go' | rg 'moved to heap: ch|escapes to heap: ch'
core/state/journal.go:297:7: moved to heap: ch
core/state/journal.go:328:7: moved to heap: ch
core/state/journal.go:343:7: moved to heap: ch
core/state/journal.go:357:7: moved to heap: ch
core/state/journal.go:372:7: moved to heap: ch
core/state/journal.go:387:7: moved to heap: ch
core/state/journal.go:402:7: moved to heap: ch

// indicates false if no address was changed.
dirtied() (common.Address, bool)

// copy returns a deep-copied journal entry.
copy() journalEntry
Expand Down Expand Up @@ -100,8 +101,8 @@ func (j *journal) revertToSnapshot(revid int, s *StateDB) {
// append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry)
if addr := entry.dirtied(); addr != nil {
j.dirties[*addr]++
if addr, dirty := entry.dirtied(); dirty {
j.dirties[addr]++
}
}

Expand All @@ -113,9 +114,9 @@ func (j *journal) revert(statedb *StateDB, snapshot int) {
j.entries[i].revert(statedb)

// Drop any dirty tracking induced by the change
if addr := j.entries[i].dirtied(); addr != nil {
if j.dirties[*addr]--; j.dirties[*addr] == 0 {
delete(j.dirties, *addr)
if addr, dirty := j.entries[i].dirtied(); dirty {
if j.dirties[addr]--; j.dirties[addr] == 0 {
delete(j.dirties, addr)
}
}
}
Expand Down Expand Up @@ -294,8 +295,8 @@ func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, ch.account)
}

func (ch createObjectChange) dirtied() *common.Address {
return &ch.account
func (ch createObjectChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch createObjectChange) copy() journalEntry {
Expand All @@ -308,8 +309,8 @@ func (ch createContractChange) revert(s *StateDB) {
s.getStateObject(ch.account).newContract = false
}

func (ch createContractChange) dirtied() *common.Address {
return nil
func (ch createContractChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch createContractChange) copy() journalEntry {
Expand All @@ -325,8 +326,8 @@ func (ch selfDestructChange) revert(s *StateDB) {
}
}

func (ch selfDestructChange) dirtied() *common.Address {
return &ch.account
func (ch selfDestructChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch selfDestructChange) copy() journalEntry {
Expand All @@ -340,8 +341,8 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) revert(s *StateDB) {
}

func (ch touchChange) dirtied() *common.Address {
return &ch.account
func (ch touchChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch touchChange) copy() journalEntry {
Expand All @@ -354,8 +355,8 @@ func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setBalance(ch.prev)
}

func (ch balanceChange) dirtied() *common.Address {
return &ch.account
func (ch balanceChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch balanceChange) copy() journalEntry {
Expand All @@ -369,8 +370,8 @@ func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setNonce(ch.prev)
}

func (ch nonceChange) dirtied() *common.Address {
return &ch.account
func (ch nonceChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch nonceChange) copy() journalEntry {
Expand All @@ -384,8 +385,8 @@ func (ch codeChange) revert(s *StateDB) {
s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode)
}

func (ch codeChange) dirtied() *common.Address {
return &ch.account
func (ch codeChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch codeChange) copy() journalEntry {
Expand All @@ -399,8 +400,8 @@ func (ch storageChange) revert(s *StateDB) {
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
}

func (ch storageChange) dirtied() *common.Address {
return &ch.account
func (ch storageChange) dirtied() (common.Address, bool) {
return ch.account, true
}

func (ch storageChange) copy() journalEntry {
Expand All @@ -416,8 +417,8 @@ func (ch transientStorageChange) revert(s *StateDB) {
s.setTransientState(ch.account, ch.key, ch.prevalue)
}

func (ch transientStorageChange) dirtied() *common.Address {
return nil
func (ch transientStorageChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch transientStorageChange) copy() journalEntry {
Expand All @@ -432,8 +433,8 @@ func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}

func (ch refundChange) dirtied() *common.Address {
return nil
func (ch refundChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch refundChange) copy() journalEntry {
Expand All @@ -452,8 +453,8 @@ func (ch addLogChange) revert(s *StateDB) {
s.logSize--
}

func (ch addLogChange) dirtied() *common.Address {
return nil
func (ch addLogChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch addLogChange) copy() journalEntry {
Expand All @@ -475,8 +476,8 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
s.accessList.DeleteAddress(ch.address)
}

func (ch accessListAddAccountChange) dirtied() *common.Address {
return nil
func (ch accessListAddAccountChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch accessListAddAccountChange) copy() journalEntry {
Expand All @@ -489,8 +490,8 @@ func (ch accessListAddSlotChange) revert(s *StateDB) {
s.accessList.DeleteSlot(ch.address, ch.slot)
}

func (ch accessListAddSlotChange) dirtied() *common.Address {
return nil
func (ch accessListAddSlotChange) dirtied() (common.Address, bool) {
return common.Address{}, false
}

func (ch accessListAddSlotChange) copy() journalEntry {
Expand Down
29 changes: 18 additions & 11 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ func (s Storage) Copy() Storage {
// - Account values as well as storages can be accessed and modified through the object.
// - Finally, call commit to return the changes of storage trie and update account data.
type stateObject struct {
db *StateDB
address common.Address // address of ethereum account
addrHash common.Hash // hash of ethereum address of the account
Comment thread
rjl493456442 marked this conversation as resolved.
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
data types.StateAccount // Account data with all mutations applied in the scope of block
db *StateDB
address common.Address // address of ethereum account
addressHash *common.Hash // hash of ethereum address of the account
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
data types.StateAccount // Account data with all mutations applied in the scope of block

// Write caches.
trie Trie // storage trie, which becomes non-nil on first access
Expand Down Expand Up @@ -101,7 +101,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
origin: origin,
data: *acct,
originStorage: make(Storage),
Expand All @@ -111,6 +110,14 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
}
}

func (s *stateObject) addrHash() common.Hash {
if s.addressHash == nil {
h := crypto.Keccak256Hash(s.address[:])
s.addressHash = &h
}
return *s.addressHash
}

func (s *stateObject) markSelfdestructed() {
s.selfDestructed = true
}
Expand Down Expand Up @@ -150,7 +157,7 @@ func (s *stateObject) getPrefetchedTrie() Trie {
return nil
}
// Attempt to retrieve the trie from the prefetcher
return s.db.prefetcher.trie(s.addrHash, s.data.Root)
return s.db.prefetcher.trie(s.addrHash(), s.data.Root)
}

// GetState retrieves a value associated with the given storage key.
Expand Down Expand Up @@ -202,7 +209,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {

// Schedule the resolved storage slots for prefetching if it's enabled.
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil {
if err = s.db.prefetcher.prefetch(s.addrHash(), s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil {
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
}
}
Expand Down Expand Up @@ -263,7 +270,7 @@ func (s *stateObject) finalise() {
s.pendingStorage[key] = value
}
if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
if err := s.db.prefetcher.prefetch(s.addrHash(), s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err)
}
}
Expand Down Expand Up @@ -358,7 +365,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
s.db.StorageDeleted.Add(1)
}
if s.db.prefetcher != nil {
s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used)
s.db.prefetcher.used(s.addrHash(), s.data.Root, nil, used)
}
s.uncommittedStorage = make(Storage) // empties the commit markers
return tr, nil
Expand Down Expand Up @@ -484,7 +491,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
obj := &stateObject{
db: db,
address: s.address,
addrHash: s.addrHash,
addressHash: nil,
origin: s.origin,
data: s.data,
code: s.code,
Expand Down
Loading