Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions archive/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,20 @@ func (c *Client) LatestBlock() uint64 {
func (c *Client) GetStorageAt(
ctx context.Context,
address common.Address,
position hexutil.Big,
slot common.Hash,
blockNr uint64,
) (hexutil.Big, error) {
) (hexutil.Bytes, error) {
storageBytes, err := c.inner.StorageAt(
ctx,
address,
common.BigToHash((*big.Int)(&position)),
slot,
new(big.Int).SetUint64(blockNr),
)
if err != nil {
return hexutil.Big{}, fmt.Errorf("archive: failed to query storage: %w", err)
return nil, fmt.Errorf("archive: failed to query storage: %w", err)
}

// Oh for fuck's sake.
var storageBig big.Int
storageBig.SetBytes(storageBytes)
return hexutil.Big(storageBig), nil
return storageBytes, nil
}

func (c *Client) GetBalance(
Expand Down
49 changes: 33 additions & 16 deletions rpc/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"context"
"crypto/sha512"
"database/sql"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"

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

func (api *publicAPI) GetStorageAt(ctx context.Context, address common.Address, position hexutil.Big, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Big, error) {
logger := api.Logger.With("method", "eth_getStorageAt", "address", address, "position", position, "block_or_hash", blockNrOrHash)
// These matches most other eth-compatible gateways, which are very lax about the slot hex encoding.
func decodeHash(s string) (common.Hash, error) {
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
s = s[2:]
}
if (len(s) & 1) > 0 {
s = "0" + s
}
b, err := hex.DecodeString(s)
if err != nil {
return common.Hash{}, fmt.Errorf("hex string invalid")
}
if len(b) > 32 {
return common.Hash{}, fmt.Errorf("hex string too long, want at most 32 bytes")
}
return common.BytesToHash(b), nil
}

func (api *publicAPI) GetStorageAt(ctx context.Context, address common.Address, slotHex string, blockNrOrHash ethrpc.BlockNumberOrHash) (hexutil.Bytes, error) {
logger := api.Logger.With("method", "eth_getStorageAt", "address", address, "slot", slotHex, "block_or_hash", blockNrOrHash)
logger.Debug("request")

slot, err := decodeHash(slotHex)
if err != nil {
return hexutil.Bytes{}, fmt.Errorf("invalid slot: %w", err)
}

round, err := api.getBlockRound(ctx, logger, blockNrOrHash)
if err != nil {
return hexutil.Big{}, err
return hexutil.Bytes{}, err
}
if api.shouldQueryArchive(round) {
return api.archiveClient.GetStorageAt(ctx, address, position, round)
return api.archiveClient.GetStorageAt(ctx, address, slot, round)
}

// EVM module takes index as H256, which needs leading zeros.
position256 := make([]byte, 32)
// Unmarshalling to hexutil.Big rejects overlong inputs. Verify in `TestRejectOverlong`.
position.ToInt().FillBytes(position256)

ethmod := evm.NewV1(api.client)
res, err := ethmod.Storage(ctx, round, address[:], position256)
res, err := ethmod.Storage(ctx, round, address[:], slot[:])
if err != nil {
logger.Error("failed to query storage", "err", err)
return hexutil.Big{}, ErrInternalError
return nil, ErrInternalError
}
// Some apps expect no leading zeros, so output as big integer.
var resultBI big.Int
resultBI.SetBytes(res)
return hexutil.Big(resultBI), nil
return res[:], nil
}

func (api *publicAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash ethrpc.BlockNumberOrHash) (*hexutil.Big, error) {
Expand Down
4 changes: 2 additions & 2 deletions rpc/eth/metrics/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,11 @@ func (m *metricsWrapper) GetLogs(ctx context.Context, filter filters.FilterCrite
}

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

res, err = m.api.GetStorageAt(ctx, address, position, blockNrOrHash)
res, err = m.api.GetStorageAt(ctx, address, slot, blockNrOrHash)
return
}

Expand Down
38 changes: 38 additions & 0 deletions tests/rpc/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,44 @@ func TestEth_GetCode(t *testing.T) {
require.NotEmpty(t, storedCode)
}

func TestEth_GetStorageZero(t *testing.T) {
ec := localClient(t, false)

code := common.FromHex(strings.TrimSpace(evmSolTestCompiledHex))

chainID, err := ec.ChainID(context.Background())
require.Nil(t, err, "get chainid")

nonce, err := ec.NonceAt(context.Background(), tests.TestKey1.EthAddress, nil)
require.Nil(t, err, "get nonce failed")
t.Logf("got nonce: %v", nonce)

// Create transaction
tx := types.NewContractCreation(nonce, big.NewInt(0), GasLimit, GasPrice, code)
signer := types.LatestSignerForChainID(chainID)
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), tests.TestKey1.Private)
require.Nil(t, err, "sign tx")

signedTx, err := tx.WithSignature(signer, signature)
require.Nil(t, err, "pack tx")

err = ec.SendTransaction(context.Background(), signedTx)
require.Nil(t, err, "send transaction failed")

ctx, cancel := context.WithTimeout(context.Background(), ethTimeout)
defer cancel()

receipt, err := waitTransaction(ctx, ec, signedTx.Hash())
require.NoError(t, err)
t.Logf("SignedTx hash: %s", signedTx.Hash().Hex())
t.Logf("Contract address: %s", receipt.ContractAddress)

key := common.HexToHash("0x0")
res, err := ec.StorageAt(context.Background(), receipt.ContractAddress, key, nil)
require.NoError(t, err, "get storage at")
require.Equal(t, len(res), 32, "storage at should return 32 bytes (even if zero)")
}

func TestEth_Call(t *testing.T) {
abidata := `
[
Expand Down
Loading