mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-13 15:45:01 +00:00
Merge pull request #1288 from nspcc-dev/fix/utxo
rpc: adjust `getutxotransfers` RPC
This commit is contained in:
commit
ffbdcb202f
5 changed files with 59 additions and 101 deletions
|
@ -609,13 +609,10 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process TX outputs.
|
// Process TX outputs.
|
||||||
if err := processOutputs(tx, cache); err != nil {
|
if err := processOutputs(tx, block, cache); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pseudoSender util.Uint160
|
|
||||||
var gasTotal, neoTotal util.Fixed8
|
|
||||||
|
|
||||||
// Process TX inputs that are grouped by previous hash.
|
// Process TX inputs that are grouped by previous hash.
|
||||||
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
||||||
prevHash := inputs[0].PrevHash
|
prevHash := inputs[0].PrevHash
|
||||||
|
@ -623,7 +620,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i, input := range inputs {
|
for _, input := range inputs {
|
||||||
if len(unspent.States) <= int(input.PrevIndex) {
|
if len(unspent.States) <= int(input.PrevIndex) {
|
||||||
return fmt.Errorf("bad input: %s/%d", input.PrevHash.StringLE(), 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].State |= state.CoinSpent
|
||||||
unspent.States[input.PrevIndex].SpendHeight = block.Index
|
unspent.States[input.PrevIndex].SpendHeight = block.Index
|
||||||
prevTXOutput := &unspent.States[input.PrevIndex].Output
|
prevTXOutput := &unspent.States[input.PrevIndex].Output
|
||||||
if i == 0 {
|
if err := processTransfer(cache, tx, block, prevTXOutput, true); err != nil {
|
||||||
pseudoSender = prevTXOutput.ScriptHash
|
return err
|
||||||
}
|
|
||||||
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
|
|
||||||
neoTotal += prevTXOutput.Amount
|
|
||||||
} else if prevTXOutput.AssetID.Equals(UtilityTokenID()) {
|
|
||||||
gasTotal += prevTXOutput.Amount
|
|
||||||
}
|
}
|
||||||
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
|
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
|
||||||
if err != nil {
|
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.
|
// Process the underlying type of the TX.
|
||||||
switch t := tx.Data.(type) {
|
switch t := tx.Data.(type) {
|
||||||
case *transaction.RegisterTX:
|
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.
|
// 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,
|
func processTransfer(cache *dao.Cached, tx *transaction.Transaction, b *block.Block, out *transaction.Output,
|
||||||
neoTotal, gasTotal util.Fixed8) error {
|
isSent bool) error {
|
||||||
|
isGoverning := out.AssetID.Equals(GoverningTokenID())
|
||||||
fromIndex, err := cache.GetNextTransferBatch(from)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := range tx.Outputs {
|
isBig, err := cache.AppendTransfer(out.ScriptHash, index, tr)
|
||||||
isGoverning := tx.Outputs[i].AssetID.Equals(GoverningTokenID())
|
if err != nil {
|
||||||
if !isGoverning && !tx.Outputs[i].AssetID.Equals(UtilityTokenID()) {
|
return err
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
for i, amount := range []util.Fixed8{neoTotal, gasTotal} {
|
if isBig {
|
||||||
if amount > 0 {
|
return cache.PutNextTransferBatch(out.ScriptHash, index+1)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return cache.PutNextTransferBatch(from, fromIndex)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUint160(addr []byte) util.Uint160 {
|
func parseUint160(addr []byte) util.Uint160 {
|
||||||
|
@ -1126,7 +1079,7 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
// processOutputs processes transaction outputs.
|
// 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 {
|
for index, output := range tx.Outputs {
|
||||||
account, err := dao.GetAccountStateOrNew(output.ScriptHash)
|
account, err := dao.GetAccountStateOrNew(output.ScriptHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1143,6 +1096,9 @@ func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||||
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
|
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err = processTransfer(dao, tx, b, &output, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TransferSize is a size of a marshaled Transfer struct in bytes.
|
// 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.
|
// Transfer represents a single Transfer event.
|
||||||
type Transfer struct {
|
type Transfer struct {
|
||||||
// IsGoverning is true iff transfer is for neo token.
|
// IsGoverning is true iff transfer is for neo token.
|
||||||
IsGoverning bool
|
IsGoverning bool
|
||||||
// Address is the address of the sender.
|
// IsSent is true iff UTXO used in the input.
|
||||||
From util.Uint160
|
IsSent bool
|
||||||
// To is the address of the receiver.
|
|
||||||
To util.Uint160
|
|
||||||
// Amount is the amount of tokens transferred.
|
// Amount is the amount of tokens transferred.
|
||||||
// It is negative when tokens are sent and positive if they are received.
|
// It is negative when tokens are sent and positive if they are received.
|
||||||
Amount int64
|
Amount int64
|
||||||
|
@ -31,21 +29,19 @@ type Transfer struct {
|
||||||
// Note: change TransferSize constant when changing this function.
|
// Note: change TransferSize constant when changing this function.
|
||||||
func (t *Transfer) EncodeBinary(w *io.BinWriter) {
|
func (t *Transfer) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteBytes(t.Tx[:])
|
w.WriteBytes(t.Tx[:])
|
||||||
w.WriteBytes(t.From[:])
|
|
||||||
w.WriteBytes(t.To[:])
|
|
||||||
w.WriteU32LE(t.Block)
|
w.WriteU32LE(t.Block)
|
||||||
w.WriteU32LE(t.Timestamp)
|
w.WriteU32LE(t.Timestamp)
|
||||||
w.WriteU64LE(uint64(t.Amount))
|
w.WriteU64LE(uint64(t.Amount))
|
||||||
w.WriteBool(t.IsGoverning)
|
w.WriteBool(t.IsGoverning)
|
||||||
|
w.WriteBool(t.IsSent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (t *Transfer) DecodeBinary(r *io.BinReader) {
|
func (t *Transfer) DecodeBinary(r *io.BinReader) {
|
||||||
r.ReadBytes(t.Tx[:])
|
r.ReadBytes(t.Tx[:])
|
||||||
r.ReadBytes(t.From[:])
|
|
||||||
r.ReadBytes(t.To[:])
|
|
||||||
t.Block = r.ReadU32LE()
|
t.Block = r.ReadU32LE()
|
||||||
t.Timestamp = r.ReadU32LE()
|
t.Timestamp = r.ReadU32LE()
|
||||||
t.Amount = int64(r.ReadU64LE())
|
t.Amount = int64(r.ReadU64LE())
|
||||||
t.IsGoverning = r.ReadBool()
|
t.IsGoverning = r.ReadBool()
|
||||||
|
t.IsSent = r.ReadBool()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ type UTXO struct {
|
||||||
Index uint32 `json:"block_index"`
|
Index uint32 `json:"block_index"`
|
||||||
Timestamp uint32 `json:"timestamp"`
|
Timestamp uint32 `json:"timestamp"`
|
||||||
TxHash util.Uint256 `json:"txid"`
|
TxHash util.Uint256 `json:"txid"`
|
||||||
Address util.Uint160 `json:"transfer_address"`
|
|
||||||
Amount int64 `json:"amount,string"`
|
Amount int64 `json:"amount,string"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -541,24 +541,16 @@ func (s *Server) getUTXOTransfers(ps request.Params) (interface{}, *response.Err
|
||||||
if !tr.IsGoverning {
|
if !tr.IsGoverning {
|
||||||
assetID = core.UtilityTokenID()
|
assetID = core.UtilityTokenID()
|
||||||
}
|
}
|
||||||
a, ok := sent[assetID]
|
m := recv
|
||||||
if ok && tr.From.Equals(addr) && !tr.To.Equals(addr) {
|
if tr.IsSent {
|
||||||
a.Transactions = append(a.Transactions, result.UTXO{
|
m = sent
|
||||||
Index: tr.Block,
|
|
||||||
Timestamp: tr.Timestamp,
|
|
||||||
TxHash: tr.Tx,
|
|
||||||
Address: tr.To,
|
|
||||||
Amount: tr.Amount,
|
|
||||||
})
|
|
||||||
a.TotalAmount += tr.Amount
|
|
||||||
}
|
}
|
||||||
a, ok = recv[assetID]
|
a, ok := m[assetID]
|
||||||
if ok && tr.To.Equals(addr) && !tr.From.Equals(addr) {
|
if ok {
|
||||||
a.Transactions = append(a.Transactions, result.UTXO{
|
a.Transactions = append(a.Transactions, result.UTXO{
|
||||||
Index: tr.Block,
|
Index: tr.Block,
|
||||||
Timestamp: tr.Timestamp,
|
Timestamp: tr.Timestamp,
|
||||||
TxHash: tr.Tx,
|
TxHash: tr.Tx,
|
||||||
Address: tr.From,
|
|
||||||
Amount: tr.Amount,
|
Amount: tr.Amount,
|
||||||
})
|
})
|
||||||
a.TotalAmount += tr.Amount
|
a.TotalAmount += tr.Amount
|
||||||
|
|
|
@ -1285,21 +1285,36 @@ func checkTransfers(t *testing.T, e *executor, acc interface{}, asset string, st
|
||||||
require.Equal(t, res.Address, "AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs")
|
require.Equal(t, res.Address, "AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs")
|
||||||
|
|
||||||
// transfer from multisig address to us
|
// 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 == "") {
|
if start <= 1 && (stop == 0 || stop >= 1) && (asset == "neo" || asset == "") {
|
||||||
require.NotNil(t, u)
|
require.NotNil(t, u)
|
||||||
require.Equal(t, "be48d3a3f5d10013ab9ffee489706078714f1ea2", u.Address.StringBE())
|
|
||||||
require.EqualValues(t, int64(util.Fixed8FromInt64(99999000)), u.Amount)
|
require.EqualValues(t, int64(util.Fixed8FromInt64(99999000)), u.Amount)
|
||||||
} else {
|
} else {
|
||||||
require.Nil(t, u)
|
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
|
// 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 == "") {
|
if start <= 206 && (stop == 0 || stop >= 206) && (asset == "neo" || asset == "") {
|
||||||
require.NotNil(t, u)
|
require.NotNil(t, u)
|
||||||
require.Equal(t, "9fbf833320ef6bc52ddee1fe6f5793b42e9b307e", u.Address.StringBE())
|
require.EqualValues(t, int64(util.Fixed8FromInt64(99999000)), u.Amount)
|
||||||
require.EqualValues(t, int64(util.Fixed8FromInt64(1000)), 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 {
|
} else {
|
||||||
require.Nil(t, u)
|
require.Nil(t, u)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue