From 9482c091b69d469816bada8ab71bf1d7934b3b54 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 21 Nov 2024 16:34:18 +0800 Subject: [PATCH] core, cmd, eth, internal, miner: rework makeReceipt --- cmd/evm/internal/t8ntool/execution.go | 9 +- core/blockchain.go | 27 +--- core/chain_makers.go | 12 +- core/state/statedb.go | 20 +-- core/state/statedb_test.go | 4 +- core/state_processor.go | 185 +++++++++++++++----------- core/types.go | 2 +- core/types/log.go | 25 ++++ core/types/receipt.go | 4 +- eth/tracers/api.go | 12 +- internal/ethapi/simulate.go | 13 +- miner/miner.go | 14 +- miner/worker.go | 10 +- 13 files changed, 198 insertions(+), 139 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index c0623a69bf..404c7c8b81 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -151,7 +151,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) @@ -312,7 +311,9 @@ 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()) + // TODO (rjl493456442) should we derive fields for logs? receipts? + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) // These three are non-consensus fields: //receipt.BlockHash @@ -367,9 +368,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, var requests [][]byte if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { // EIP-6110 deposits - var allLogs []*types.Log + var allLogs [][]*types.Log for _, receipt := range receipts { - allLogs = append(allLogs, receipt.Logs...) + allLogs = append(allLogs, receipt.Logs) } depositRequests, err := core.ParseDepositLogs(allLogs, chainConfig) if err != nil { diff --git a/core/blockchain.go b/core/blockchain.go index c3da61b281..97b369ab88 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" @@ -1534,7 +1533,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // 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) { +func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs [][]*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { if err := bc.writeBlockWithState(block, receipts, state); err != nil { return NonStatTy, err } @@ -1552,7 +1551,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types bc.chainFeed.Send(ChainEvent{Header: block.Header()}) if len(logs) > 0 { - bc.logsFeed.Send(logs) + bc.logsFeed.Send(types.DeriveLogFields(block.Hash(), block.NumberU64(), logs, block.Transactions(), false)) } // In theory, we should fire a ChainHeadEvent when we inject // a canonical block, but sometimes we can insert a batch of @@ -2157,25 +2156,11 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co // collectLogs collects the logs that were generated or removed during the // processing of a block. These logs are later announced as deleted or reborn. func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { - var blobGasPrice *big.Int - excessBlobGas := b.ExcessBlobGas() - if excessBlobGas != nil { - blobGasPrice = eip4844.CalcBlobFee(*excessBlobGas) + var logs [][]*types.Log + for _, receipt := range rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) { + logs = append(logs, receipt.Logs) } - receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) - if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), blobGasPrice, b.Transactions()); err != nil { - log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err) - } - var logs []*types.Log - for _, receipt := range receipts { - for _, log := range receipt.Logs { - if removed { - log.Removed = true - } - logs = append(logs, log) - } - } - return logs + return types.DeriveLogFields(b.Hash(), b.NumberU64(), logs, b.Transactions(), removed) } // reorg takes two blocks, an old chain and a new chain and will reconstruct the diff --git a/core/chain_makers.go b/core/chain_makers.go index e679a9e557..e355d5a413 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -44,6 +44,7 @@ type BlockGen struct { parent *types.Block header *types.Header statedb *state.StateDB + signer types.Signer gasPool *GasPool txs []*types.Transaction @@ -118,14 +119,14 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) ) b.statedb.SetTxContext(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) + receipt, err := ApplyTransaction(b.signer, b.gasPool, b.statedb, tx, &b.header.GasUsed, evm) if err != nil { panic(err) } 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 += tx.BlobGas() } } @@ -317,6 +318,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine} b.header = cm.makeHeader(parent, statedb, b.engine) + b.signer = types.MakeSigner(config, b.header.Number, b.header.Time) // Set the difficulty for clique block. The chain maker doesn't have access // to a chain, so the difficulty will be left unset (nil). Set it here to the @@ -350,9 +352,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { // EIP-6110 deposits - var blockLogs []*types.Log + var blockLogs [][]*types.Log for _, r := range b.receipts { - blockLogs = append(blockLogs, r.Logs...) + blockLogs = append(blockLogs, r.Logs) } depositRequests, err := ParseDepositLogs(blockLogs, config) if err != nil { @@ -421,7 +423,6 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil { panic(err) } - // Re-expand to ensure all receipts are returned. receipts = receipts[:receiptsCount] @@ -458,6 +459,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine} b.header = cm.makeHeader(parent, statedb, b.engine) + b.signer = types.MakeSigner(config, b.header.Number, b.header.Time) // TODO uncomment when proof generation is merged // Save pre state for proof generation diff --git a/core/state/statedb.go b/core/state/statedb.go index 9cc91c9332..31f1f0df2f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -244,15 +244,12 @@ 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. +// +// TODO (rjl493456442) these logs are partially annotated (with transaction +// information), please get rid of these annotations as well. +func (s *StateDB) GetLogs(hash common.Hash) []*types.Log { + return s.logs[hash] } func (s *StateDB) Logs() []*types.Log { @@ -333,11 +330,6 @@ func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { return common.Hash{} } -// TxIndex returns the current transaction index set by SetTxContext. -func (s *StateDB) TxIndex() int { - return s.txIndex -} - func (s *StateDB) GetCode(addr common.Address) []byte { stateObject := s.getStateObject(addr) if stateObject != nil { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 57886e6e03..d12ac48ec6 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -673,9 +673,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 1703377111..3b98469c12 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -18,11 +18,11 @@ package core import ( "fmt" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" + "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" @@ -55,15 +55,12 @@ func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StatePro // transactions failed to execute due to insufficient gas it will return an error. func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) { var ( - receipts types.Receipts - usedGas = new(uint64) - header = block.Header() - blockHash = block.Hash() - blockNumber = block.Number() - allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + allLogs [][]*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) ) - // Mutate the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) @@ -90,18 +87,27 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - msg, err := TransactionToMessage(tx, signer, header.BaseFee) - if err != nil { - return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) - } + var ( + err error + receipt *types.Receipt + ) statedb.SetTxContext(tx.Hash(), i) - - receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm) + if evm.Config.Tracer != nil { + // TODO (rjl493456442) move the message construction + // into the ApplyTransactionWithHooks. + msg, merr := TransactionToMessage(tx, signer, header.BaseFee) + if merr != nil { + return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), merr) + } + receipt, err = ApplyTransactionWithHooks(msg, gp, statedb, tx, i, block.Hash(), usedGas, evm, evm.Config.Tracer) + } else { + receipt, err = ApplyTransaction(signer, gp, statedb, tx, usedGas, evm) + } if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) - allLogs = append(allLogs, receipt.Logs...) + allLogs = append(allLogs, receipt.Logs) } // Read requests if Prague is enabled. var requests [][]byte @@ -131,19 +137,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg }, nil } -// 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) { - 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) }() - } - } - +// applyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +// +// Note that the generated receipt only includes the consensus fields. Any +// additional fields must be derived separately by the caller if needed. +func applyTransaction(msg *Message, gp *GasPool, statedb *state.StateDB, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.SetTxContext(txContext) @@ -151,23 +152,84 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, // Apply the transaction to the current state (included in the env). result, err := ApplyMessage(evm, msg, gp) if err != nil { - return nil, err + return nil, 0, err } - // Update the state with pending changes. var root []byte + blockNumber := evm.Context.BlockNumber if evm.ChainConfig().IsByzantium(blockNumber) { evm.StateDB.Finalise(true) } else { root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes() } *usedGas += result.UsedGas - - return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil + return MakeReceipt(evm, result, statedb, tx, *usedGas, root), result.UsedGas, 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 { +// ApplyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +// +// Note that the generated receipt only includes the consensus fields. Any +// additional fields must be derived separately by the caller if needed. +func ApplyTransaction(signer types.Signer, gp *GasPool, statedb *state.StateDB, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { + msg, err := TransactionToMessage(tx, signer, evm.Context.BaseFee) + if err != nil { + return nil, err + } + receipt, _, err := applyTransaction(msg, gp, statedb, tx, usedGas, evm) + return receipt, err +} + +// annotateReceipt annotates receipts with chain contextual information. +func annotateReceipt(receipt *types.Receipt, tx *types.Transaction, txIndex uint, blockHash common.Hash, gasUsed uint64, evm *vm.EVM) *types.Receipt { + receipt.TxHash = tx.Hash() + receipt.GasUsed = gasUsed + + if tx.Type() == types.BlobTxType { + receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) + receipt.BlobGasPrice = evm.Context.BlobBaseFee + } + // If the transaction created a contract, store the creation address in the receipt. + if tx.To() == nil { + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) + } + // Set the receipt logs and create the bloom filter. + blockNumber := evm.Context.BlockNumber + for _, log := range receipt.Logs { + log.BlockNumber = blockNumber.Uint64() + log.BlockHash = blockHash + } + receipt.BlockNumber = evm.Context.BlockNumber + receipt.BlockHash = blockHash + receipt.TransactionIndex = txIndex + return receipt +} + +// ApplyTransactionWithHooks is a wrapper of ApplyTransaction, with the additional +// hooks specified. +// +// TODO (rjl493456442) get rid of the message parameter and construct the message +// internally. +func ApplyTransactionWithHooks(msg *Message, gp *GasPool, statedb *state.StateDB, tx *types.Transaction, txIndex int, blockHash common.Hash, usedGas *uint64, evm *vm.EVM, hooks *tracing.Hooks) (*types.Receipt, error) { + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + receipt, gasUsed, err := applyTransaction(msg, gp, statedb, tx, usedGas, evm) + if err != nil { + return nil, err + } + if hooks.OnTxEnd != nil { + hooks.OnTxEnd(annotateReceipt(receipt, tx, uint(txIndex), blockHash, gasUsed, evm), err) + } + return receipt, err +} + +// MakeReceipt generates the receipt object for a transaction based on its execution +// result. Note that the generated receipt only includes the consensus fields. Any +// additional fields must be derived separately by the caller if needed. +func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, 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 := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} @@ -176,47 +238,20 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b } else { receipt.Status = types.ReceiptStatusSuccessful } - receipt.TxHash = tx.Hash() - receipt.GasUsed = result.UsedGas - - if tx.Type() == types.BlobTxType { - receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) - receipt.BlobGasPrice = evm.Context.BlobBaseFee - } - - // If the transaction created a contract, store the creation address in the receipt. - if tx.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) - } + // Set the receipt logs and create the bloom filter. + receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) // Merge the tx-local access event into the "block-local" one, in order to collect // all values, so that the witness can be built. + // + // TODO (rjl493456442) relocate it to a better place. if statedb.GetTrie().IsVerkle() { statedb.AccessEvents().Merge(evm.AccessEvents) } - - // Set the receipt logs and create the bloom filter. - receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = blockHash - receipt.BlockNumber = blockNumber - receipt.TransactionIndex = uint(statedb.TxIndex()) return receipt } -// ApplyTransaction attempts to apply a transaction to the given state database -// and uses the input parameters for its environment. It returns the receipt -// for the transaction, gas used and an error if the transaction failed, -// indicating the block was invalid. -func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { - msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) - if err != nil { - 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) -} - // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) { @@ -312,15 +347,17 @@ func processRequestsSystemCall(evm *vm.EVM, requestType byte, addr common.Addres // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // BeaconDepositContract. -func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([]byte, error) { +func ParseDepositLogs(logs [][]*types.Log, config *params.ChainConfig) ([]byte, error) { deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type) - for _, log := range logs { - if log.Address == config.DepositContractAddress { - request, err := types.DepositLogToRequest(log.Data) - if err != nil { - return nil, fmt.Errorf("unable to parse deposit data: %v", err) + for _, subLogs := range logs { + for _, log := range subLogs { + if log.Address == config.DepositContractAddress { + request, err := types.DepositLogToRequest(log.Data) + if err != nil { + return nil, fmt.Errorf("unable to parse deposit data: %v", err) + } + deposits = append(deposits, request...) } - deposits = append(deposits, request...) } } return deposits, nil diff --git a/core/types.go b/core/types.go index bed20802ab..0d2aa1030f 100644 --- a/core/types.go +++ b/core/types.go @@ -55,6 +55,6 @@ type Processor interface { type ProcessResult struct { Receipts types.Receipts Requests [][]byte - Logs []*types.Log + Logs [][]*types.Log GasUsed uint64 } diff --git a/core/types/log.go b/core/types/log.go index 54c7ff6372..07b6c46e58 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -59,3 +59,28 @@ type logMarshaling struct { TxIndex hexutil.Uint Index hexutil.Uint } + +// DeriveLogFields fills the logs with contextual infos like corresponding block +// and transaction info. +func DeriveLogFields(hash common.Hash, number uint64, logs [][]*Log, txs []*Transaction, removed bool) []*Log { + var ( + combined []*Log + logIndex = uint(0) + ) + for i := 0; i < len(txs); i++ { + for j := 0; j < len(logs[i]); j++ { + l := logs[i][j] + l.BlockNumber = number + l.BlockHash = hash + l.TxHash = txs[i].Hash() + l.TxIndex = uint(i) + l.Index = logIndex + if removed { + l.Removed = true + } + combined = append(combined, l) + logIndex++ + } + } + return combined +} diff --git a/core/types/receipt.go b/core/types/receipt.go index 4f96fde59c..174f629900 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -321,8 +321,8 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { } } -// DeriveFields fills the receipts with their computed fields based on consensus -// data and contextual infos like containing block and transactions. +// DeriveFields fills the receipts and logs with their computed fields based +// on consensus data and contextual infos like containing block and transactions. func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, baseFee *big.Int, blobGasPrice *big.Int, txs []*Transaction) error { signer := MakeSigner(config, new(big.Int).SetUint64(number), time) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index a2c11e0fe2..74062561f3 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -972,6 +972,12 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc msg = args.ToMessage(vmctx.BaseFee, true, true) tx = args.ToTransaction(types.LegacyTxType) traceConfig *TraceConfig + txContext = &Context{ + BlockNumber: new(big.Int).Add(block.Number(), common.Big1), + BlockHash: common.Hash{}, + TxIndex: 0, + TxHash: tx.Hash(), + } ) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). @@ -984,7 +990,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, tx, msg, txContext, vmctx, statedb, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and @@ -1014,7 +1020,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor return nil, err } } - // The actual TxContext will be created as part of ApplyTransactionWithEVM. + // The actual TxContext will be created as part of ApplyTransaction. evm := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) evm.SetTxContext(vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}) @@ -1037,7 +1043,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.ApplyTransactionWithHooks(message, new(core.GasPool).AddGas(message.GasLimit), statedb, tx, txctx.TxIndex, txctx.BlockHash, &usedGas, evm, tracer.Hooks) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 2161e1d5f4..3188e5a11e 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -178,6 +178,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, txes = make([]*types.Transaction, len(block.Calls)) callResults = make([]simCallResult, len(block.Calls)) receipts = make([]*types.Receipt, len(block.Calls)) + // Block hash will be repaired after execution. tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, common.Hash{}, 0) vmConfig = &vm.Config{ @@ -205,7 +206,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tx := call.ToTransaction(types.DynamicFeeTxType) txes[i] = tx tracer.reset(tx.Hash(), uint(i)) - // EoA check is always skipped, even in validation mode. + + // EOA check is always skipped, even in validation mode. msg := call.ToMessage(header.BaseFee, !sim.validate, true) evm.SetTxContext(core.NewEVMTxContext(msg)) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) @@ -221,10 +223,10 @@ 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) - blobGasUsed += receipts[i].BlobGasUsed - logs := tracer.Logs() - callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} + receipts[i] = core.MakeReceipt(evm, result, sim.state, tx, gasUsed, root) + blobGasUsed += tx.BlobGas() + + callRes := simCallResult{ReturnValue: result.Return(), Logs: tracer.Logs(), GasUsed: hexutil.Uint64(result.UsedGas)} if result.Failed() { callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) if errors.Is(result.Err, vm.ErrExecutionReverted) { @@ -241,6 +243,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } header.Root = sim.state.IntermediateRoot(true) header.GasUsed = gasUsed + if sim.chainConfig.IsCancun(header.Number, header.Time) { header.BlobGasUsed = &blobGasUsed } diff --git a/miner/miner.go b/miner/miner.go index 9892c08ed6..b59684b238 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" @@ -133,13 +134,13 @@ func (miner *Miner) BuildPayload(args *BuildPayloadArgs, witness bool) (*Payload // getPending retrieves the pending block based on the current head block. // The result might be nil if pending generation is failed. func (miner *Miner) getPending() *newPayloadResult { - header := miner.chain.CurrentHeader() miner.pendingMu.Lock() defer miner.pendingMu.Unlock() + + header := miner.chain.CurrentHeader() if cached := miner.pending.resolve(header.Hash()); cached != nil { return cached } - var ( timestamp = uint64(time.Now().Unix()) withdrawal types.Withdrawals @@ -160,6 +161,15 @@ func (miner *Miner) getPending() *newPayloadResult { if ret.err != nil { return nil } + // Derive the receipt fields for RPC querying. + var blobGasPrice *big.Int + if ret.block.ExcessBlobGas() != nil { + blobGasPrice = eip4844.CalcBlobFee(*ret.block.ExcessBlobGas()) + } + err := types.Receipts(ret.receipts).DeriveFields(miner.chain.Config(), ret.block.Hash(), ret.block.NumberU64(), ret.block.Time(), ret.block.BaseFee(), blobGasPrice, ret.block.Transactions()) + if err != nil { + return nil + } miner.pending.update(header.Hash(), ret) return ret } diff --git a/miner/worker.go b/miner/worker.go index aeb6cfcdc2..f8d95335c8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -76,7 +76,7 @@ type newPayloadResult struct { fees *big.Int // total block fees sidecars []*types.BlobTxSidecar // collected blobs of blob transactions stateDB *state.StateDB // StateDB after executing the transactions - receipts []*types.Receipt // Receipts collected during construction + receipts []*types.Receipt // Receipts collected during construction, only contain consensus fields requests [][]byte // Consensus layer requests collected during block construction witness *stateless.Witness // Witness is an optional stateless proof } @@ -113,11 +113,10 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo } body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals} - allLogs := make([]*types.Log, 0) + var allLogs [][]*types.Log for _, r := range work.receipts { - allLogs = append(allLogs, r.Logs...) + allLogs = append(allLogs, r.Logs) } - // Collect consensus-layer requests if Prague is enabled. var requests [][]byte if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { @@ -138,7 +137,6 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo reqHash := types.CalcRequestsHash(requests) work.header.RequestsHash = &reqHash } - block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) if err != nil { return &newPayloadResult{err: err} @@ -309,7 +307,7 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (* snap = env.state.Snapshot() gp = env.gasPool.Gas() ) - receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed) + receipt, err := core.ApplyTransaction(env.signer, env.gasPool, env.state, tx, &env.header.GasUsed, env.evm) if err != nil { env.state.RevertToSnapshot(snap) env.gasPool.SetGas(gp)