diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d9cec560ea..171b5c5f08 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -770,7 +770,7 @@ func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockN // // Note, this function doesn't make any changes in the state/blockchain and is // useful to execute and retrieve values. -func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { +func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]*simBlockResult, error) { if len(opts.BlockStateCalls) == 0 { return nil, &invalidParamsError{message: "empty input"} } else if len(opts.BlockStateCalls) > maxSimulateBlocks { diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 130eaa9724..b36f7bb904 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -73,6 +73,19 @@ func (r *simCallResult) MarshalJSON() ([]byte, error) { return json.Marshal((*callResultAlias)(r)) } +// simBlockResult is the result of a simulated block. +type simBlockResult struct { + sim *simulator + Block *types.Block + Calls []simCallResult +} + +func (r *simBlockResult) MarshalJSON() ([]byte, error) { + blockData := RPCMarshalBlock(r.Block, true, r.sim.fullTx, r.sim.chainConfig) + blockData["calls"] = r.Calls + return json.Marshal(blockData) +} + // simOpts are the inputs to eth_simulateV1. type simOpts struct { BlockStateCalls []simBlock @@ -95,7 +108,7 @@ type simulator struct { } // execute runs the simulation of a series of blocks. -func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[string]interface{}, error) { +func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlockResult, error) { if err := ctx.Err(); err != nil { return nil, err } @@ -123,19 +136,21 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[str return nil, err } var ( - results = make([]map[string]interface{}, len(blocks)) - parent = sim.base + results = make([]*simBlockResult, len(blocks)) + // prevHeaders is the header for all previously simulated blocks. + // It is "filled" compared to the barebone header available prior to execution. + // It will be used for serving the BLOCKHASH opcode. + prevHeaders = make([]*types.Header, 0, len(blocks)) + parent = sim.base ) for bi, block := range blocks { - result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout) + result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, prevHeaders, timeout) if err != nil { return nil, err } - enc := RPCMarshalBlock(result, true, sim.fullTx, sim.chainConfig) - enc["calls"] = callResults - results[bi] = enc - - parent = headers[bi] + results[bi] = &simBlockResult{sim: sim, Block: result, Calls: callResults} + parent = result.Header() + prevHeaders = append(prevHeaders, parent) } return results, nil } diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go index c747b76477..2d8f2a5a70 100644 --- a/internal/ethapi/simulate_test.go +++ b/internal/ethapi/simulate_test.go @@ -17,9 +17,11 @@ package ethapi import ( + "fmt" "math/big" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi/override" @@ -119,3 +121,18 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) { func newInt(n int64) *hexutil.Big { return (*hexutil.Big)(big.NewInt(n)) } + +func TestHeaderPassing(t *testing.T) { + sl := []*types.Header{new(types.Header)} + procHeader(sl[0]) + fmt.Printf("sl[0].Number: %v\n", sl[0].Number) + fmt.Printf("sl[0].ParentHash: %v\n", sl[0].ParentHash) + if sl[0].Number.Cmp(big.NewInt(100)) != 0 { + t.Errorf("Header not processed") + } +} + +func procHeader(h *types.Header) { + h.Number = big.NewInt(100) + h.ParentHash = common.HexToHash("0x1") +}