From df147182918e38c5875ffcafd97b8323f13da6c7 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 16 Jan 2024 12:55:51 +0330 Subject: [PATCH] compute receipts --- core/state_processor.go | 11 +++++--- internal/ethapi/api.go | 4 +++ internal/ethapi/simulate.go | 42 ++++++++++++++++++++++++------- internal/ethapi/transfertracer.go | 7 ++++++ 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 9a4333f723..e63107e884 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -124,9 +124,14 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta } *usedGas += result.UsedGas + return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), err +} + +// 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. - receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} + receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { @@ -141,7 +146,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta } // If the transaction created a contract, store the creation address in the receipt. - if msg.To == nil { + if tx.To() == nil { receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } @@ -151,7 +156,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) - return receipt, err + return receipt } // ApplyTransaction attempts to apply a transaction to the given state database diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6bc5babc71..ae3c17172d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1147,6 +1147,10 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s evm.SetPrecompiles(precompiles) } + return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) +} + +func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 280156bb77..13821759ae 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -162,29 +162,54 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult, gasUsed uint64 txes = make([]*types.Transaction, len(block.Calls)) callResults = make([]mcCallResult, len(block.Calls)) + receipts = make([]*types.Receipt, len(block.Calls)) + tracer = newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, common.Hash{}, 0) + config = mc.b.ChainConfig() + vmConfig = &vm.Config{ + NoBaseFee: true, + // Block hash will be repaired after execution. + Tracer: tracer, + } + evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, state, config, *vmConfig) ) + if precompiles != nil { + evm.SetPrecompiles(precompiles) + } for i, call := range block.Calls { + // TODO: Pre-estimate nonce and gas + // TODO: Move gas fees sanitizing to beginning of func if err := mc.sanitizeCall(&call, state, &gasUsed, blockContext); err != nil { return nil, err } tx := call.ToTransaction() txes[i] = tx - // TODO: repair log block hashes post execution. - vmConfig := &vm.Config{ - NoBaseFee: true, - // Block hash will be repaired after execution. - Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, tx.Hash(), uint(i)), + + msg, err := call.ToMessage(gp.Gas(), header.BaseFee, !opts.Validation) + if err != nil { + return nil, err } - result, err := applyMessage(ctx, mc.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, !opts.Validation) + tracer.reset(tx.Hash(), uint(i)) + evm.Reset(core.NewEVMTxContext(msg), state) + result, err := applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) if err != nil { txErr := txValidationError(err) return nil, txErr } + // Update the state with pending changes. + var root []byte + if config.IsByzantium(blockContext.BlockNumber) { + state.Finalise(true) + } else { + root = state.IntermediateRoot(config.IsEIP158(blockContext.BlockNumber)).Bytes() + } + gasUsed += result.UsedGas + receipt := core.MakeReceipt(evm, result, state, blockContext.BlockNumber, common.Hash{}, tx, gasUsed, root) + receipts[i] = receipt // If the result contains a revert reason, try to unpack it. if len(result.Revert()) > 0 { result.Err = newRevertError(result.Revert()) } - logs := vmConfig.Tracer.(*tracer).Logs() + logs := tracer.Logs() callRes := mcCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} if result.Failed() { callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) @@ -197,8 +222,6 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult, callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful) } callResults[i] = callRes - gasUsed += result.UsedGas - state.Finalise(true) } var ( parentHash common.Hash @@ -212,6 +235,7 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult, header.Root = state.IntermediateRoot(true) header.GasUsed = gasUsed header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) + header.ReceiptHash = types.DeriveSha(types.Receipts(receipts), trie.NewStackTrie(nil)) results[bi] = mcBlockResultFromHeader(header, callResults) repairLogs(results, header.Hash()) } diff --git a/internal/ethapi/transfertracer.go b/internal/ethapi/transfertracer.go index 96eb78e120..e4d547818b 100644 --- a/internal/ethapi/transfertracer.go +++ b/internal/ethapi/transfertracer.go @@ -175,6 +175,13 @@ func (t *tracer) captureTransfer(from, to common.Address, value *big.Int) { t.captureLog(transferAddress, topics, common.BigToHash(value).Bytes()) } +// reset prepares the tracer for the next transaction. +func (t *tracer) reset(txHash common.Hash, txIdx uint) { + t.logs = make([][]*types.Log, 1) + t.txHash = txHash + t.txIdx = txIdx +} + func (t *tracer) Logs() []*types.Log { return t.logs[0] }