forked from TrueCloudLab/neoneo-go
core: process NEP5 transfers emitted by Native.onPersist
All GAS is minted/burnt during `onPersist` call.
This commit is contained in:
parent
c9df5d3aed
commit
8a0b2be285
2 changed files with 68 additions and 40 deletions
|
@ -578,6 +578,9 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
} else if _, err := systemInterop.DAO.Persist(); err != nil {
|
} else if _, err := systemInterop.DAO.Persist(); err != nil {
|
||||||
return errors.Wrap(err, "can't persist `onPersist` changes")
|
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{
|
aer := &state.AppExecResult{
|
||||||
TxHash: block.Hash(), // application logs can be retrieved by block hash
|
TxHash: block.Hash(), // application logs can be retrieved by block hash
|
||||||
Trigger: trigger.System,
|
Trigger: trigger.System,
|
||||||
|
@ -612,42 +615,8 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to persist invocation results")
|
return errors.Wrap(err, "failed to persist invocation results")
|
||||||
}
|
}
|
||||||
for _, note := range systemInterop.Notifications {
|
for i := range systemInterop.Notifications {
|
||||||
arr, ok := note.Item.Value().([]stackitem.Item)
|
bc.handleNotification(&systemInterop.Notifications[i], cache, block, tx.Hash())
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bc.log.Warn("contract invocation failed",
|
bc.log.Warn("contract invocation failed",
|
||||||
|
@ -695,6 +664,44 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
return nil
|
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 {
|
func parseUint160(addr []byte) util.Uint160 {
|
||||||
if u, err := util.Uint160DecodeBytesBE(addr); err == nil {
|
if u, err := util.Uint160DecodeBytesBE(addr); err == nil {
|
||||||
return u
|
return u
|
||||||
|
@ -702,7 +709,7 @@ func parseUint160(addr []byte) util.Uint160 {
|
||||||
return 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)
|
toAddr := parseUint160(to)
|
||||||
fromAddr := parseUint160(from)
|
fromAddr := parseUint160(from)
|
||||||
transfer := &state.NEP5Transfer{
|
transfer := &state.NEP5Transfer{
|
||||||
|
@ -711,7 +718,7 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, tx *transaction.Tra
|
||||||
To: toAddr,
|
To: toAddr,
|
||||||
Block: b.Index,
|
Block: b.Index,
|
||||||
Timestamp: b.Timestamp,
|
Timestamp: b.Timestamp,
|
||||||
Tx: tx.Hash(),
|
Tx: h,
|
||||||
}
|
}
|
||||||
if !fromAddr.Equals(util.Uint160{}) {
|
if !fromAddr.Equals(util.Uint160{}) {
|
||||||
balances, err := cache.GetNEP5Balances(fromAddr)
|
balances, err := cache.GetNEP5Balances(fromAddr)
|
||||||
|
|
|
@ -148,8 +148,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Amount: "1023.99976000",
|
Amount: "923.96934740",
|
||||||
LastUpdated: 4,
|
LastUpdated: 6,
|
||||||
}},
|
}},
|
||||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||||
}
|
}
|
||||||
|
@ -256,6 +256,27 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
},
|
},
|
||||||
Address: testchain.PrivateKeyByID(0).Address(),
|
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.Equal(t, expected.Address, res.Address)
|
||||||
require.ElementsMatch(t, expected.Sent, res.Sent)
|
require.ElementsMatch(t, expected.Sent, res.Sent)
|
||||||
require.ElementsMatch(t, expected.Received, res.Received)
|
require.ElementsMatch(t, expected.Received, res.Received)
|
||||||
|
|
Loading…
Reference in a new issue