mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-04 19:19:44 +00:00
Merge pull request #1395 from nspcc-dev/add-limits-to-gettransfers
Add limits to get(nep5|utxo)transfers
This commit is contained in:
commit
8865d5b2c5
7 changed files with 89 additions and 51 deletions
|
@ -1027,39 +1027,45 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, tx *transaction.Tra
|
|||
}
|
||||
|
||||
// ForEachTransfer executes f for each transfer in log.
|
||||
func (bc *Blockchain) ForEachTransfer(acc util.Uint160, tr *state.Transfer, f func() error) error {
|
||||
func (bc *Blockchain) ForEachTransfer(acc util.Uint160, tr *state.Transfer, f func() (bool, error)) error {
|
||||
nb, err := bc.dao.GetNextTransferBatch(acc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := uint32(0); i <= nb; i++ {
|
||||
lg, err := bc.dao.GetTransferLog(acc, i)
|
||||
for i := int(nb); i >= 0; i-- {
|
||||
lg, err := bc.dao.GetTransferLog(acc, uint32(i))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err = lg.ForEach(state.TransferSize, tr, f)
|
||||
cont, err := lg.ForEach(state.TransferSize, tr, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForEachNEP5Transfer executes f for each nep5 transfer in log.
|
||||
func (bc *Blockchain) ForEachNEP5Transfer(acc util.Uint160, tr *state.NEP5Transfer, f func() error) error {
|
||||
func (bc *Blockchain) ForEachNEP5Transfer(acc util.Uint160, tr *state.NEP5Transfer, f func() (bool, error)) error {
|
||||
balances, err := bc.dao.GetNEP5Balances(acc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := uint32(0); i <= balances.NextTransferBatch; i++ {
|
||||
lg, err := bc.dao.GetNEP5TransferLog(acc, i)
|
||||
for i := int(balances.NextTransferBatch); i >= 0; i-- {
|
||||
lg, err := bc.dao.GetNEP5TransferLog(acc, uint32(i))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err = lg.ForEach(state.NEP5TransferSize, tr, f)
|
||||
cont, err := lg.ForEach(state.NEP5TransferSize, tr, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ type Blockchainer interface {
|
|||
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||
GetContractState(hash util.Uint160) *state.Contract
|
||||
GetEnrollments() ([]*state.Validator, error)
|
||||
ForEachNEP5Transfer(util.Uint160, *state.NEP5Transfer, func() error) error
|
||||
ForEachTransfer(util.Uint160, *state.Transfer, func() error) error
|
||||
ForEachNEP5Transfer(util.Uint160, *state.NEP5Transfer, func() (bool, error)) error
|
||||
ForEachTransfer(util.Uint160, *state.Transfer, func() (bool, error)) error
|
||||
GetHeaderHash(int) util.Uint256
|
||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||
CurrentHeaderHash() util.Uint256
|
||||
|
|
|
@ -110,20 +110,25 @@ func (lg *TransferLog) Append(tr io.Serializable) error {
|
|||
}
|
||||
|
||||
// ForEach iterates over transfer log returning on first error.
|
||||
func (lg *TransferLog) ForEach(size int, tr io.Serializable, f func() error) error {
|
||||
func (lg *TransferLog) ForEach(size int, tr io.Serializable, f func() (bool, error)) (bool, error) {
|
||||
if lg == nil {
|
||||
return nil
|
||||
return true, nil
|
||||
}
|
||||
for i := 0; i < len(lg.Raw); i += size {
|
||||
r := io.NewBinReaderFromBuf(lg.Raw[i : i+size])
|
||||
for i := len(lg.Raw); i > 0; i -= size {
|
||||
r := io.NewBinReaderFromBuf(lg.Raw[i-size : i])
|
||||
tr.DecodeBinary(r)
|
||||
if r.Err != nil {
|
||||
return r.Err
|
||||
} else if err := f(); err != nil {
|
||||
return nil
|
||||
return false, r.Err
|
||||
}
|
||||
cont, err := f()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !cont {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Size returns an amount of transfer written in log provided size of a single transfer.
|
||||
|
|
|
@ -28,15 +28,15 @@ func TestNEP5TransferLog_Append(t *testing.T) {
|
|||
|
||||
require.Equal(t, len(expected), lg.Size()/NEP5TransferSize)
|
||||
|
||||
i := 0
|
||||
i := len(expected) - 1
|
||||
tr := new(NEP5Transfer)
|
||||
err := lg.ForEach(NEP5TransferSize, tr, func() error {
|
||||
cont, err := lg.ForEach(NEP5TransferSize, tr, func() (bool, error) {
|
||||
require.Equal(t, expected[i], tr)
|
||||
i++
|
||||
return nil
|
||||
i--
|
||||
return true, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, cont)
|
||||
}
|
||||
|
||||
func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
||||
|
|
|
@ -96,7 +96,7 @@ func (chain testChain) GetAccountState(util.Uint160) *state.Account {
|
|||
func (chain testChain) GetNEP5Metadata(util.Uint160) (*state.NEP5Metadata, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) ForEachNEP5Transfer(util.Uint160, *state.NEP5Transfer, func() error) error {
|
||||
func (chain testChain) ForEachNEP5Transfer(util.Uint160, *state.NEP5Transfer, func() (bool, error)) error {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
|
||||
|
@ -108,7 +108,7 @@ func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.Publi
|
|||
func (chain testChain) GetEnrollments() ([]*state.Validator, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) ForEachTransfer(util.Uint160, *state.Transfer, func() error) error {
|
||||
func (chain testChain) ForEachTransfer(util.Uint160, *state.Transfer, func() (bool, error)) error {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) {
|
||||
|
|
|
@ -449,23 +449,34 @@ func (s *Server) getVersion(_ request.Params) (interface{}, *response.Error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func getTimestamps(p1, p2 *request.Param) (uint32, uint32, error) {
|
||||
func getTimestampsAndLimit(p1, p2, p3 *request.Param) (uint32, uint32, int, error) {
|
||||
var start, end uint32
|
||||
var limit int
|
||||
if p1 != nil {
|
||||
val, err := p1.GetInt()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
start = uint32(val)
|
||||
}
|
||||
if p2 != nil {
|
||||
val, err := p2.GetInt()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
end = uint32(val)
|
||||
}
|
||||
return start, end, nil
|
||||
if p3 != nil {
|
||||
l, err := p3.GetInt()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
if l <= 0 {
|
||||
return 0, 0, 0, errors.New("can't use negative or zero limit")
|
||||
}
|
||||
limit = l
|
||||
}
|
||||
return start, end, limit, nil
|
||||
}
|
||||
|
||||
func getAssetMaps(name string) (map[util.Uint256]*result.AssetUTXO, map[util.Uint256]*result.AssetUTXO, error) {
|
||||
|
@ -516,8 +527,8 @@ func (s *Server) getUTXOTransfers(ps request.Params) (interface{}, *response.Err
|
|||
index++
|
||||
}
|
||||
|
||||
p1, p2 := ps.Value(index), ps.Value(index+1)
|
||||
start, end, err := getTimestamps(p1, p2)
|
||||
p1, p2, p3 := ps.Value(index), ps.Value(index+1), ps.Value(index+2)
|
||||
start, end, limit, err := getTimestampsAndLimit(p1, p2, p3)
|
||||
if err != nil {
|
||||
return nil, response.NewInvalidParamsError("", err)
|
||||
}
|
||||
|
@ -533,9 +544,20 @@ func (s *Server) getUTXOTransfers(ps request.Params) (interface{}, *response.Err
|
|||
return nil, response.NewInvalidParamsError("", err)
|
||||
}
|
||||
tr := new(state.Transfer)
|
||||
err = s.chain.ForEachTransfer(addr, tr, func() error {
|
||||
if tr.Timestamp < start || end != 0 && tr.Timestamp > end {
|
||||
return nil
|
||||
err = s.chain.ForEachTransfer(addr, tr, func() (bool, error) {
|
||||
if tr.Timestamp > end {
|
||||
return true, nil
|
||||
}
|
||||
var count int
|
||||
for _, res := range sent {
|
||||
count += len(res.Transactions)
|
||||
}
|
||||
for _, res := range recv {
|
||||
count += len(res.Transactions)
|
||||
}
|
||||
if tr.Timestamp < start ||
|
||||
(limit != 0 && count >= limit) {
|
||||
return false, nil
|
||||
}
|
||||
assetID := core.GoverningTokenID()
|
||||
if !tr.IsGoverning {
|
||||
|
@ -555,7 +577,7 @@ func (s *Server) getUTXOTransfers(ps request.Params) (interface{}, *response.Err
|
|||
})
|
||||
a.TotalAmount += tr.Amount
|
||||
}
|
||||
return nil
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("", err)
|
||||
|
@ -720,8 +742,8 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
p1, p2 := ps.Value(1), ps.Value(2)
|
||||
start, end, err := getTimestamps(p1, p2)
|
||||
p1, p2, p3 := ps.Value(1), ps.Value(2), ps.Value(3)
|
||||
start, end, limit, err := getTimestampsAndLimit(p1, p2, p3)
|
||||
if err != nil {
|
||||
return nil, response.NewInvalidParamsError("", err)
|
||||
}
|
||||
|
@ -738,9 +760,13 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
Sent: []result.NEP5Transfer{},
|
||||
}
|
||||
tr := new(state.NEP5Transfer)
|
||||
err = s.chain.ForEachNEP5Transfer(u, tr, func() error {
|
||||
if tr.Timestamp < start || tr.Timestamp > end {
|
||||
return nil
|
||||
err = s.chain.ForEachNEP5Transfer(u, tr, func() (bool, error) {
|
||||
if tr.Timestamp > end {
|
||||
return true, nil
|
||||
}
|
||||
if tr.Timestamp < start ||
|
||||
(limit != 0 && (len(bs.Received)+len(bs.Sent) >= limit)) {
|
||||
return false, nil
|
||||
}
|
||||
transfer := result.NEP5Transfer{
|
||||
Timestamp: tr.Timestamp,
|
||||
|
@ -756,7 +782,7 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
transfer.Address = address.Uint160ToString(tr.From)
|
||||
}
|
||||
bs.Received = append(bs.Received, transfer)
|
||||
return nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
transfer.Amount = strconv.FormatInt(-tr.Amount, 10)
|
||||
|
@ -764,7 +790,7 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
transfer.Address = address.Uint160ToString(tr.To)
|
||||
}
|
||||
bs.Sent = append(bs.Sent, transfer)
|
||||
return nil
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("invalid NEP5 transfer log", err)
|
||||
|
|
|
@ -1284,24 +1284,25 @@ func checkNep5TransfersAux(t *testing.T, e *executor, acc interface{}, onlyFirst
|
|||
|
||||
if onlyFirst {
|
||||
require.Equal(t, 1, len(res.Received))
|
||||
require.Equal(t, "1000", res.Received[0].Amount)
|
||||
require.Equal(t, assetHashOld, res.Received[0].Asset)
|
||||
require.Equal(t, address.Uint160ToString(assetHashOld), res.Received[0].Address)
|
||||
} else {
|
||||
require.Equal(t, 3, len(res.Received))
|
||||
}
|
||||
|
||||
require.Equal(t, "1000", res.Received[0].Amount)
|
||||
require.Equal(t, assetHashOld, res.Received[0].Asset)
|
||||
require.Equal(t, address.Uint160ToString(assetHashOld), res.Received[0].Address)
|
||||
require.Equal(t, "1000", res.Received[2].Amount)
|
||||
require.Equal(t, assetHashOld, res.Received[2].Asset)
|
||||
require.Equal(t, address.Uint160ToString(assetHashOld), res.Received[2].Address)
|
||||
|
||||
if !onlyFirst {
|
||||
require.Equal(t, "2", res.Received[1].Amount)
|
||||
require.Equal(t, assetHash, res.Received[1].Asset)
|
||||
require.Equal(t, "AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17", res.Received[1].Address)
|
||||
require.Equal(t, uint32(0), res.Received[1].NotifyIndex)
|
||||
|
||||
require.Equal(t, "1", res.Received[2].Amount)
|
||||
require.Equal(t, assetHash, res.Received[2].Asset)
|
||||
require.Equal(t, "AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17", res.Received[2].Address)
|
||||
require.Equal(t, uint32(1), res.Received[2].NotifyIndex)
|
||||
require.Equal(t, "1", res.Received[0].Amount)
|
||||
require.Equal(t, assetHash, res.Received[0].Asset)
|
||||
require.Equal(t, "AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17", res.Received[0].Address)
|
||||
require.Equal(t, uint32(1), res.Received[0].NotifyIndex)
|
||||
}
|
||||
|
||||
require.Equal(t, 1, len(res.Sent))
|
||||
|
|
Loading…
Reference in a new issue