diff --git a/evmrpc/state.go b/evmrpc/state.go index 96f7591210..f379d5352c 100644 --- a/evmrpc/state.go +++ b/evmrpc/state.go @@ -28,6 +28,8 @@ import ( var errNoProofCapableQueryableKVStore = errors.New("cannot find a proof-capable queryable KV store") +const MaxStorageKeysPerProof = 1024 + type StateAPI struct { tmClient client.LocalClient keeper *keeper.Keeper @@ -132,9 +134,19 @@ func (a *StateAPI) GetProof(ctx context.Context, address common.Address, storage if err != nil { return nil, err } + if len(storageKeys) > MaxStorageKeysPerProof { + return nil, fmt.Errorf("too many storage keys: got %d, max %d", len(storageKeys), MaxStorageKeysPerProof) + } + paddedKeys := make([]common.Hash, len(storageKeys)) + for i, key := range storageKeys { + paddedKey, _, err := decodeHash(key) + if err != nil { + return nil, fmt.Errorf("invalid storage key %q: %w", key, err) + } + paddedKeys[i] = paddedKey + } proofResult := ProofResult{Address: address} - for _, key := range storageKeys { - paddedKey := common.BytesToHash([]byte(key)) + for _, paddedKey := range paddedKeys { formattedKey := append(types.StateKey(address), paddedKey[:]...) qres := queryStore.Query(ctx, abci.RequestQuery{ Path: "/key", diff --git a/evmrpc/state_test.go b/evmrpc/state_test.go index c066522da3..1b2dd9c5ba 100644 --- a/evmrpc/state_test.go +++ b/evmrpc/state_test.go @@ -235,34 +235,50 @@ func TestGetProof(t *testing.T) { watermarks := evmrpc.NewWatermarkManager(client, ctxProvider, nil, testApp.EvmKeeper.ReceiptStore()) stateAPI := evmrpc.NewStateAPI(client, &testApp.EvmKeeper, ctxProvider, evmrpc.ConnectionTypeHTTP, watermarks) require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000616263", testApp.EvmKeeper.GetState(testApp.GetCheckCtx(), evmAddr, common.BytesToHash(key)).Hex()) + // hex-encode the storage slot as eth_getProof requires + hexKey := common.BytesToHash(key).Hex() tests := []struct { key string blockNr rpc.BlockNumber expectedVal []byte }{ { - key: string(key), + key: hexKey, blockNr: rpc.BlockNumber(-2), expectedVal: val, }, { - key: string(key), + key: hexKey, blockNr: rpc.BlockNumber(8), expectedVal: val, }, { - key: "non existent", + // valid hex slot that has no state set + key: "0x0000000000000000000000000000000000000000000000000000000000000001", blockNr: rpc.BlockNumber(-2), expectedVal: []byte{}, }, } for _, test := range tests { bptr := &rpc.BlockNumberOrHash{BlockNumber: &test.blockNr} - res, err := stateAPI.GetProof(context.Background(), evmAddr, []string{test.key}, *bptr) + res, err := stateAPI.GetProof(t.Context(), evmAddr, []string{test.key}, *bptr) require.Nil(t, err) vals := res.HexValues require.Equal(t, common.BytesToHash(test.expectedVal), common.HexToHash(vals[0])) proofs := res.StorageProof require.Equal(t, "ics23:iavl", proofs[0].Ops[0].Type) } + + // malformed key must be rejected + bptr := &rpc.BlockNumberOrHash{BlockNumber: func() *rpc.BlockNumber { n := rpc.BlockNumber(-2); return &n }()} + _, err := stateAPI.GetProof(t.Context(), evmAddr, []string{"not-hex"}, *bptr) + require.Error(t, err) + + // too many keys must be rejected + tooManyKeys := make([]string, evmrpc.MaxStorageKeysPerProof+1) + for i := range tooManyKeys { + tooManyKeys[i] = hexKey + } + _, err = stateAPI.GetProof(t.Context(), evmAddr, tooManyKeys, *bptr) + require.Error(t, err) }