diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 477ba40118..d8103c926a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -919,7 +919,7 @@ func (diff *StateOverride) Apply(state *state.StateDB) error { // BlockOverrides is a set of header fields to override. type BlockOverrides struct { Number *hexutil.Big - Difficulty *hexutil.Big + Difficulty *hexutil.Big // No-op if we're simulating post-merge calls. Time *hexutil.Uint64 GasLimit *hexutil.Uint64 Coinbase *common.Address @@ -1141,8 +1141,18 @@ func (s *BlockChainAPI) Multicall(ctx context.Context, blocks []CallBatch, block // Each tx and all the series of txes shouldn't consume more gas than cap globalGasCap = s.b.RPCGasCap() gp = new(core.GasPool).AddGas(globalGasCap) + prevNumber = header.Number.Uint64() ) for bi, block := range blocks { + blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) + if block.BlockOverrides != nil { + block.BlockOverrides.Apply(&blockContext) + } + // TODO: Consider hoisting this check up + if blockContext.BlockNumber.Uint64() < prevNumber { + return nil, fmt.Errorf("block numbers must be in order") + } + prevNumber = blockContext.BlockNumber.Uint64() // State overrides are applied prior to execution of a block if err := block.StateOverrides.Apply(state); err != nil { return nil, err @@ -1151,10 +1161,6 @@ func (s *BlockChainAPI) Multicall(ctx context.Context, blocks []CallBatch, block if block.ECRecoverOverride != nil { state.SetCode(common.BytesToAddress([]byte{1}), *block.ECRecoverOverride) } - blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) - if block.BlockOverrides != nil { - block.BlockOverrides.Apply(&blockContext) - } results[bi] = make([]callResult, len(block.Calls)) for i, call := range block.Calls { // Hack to get logs from statedb which stores logs by txhash. diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 3cc27e28d0..12682afb33 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -758,18 +758,6 @@ func TestMulticall(t *testing.T) { 0x60, 0x20, 0x60, 0x00, 0xf3, }, }}, - }, { - // No block number override, should use latest - Calls: []TransactionArgs{ - { - From: &accounts[0].addr, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN - }, - }, - }, }}, want: [][]res{{ res{ @@ -781,13 +769,39 @@ func TestMulticall(t *testing.T) { ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000c", GasUsed: "0xe891", }, - }, { - res{ - ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000a", - GasUsed: "0xe891", - }, }}, }, + // Block numbers must be in order. + { + tag: latest, + blocks: []CallBatch{{ + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(12)), + }, + Calls: []TransactionArgs{{ + From: &accounts[1].addr, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN + }, + }}, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(11)), + }, + Calls: []TransactionArgs{{ + From: &accounts[0].addr, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN + }, + }}, + }}, + want: [][]res{}, + expectErr: errors.New("block numbers must be in order"), + }, // Test on solidity storage example. Set value in one call, read in next. { tag: latest, @@ -915,7 +929,10 @@ func TestMulticall(t *testing.T) { continue } if !errors.Is(err, tc.expectErr) { - t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) + // Second try + if !reflect.DeepEqual(err, tc.expectErr) { + t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) + } } continue }