Merge pull request #1288 from nspcc-dev/fix/utxo

rpc: adjust `getutxotransfers` RPC
This commit is contained in:
Roman Khimov 2020-08-07 19:08:46 +03:00 committed by GitHub
commit ffbdcb202f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 101 deletions

View file

@ -609,13 +609,10 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
}
// Process TX outputs.
if err := processOutputs(tx, cache); err != nil {
if err := processOutputs(tx, block, cache); err != nil {
return err
}
var pseudoSender util.Uint160
var gasTotal, neoTotal util.Fixed8
// Process TX inputs that are grouped by previous hash.
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
prevHash := inputs[0].PrevHash
@ -623,7 +620,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if err != nil {
return err
}
for i, input := range inputs {
for _, input := range inputs {
if len(unspent.States) <= int(input.PrevIndex) {
return fmt.Errorf("bad input: %s/%d", input.PrevHash.StringLE(), input.PrevIndex)
}
@ -633,13 +630,8 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
unspent.States[input.PrevIndex].State |= state.CoinSpent
unspent.States[input.PrevIndex].SpendHeight = block.Index
prevTXOutput := &unspent.States[input.PrevIndex].Output
if i == 0 {
pseudoSender = prevTXOutput.ScriptHash
}
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
neoTotal += prevTXOutput.Amount
} else if prevTXOutput.AssetID.Equals(UtilityTokenID()) {
gasTotal += prevTXOutput.Amount
if err := processTransfer(cache, tx, block, prevTXOutput, true); err != nil {
return err
}
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
if err != nil {
@ -690,10 +682,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
}
}
if err := bc.processTransfer(cache, pseudoSender, tx, block, neoTotal, gasTotal); err != nil {
return err
}
// Process the underlying type of the TX.
switch t := tx.Data.(type) {
case *transaction.RegisterTX:
@ -939,67 +927,32 @@ func appendSingleTransfer(cache *dao.Cached, acc util.Uint160, tr *state.Transfe
}
// processTransfer processes single UTXO transfer. Totals is a slice of neo (0) and gas (1) total transfer amount.
func (bc *Blockchain) processTransfer(cache *dao.Cached, from util.Uint160, tx *transaction.Transaction, b *block.Block,
neoTotal, gasTotal util.Fixed8) error {
fromIndex, err := cache.GetNextTransferBatch(from)
func processTransfer(cache *dao.Cached, tx *transaction.Transaction, b *block.Block, out *transaction.Output,
isSent bool) error {
isGoverning := out.AssetID.Equals(GoverningTokenID())
if !isGoverning && !out.AssetID.Equals(UtilityTokenID()) {
return nil
}
tr := &state.Transfer{
IsGoverning: isGoverning,
IsSent: isSent,
Amount: int64(out.Amount),
Block: b.Index,
Timestamp: b.Timestamp,
Tx: tx.Hash(),
}
index, err := cache.GetNextTransferBatch(out.ScriptHash)
if err != nil {
return err
}
for i := range tx.Outputs {
isGoverning := tx.Outputs[i].AssetID.Equals(GoverningTokenID())
if !isGoverning && !tx.Outputs[i].AssetID.Equals(UtilityTokenID()) {
continue
}
if !from.Equals(tx.Outputs[i].ScriptHash) {
tr := &state.Transfer{
IsGoverning: isGoverning,
From: from,
To: tx.Outputs[i].ScriptHash,
Amount: int64(tx.Outputs[i].Amount),
Block: b.Index,
Timestamp: b.Timestamp,
Tx: tx.Hash(),
}
isBig, err := cache.AppendTransfer(from, fromIndex, tr)
if err != nil {
return err
} else if isBig {
fromIndex++
}
if err := appendSingleTransfer(cache, tx.Outputs[i].ScriptHash, tr); err != nil {
return err
}
}
if isGoverning {
neoTotal -= tx.Outputs[i].Amount
} else {
gasTotal -= tx.Outputs[i].Amount
}
isBig, err := cache.AppendTransfer(out.ScriptHash, index, tr)
if err != nil {
return err
}
for i, amount := range []util.Fixed8{neoTotal, gasTotal} {
if amount > 0 {
tr := &state.Transfer{
IsGoverning: i == 0,
From: from,
Amount: int64(amount),
Block: b.Index,
Timestamp: b.Timestamp,
Tx: tx.Hash(),
}
isBig, err := cache.AppendTransfer(from, fromIndex, tr)
if err != nil {
return err
} else if isBig {
fromIndex++
}
if err := appendSingleTransfer(cache, util.Uint160{}, tr); err != nil {
return err
}
}
if isBig {
return cache.PutNextTransferBatch(out.ScriptHash, index+1)
}
return cache.PutNextTransferBatch(from, fromIndex)
return nil
}
func parseUint160(addr []byte) util.Uint160 {
@ -1126,7 +1079,7 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
}
// processOutputs processes transaction outputs.
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
func processOutputs(tx *transaction.Transaction, b *block.Block, dao *dao.Cached) error {
for index, output := range tx.Outputs {
account, err := dao.GetAccountStateOrNew(output.ScriptHash)
if err != nil {
@ -1143,6 +1096,9 @@ func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
return err
}
if err = processTransfer(dao, tx, b, &output, false); err != nil {
return err
}
}
return nil
}

View file

@ -6,16 +6,14 @@ import (
)
// TransferSize is a size of a marshaled Transfer struct in bytes.
const TransferSize = 1 + util.Uint160Size*2 + 8 + 4 + 4 + util.Uint256Size
const TransferSize = 2 + 8 + 4 + 4 + util.Uint256Size
// Transfer represents a single Transfer event.
type Transfer struct {
// IsGoverning is true iff transfer is for neo token.
IsGoverning bool
// Address is the address of the sender.
From util.Uint160
// To is the address of the receiver.
To util.Uint160
// IsSent is true iff UTXO used in the input.
IsSent bool
// Amount is the amount of tokens transferred.
// It is negative when tokens are sent and positive if they are received.
Amount int64
@ -31,21 +29,19 @@ type Transfer struct {
// Note: change TransferSize constant when changing this function.
func (t *Transfer) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(t.Tx[:])
w.WriteBytes(t.From[:])
w.WriteBytes(t.To[:])
w.WriteU32LE(t.Block)
w.WriteU32LE(t.Timestamp)
w.WriteU64LE(uint64(t.Amount))
w.WriteBool(t.IsGoverning)
w.WriteBool(t.IsSent)
}
// DecodeBinary implements io.Serializable interface.
func (t *Transfer) DecodeBinary(r *io.BinReader) {
r.ReadBytes(t.Tx[:])
r.ReadBytes(t.From[:])
r.ReadBytes(t.To[:])
t.Block = r.ReadU32LE()
t.Timestamp = r.ReadU32LE()
t.Amount = int64(r.ReadU64LE())
t.IsGoverning = r.ReadBool()
t.IsSent = r.ReadBool()
}

View file

@ -7,7 +7,6 @@ type UTXO struct {
Index uint32 `json:"block_index"`
Timestamp uint32 `json:"timestamp"`
TxHash util.Uint256 `json:"txid"`
Address util.Uint160 `json:"transfer_address"`
Amount int64 `json:"amount,string"`
}

View file

@ -541,24 +541,16 @@ func (s *Server) getUTXOTransfers(ps request.Params) (interface{}, *response.Err
if !tr.IsGoverning {
assetID = core.UtilityTokenID()
}
a, ok := sent[assetID]
if ok && tr.From.Equals(addr) && !tr.To.Equals(addr) {
a.Transactions = append(a.Transactions, result.UTXO{
Index: tr.Block,
Timestamp: tr.Timestamp,
TxHash: tr.Tx,
Address: tr.To,
Amount: tr.Amount,
})
a.TotalAmount += tr.Amount
m := recv
if tr.IsSent {
m = sent
}
a, ok = recv[assetID]
if ok && tr.To.Equals(addr) && !tr.From.Equals(addr) {
a, ok := m[assetID]
if ok {
a.Transactions = append(a.Transactions, result.UTXO{
Index: tr.Block,
Timestamp: tr.Timestamp,
TxHash: tr.Tx,
Address: tr.From,
Amount: tr.Amount,
})
a.TotalAmount += tr.Amount

View file

@ -1285,21 +1285,36 @@ func checkTransfers(t *testing.T, e *executor, acc interface{}, asset string, st
require.Equal(t, res.Address, "AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs")
// transfer from multisig address to us
u := getUTXOForBlock(res, false, asset, 1)
u := getUTXOForBlock(res, false, "neo", 1)
if start <= 1 && (stop == 0 || stop >= 1) && (asset == "neo" || asset == "") {
require.NotNil(t, u)
require.Equal(t, "be48d3a3f5d10013ab9ffee489706078714f1ea2", u.Address.StringBE())
require.EqualValues(t, int64(util.Fixed8FromInt64(99999000)), u.Amount)
} else {
require.Nil(t, u)
}
// gas claim
u = getUTXOForBlock(res, false, "gas", 203)
if start <= 203 && (stop == 0 || stop >= 203) && (asset == "gas" || asset == "") {
require.NotNil(t, u)
require.EqualValues(t, int64(160798392000), u.Amount)
} else {
require.Nil(t, u)
}
// transfer from us to another validator
u = getUTXOForBlock(res, true, asset, 206)
u = getUTXOForBlock(res, true, "neo", 206)
if start <= 206 && (stop == 0 || stop >= 206) && (asset == "neo" || asset == "") {
require.NotNil(t, u)
require.Equal(t, "9fbf833320ef6bc52ddee1fe6f5793b42e9b307e", u.Address.StringBE())
require.EqualValues(t, int64(util.Fixed8FromInt64(1000)), u.Amount)
require.EqualValues(t, int64(util.Fixed8FromInt64(99999000)), u.Amount)
} else {
require.Nil(t, u)
}
u = getUTXOForBlock(res, false, "neo", 206)
if start <= 206 && (stop == 0 || stop >= 206) && (asset == "neo" || asset == "") {
require.NotNil(t, u)
require.EqualValues(t, int64(util.Fixed8FromInt64(99998000)), u.Amount)
} else {
require.Nil(t, u)
}