diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index e3703398c..b38129bb1 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -95,6 +95,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon "getpeers": (*Server).getPeers, "getrawmempool": (*Server).getRawMempool, "getrawtransaction": (*Server).getrawtransaction, + "getstateroot": (*Server).getStateRoot, "getstorage": (*Server).getStorage, "gettransactionheight": (*Server).getTransactionHeight, "gettxout": (*Server).getTxOut, @@ -687,6 +688,28 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6 return d, nil } +func (s *Server) getStateRoot(ps request.Params) (interface{}, *response.Error) { + p := ps.Value(0) + if p == nil { + return nil, response.NewRPCError("Invalid parameter.", "", nil) + } + var rt *state.MPTRootState + var h util.Uint256 + height, err := p.GetInt() + if err == nil { + rt, err = s.chain.GetStateRoot(uint32(height)) + } else if h, err = p.GetUint256(); err == nil { + hdr, err := s.chain.GetHeader(h) + if err == nil { + rt, err = s.chain.GetStateRoot(hdr.Index) + } + } + if err != nil { + return nil, response.NewRPCError("Unknown state root.", "", err) + } + return rt, nil +} + func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) { scriptHash, err := ps.Value(0).GetUint160FromHex() if err != nil { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 6103156f7..c6d384f80 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -16,6 +16,7 @@ import ( "github.com/gorilla/websocket" "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" @@ -213,6 +214,18 @@ var rpcTestCases = map[string][]rpcTestCase{ }, }, }, + "getstateroot": { + { + name: "no params", + params: `[]`, + fail: true, + }, + { + name: "invalid hash", + params: `["0x1234567890"]`, + fail: true, + }, + }, "getstorage": { { name: "positive", @@ -928,6 +941,25 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] }) } + t.Run("getstateroot", func(t *testing.T) { + testRoot := func(t *testing.T, p string) { + rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p) + fmt.Println(rpc) + body := doRPCCall(rpc, httpSrv.URL, t) + rawRes := checkErrGetResult(t, body, false) + + res := new(state.MPTRootState) + require.NoError(t, json.Unmarshal(rawRes, res)) + require.NotEqual(t, util.Uint256{}, res.Root) // be sure this test uses valid height + + expected, err := e.chain.GetStateRoot(205) + require.NoError(t, err) + require.Equal(t, expected, res) + } + t.Run("ByHeight", func(t *testing.T) { testRoot(t, strconv.FormatInt(205, 10)) }) + t.Run("ByHash", func(t *testing.T) { testRoot(t, `"`+chain.GetHeaderHash(205).StringLE()+`"`) }) + }) + t.Run("getrawtransaction", func(t *testing.T) { block, _ := chain.GetBlock(chain.GetHeaderHash(0)) TXHash := block.Transactions[1].Hash()