From 8fff7fd10130c18d1779cb7e0ceb422a96cac00b Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 3 Jul 2023 18:07:36 +0200 Subject: [PATCH] return block info except blockhash --- internal/ethapi/api.go | 34 +++++- internal/ethapi/api_test.go | 214 +++++++++++++++++++++++------------- 2 files changed, 164 insertions(+), 84 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 627cae0239..2c35738552 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1103,6 +1103,17 @@ type CallBatch struct { Calls []TransactionArgs } +type blockResult 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"` + Calls []callResult `json:"calls"` +} + type callResult struct { ReturnValue hexutil.Bytes `json:"return"` Logs []*types.Log `json:"logs"` @@ -1120,14 +1131,14 @@ func (r *callResult) MarshalJSON() ([]byte, error) { return json.Marshal((*callResultAlias)(r)) } -// Multicall executes series of transactions on top of a base state. +// MulticallV1 executes series of transactions on top of a base state. // The transactions are packed into blocks. For each block, block header // fields can be overridden. The state can also be overridden prior to // execution of each block. // // Note, this function doesn't make any changes in the state/blockchain and is // useful to execute and retrieve values. -func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blockNrOrHash rpc.BlockNumberOrHash, includeTransfers *bool) ([][]callResult, error) { +func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blockNrOrHash rpc.BlockNumberOrHash, includeTransfers *bool) ([]blockResult, error) { state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err @@ -1147,7 +1158,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blo // this makes sure resources are cleaned up. defer cancel() var ( - results = make([][]callResult, len(blocks)) + results = make([]blockResult, len(blocks)) // 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) @@ -1171,7 +1182,16 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blo if block.ECRecoverOverride != nil { state.SetCode(common.BytesToAddress([]byte{1}), *block.ECRecoverOverride) } - results[bi] = make([]callResult, len(block.Calls)) + results[bi] = blockResult{ + Number: hexutil.Uint64(blockContext.BlockNumber.Uint64()), + Hash: common.Hash{}, // TODO + Time: hexutil.Uint64(blockContext.Time), + GasLimit: hexutil.Uint64(blockContext.GasLimit), + FeeRecipient: blockContext.Coinbase, + BaseFee: (*hexutil.Big)(blockContext.BaseFee), + Calls: make([]callResult, len(block.Calls)), + } + gasUsed := uint64(0) for i, call := range block.Calls { // Hack to get logs from statedb which stores logs by txhash. txhash := common.BigToHash(big.NewInt(int64(i))) @@ -1185,7 +1205,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blo } result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig) if err != nil { - results[bi][i] = callResult{Error: err.Error()} + results[bi].Calls[i] = callResult{Error: err.Error()} continue } // If the result contains a revert reason, try to unpack it. @@ -1205,8 +1225,10 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, blocks []CallBatch, blo if result.Err != nil { callRes.Error = result.Err.Error() } - results[bi][i] = callRes + results[bi].Calls[i] = callRes + gasUsed += result.UsedGas } + results[bi].GasUsed = hexutil.Uint64(gasUsed) } return results, nil } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index eeba0830da..dae567deaa 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -633,19 +633,29 @@ func TestMulticallV1(t *testing.T) { latest = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) includeTransfers = true ) - type res struct { + type callRes struct { ReturnValue string `json:"return"` Error string Logs []types.Log GasUsed string Transfers []transfer } + type blockRes struct { + Number string + Hash string + // Ignore timestamp + GasLimit string + GasUsed string + FeeRecipient string + BaseFee string + Calls []callRes + } var testSuite = []struct { blocks []CallBatch tag rpc.BlockNumberOrHash includeTransfers *bool expectErr error - want [][]res + want []blockRes }{ // State build-up over calls: // First value transfer OK after state override. @@ -666,17 +676,21 @@ func TestMulticallV1(t *testing.T) { Value: (*hexutil.Big)(big.NewInt(1000)), }}, }}, - want: [][]res{{ - res{ + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0xa410", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ ReturnValue: "0x", GasUsed: "0x5208", Logs: []types.Log{}, - }, - res{ + }, { ReturnValue: "0x", GasUsed: "0x5208", Logs: []types.Log{}, - }, + }}, }}, }, { // State build-up over blocks. @@ -712,32 +726,38 @@ func TestMulticallV1(t *testing.T) { }, }, }}, - want: [][]res{ - { - res{ - ReturnValue: "0x", - GasUsed: "0x5208", - Logs: []types.Log{}, - }, - res{ - ReturnValue: "0x", - GasUsed: "0x5208", - Logs: []types.Log{}, - }, + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0xa410", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + GasUsed: "0x5208", + Logs: []types.Log{}, }, { - res{ - ReturnValue: "0x", - GasUsed: "0x5208", - Logs: []types.Log{}, - }, - res{ - ReturnValue: "0x", - GasUsed: "0x0", - Logs: []types.Log{}, - Error: fmt.Sprintf("err: insufficient funds for gas * price + value: address %s have 0 want 1000 (supplied gas 9937000)", randomAccounts[3].addr.String()), - }, - }, - }, + ReturnValue: "0x", + GasUsed: "0x5208", + Logs: []types.Log{}, + }}, + }, { + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0x5208", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + GasUsed: "0x5208", + Logs: []types.Log{}, + }, { + ReturnValue: "0x", + GasUsed: "0x0", + Logs: []types.Log{}, + Error: fmt.Sprintf("err: insufficient funds for gas * price + value: address %s have 0 want 1000 (supplied gas 9937000)", randomAccounts[3].addr.String()), + }}, + }}, }, { // Block overrides should work, each call is simulated on a different block number tag: latest, @@ -768,18 +788,28 @@ func TestMulticallV1(t *testing.T) { }, }}, }}, - want: [][]res{{ - res{ + want: []blockRes{{ + Number: "0xb", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0xe891", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000b", GasUsed: "0xe891", Logs: []types.Log{}, - }, + }}, }, { - res{ + Number: "0xc", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0xe891", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000c", GasUsed: "0xe891", Logs: []types.Log{}, - }, + }}, }}, }, // Block numbers must be in order. @@ -810,7 +840,7 @@ func TestMulticallV1(t *testing.T) { }, }}, }}, - want: [][]res{}, + want: []blockRes{}, expectErr: errors.New("block numbers must be in order"), }, // Test on solidity storage example. Set value in one call, read in next. @@ -835,15 +865,22 @@ func TestMulticallV1(t *testing.T) { }, }, }}, - want: [][]res{{{ - ReturnValue: "0x", - GasUsed: "0xaacc", - Logs: []types.Log{}, - }, { - ReturnValue: "0x0000000000000000000000000000000000000000000000000000000000000005", - GasUsed: "0x5bb7", - Logs: []types.Log{}, - }}}, + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0x10683", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + GasUsed: "0xaacc", + Logs: []types.Log{}, + }, { + ReturnValue: "0x0000000000000000000000000000000000000000000000000000000000000005", + GasUsed: "0x5bb7", + Logs: []types.Log{}, + }}, + }}, }, // Test logs output. { @@ -867,16 +904,23 @@ func TestMulticallV1(t *testing.T) { To: &randomAccounts[2].addr, }}, }}, - want: [][]res{{{ - ReturnValue: "0x", - Logs: []types.Log{{ - Address: randomAccounts[2].addr, - Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")}, - BlockNumber: 10, - Data: []byte{}, + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0x5508", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + Logs: []types.Log{{ + Address: randomAccounts[2].addr, + Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")}, + BlockNumber: 10, + Data: []byte{}, + }}, + GasUsed: "0x5508", }}, - GasUsed: "0x5508", - }}}, + }}, }, // Test ecrecover override { @@ -926,12 +970,19 @@ func TestMulticallV1(t *testing.T) { To: &randomAccounts[2].addr, }}, }}, - want: [][]res{{{ - // Caller is in this case the contract that invokes ecrecover. - ReturnValue: strings.ToLower(randomAccounts[2].addr.String()), - GasUsed: "0x52f6", - Logs: []types.Log{}, - }}}, + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0x52f6", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + // Caller is in this case the contract that invokes ecrecover. + ReturnValue: strings.ToLower(randomAccounts[2].addr.String()), + GasUsed: "0x52f6", + Logs: []types.Log{}, + }}, + }}, }, // Test ether transfers. { @@ -962,22 +1013,29 @@ func TestMulticallV1(t *testing.T) { }}, }}, includeTransfers: &includeTransfers, - want: [][]res{{{ - ReturnValue: "0x", - GasUsed: "0xd984", - Transfers: []transfer{ - { - From: accounts[0].addr, - To: randomAccounts[0].addr, - Value: big.NewInt(50), - }, { - From: randomAccounts[0].addr, - To: randomAccounts[1].addr, - Value: big.NewInt(100), + want: []blockRes{{ + Number: "0xa", + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + GasLimit: "0x47e7c4", + GasUsed: "0xd984", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + GasUsed: "0xd984", + Transfers: []transfer{ + { + From: accounts[0].addr, + To: randomAccounts[0].addr, + Value: big.NewInt(50), + }, { + From: randomAccounts[0].addr, + To: randomAccounts[1].addr, + Value: big.NewInt(100), + }, }, - }, - Logs: []types.Log{}, - }}}, + Logs: []types.Log{}, + }}, + }}, }, } @@ -1001,7 +1059,7 @@ func TestMulticallV1(t *testing.T) { continue } // Turn result into res-struct - var have [][]res + var have []blockRes resBytes, _ := json.Marshal(result) if err := json.Unmarshal(resBytes, &have); err != nil { t.Fatalf("failed to unmarshal result: %v", err)