Skip to content

Commit 6a7f84f

Browse files
authored
Merge pull request #760 from oasisprotocol/ptrus/feature/getStorageAt-0x
feat: eth_getStorageAt standard empty response
2 parents e9f0b22 + ac66ea6 commit 6a7f84f

File tree

4 files changed

+78
-26
lines changed

4 files changed

+78
-26
lines changed

archive/client.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,20 @@ func (c *Client) LatestBlock() uint64 {
3232
func (c *Client) GetStorageAt(
3333
ctx context.Context,
3434
address common.Address,
35-
position hexutil.Big,
35+
slot common.Hash,
3636
blockNr uint64,
37-
) (hexutil.Big, error) {
37+
) (hexutil.Bytes, error) {
3838
storageBytes, err := c.inner.StorageAt(
3939
ctx,
4040
address,
41-
common.BigToHash((*big.Int)(&position)),
41+
slot,
4242
new(big.Int).SetUint64(blockNr),
4343
)
4444
if err != nil {
45-
return hexutil.Big{}, fmt.Errorf("archive: failed to query storage: %w", err)
45+
return nil, fmt.Errorf("archive: failed to query storage: %w", err)
4646
}
4747

48-
// Oh for fuck's sake.
49-
var storageBig big.Int
50-
storageBig.SetBytes(storageBytes)
51-
return hexutil.Big(storageBig), nil
48+
return storageBytes, nil
5249
}
5350

5451
func (c *Client) GetBalance(

rpc/eth/api.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import (
66
"context"
77
"crypto/sha512"
88
"database/sql"
9+
"encoding/hex"
910
"errors"
1011
"fmt"
1112
"math/big"
13+
"strings"
1214

1315
"github.com/ethereum/go-ethereum/common"
1416
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -58,7 +60,7 @@ type API interface {
5860
// GetBlockTransactionCountByNumber returns the number of transactions in the block.
5961
GetBlockTransactionCountByNumber(ctx context.Context, blockNum ethrpc.BlockNumber) (hexutil.Uint, error)
6062
// GetStorageAt returns the storage value at the provided position.
61-
GetStorageAt(ctx context.Context, address common.Address, position hexutil.Big, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Big, error)
63+
GetStorageAt(ctx context.Context, address common.Address, slot string, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Bytes, error)
6264
// GetBalance returns the provided account's balance up to the provided block number.
6365
GetBalance(ctx context.Context, address common.Address, blockNrOrHash ethrpc.BlockNumberOrHash) (*hexutil.Big, error)
6466
// ChainId return the EIP-155 chain id for the current network.
@@ -232,33 +234,48 @@ func (api *publicAPI) GetBlockTransactionCountByNumber(ctx context.Context, bloc
232234
return hexutil.Uint(n), nil
233235
}
234236

235-
func (api *publicAPI) GetStorageAt(ctx context.Context, address common.Address, position hexutil.Big, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Big, error) {
236-
logger := api.Logger.With("method", "eth_getStorageAt", "address", address, "position", position, "block_or_hash", blockNrOrHash)
237+
// These matches most other eth-compatible gateways, which are very lax about the slot hex encoding.
238+
func decodeHash(s string) (common.Hash, error) {
239+
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
240+
s = s[2:]
241+
}
242+
if (len(s) & 1) > 0 {
243+
s = "0" + s
244+
}
245+
b, err := hex.DecodeString(s)
246+
if err != nil {
247+
return common.Hash{}, fmt.Errorf("hex string invalid")
248+
}
249+
if len(b) > 32 {
250+
return common.Hash{}, fmt.Errorf("hex string too long, want at most 32 bytes")
251+
}
252+
return common.BytesToHash(b), nil
253+
}
254+
255+
func (api *publicAPI) GetStorageAt(ctx context.Context, address common.Address, slotHex string, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Bytes, error) {
256+
logger := api.Logger.With("method", "eth_getStorageAt", "address", address, "slot", slotHex, "block_or_hash", blockNrOrHash)
237257
logger.Debug("request")
238258

259+
slot, err := decodeHash(slotHex)
260+
if err != nil {
261+
return hexutil.Bytes{}, fmt.Errorf("invalid slot: %w", err)
262+
}
263+
239264
round, err := api.getBlockRound(ctx, logger, blockNrOrHash)
240265
if err != nil {
241-
return hexutil.Big{}, err
266+
return hexutil.Bytes{}, err
242267
}
243268
if api.shouldQueryArchive(round) {
244-
return api.archiveClient.GetStorageAt(ctx, address, position, round)
269+
return api.archiveClient.GetStorageAt(ctx, address, slot, round)
245270
}
246271

247-
// EVM module takes index as H256, which needs leading zeros.
248-
position256 := make([]byte, 32)
249-
// Unmarshalling to hexutil.Big rejects overlong inputs. Verify in `TestRejectOverlong`.
250-
position.ToInt().FillBytes(position256)
251-
252272
ethmod := evm.NewV1(api.client)
253-
res, err := ethmod.Storage(ctx, round, address[:], position256)
273+
res, err := ethmod.Storage(ctx, round, address[:], slot[:])
254274
if err != nil {
255275
logger.Error("failed to query storage", "err", err)
256-
return hexutil.Big{}, ErrInternalError
276+
return nil, ErrInternalError
257277
}
258-
// Some apps expect no leading zeros, so output as big integer.
259-
var resultBI big.Int
260-
resultBI.SetBytes(res)
261-
return hexutil.Big(resultBI), nil
278+
return res[:], nil
262279
}
263280

264281
func (api *publicAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash ethrpc.BlockNumberOrHash) (*hexutil.Big, error) {

rpc/eth/metrics/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,11 @@ func (m *metricsWrapper) GetLogs(ctx context.Context, filter filters.FilterCrite
286286
}
287287

288288
// GetStorageAt implements eth.API.
289-
func (m *metricsWrapper) GetStorageAt(ctx context.Context, address common.Address, position hexutil.Big, blockNrOrHash ethrpc.BlockNumberOrHash) (res hexutil.Big, err error) {
289+
func (m *metricsWrapper) GetStorageAt(ctx context.Context, address common.Address, slot string, blockNrOrHash ethrpc.BlockNumberOrHash) (res hexutil.Bytes, err error) {
290290
r, s, f, i, d := metrics.GetAPIMethodMetrics("eth_getStorageAt")
291291
defer metrics.InstrumentCaller(r, s, f, i, d, &err)()
292292

293-
res, err = m.api.GetStorageAt(ctx, address, position, blockNrOrHash)
293+
res, err = m.api.GetStorageAt(ctx, address, slot, blockNrOrHash)
294294
return
295295
}
296296

tests/rpc/tx_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,44 @@ func TestEth_GetCode(t *testing.T) {
430430
require.NotEmpty(t, storedCode)
431431
}
432432

433+
func TestEth_GetStorageZero(t *testing.T) {
434+
ec := localClient(t, false)
435+
436+
code := common.FromHex(strings.TrimSpace(evmSolTestCompiledHex))
437+
438+
chainID, err := ec.ChainID(context.Background())
439+
require.Nil(t, err, "get chainid")
440+
441+
nonce, err := ec.NonceAt(context.Background(), tests.TestKey1.EthAddress, nil)
442+
require.Nil(t, err, "get nonce failed")
443+
t.Logf("got nonce: %v", nonce)
444+
445+
// Create transaction
446+
tx := types.NewContractCreation(nonce, big.NewInt(0), GasLimit, GasPrice, code)
447+
signer := types.LatestSignerForChainID(chainID)
448+
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), tests.TestKey1.Private)
449+
require.Nil(t, err, "sign tx")
450+
451+
signedTx, err := tx.WithSignature(signer, signature)
452+
require.Nil(t, err, "pack tx")
453+
454+
err = ec.SendTransaction(context.Background(), signedTx)
455+
require.Nil(t, err, "send transaction failed")
456+
457+
ctx, cancel := context.WithTimeout(context.Background(), ethTimeout)
458+
defer cancel()
459+
460+
receipt, err := waitTransaction(ctx, ec, signedTx.Hash())
461+
require.NoError(t, err)
462+
t.Logf("SignedTx hash: %s", signedTx.Hash().Hex())
463+
t.Logf("Contract address: %s", receipt.ContractAddress)
464+
465+
key := common.HexToHash("0x0")
466+
res, err := ec.StorageAt(context.Background(), receipt.ContractAddress, key, nil)
467+
require.NoError(t, err, "get storage at")
468+
require.Equal(t, len(res), 32, "storage at should return 32 bytes (even if zero)")
469+
}
470+
433471
func TestEth_Call(t *testing.T) {
434472
abidata := `
435473
[

0 commit comments

Comments
 (0)