compute receipts

This commit is contained in:
Sina Mahmoodi 2024-01-16 12:55:51 +03:30
parent 115117c682
commit df14718291
4 changed files with 52 additions and 12 deletions

View file

@ -124,9 +124,14 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
} }
*usedGas += result.UsedGas *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 // Create a new receipt for the transaction, storing the intermediate root and gas used
// by the tx. // 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() { if result.Failed() {
receipt.Status = types.ReceiptStatusFailed receipt.Status = types.ReceiptStatusFailed
} else { } 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 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()) 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.BlockHash = blockHash
receipt.BlockNumber = blockNumber receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex()) receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, err return receipt
} }
// ApplyTransaction attempts to apply a transaction to the given state database // ApplyTransaction attempts to apply a transaction to the given state database

View file

@ -1147,6 +1147,10 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
evm.SetPrecompiles(precompiles) 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 // Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly) // EVM has finished, cancelling may be done (repeatedly)
go func() { go func() {

View file

@ -162,29 +162,54 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult,
gasUsed uint64 gasUsed uint64
txes = make([]*types.Transaction, len(block.Calls)) txes = make([]*types.Transaction, len(block.Calls))
callResults = make([]mcCallResult, 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 { 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 { if err := mc.sanitizeCall(&call, state, &gasUsed, blockContext); err != nil {
return nil, err return nil, err
} }
tx := call.ToTransaction() tx := call.ToTransaction()
txes[i] = tx txes[i] = tx
// TODO: repair log block hashes post execution.
vmConfig := &vm.Config{ msg, err := call.ToMessage(gp.Gas(), header.BaseFee, !opts.Validation)
NoBaseFee: true, if err != nil {
// Block hash will be repaired after execution. return nil, err
Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, tx.Hash(), uint(i)),
} }
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 { if err != nil {
txErr := txValidationError(err) txErr := txValidationError(err)
return nil, txErr 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 the result contains a revert reason, try to unpack it.
if len(result.Revert()) > 0 { if len(result.Revert()) > 0 {
result.Err = newRevertError(result.Revert()) 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)} callRes := mcCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)}
if result.Failed() { if result.Failed() {
callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) 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) callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful)
} }
callResults[i] = callRes callResults[i] = callRes
gasUsed += result.UsedGas
state.Finalise(true)
} }
var ( var (
parentHash common.Hash parentHash common.Hash
@ -212,6 +235,7 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult,
header.Root = state.IntermediateRoot(true) header.Root = state.IntermediateRoot(true)
header.GasUsed = gasUsed header.GasUsed = gasUsed
header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) 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) results[bi] = mcBlockResultFromHeader(header, callResults)
repairLogs(results, header.Hash()) repairLogs(results, header.Hash())
} }

View file

@ -175,6 +175,13 @@ func (t *tracer) captureTransfer(from, to common.Address, value *big.Int) {
t.captureLog(transferAddress, topics, common.BigToHash(value).Bytes()) 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 { func (t *tracer) Logs() []*types.Log {
return t.logs[0] return t.logs[0]
} }