From bea23a3c5fca4f2bda610ec235e252373f3d762c Mon Sep 17 00:00:00 2001 From: mralj Date: Fri, 27 Sep 2024 22:40:37 +0200 Subject: [PATCH] added batch call for StoragesAt as well as tests --- core/vm/contracts_rollup.go | 2 +- core/vm/contracts_rollup_test.go | 22 +++++------ core/vm/interpreter.go | 2 +- .../vm/testdata/precompiles/fail-l1sload.json | 16 ++++++++ core/vm/testdata/precompiles/l1sload.json | 24 ++++++++++++ ethclient/ethclient_rollup.go | 38 +++++++++++++++++++ params/protocol_params_rollup.go | 3 +- 7 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 core/vm/testdata/precompiles/fail-l1sload.json create mode 100644 core/vm/testdata/precompiles/l1sload.json create mode 100644 ethclient/ethclient_rollup.go diff --git a/core/vm/contracts_rollup.go b/core/vm/contracts_rollup.go index 8dee19ed6f4f..b1b2f17b4cc3 100644 --- a/core/vm/contracts_rollup.go +++ b/core/vm/contracts_rollup.go @@ -100,7 +100,7 @@ func (c *L1SLoad) Run(input []byte) ([]byte, error) { ctx = context.Background() } - res, err := c.L1RpcClient.StorageAt(ctx, contractAddress, contractStorageKeys[0], c.GetLatestL1BlockNumber()) + res, err := c.L1RpcClient.StoragesAt(ctx, contractAddress, contractStorageKeys, c.GetLatestL1BlockNumber()) if err != nil { return nil, err } diff --git a/core/vm/contracts_rollup_test.go b/core/vm/contracts_rollup_test.go index cbca8188c9f6..b798b5a24535 100644 --- a/core/vm/contracts_rollup_test.go +++ b/core/vm/contracts_rollup_test.go @@ -10,8 +10,15 @@ import ( type MockL1RPCClient struct{} -func (m MockL1RPCClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { - return common.Hex2Bytes("abab"), nil +func (m MockL1RPCClient) StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) { + // testcase is in format "abab", this makes output lenght 2 bytes + const mockedRespValueSize = 2 + mockResp := make([]byte, mockedRespValueSize*len(keys)) + for i := range keys { + copy(mockResp[mockedRespValueSize*i:], common.Hex2Bytes("abab")) + } + + return mockResp, nil } func TestPrecompiledL1SLOAD(t *testing.T) { @@ -20,13 +27,6 @@ func TestPrecompiledL1SLOAD(t *testing.T) { allPrecompiles[rollupL1SloadAddress] = &L1SLoad{} allPrecompiles.activateL1SLoad(mockL1RPCClient, func() *big.Int { return big1 }) - l1SLoadTestcase := precompiledTest{ - Name: "L1SLOAD", - Input: "C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc22d2c7bb6fc06067df8b0223aec460d1ebb51febb9012bc2554141a4dca08e864", - Expected: "abab", - Gas: 4000, - NoBenchmark: true, - } - - testPrecompiled(rollupL1SloadAddress.Hex(), l1SLoadTestcase, t) + testJson("l1sload", rollupL1SloadAddress.Hex(), t) + testJsonFail("l1sload", rollupL1SloadAddress.Hex(), t) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ebb07b8b8b5a..0872dfde4048 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -42,7 +42,7 @@ type Config struct { // [rollup-geth] type L1RpcClient interface { - StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) + StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) } // ScopeContext contains the things that are per-call, such as stack and memory, diff --git a/core/vm/testdata/precompiles/fail-l1sload.json b/core/vm/testdata/precompiles/fail-l1sload.json new file mode 100644 index 000000000000..2128bec29b86 --- /dev/null +++ b/core/vm/testdata/precompiles/fail-l1sload.json @@ -0,0 +1,16 @@ +[ + { + "Name": "L1SLOAD FAIL: input contains only address", + "Input": "a83114A443dA1CecEFC50368531cACE9F37fCCcb", + "ExpectedError": "L1SLOAD input too short", + "Gas": 4000, + "NoBenchmark": true + }, + { + "Name": "L1SLOAD FAIL: input key not 32 bytes", + "Input": "a83114A443dA1CecEFC50368531cACE9F37fCCcb112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7122", + "ExpectedError": "L1SLOAD input is malformed", + "Gas": 4000, + "NoBenchmark": true + } +] diff --git a/core/vm/testdata/precompiles/l1sload.json b/core/vm/testdata/precompiles/l1sload.json new file mode 100644 index 000000000000..b0debfd3b49a --- /dev/null +++ b/core/vm/testdata/precompiles/l1sload.json @@ -0,0 +1,24 @@ +[ + { + "Name": "L1SLOAD: 1 key", + "Input": "C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc22d2c7bb6fc06067df8b0223aec460d1ebb51febb9012bc2554141a4dca08e864", + "Expected": "abab", + "Gas": 4000, + "NoBenchmark": true + }, + { + "Name": "L1SLOAD: 2 keys", + "Input": "C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc22d2c7bb6fc06067df8b0223aec460d1ebb51febb9012bc2554141a4dca08e8640112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7a", + "Expected": "abababab", + "Gas": 6000, + "NoBenchmark": true + }, + + { + "Name": "L1SLOAD: 5 keys", + "Input": "C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc22d2c7bb6fc06067df8b0223aec460d1ebb51febb9012bc2554141a4dca08e8640112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7a112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7a112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7a112d016b65e9c617ad9ab60604f772a3620177bada4cdc773d9b6a982d3c2a7a", + "Expected": "abababababababababab", + "Gas": 12000, + "NoBenchmark": true + } +] diff --git a/ethclient/ethclient_rollup.go b/ethclient/ethclient_rollup.go new file mode 100644 index 000000000000..4571cdca8509 --- /dev/null +++ b/ethclient/ethclient_rollup.go @@ -0,0 +1,38 @@ +package ethclient + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" +) + +// StoragesAt returns the values of keys in the contract storage of the given account. +// The block number can be nil, in which case the value is taken from the latest known block. +func (ec *Client) StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) { + results := make([]hexutil.Bytes, len(keys)) + reqs := make([]rpc.BatchElem, len(keys)) + + for i := range reqs { + reqs[i] = rpc.BatchElem{ + Method: "eth_getStorageAt", + Args: []interface{}{account, keys[i], toBlockNumArg(blockNumber)}, + Result: &results[i], + } + } + if err := ec.c.BatchCallContext(ctx, reqs); err != nil { + return nil, err + } + + output := make([]byte, common.HashLength*len(keys)) + for i := range reqs { + if reqs[i].Error != nil { + return nil, reqs[i].Error + } + copy(output[i*common.HashLength:], results[i]) + } + + return output, nil +} diff --git a/params/protocol_params_rollup.go b/params/protocol_params_rollup.go index 46a0c9f570a9..d75663d81c78 100644 --- a/params/protocol_params_rollup.go +++ b/params/protocol_params_rollup.go @@ -4,6 +4,5 @@ const ( L1SLoadBaseGas uint64 = 2000 // Base price for L1Sload L1SLoadPerLoadGas uint64 = 2000 // Per-load price for loading one storage slot L1SLoadMaxNumStorageSlots = 5 // Max number of storage slots requested in L1Sload precompile + L1SLoadRPCTimeoutInSec = 3 ) - -var L1SLoadRPCTimeoutInSec = MainnetChainConfig.Clique.Period // After how many ms will RPC call timeout