diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 2daca2a6c5..0a3b31a3dc 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1227,7 +1227,7 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO // // Note, this function doesn't make any changes in the state/blockchain and is // useful to execute and retrieve values. -func (s *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]simBlockResult, error) { +func (s *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { if len(opts.BlockStateCalls) == 0 { return nil, &invalidParamsError{message: "empty input"} } else if len(opts.BlockStateCalls) > maxSimulateBlocks { diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 12edaf6dc8..b18c238e30 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -1108,11 +1108,11 @@ func TestSimulateV1(t *testing.T) { Number string //Hash string // Ignore timestamp - GasLimit string - GasUsed string - FeeRecipient string - BaseFee string - Calls []callRes + GasLimit string + GasUsed string + Miner string + BaseFee string + Calls []callRes } var testSuite = []struct { name string @@ -1146,10 +1146,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xf618", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xf618", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x5208", @@ -1199,10 +1199,10 @@ func TestSimulateV1(t *testing.T) { }, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xa410", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xa410", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x5208", @@ -1215,10 +1215,10 @@ func TestSimulateV1(t *testing.T) { Status: "0x1", }}, }, { - Number: "0xc", - GasLimit: "0x47e7c4", - GasUsed: "0x5208", - FeeRecipient: coinbase, + Number: "0xc", + GasLimit: "0x47e7c4", + GasUsed: "0x5208", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x5208", @@ -1253,10 +1253,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x47e7c4", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x47e7c4", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", Error: callErr{Message: "stack underflow (0 <=> 2)", Code: errCodeVMError}, @@ -1298,10 +1298,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xe891", - FeeRecipient: strings.ToLower(cac.String()), + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xe891", + Miner: strings.ToLower(cac.String()), Calls: []callRes{{ ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000b", GasUsed: "0xe891", @@ -1309,10 +1309,10 @@ func TestSimulateV1(t *testing.T) { Status: "0x1", }}, }, { - Number: "0xc", - GasLimit: "0x47e7c4", - GasUsed: "0xe891", - FeeRecipient: strings.ToLower(cac.String()), + Number: "0xc", + GasLimit: "0x47e7c4", + GasUsed: "0xe891", + Miner: strings.ToLower(cac.String()), Calls: []callRes{{ ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000c", GasUsed: "0xe891", @@ -1377,10 +1377,10 @@ func TestSimulateV1(t *testing.T) { }, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x10683", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x10683", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0xaacc", @@ -1418,10 +1418,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x5508", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x5508", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", Logs: []log{{ @@ -1486,10 +1486,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x52f6", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x52f6", + Miner: coinbase, Calls: []callRes{{ // Caller is in this case the contract that invokes ecrecover. ReturnValue: strings.ToLower(randomAccounts[2].addr.String()), @@ -1536,10 +1536,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xa58c", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xa58c", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5", GasUsed: "0x52dc", @@ -1584,10 +1584,10 @@ func TestSimulateV1(t *testing.T) { }}, includeTransfers: &includeTransfers, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xd984", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xd984", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0xd984", @@ -1650,10 +1650,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x1b83f", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x1b83f", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0xd166", @@ -1666,10 +1666,10 @@ func TestSimulateV1(t *testing.T) { Status: "0x1", }}, }, { - Number: "0xc", - GasLimit: "0x47e7c4", - GasUsed: "0xe6d9", - FeeRecipient: coinbase, + Number: "0xc", + GasLimit: "0x47e7c4", + GasUsed: "0xe6d9", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0xe6d9", @@ -1713,10 +1713,10 @@ func TestSimulateV1(t *testing.T) { }}, validation: &validation, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x5208", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x5208", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x5208", @@ -1765,10 +1765,10 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0xc542", - FeeRecipient: coinbase, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0xc542", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x0000000000000000000000000000000200000000000000000000000000000003", GasUsed: "0x62a1", @@ -1781,10 +1781,10 @@ func TestSimulateV1(t *testing.T) { Status: "0x1", }}, }, { - Number: "0xc", - GasLimit: "0x47e7c4", - GasUsed: "0x62a1", - FeeRecipient: coinbase, + Number: "0xc", + GasLimit: "0x47e7c4", + GasUsed: "0x62a1", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x0000000000000000000000000000000500000000000000000000000000000000", GasUsed: "0x62a1", @@ -1838,16 +1838,16 @@ func TestSimulateV1(t *testing.T) { }}, }}, want: []blockRes{{ - Number: "0xb", - GasLimit: "0x47e7c4", - GasUsed: "0x0", - FeeRecipient: coinbase, - Calls: []callRes{}, + Number: "0xb", + GasLimit: "0x47e7c4", + GasUsed: "0x0", + Miner: coinbase, + Calls: []callRes{}, }, { - Number: "0xc", - GasLimit: "0x47e7c4", - GasUsed: "0xf864", - FeeRecipient: coinbase, + Number: "0xc", + GasLimit: "0x47e7c4", + GasUsed: "0xf864", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x52cc", @@ -1866,28 +1866,28 @@ func TestSimulateV1(t *testing.T) { Status: "0x1", }}, }, { - Number: "0xd", - GasLimit: "0x47e7c4", - GasUsed: "0x0", - FeeRecipient: coinbase, - Calls: []callRes{}, + Number: "0xd", + GasLimit: "0x47e7c4", + GasUsed: "0x0", + Miner: coinbase, + Calls: []callRes{}, }, { - Number: "0xe", - GasLimit: "0x47e7c4", - GasUsed: "0x0", - FeeRecipient: coinbase, - Calls: []callRes{}, + Number: "0xe", + GasLimit: "0x47e7c4", + GasUsed: "0x0", + Miner: coinbase, + Calls: []callRes{}, }, { - Number: "0xf", - GasLimit: "0x47e7c4", - GasUsed: "0x0", - FeeRecipient: coinbase, - Calls: []callRes{}, + Number: "0xf", + GasLimit: "0x47e7c4", + GasUsed: "0x0", + Miner: coinbase, + Calls: []callRes{}, }, { - Number: "0x10", - GasLimit: "0x47e7c4", - GasUsed: "0xa598", - FeeRecipient: coinbase, + Number: "0x10", + GasLimit: "0x47e7c4", + GasUsed: "0xa598", + Miner: coinbase, Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x52cc", diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 0850badc3e..c0811399ea 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -50,53 +50,6 @@ type simBlock struct { Calls []TransactionArgs } -type simBlockResult struct { - Number hexutil.Uint64 `json:"number"` - Hash common.Hash `json:"hash"` - Time hexutil.Uint64 `json:"timestamp"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - FeeRecipient common.Address `json:"feeRecipient"` - BaseFee *hexutil.Big `json:"baseFeePerGas"` - PrevRandao common.Hash `json:"prevRandao"` - Withdrawals types.Withdrawals `json:"withdrawals"` - BlobGasUsed hexutil.Uint64 `json:"blobGasUsed"` - ExcessBlobGas hexutil.Uint64 `json:"excessBlobGas"` - Calls []simCallResult `json:"calls"` -} - -func simBlockResultFromHeader(header *types.Header, callResults []simCallResult) simBlockResult { - r := simBlockResult{ - Number: hexutil.Uint64(header.Number.Uint64()), - Hash: header.Hash(), - Time: hexutil.Uint64(header.Time), - GasLimit: hexutil.Uint64(header.GasLimit), - GasUsed: hexutil.Uint64(header.GasUsed), - FeeRecipient: header.Coinbase, - BaseFee: (*hexutil.Big)(header.BaseFee), - PrevRandao: header.MixDigest, - // Withdrawals will be always empty in the context of a simulated block. - Withdrawals: make(types.Withdrawals, 0), - Calls: callResults, - } - if header.BlobGasUsed != nil && header.ExcessBlobGas != nil { - r.BlobGasUsed = hexutil.Uint64(*header.BlobGasUsed) - r.ExcessBlobGas = hexutil.Uint64(*header.ExcessBlobGas) - } - return r -} - -// repairLogs updates the block hash in the logs present in the result of -// a simulated block. This is needed as during execution when logs are collected -// the block hash is not known. -func (b *simBlockResult) repairLogs() { - for i := range b.Calls { - for j := range b.Calls[i].Logs { - b.Calls[i].Logs[j].BlockHash = b.Hash - } - } -} - type simCallResult struct { ReturnValue hexutil.Bytes `json:"returnData"` Logs []*types.Log `json:"logs"` @@ -129,7 +82,7 @@ type simulator struct { validate bool } -func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]simBlockResult, error) { +func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[string]interface{}, error) { // Setup context so it may be cancelled before the calls completed // or, in case of unmetered gas, setup a context with a timeout. var ( @@ -155,7 +108,7 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]simBloc return nil, err } var ( - results = make([]simBlockResult, len(blocks)) + results = make([]map[string]interface{}, len(blocks)) // Each tx and all the series of txes shouldn't consume more gas than cap gp = new(core.GasPool).AddGas(sim.b.RPCGasCap()) precompiles = sim.activePrecompiles(ctx, sim.base) @@ -169,13 +122,13 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]simBloc if err != nil { return nil, err } - results[bi] = *result + results[bi] = result parent = headers[bi] } return results, nil } -func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, gp *core.GasPool, precompiles vm.PrecompiledContracts, timeout time.Duration) (*simBlockResult, error) { +func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, gp *core.GasPool, precompiles vm.PrecompiledContracts, timeout time.Duration) (map[string]interface{}, error) { // Set header fields that depend only on parent block. config := sim.b.ChainConfig() // Parent hash is needed for evm.GetHashFn to work. @@ -272,19 +225,30 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } header.Root = sim.state.IntermediateRoot(true) header.GasUsed = gasUsed - if len(txes) > 0 { - header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) - } - if len(receipts) > 0 { - header.ReceiptHash = types.DeriveSha(types.Receipts(receipts), trie.NewStackTrie(nil)) - header.Bloom = types.CreateBloom(types.Receipts(receipts)) - } if config.IsCancun(header.Number, header.Time) { header.BlobGasUsed = &blobGasUsed } - result := simBlockResultFromHeader(header, callResults) - result.repairLogs() - return &result, nil + var withdrawals types.Withdrawals + if config.IsShanghai(header.Number, header.Time) { + withdrawals = make([]*types.Withdrawal, 0) + } + b := types.NewBlockWithWithdrawals(header, txes, nil, receipts, withdrawals, trie.NewStackTrie(nil)) + res := RPCMarshalBlock(b, true, false, config) + repairLogs(callResults, res["hash"].(common.Hash)) + res["calls"] = callResults + + return res, nil +} + +// repairLogs updates the block hash in the logs present in the result of +// a simulated block. This is needed as during execution when logs are collected +// the block hash is not known. +func repairLogs(calls []simCallResult, hash common.Hash) { + for i := range calls { + for j := range calls[i].Logs { + calls[i].Logs[j].BlockHash = hash + } + } } func (sim *simulator) sanitizeCall(call *TransactionArgs, state *state.StateDB, gasUsed *uint64, blockContext vm.BlockContext) error {