diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 9cda7271c..e783a43f5 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -530,12 +530,43 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, *response.Erro return bs, nil } +func getTimestamps(p1, p2 *request.Param) (uint64, uint64, error) { + var start, end uint64 + if p1 != nil { + val, err := p1.GetInt() + if err != nil { + return 0, 0, err + } + start = uint64(val) + } + if p2 != nil { + val, err := p2.GetInt() + if err != nil { + return 0, 0, err + } + end = uint64(val) + } + return start, end, nil +} + func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Error) { u, err := ps.Value(0).GetUint160FromAddressOrHex() if err != nil { return nil, response.ErrInvalidParams } + p1, p2 := ps.Value(1), ps.Value(2) + start, end, err := getTimestamps(p1, p2) + if err != nil { + return nil, response.NewInvalidParamsError(err.Error(), err) + } + if p2 == nil { + end = uint64(time.Now().Unix() * 1000) + if p1 == nil { + start = uint64(time.Now().Add(-time.Hour*24*7).Unix() * 1000) + } + } + bs := &result.NEP5Transfers{ Address: address.Uint160ToString(u), Received: []result.NEP5Transfer{}, @@ -543,6 +574,9 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err } cache := make(map[int32]decimals) err = s.chain.ForEachNEP5Transfer(u, func(tr *state.NEP5Transfer) error { + if tr.Timestamp < start || tr.Timestamp > end { + return nil + } d, err := s.getDecimals(tr.Asset, cache) if err != nil { return nil diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 76fa45cb4..8c45b8ac4 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "reflect" + "strconv" "strings" "testing" "time" @@ -149,15 +150,20 @@ var rpcTestCases = map[string][]rpcTestCase{ params: `["notahex"]`, fail: true, }, + { + name: "invalid timestamp", + params: `["` + testchain.PrivateKeyByID(0).Address() + `", "notanumber"]`, + fail: true, + }, { name: "positive", - params: `["` + testchain.PrivateKeyByID(0).Address() + `"]`, + params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`, result: func(e *executor) interface{} { return &result.NEP5Transfers{} }, check: checkNep5Transfers, }, { name: "positive_hash", - params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`, + params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `", 0]`, result: func(e *executor) interface{} { return &result.NEP5Transfers{} }, check: checkNep5Transfers, }, @@ -887,6 +893,24 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] assert.ElementsMatch(t, expected, actual) }) + + t.Run("getnep5transfers", func(t *testing.T) { + ps := []string{`"` + testchain.PrivateKeyByID(0).Address() + `"`} + h, err := e.chain.GetHeader(e.chain.GetHeaderHash(4)) + require.NoError(t, err) + ps = append(ps, strconv.FormatUint(h.Timestamp, 10)) + h, err = e.chain.GetHeader(e.chain.GetHeaderHash(5)) + require.NoError(t, err) + ps = append(ps, strconv.FormatUint(h.Timestamp, 10)) + + p := strings.Join(ps, ", ") + rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getnep5transfers", "params": [%s]}`, p) + body := doRPCCall(rpc, httpSrv.URL, t) + res := checkErrGetResult(t, body, false) + actual := new(result.NEP5Transfers) + require.NoError(t, json.Unmarshal(res, actual)) + checkNep5TransfersAux(t, e, actual, 4, 5) + }) } func (e *executor) getHeader(s string) *block.Header { @@ -1011,6 +1035,10 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) { } func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) { + checkNep5TransfersAux(t, e, acc, 0, e.chain.HeaderHeight()) +} + +func checkNep5TransfersAux(t *testing.T, e *executor, acc interface{}, start, end uint32) { res, ok := acc.(*result.NEP5Transfers) require.True(t, ok) rublesHash, err := util.Uint160DecodeStringLE(testContractHash) @@ -1034,15 +1062,6 @@ func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) { txSendNEOHash := blockSendNEO.Transactions[0].Hash() expected := result.NEP5Transfers{ Sent: []result.NEP5Transfer{ - { - Timestamp: blockSendRubles.Timestamp, - Asset: rublesHash, - Address: testchain.PrivateKeyByID(1).Address(), - Amount: "1.23", - Index: 6, - NotifyIndex: 0, - TxHash: txSendRublesHash, - }, { Timestamp: blockSendNEO.Timestamp, Asset: e.chain.GoverningTokenHash(), @@ -1052,6 +1071,15 @@ func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) { NotifyIndex: 0, TxHash: txSendNEOHash, }, + { + Timestamp: blockSendRubles.Timestamp, + Asset: rublesHash, + Address: testchain.PrivateKeyByID(1).Address(), + Amount: "1.23", + Index: 6, + NotifyIndex: 0, + TxHash: txSendRublesHash, + }, }, Received: []result.NEP5Transfer{ { @@ -1127,6 +1155,20 @@ func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) { } } require.Equal(t, expected.Address, res.Address) - require.ElementsMatch(t, expected.Sent, res.Sent) - require.ElementsMatch(t, expected.Received, res.Received) + + arr := make([]result.NEP5Transfer, 0, len(expected.Sent)) + for i := range expected.Sent { + if expected.Sent[i].Index >= start && expected.Sent[i].Index <= end { + arr = append(arr, expected.Sent[i]) + } + } + require.ElementsMatch(t, arr, res.Sent) + + arr = arr[:0] + for i := range expected.Received { + if expected.Received[i].Index >= start && expected.Received[i].Index <= end { + arr = append(arr, expected.Received[i]) + } + } + require.ElementsMatch(t, arr, res.Received) }