From 8a8fd7cef6f8978bd72a713801a54413c7ffaa74 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 12 May 2025 13:32:11 +0200 Subject: [PATCH 1/3] core, miner: do not populate receipts field during mining --- cmd/evm/internal/t8ntool/execution.go | 3 +-- core/chain_makers.go | 2 +- core/state/statedb.go | 12 +++------ core/state/statedb_test.go | 4 +-- core/state_processor.go | 37 +++++++++++++++++++-------- eth/tracers/api.go | 2 +- internal/ethapi/simulate.go | 2 +- miner/worker.go | 2 +- 8 files changed, 36 insertions(+), 28 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index b2e5f70714..1f1db4e7a6 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -146,7 +146,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre) signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) gaspool = new(core.GasPool) - blockHash = common.Hash{0x13, 0x37} rejectedTxs []*rejectedTx includedTxs types.Transactions gasUsed = uint64(0) @@ -303,7 +302,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } // Set the receipt logs and create the bloom filter. - receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash) + receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(receipt) // These three are non-consensus fields: diff --git a/core/chain_makers.go b/core/chain_makers.go index 37bddcfda5..e3e5867e99 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -130,7 +130,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) if b.header.BlobGasUsed != nil { - *b.header.BlobGasUsed += receipt.BlobGasUsed + *b.header.BlobGasUsed += uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 2453d67f3e..f09356e403 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -250,15 +250,9 @@ func (s *StateDB) AddLog(log *types.Log) { s.logSize++ } -// GetLogs returns the logs matching the specified transaction hash, and annotates -// them with the given blockNumber and blockHash. -func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log { - logs := s.logs[hash] - for _, l := range logs { - l.BlockNumber = blockNumber - l.BlockHash = blockHash - } - return logs +// GetLogs returns the logs matching the specified transaction hash. +func (s *StateDB) GetLogs(hash common.Hash) []*types.Log { + return s.logs[hash] } func (s *StateDB) Logs() []*types.Log { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index e740c64faa..90ec22f40c 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -669,9 +669,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", state.GetRefund(), checkstate.GetRefund()) } - if !reflect.DeepEqual(state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) { + if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", - state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) + state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) } if !maps.Equal(state.journal.dirties, checkstate.journal.dirties) { getKeys := func(dirty map[common.Address]int) string { diff --git a/core/state_processor.go b/core/state_processor.go index 322bd24f41..c42428aa9b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -97,7 +97,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } statedb.SetTxContext(tx.Hash(), i) - receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm) + receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm, true) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -136,7 +136,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // ApplyTransactionWithEVM attempts to apply a transaction to the given state database // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. -func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { +func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, fullReceipts bool) (receipt *types.Receipt, err error) { if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) @@ -165,19 +165,32 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, statedb.AccessEvents().Merge(evm.AccessEvents) } - return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil + if fullReceipts { + return MakeFullReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil + } + return makeConsensusReceipt(result, statedb, tx, *usedGas, root), nil } -// MakeReceipt generates the receipt object for a transaction given its execution result. -func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { - // Create a new receipt for the transaction, storing the intermediate root and gas used - // by the tx. +// makeConsensusReceipt generates the receipt object for a transaction given its execution result. +// It only populates the fields strictly required for consensus. +func makeConsensusReceipt(result *ExecutionResult, statedb *state.StateDB, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { receipt.Status = types.ReceiptStatusSuccessful } + receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Bloom = types.CreateBloom(receipt) + return receipt +} + +// MakeFullReceipt generates the receipt object for a transaction given its execution result. +// It also populates non-consensus fields. +func MakeFullReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { + // Create a new receipt for the transaction, storing the intermediate root and gas used + // by the tx. + receipt := makeConsensusReceipt(result, statedb, tx, usedGas, root) receipt.TxHash = tx.Hash() receipt.GasUsed = result.UsedGas @@ -191,9 +204,11 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } - // Set the receipt logs and create the bloom filter. - receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) - receipt.Bloom = types.CreateBloom(receipt) + // Annotate the logs with blockhash and blocknumber. + for _, log := range receipt.Logs { + log.BlockHash = blockHash + log.BlockNumber = blockNumber.Uint64() + } receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) @@ -210,7 +225,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header * return nil, err } // Create a new context to be used in the EVM environment - return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm) + return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, common.Hash{}, tx, usedGas, evm, false) } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root diff --git a/eth/tracers/api.go b/eth/tracers/api.go index fe72924828..fecac58268 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -1041,7 +1041,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor // Call Prepare to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm) + _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm, true) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index b997cf297b..61baffae2a 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -301,7 +301,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() } gasUsed += result.UsedGas - receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, tx, gasUsed, root) + receipts[i] = core.MakeFullReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, tx, gasUsed, root) blobGasUsed += receipts[i].BlobGasUsed logs := tracer.Logs() callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} diff --git a/miner/worker.go b/miner/worker.go index d80cb8913b..44041281b8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -297,7 +297,7 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio env.receipts = append(env.receipts, receipt) env.sidecars = append(env.sidecars, sc) env.blobs += len(sc.Blobs) - *env.header.BlobGasUsed += receipt.BlobGasUsed + *env.header.BlobGasUsed += uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) env.tcount++ return nil } From 4979d85ea31d5e51d04f84012b9ebba2986467f2 Mon Sep 17 00:00:00 2001 From: MariusVanDerWijden Date: Mon, 12 May 2025 16:29:51 +0200 Subject: [PATCH 2/3] core: only derive full receipts for tracing --- core/state_processor.go | 32 +++++++++++++++++--------------- eth/tracers/api.go | 2 +- internal/ethapi/simulate.go | 4 ++-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index c42428aa9b..7b394bb9cd 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -97,7 +97,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } statedb.SetTxContext(tx.Hash(), i) - receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm, true) + receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -136,17 +136,23 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // ApplyTransactionWithEVM attempts to apply a transaction to the given state database // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. -func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, fullReceipts bool) (receipt *types.Receipt, err error) { +func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { + var result *ExecutionResult if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) } if hooks.OnTxEnd != nil { - defer func() { hooks.OnTxEnd(receipt, err) }() + defer func() { + if receipt != nil { + receipt = deriveFullReceipt(receipt, evm, result, statedb, blockNumber, blockHash, tx) + } + hooks.OnTxEnd(receipt, err) + }() } } // Apply the transaction to the current state (included in the env). - result, err := ApplyMessage(evm, msg, gp) + result, err = ApplyMessage(evm, msg, gp) if err != nil { return nil, err } @@ -165,15 +171,13 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, statedb.AccessEvents().Merge(evm.AccessEvents) } - if fullReceipts { - return MakeFullReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil - } - return makeConsensusReceipt(result, statedb, tx, *usedGas, root), nil + receipt = MakeReceipt(result, statedb, tx, *usedGas, root) + return receipt, nil } -// makeConsensusReceipt generates the receipt object for a transaction given its execution result. +// MakeReceipt generates the receipt object for a transaction given its execution result. // It only populates the fields strictly required for consensus. -func makeConsensusReceipt(result *ExecutionResult, statedb *state.StateDB, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { +func MakeReceipt(result *ExecutionResult, statedb *state.StateDB, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed @@ -185,12 +189,10 @@ func makeConsensusReceipt(result *ExecutionResult, statedb *state.StateDB, tx *t return receipt } -// MakeFullReceipt generates the receipt object for a transaction given its execution result. -// It also populates non-consensus fields. -func MakeFullReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { +// deriveFullReceipt populates non-consensus fields. +func deriveFullReceipt(receipt *types.Receipt, evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction) *types.Receipt { // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. - receipt := makeConsensusReceipt(result, statedb, tx, usedGas, root) receipt.TxHash = tx.Hash() receipt.GasUsed = result.UsedGas @@ -225,7 +227,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header * return nil, err } // Create a new context to be used in the EVM environment - return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, common.Hash{}, tx, usedGas, evm, false) + return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, common.Hash{}, tx, usedGas, evm) } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root diff --git a/eth/tracers/api.go b/eth/tracers/api.go index fecac58268..fe72924828 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -1041,7 +1041,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor // Call Prepare to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm, true) + _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 61baffae2a..787311a40c 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -301,8 +301,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() } gasUsed += result.UsedGas - receipts[i] = core.MakeFullReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, tx, gasUsed, root) - blobGasUsed += receipts[i].BlobGasUsed + receipts[i] = core.MakeReceipt(result, sim.state, tx, gasUsed, root) + blobGasUsed += uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) logs := tracer.Logs() callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} if result.Failed() { From d4e6a9f76ecea8881ebcce43575f55d6f314cf5b Mon Sep 17 00:00:00 2001 From: MariusVanDerWijden Date: Wed, 21 May 2025 16:25:45 +0200 Subject: [PATCH 3/3] core/state: do not annotate logs in statedb --- core/state/journal.go | 1 - core/state/statedb.go | 6 ------ core/state/statedb_hooked_test.go | 5 ++--- core/state_processor.go | 7 +++++-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index 13332dbd79..854497a531 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -448,7 +448,6 @@ func (ch addLogChange) revert(s *StateDB) { } else { s.logs[ch.txhash] = logs[:len(logs)-1] } - s.logSize-- } func (ch addLogChange) dirtied() *common.Address { diff --git a/core/state/statedb.go b/core/state/statedb.go index f09356e403..53a7932753 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -119,7 +119,6 @@ type StateDB struct { thash common.Hash txIndex int logs map[common.Hash][]*types.Log - logSize uint // Preimages occurred seen by VM in the scope of block. preimages map[common.Hash][]byte @@ -243,11 +242,7 @@ func (s *StateDB) Error() error { func (s *StateDB) AddLog(log *types.Log) { s.journal.logChange(s.thash) - log.TxHash = s.thash - log.TxIndex = uint(s.txIndex) - log.Index = s.logSize s.logs[s.thash] = append(s.logs[s.thash], log) - s.logSize++ } // GetLogs returns the logs matching the specified transaction hash. @@ -669,7 +664,6 @@ func (s *StateDB) Copy() *StateDB { thash: s.thash, txIndex: s.txIndex, logs: make(map[common.Hash][]*types.Log, len(s.logs)), - logSize: s.logSize, preimages: maps.Clone(s.preimages), // Do we need to copy the access list and transient storage? diff --git a/core/state/statedb_hooked_test.go b/core/state/statedb_hooked_test.go index f319b0e63c..47b0b7e2ab 100644 --- a/core/state/statedb_hooked_test.go +++ b/core/state/statedb_hooked_test.go @@ -80,7 +80,6 @@ func TestBurn(t *testing.T) { // TestHooks is a basic sanity-check of all hooks func TestHooks(t *testing.T) { inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) - inner.SetTxContext(common.Hash{0x11}, 100) // For the log var result []string var wants = []string{ "0xaa00000000000000000000000000000000000000.balance: 0->100 (Unspecified)", @@ -89,7 +88,7 @@ func TestHooks(t *testing.T) { "0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)", "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011", "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022", - "log 100", + "log 0xbb00000000000000000000000000000000000000", } emitF := func(format string, a ...any) { result = append(result, fmt.Sprintf(format, a...)) @@ -108,7 +107,7 @@ func TestHooks(t *testing.T) { emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new) }, OnLog: func(log *types.Log) { - emitF("log %v", log.TxIndex) + emitF("log %v", log.Address) }, }) sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified) diff --git a/core/state_processor.go b/core/state_processor.go index 7b394bb9cd..6dd114def2 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -206,8 +206,11 @@ func deriveFullReceipt(receipt *types.Receipt, evm *vm.EVM, result *ExecutionRes receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } - // Annotate the logs with blockhash and blocknumber. - for _, log := range receipt.Logs { + // Annotate the logs + for index, log := range receipt.Logs { + log.TxHash = tx.Hash() + log.TxIndex = uint(statedb.TxIndex()) + log.Index = uint(index) log.BlockHash = blockHash log.BlockNumber = blockNumber.Uint64() }