diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 75290e7043..08852d9094 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -119,10 +120,6 @@ func TestGethClient(t *testing.T) { "TestAccessList", func(t *testing.T) { testAccessList(t, client) }, }, - { - "TestBatchAccessList", - func(t *testing.T) { testBatchAccessList(t, client) }, - }, { "TestGetProof", func(t *testing.T) { testGetProof(t, client, testAddr) }, @@ -175,6 +172,10 @@ func TestGethClient(t *testing.T) { "TestSetHead", func(t *testing.T) { testSetHead(t, client) }, }, + { + "TestBatchAccessList", + func(t *testing.T) { testBatchAccessList(t, client) }, + }, } for _, tt := range tests { t.Run(tt.name, tt.test) @@ -265,104 +266,6 @@ func testAccessList(t *testing.T, client *rpc.Client) { } } -func testBatchAccessList(t *testing.T, client *rpc.Client) { - // Skip this test as the "eth_createBatchAccessList" RPC method may not be - // implemented in the test backend - t.Skip("Skipping batch access list test as the RPC method may not be implemented in the test backend") - - ec := New(client) - - testCases := []struct { - msg ethereum.CallMsg - wantGas uint64 - wantErr string - wantVMErr string - wantAL string - }{ - { // Test transfer - msg: ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - GasPrice: big.NewInt(875000000), - Value: big.NewInt(1), - }, - wantGas: 21000, - wantAL: `[]`, - }, - { // Test reverting transaction - msg: ethereum.CallMsg{ - From: testAddr, - To: nil, - Gas: 100000, - GasPrice: big.NewInt(1000000000), - Value: big.NewInt(1), - Data: common.FromHex("0x608060806080608155fd"), - }, - wantGas: 77496, - wantVMErr: "execution reverted", - wantAL: `[ - { - "address": "0x3a220f351252089d385b29beca14e27f204c296a", - "storageKeys": [ - "0x0000000000000000000000000000000000000000000000000000000000000081" - ] - } -]`, - }, - { // when gasPrice is not specified - msg: ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - Value: big.NewInt(1), - }, - wantGas: 21000, - wantAL: `[]`, - }, - } - - // Create a batch of messages for testing - var msgs []ethereum.CallMsg - for _, tc := range testCases { - msgs = append(msgs, tc.msg) - } - - // Run the batch access list request - accessLists, gasUsed, vmErrs, err := ec.CreateBatchAccessList(context.Background(), msgs) - if err != nil { - t.Fatalf("batch access list request failed: %v", err) - } - - // Verify each result in the batch matches expectations - for i, tc := range testCases { - // Skip error cases that would fail the batch request - if tc.wantErr != "" { - continue - } - - // Verify gas used (allow some flexibility because of blockchain state changes) - if gasUsed[i] < tc.wantGas*9/10 || gasUsed[i] > tc.wantGas*11/10 { - t.Errorf("test %d: gas wrong, have %v want %v", i, gasUsed[i], tc.wantGas) - } - - // Verify VM errors - if tc.wantVMErr != "" && !strings.Contains(vmErrs[i], tc.wantVMErr) { - t.Errorf("test %d: vmErr wrong, have %v want %v", i, vmErrs[i], tc.wantVMErr) - } else if tc.wantVMErr == "" && vmErrs[i] != "" { - t.Errorf("test %d: unexpected VM error: %v", i, vmErrs[i]) - } - - // Verify access list matches expected JSON format - if accessLists[i] != nil { - haveList, _ := json.MarshalIndent(accessLists[i], "", " ") - if have, want := string(haveList), tc.wantAL; have != want { - t.Errorf("test %d: access list wrong, have:\n%v\nwant:\n%v", i, have, want) - } - } - } -} - func testGetProof(t *testing.T, client *rpc.Client, addr common.Address) { ec := New(client) ethcl := ethclient.NewClient(client) @@ -715,3 +618,156 @@ func testCallContractWithBlockOverrides(t *testing.T, client *rpc.Client) { t.Fatalf("unexpected result: %x", res) } } + +func testBatchAccessList(t *testing.T, client *rpc.Client) { + ec := New(client) + + testCases := []struct { + name string + msgs []ethereum.CallMsg + wantGas []uint64 + wantErr string + wantVMErr []string + wantALs []string + }{ + { + name: "Simple transfers", + msgs: []ethereum.CallMsg{ + { + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(875000000), + Value: big.NewInt(1), + }, + { + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(875000000), + Value: big.NewInt(2), + }, + }, + wantGas: []uint64{21000, 21000}, + wantALs: []string{ + `[]`, + `[]`, + }, + }, + { + name: "Contract creation and interaction", + msgs: []ethereum.CallMsg{ + { + From: testAddr, + To: nil, + Gas: 100000, + GasPrice: big.NewInt(1000000000), + Value: big.NewInt(0), + Data: common.FromHex("0x608060806080608155fd"), + }, + { + From: testAddr, + To: &testContract, + Gas: 100000, + GasPrice: big.NewInt(1000000000), + Value: big.NewInt(0), + Data: common.FromHex("0x1234"), + }, + }, + wantGas: []uint64{77496, 21896}, + wantVMErr: []string{ + "execution reverted", + "", + }, + wantALs: []string{ + `[ + { + "address": "0x3a220f351252089d385b29beca14e27f204c296a", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000081" + ] + } +]`, + `[ + { + "address": "0x000000000000000000000000000000000000beef", + "storageKeys": [] + } +]`, + }, + }, + { + name: "Invalid gas price", + msgs: []ethereum.CallMsg{ + { + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(1), // less than baseFee + Value: big.NewInt(1), + }, + }, + wantErr: "max fee per gas less than block base fee", + }, + } + + for i, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Convert CallMsg to call arguments + callArgs := make([]interface{}, len(tc.msgs)) + for i, msg := range tc.msgs { + callArgs[i] = toCallArg(msg) + } + + var result struct { + AccessLists []*types.AccessList `json:"accessLists"` + Errors []string `json:"errors,omitempty"` + GasUsed []hexutil.Uint64 `json:"gasUsed"` + } + err := ec.c.CallContext(context.Background(), &result, "eth_createBatchAccessList", callArgs) + + if tc.wantErr != "" { + if err == nil || !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("test %d: expected error containing %q, got %v", i, tc.wantErr, err) + } + return + } else if err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + + // Convert gas values + gas := make([]uint64, len(result.GasUsed)) + for i, g := range result.GasUsed { + gas[i] = uint64(g) + } + + if len(gas) != len(tc.wantGas) { + t.Fatalf("test %d: wrong number of gas values, want %d got %d", i, len(tc.wantGas), len(gas)) + } + for j, want := range tc.wantGas { + if have := gas[j]; have != want { + t.Errorf("test %d, msg %d: gas wrong, have %v want %v", i, j, have, want) + } + } + + if len(result.Errors) != len(tc.wantVMErr) { + t.Fatalf("test %d: wrong number of vm errors, want %d got %d", i, len(tc.wantVMErr), len(result.Errors)) + } + for j, want := range tc.wantVMErr { + if have := result.Errors[j]; have != want { + t.Errorf("test %d, msg %d: vm error wrong, have %v want %v", i, j, have, want) + } + } + + if len(result.AccessLists) != len(tc.wantALs) { + t.Fatalf("test %d: wrong number of access lists, want %d got %d", i, len(tc.wantALs), len(result.AccessLists)) + } + for j, want := range tc.wantALs { + haveList, _ := json.MarshalIndent(result.AccessLists[j], "", " ") + if have := string(haveList); have != want { + t.Errorf("test %d, msg %d: access list wrong,\nhave:\n%v\nwant:\n%v", i, j, have, want) + } + } + }) + } +}