Merge pull request #1289 from nspcc-dev/fix/nep5

Port `getnep5transfers` changes to master
This commit is contained in:
Roman Khimov 2020-08-10 11:11:24 +03:00 committed by GitHub
commit 4aeaf05f90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 22 deletions

View file

@ -805,21 +805,23 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, h util.Uint256, b *
}
}
// GetNEP5TransferLog returns NEP5 transfer log for the acc.
func (bc *Blockchain) GetNEP5TransferLog(acc util.Uint160) *state.NEP5TransferLog {
// ForEachNEP5Transfer executes f for each nep5 transfer in log.
func (bc *Blockchain) ForEachNEP5Transfer(acc util.Uint160, f func(*state.NEP5Transfer) error) error {
balances, err := bc.dao.GetNEP5Balances(acc)
if err != nil {
return nil
}
result := new(state.NEP5TransferLog)
for i := uint32(0); i <= balances.NextTransferBatch; i++ {
lg, err := bc.dao.GetNEP5TransferLog(acc, i)
if err != nil {
return nil
}
result.Raw = append(result.Raw, lg.Raw...)
err = lg.ForEach(f)
if err != nil {
return err
}
}
return result
return nil
}
// GetNEP5Balances returns NEP5 balances for the acc.

View file

@ -30,6 +30,7 @@ type Blockchainer interface {
GetContractScriptHash(id int32) (util.Uint160, error)
GetEnrollments() ([]state.Validator, error)
GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32)
ForEachNEP5Transfer(util.Uint160, func(*state.NEP5Transfer) error) error
GetHeaderHash(int) util.Uint256
GetHeader(hash util.Uint256) (*block.Header, error)
CurrentHeaderHash() util.Uint256
@ -39,7 +40,6 @@ type Blockchainer interface {
GetAccountState(util.Uint160) *state.Account
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
GetNextBlockValidators() ([]*keys.PublicKey, error)
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators() ([]*keys.PublicKey, error)
GetStandByCommittee() keys.PublicKeys

View file

@ -94,7 +94,7 @@ func (chain testChain) GetAccountState(util.Uint160) *state.Account {
func (chain testChain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
panic("TODO")
}
func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog {
func (chain testChain) ForEachNEP5Transfer(util.Uint160, func(*state.NEP5Transfer) error) error {
panic("TODO")
}
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {

View file

@ -530,20 +530,53 @@ 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{},
Sent: []result.NEP5Transfer{},
}
lg := s.chain.GetNEP5TransferLog(u)
cache := make(map[int32]decimals)
err = lg.ForEach(func(tr *state.NEP5Transfer) error {
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

View file

@ -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)
}