diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 82b26fbd7e..e3d44fa4dd 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 } diff --git a/pkg/core/state/transfer_log.go b/pkg/core/state/transfer_log.go index c89d638c2e..78b291442a 100644 --- a/pkg/core/state/transfer_log.go +++ b/pkg/core/state/transfer_log.go @@ -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() } diff --git a/pkg/rpc/response/result/utxo.go b/pkg/rpc/response/result/utxo.go index 1bd2bb437a..ae32577e8d 100644 --- a/pkg/rpc/response/result/utxo.go +++ b/pkg/rpc/response/result/utxo.go @@ -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"` } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 5a215cdf9f..36b6bc643e 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -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 diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 64ce7d6b35..38109c223e 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -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.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.Equal(t, "9fbf833320ef6bc52ddee1fe6f5793b42e9b307e", u.Address.StringBE()) - require.EqualValues(t, int64(util.Fixed8FromInt64(1000)), u.Amount) + require.EqualValues(t, int64(util.Fixed8FromInt64(99998000)), u.Amount) } else { require.Nil(t, u) }