rpcsrv, rpcclient: support getstoragehistoric call

Make it similar to `findstoragehistoric`.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
Anna Shaleva 2023-08-22 19:29:22 +03:00
parent 97d523ceed
commit 1fb0c96e2c
5 changed files with 180 additions and 0 deletions

View file

@ -559,6 +559,26 @@ func (c *Client) getStorage(params []any) ([]byte, error) {
return resp, nil
}
// GetStorageByIDHistoric returns the historical stored value according to the
// contract ID and, stored key and specified stateroot.
func (c *Client) GetStorageByIDHistoric(root util.Uint256, id int32, key []byte) ([]byte, error) {
return c.getStorageHistoric([]any{root.StringLE(), id, key})
}
// GetStorageByHashHistoric returns the historical stored value according to the
// contract script hash, the stored key and specified stateroot.
func (c *Client) GetStorageByHashHistoric(root util.Uint256, hash util.Uint160, key []byte) ([]byte, error) {
return c.getStorageHistoric([]any{root.StringLE(), hash.StringLE(), key})
}
func (c *Client) getStorageHistoric(params []any) ([]byte, error) {
var resp []byte
if err := c.performRequest("getstoragehistoric", params, &resp); err != nil {
return nil, err
}
return resp, nil
}
// FindStorageByHash returns contract storage items by the given contract hash and prefix.
// If `start` index is specified, items starting from `start` index are being returned
// (including item located at the start index).

View file

@ -1040,6 +1040,50 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
},
},
},
"getstoragehistoric": {
{
name: "by hash, positive",
invoke: func(c *Client) (any, error) {
root, _ := util.Uint256DecodeStringLE("252e9d73d49c95c7618d40650da504e05183a1b2eed0685e42c360413c329170")
hash, err := util.Uint160DecodeStringLE("03febccf81ac85e3d795bc5cbd4e84e907812aa3")
if err != nil {
panic(err)
}
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByHashHistoric(root, hash, key)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"TGlu"}`,
result: func(c *Client) any {
value, err := hex.DecodeString("4c696e")
if err != nil {
panic(err)
}
return value
},
},
{
name: "by ID, positive",
invoke: func(c *Client) (any, error) {
root, _ := util.Uint256DecodeStringLE("252e9d73d49c95c7618d40650da504e05183a1b2eed0685e42c360413c329170")
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByIDHistoric(root, -1, key)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"TGlu"}`,
result: func(c *Client) any {
value, err := hex.DecodeString("4c696e")
if err != nil {
panic(err)
}
return value
},
},
},
"gettransactionheight": {
{
name: "positive",

View file

@ -2690,3 +2690,36 @@ func TestClient_FindStorageHistoric(t *testing.T) {
require.NoError(t, err)
require.Equal(t, result.FindStorage{}, actual)
}
func TestClient_GetStorageHistoric(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
root, err := util.Uint256DecodeStringLE(block20StateRootLE)
require.NoError(t, err)
h, err := util.Uint160DecodeStringLE(testContractHash)
require.NoError(t, err)
key := []byte("aa10")
expected := []byte("v2")
// By hash.
actual, err := c.GetStorageByHashHistoric(root, h, key)
require.NoError(t, err)
require.Equal(t, expected, actual)
// By ID.
actual, err = c.GetStorageByIDHistoric(root, 1, key) // Rubles contract
require.NoError(t, err)
require.Equal(t, expected, actual)
// Missing item.
earlyRoot, err := chain.GetStateRoot(15) // there's no `aa10` value in Rubles contract by the moment of block #15
require.NoError(t, err)
_, err = c.GetStorageByHashHistoric(earlyRoot.Root, h, key)
require.ErrorIs(t, neorpc.ErrUnknownStorageItem, err)
}

View file

@ -229,6 +229,7 @@ var rpcHandlers = map[string]func(*Server, params.Params) (any, *neorpc.Error){
"getstateheight": (*Server).getStateHeight,
"getstateroot": (*Server).getStateRoot,
"getstorage": (*Server).getStorage,
"getstoragehistoric": (*Server).getStorageHistoric,
"gettransactionheight": (*Server).getTransactionHeight,
"getunclaimedgas": (*Server).getUnclaimedGas,
"getnextblockvalidators": (*Server).getNextBlockValidators,
@ -1855,6 +1856,36 @@ func (s *Server) getStorage(ps params.Params) (any, *neorpc.Error) {
return []byte(item), nil
}
func (s *Server) getStorageHistoric(ps params.Params) (any, *neorpc.Error) {
root, respErr := s.getStateRootFromParam(ps.Value(0))
if respErr != nil {
return nil, respErr
}
if len(ps) < 2 {
return nil, neorpc.ErrInvalidParams
}
id, rErr := s.contractIDFromParam(ps.Value(1), root)
if rErr != nil {
return nil, rErr
}
key, err := ps.Value(2).GetBytesBase64()
if err != nil {
return nil, neorpc.ErrInvalidParams
}
pKey := makeStorageKey(id, key)
v, err := s.chain.GetStateModule().GetState(root, pKey)
if err != nil && !errors.Is(err, mpt.ErrNotFound) {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to get state item: %s", err))
}
if v == nil {
return "", neorpc.ErrUnknownStorageItem
}
return v, nil
}
func (s *Server) getrawtransaction(reqParams params.Params) (any, *neorpc.Error) {
txHash, err := reqParams.Value(0).GetUint256()
if err != nil {

View file

@ -659,6 +659,58 @@ var rpcTestCases = map[string][]rpcTestCase{
errCode: neorpc.InvalidParamsCode,
},
},
"getstoragehistoric": {
{
name: "positive",
params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa10"))),
result: func(e *executor) any {
v := base64.StdEncoding.EncodeToString([]byte("v2"))
return &v
},
},
{
name: "missing key",
params: fmt.Sprintf(`["%s", "%s", "dGU="]`, block20StateRootLE, testContractHash),
fail: true,
errCode: neorpc.ErrUnknownStorageItemCode,
},
{
name: "no params",
params: `[]`,
fail: true,
errCode: neorpc.InvalidParamsCode,
},
{
name: "no second parameter",
params: fmt.Sprintf(`["%s"]`, block20StateRootLE),
fail: true,
errCode: neorpc.InvalidParamsCode,
},
{
name: "no third parameter",
params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash),
fail: true,
errCode: neorpc.InvalidParamsCode,
},
{
name: "invalid stateroot",
params: `["notahex"]`,
fail: true,
errCode: neorpc.InvalidParamsCode,
},
{
name: "invalid hash",
params: fmt.Sprintf(`["%s", "notahex"]`, block20StateRootLE),
fail: true,
errCode: neorpc.InvalidParamsCode,
},
{
name: "invalid key",
params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash),
fail: true,
errCode: neorpc.InvalidParamsCode,
},
},
"findstorage": {
{
name: "not truncated",