mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
create batch access list
This commit is contained in:
parent
624a5d8b23
commit
d20066f29e
5 changed files with 391 additions and 23 deletions
|
|
@ -60,6 +60,35 @@ func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*
|
||||||
return result.Accesslist, uint64(result.GasUsed), result.Error, nil
|
return result.Accesslist, uint64(result.GasUsed), result.Error, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateBatchAccessList tries to create access lists for a sequence of transactions where
|
||||||
|
// each transaction's state changes affect subsequent transactions.
|
||||||
|
func (ec *Client) CreateBatchAccessList(ctx context.Context, msgs []ethereum.CallMsg) ([]*types.AccessList, []uint64, []string, error) {
|
||||||
|
type batchAccessListResult struct {
|
||||||
|
Accesslists []*types.AccessList `json:"accessLists"`
|
||||||
|
Errors []string `json:"errors,omitempty"`
|
||||||
|
GasUsed []hexutil.Uint64 `json:"gasUsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert all messages to call args
|
||||||
|
callArgs := make([]interface{}, len(msgs))
|
||||||
|
for i, msg := range msgs {
|
||||||
|
callArgs[i] = toCallArg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result batchAccessListResult
|
||||||
|
if err := ec.c.CallContext(ctx, &result, "eth_createBatchAccessList", []interface{}{callArgs}); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert results to expected return types
|
||||||
|
gasUsed := make([]uint64, len(result.GasUsed))
|
||||||
|
for i, gas := range result.GasUsed {
|
||||||
|
gasUsed[i] = uint64(gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Accesslists, gasUsed, result.Errors, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AccountResult is the result of a GetProof operation.
|
// AccountResult is the result of a GetProof operation.
|
||||||
type AccountResult struct {
|
type AccountResult struct {
|
||||||
Address common.Address `json:"address"`
|
Address common.Address `json:"address"`
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,16 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||||
t.Fatalf("can't create new ethereum service: %v", err)
|
t.Fatalf("can't create new ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
||||||
n.RegisterAPIs([]rpc.API{{
|
n.RegisterAPIs([]rpc.API{
|
||||||
Namespace: "eth",
|
{
|
||||||
Service: filters.NewFilterAPI(filterSystem),
|
Namespace: "eth",
|
||||||
}})
|
Service: filters.NewFilterAPI(filterSystem),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Namespace: "eth",
|
||||||
|
Service: ethservice.APIBackend,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// Import the test chain.
|
// Import the test chain.
|
||||||
if err := n.Start(); err != nil {
|
if err := n.Start(); err != nil {
|
||||||
|
|
@ -110,50 +116,62 @@ func TestGethClient(t *testing.T) {
|
||||||
test func(t *testing.T)
|
test func(t *testing.T)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"TestGetProof1",
|
"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) },
|
func(t *testing.T) { testGetProof(t, client, testAddr) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGetProof2",
|
"TestGetProof2",
|
||||||
func(t *testing.T) { testGetProof(t, client, testContract) },
|
func(t *testing.T) { testGetProof(t, client, testContract) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGetProofEmpty",
|
"TestGetProofEmpty",
|
||||||
func(t *testing.T) { testGetProof(t, client, testEmpty) },
|
func(t *testing.T) { testGetProof(t, client, testEmpty) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGetProofNonExistent",
|
"TestGetProofNonExistent",
|
||||||
func(t *testing.T) { testGetProofNonExistent(t, client) },
|
func(t *testing.T) { testGetProofNonExistent(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGetProofCanonicalizeKeys",
|
"TestGetProofCanonicalizeKeys",
|
||||||
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGCStats",
|
"TestGCStats",
|
||||||
func(t *testing.T) { testGCStats(t, client) },
|
func(t *testing.T) { testGCStats(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestMemStats",
|
"TestMemStats",
|
||||||
func(t *testing.T) { testMemStats(t, client) },
|
func(t *testing.T) { testMemStats(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestGetNodeInfo",
|
"TestGetNodeInfo",
|
||||||
func(t *testing.T) { testGetNodeInfo(t, client) },
|
func(t *testing.T) { testGetNodeInfo(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestSubscribePendingTxHashes",
|
"TestSubscribePendingTxHashes",
|
||||||
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
|
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestSubscribePendingTxs",
|
"TestSubscribePendingTxs",
|
||||||
func(t *testing.T) { testSubscribeFullPendingTransactions(t, client) },
|
func(t *testing.T) { testSubscribeFullPendingTransactions(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestCallContract",
|
"TestCallContract",
|
||||||
func(t *testing.T) { testCallContract(t, client) },
|
func(t *testing.T) { testCallContract(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"TestCallContractWithBlockOverrides",
|
"TestCallContractWithBlockOverrides",
|
||||||
func(t *testing.T) { testCallContractWithBlockOverrides(t, client) },
|
func(t *testing.T) { testCallContractWithBlockOverrides(t, client) },
|
||||||
},
|
},
|
||||||
// The testaccesslist is a bit time-sensitive: the newTestBackend imports
|
|
||||||
// one block. The `testAccessList` fails if the miner has not yet created a
|
|
||||||
// new pending-block after the import event.
|
|
||||||
// Hence: this test should be last, execute the tests serially.
|
|
||||||
{
|
{
|
||||||
"TestAccessList",
|
|
||||||
func(t *testing.T) { testAccessList(t, client) },
|
|
||||||
}, {
|
|
||||||
"TestSetHead",
|
"TestSetHead",
|
||||||
func(t *testing.T) { testSetHead(t, client) },
|
func(t *testing.T) { testSetHead(t, client) },
|
||||||
},
|
},
|
||||||
|
|
@ -247,6 +265,104 @@ 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) {
|
func testGetProof(t *testing.T, client *rpc.Client, addr common.Address) {
|
||||||
ec := New(client)
|
ec := New(client)
|
||||||
ethcl := ethclient.NewClient(client)
|
ethcl := ethclient.NewClient(client)
|
||||||
|
|
|
||||||
|
|
@ -1114,6 +1114,83 @@ type accessListResult struct {
|
||||||
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// batchAccessListResult is the result of creating a tx accesslist for batch transactions.
|
||||||
|
type batchAccessListResult struct {
|
||||||
|
Accesslists []*types.AccessList `json:"accessLists"`
|
||||||
|
GasUsed []hexutil.Uint64 `json:"gasUsed"`
|
||||||
|
Errors []string `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBatchAccessList creates EIP-2930 type AccessLists for a batch of transactions.
|
||||||
|
// This function executes transactions sequentially, with each transaction's state changes
|
||||||
|
// affecting the subsequent transactions in the batch.
|
||||||
|
func (api *BlockChainAPI) CreateBatchAccessList(ctx context.Context, argsList []TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*batchAccessListResult, error) {
|
||||||
|
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
|
if blockNrOrHash != nil {
|
||||||
|
bNrOrHash = *blockNrOrHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the initial state
|
||||||
|
stateDB, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, bNrOrHash)
|
||||||
|
if stateDB == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &batchAccessListResult{
|
||||||
|
Accesslists: make([]*types.AccessList, len(argsList)),
|
||||||
|
GasUsed: make([]hexutil.Uint64, len(argsList)),
|
||||||
|
Errors: make([]string, len(argsList)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each transaction in the batch sequentially
|
||||||
|
for i, args := range argsList {
|
||||||
|
// Use a copy of the state so we can apply changes without affecting the original state
|
||||||
|
txStateDB := stateDB.Copy()
|
||||||
|
|
||||||
|
// Create access list for this transaction
|
||||||
|
acl, gasUsed, vmerr, err := AccessList(ctx, api.b, bNrOrHash, args)
|
||||||
|
if err != nil {
|
||||||
|
// If there's an error creating the access list, record it and stop processing
|
||||||
|
result.Errors[i] = err.Error()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result.Accesslists[i] = &acl
|
||||||
|
result.GasUsed[i] = hexutil.Uint64(gasUsed)
|
||||||
|
if vmerr != nil {
|
||||||
|
result.Errors[i] = vmerr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the transaction to update the state for the next transaction
|
||||||
|
if i < len(argsList)-1 {
|
||||||
|
// Set fee defaults and any missing fields
|
||||||
|
if err = args.setFeeDefaults(ctx, api.b, header); err != nil {
|
||||||
|
result.Errors[i] = "failed to set fee defaults: " + err.Error()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the transaction to the state
|
||||||
|
msg := args.ToMessage(header.BaseFee, true, true)
|
||||||
|
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, api.b), nil)
|
||||||
|
txCtx := core.NewEVMTxContext(msg)
|
||||||
|
evm := vm.NewEVM(blockCtx, txStateDB, api.b.ChainConfig(), vm.Config{})
|
||||||
|
evm.SetTxContext(txCtx)
|
||||||
|
|
||||||
|
// Apply the message to update the state
|
||||||
|
gp := new(core.GasPool).AddGas(msg.GasLimit)
|
||||||
|
_, err := core.ApplyMessage(evm, msg, gp)
|
||||||
|
if err != nil {
|
||||||
|
result.Errors[i] = "failed to apply transaction: " + err.Error()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the state for the next transaction
|
||||||
|
stateDB = txStateDB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateAccessList creates an EIP-2930 type AccessList for the given transaction.
|
// CreateAccessList creates an EIP-2930 type AccessList for the given transaction.
|
||||||
// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state.
|
// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state.
|
||||||
func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) {
|
func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) {
|
||||||
|
|
|
||||||
|
|
@ -3511,3 +3511,143 @@ func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc s
|
||||||
func addressToHash(a common.Address) common.Hash {
|
func addressToHash(a common.Address) common.Hash {
|
||||||
return common.BytesToHash(a.Bytes())
|
return common.BytesToHash(a.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCreateAccessList tests the CreateAccessList API method.
|
||||||
|
func TestCreateBatchAccessList(t *testing.T) {
|
||||||
|
// Create a test backend
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
GasLimit: 30000000,
|
||||||
|
Timestamp: 87362,
|
||||||
|
Alloc: core.GenesisAlloc{
|
||||||
|
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): {
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Create backend with blocks generated
|
||||||
|
backend := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
|
||||||
|
// No special block generation needed for this test
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set up test transactions
|
||||||
|
signer := types.LatestSigner(params.TestChainConfig)
|
||||||
|
testAddr := common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
||||||
|
testKey, err := crypto.HexToECDSA("0c06818f82e04c564290b32ab86b25676731fc34e9a546383b41b8a6f5c088ac")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create test key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a simple transaction
|
||||||
|
simpleTx := types.NewTransaction(
|
||||||
|
0, // nonce
|
||||||
|
common.Address{}, // to
|
||||||
|
big.NewInt(1000), // value
|
||||||
|
21000, // gas limit
|
||||||
|
big.NewInt(1000000000), // gas price
|
||||||
|
nil, // data
|
||||||
|
)
|
||||||
|
simpleTx, err = types.SignTx(simpleTx, signer, testKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contract creation transaction
|
||||||
|
contractCreationTx := types.NewContractCreation(
|
||||||
|
1, // nonce
|
||||||
|
big.NewInt(0), // value
|
||||||
|
500000, // gas
|
||||||
|
big.NewInt(1000000000), // gas price
|
||||||
|
common.FromHex("0x608060806080608155fd"), // sample contract code
|
||||||
|
)
|
||||||
|
contractCreationTx, err = types.SignTx(contractCreationTx, signer, testKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign contract creation tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to TransactionArgs for API
|
||||||
|
nonce0 := hexutil.Uint64(0)
|
||||||
|
nonce1 := hexutil.Uint64(1)
|
||||||
|
simpleArgs := TransactionArgs{
|
||||||
|
From: &testAddr,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
Gas: (*hexutil.Uint64)(new(uint64)),
|
||||||
|
GasPrice: (*hexutil.Big)(big.NewInt(1000000000)),
|
||||||
|
Nonce: &nonce0,
|
||||||
|
}
|
||||||
|
*(*uint64)(simpleArgs.Gas) = 21000
|
||||||
|
|
||||||
|
contractArgs := TransactionArgs{
|
||||||
|
From: &testAddr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
Gas: (*hexutil.Uint64)(new(uint64)),
|
||||||
|
GasPrice: (*hexutil.Big)(big.NewInt(1000000000)),
|
||||||
|
Data: (*hexutil.Bytes)(&[]byte{0x60, 0x80, 0x60, 0x80, 0x60, 0x80, 0x60, 0x81, 0x55, 0xfd}),
|
||||||
|
Nonce: &nonce1,
|
||||||
|
}
|
||||||
|
*(*uint64)(contractArgs.Gas) = 500000
|
||||||
|
|
||||||
|
// Create API instance
|
||||||
|
api := NewBlockChainAPI(backend)
|
||||||
|
|
||||||
|
// Test single access list creation first for comparison
|
||||||
|
ctx := context.Background()
|
||||||
|
blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
|
|
||||||
|
// Create batch access list
|
||||||
|
result, err := api.CreateBatchAccessList(ctx, []TransactionArgs{simpleArgs, contractArgs}, &blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create batch access list: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify results
|
||||||
|
if len(result.Accesslists) != 2 {
|
||||||
|
t.Fatalf("Expected 2 access lists, got %d", len(result.Accesslists))
|
||||||
|
}
|
||||||
|
if len(result.GasUsed) != 2 {
|
||||||
|
t.Fatalf("Expected 2 gas values, got %d", len(result.GasUsed))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check first transaction (simple transfer)
|
||||||
|
if len(*result.Accesslists[0]) != 0 {
|
||||||
|
t.Fatalf("Expected empty access list for simple transfer, got %v", result.Accesslists[0])
|
||||||
|
}
|
||||||
|
if uint64(result.GasUsed[0]) != 21000 {
|
||||||
|
t.Fatalf("Expected gas used 21000 for simple transfer, got %d", result.GasUsed[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check second transaction (contract creation)
|
||||||
|
// The exact values might depend on the execution environment, but we can check the structure
|
||||||
|
if result.Accesslists[1] == nil || len(*result.Accesslists[1]) == 0 {
|
||||||
|
t.Fatalf("Expected non-empty access list for contract creation, got %v", result.Accesslists[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare with individual access list creation
|
||||||
|
singleResult1, err := api.CreateAccessList(ctx, simpleArgs, &blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create single access list for tx1: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
singleResult2, err := api.CreateAccessList(ctx, contractArgs, &blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create single access list for tx2: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify batch results match individual results
|
||||||
|
if !reflect.DeepEqual(singleResult1.Accesslist, result.Accesslists[0]) {
|
||||||
|
t.Fatalf("Batch access list for tx1 doesn't match individual access list")
|
||||||
|
}
|
||||||
|
|
||||||
|
// For contract transactions, we need to compare the structure rather than exact values
|
||||||
|
// because the contract address might be different
|
||||||
|
if (singleResult2.Accesslist == nil) != (result.Accesslists[1] == nil) ||
|
||||||
|
(singleResult2.Accesslist != nil && result.Accesslists[1] != nil &&
|
||||||
|
len(*singleResult2.Accesslist) != len(*result.Accesslists[1])) {
|
||||||
|
t.Fatalf("Batch access list for tx2 doesn't match individual access list in structure")
|
||||||
|
}
|
||||||
|
|
||||||
|
if singleResult1.GasUsed != result.GasUsed[0] {
|
||||||
|
t.Fatalf("Batch gas used for tx1 doesn't match individual gas used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -567,6 +567,12 @@ web3._extend({
|
||||||
params: 2,
|
params: 2,
|
||||||
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
|
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'createBatchAccessList',
|
||||||
|
call: 'eth_createBatchAccessList',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
|
||||||
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'feeHistory',
|
name: 'feeHistory',
|
||||||
call: 'eth_feeHistory',
|
call: 'eth_feeHistory',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue