From 8a0b2be285600f050db541e6597e290840102c61 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 18 Jun 2020 16:34:56 +0300 Subject: [PATCH] core: process NEP5 transfers emitted by Native.onPersist All GAS is minted/burnt during `onPersist` call. --- pkg/core/blockchain.go | 83 +++++++++++++++++++---------------- pkg/rpc/server/server_test.go | 25 ++++++++++- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 620e330f1..2bb20863d 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -578,6 +578,9 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { } else if _, err := systemInterop.DAO.Persist(); err != nil { return errors.Wrap(err, "can't persist `onPersist` changes") } + for i := range systemInterop.Notifications { + bc.handleNotification(&systemInterop.Notifications[i], cache, block, block.Hash()) + } aer := &state.AppExecResult{ TxHash: block.Hash(), // application logs can be retrieved by block hash Trigger: trigger.System, @@ -612,42 +615,8 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { if err != nil { return errors.Wrap(err, "failed to persist invocation results") } - for _, note := range systemInterop.Notifications { - arr, ok := note.Item.Value().([]stackitem.Item) - if !ok || len(arr) != 4 { - continue - } - op, ok := arr[0].Value().([]byte) - if !ok || (string(op) != "transfer" && string(op) != "Transfer") { - continue - } - var from []byte - fromValue := arr[1].Value() - // we don't have `from` set when we are minting tokens - if fromValue != nil { - from, ok = fromValue.([]byte) - if !ok { - continue - } - } - var to []byte - toValue := arr[2].Value() - // we don't have `to` set when we are burning tokens - if toValue != nil { - to, ok = toValue.([]byte) - if !ok { - continue - } - } - amount, ok := arr[3].Value().(*big.Int) - if !ok { - bs, ok := arr[3].Value().([]byte) - if !ok { - continue - } - amount = bigint.FromBytes(bs) - } - bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64()) + for i := range systemInterop.Notifications { + bc.handleNotification(&systemInterop.Notifications[i], cache, block, tx.Hash()) } } else { bc.log.Warn("contract invocation failed", @@ -695,6 +664,44 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { return nil } +func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.Cached, b *block.Block, h util.Uint256) { + arr, ok := note.Item.Value().([]stackitem.Item) + if !ok || len(arr) != 4 { + return + } + op, ok := arr[0].Value().([]byte) + if !ok || (string(op) != "transfer" && string(op) != "Transfer") { + return + } + var from []byte + fromValue := arr[1].Value() + // we don't have `from` set when we are minting tokens + if fromValue != nil { + from, ok = fromValue.([]byte) + if !ok { + return + } + } + var to []byte + toValue := arr[2].Value() + // we don't have `to` set when we are burning tokens + if toValue != nil { + to, ok = toValue.([]byte) + if !ok { + return + } + } + amount, ok := arr[3].Value().(*big.Int) + if !ok { + bs, ok := arr[3].Value().([]byte) + if !ok { + return + } + amount = bigint.FromBytes(bs) + } + bc.processNEP5Transfer(d, h, b, note.ScriptHash, from, to, amount.Int64()) +} + func parseUint160(addr []byte) util.Uint160 { if u, err := util.Uint160DecodeBytesBE(addr); err == nil { return u @@ -702,7 +709,7 @@ func parseUint160(addr []byte) util.Uint160 { return util.Uint160{} } -func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, tx *transaction.Transaction, b *block.Block, sc util.Uint160, from, to []byte, amount int64) { +func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, h util.Uint256, b *block.Block, sc util.Uint160, from, to []byte, amount int64) { toAddr := parseUint160(to) fromAddr := parseUint160(from) transfer := &state.NEP5Transfer{ @@ -711,7 +718,7 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, tx *transaction.Tra To: toAddr, Block: b.Index, Timestamp: b.Timestamp, - Tx: tx.Hash(), + Tx: h, } if !fromAddr.Equals(util.Uint160{}) { balances, err := cache.GetNEP5Balances(fromAddr) diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index f08d397cd..a8428a642 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -148,8 +148,8 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { Asset: e.chain.UtilityTokenHash(), - Amount: "1023.99976000", - LastUpdated: 4, + Amount: "923.96934740", + LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), } @@ -256,6 +256,27 @@ var rpcTestCases = map[string][]rpcTestCase{ }, Address: testchain.PrivateKeyByID(0).Address(), } + + // take burned gas into account + u := testchain.PrivateKeyByID(0).GetScriptHash() + for i := 0; i <= int(e.chain.BlockHeight()); i++ { + h := e.chain.GetHeaderHash(i) + b, err := e.chain.GetBlock(h) + require.NoError(t, err) + for j := range b.Transactions { + if u.Equals(b.Transactions[j].Sender) { + amount := b.Transactions[j].SystemFee + b.Transactions[j].NetworkFee + expected.Sent = append(expected.Sent, result.NEP5Transfer{ + Timestamp: b.Timestamp, + Asset: e.chain.UtilityTokenHash(), + Address: "", // burn has empty receiver + Amount: amountToString(int64(amount), 8), + Index: b.Index, + TxHash: b.Hash(), + }) + } + } + } require.Equal(t, expected.Address, res.Address) require.ElementsMatch(t, expected.Sent, res.Sent) require.ElementsMatch(t, expected.Received, res.Received)