forked from TrueCloudLab/neoneo-go
core: track NEP5 transfers
This commit is contained in:
parent
e8c4179a9c
commit
f92fd3c948
5 changed files with 137 additions and 7 deletions
|
@ -754,18 +754,26 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
return nil
|
return util.Uint160{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Transaction, b *block.Block, sc util.Uint160, from, to []byte, amount int64) {
|
func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Transaction, b *block.Block, sc util.Uint160, from, to []byte, amount int64) {
|
||||||
toAddr := parseUint160(to)
|
toAddr := parseUint160(to)
|
||||||
fromAddr := parseUint160(from)
|
fromAddr := parseUint160(from)
|
||||||
if fromAddr != nil {
|
transfer := &state.NEP5Transfer{
|
||||||
acc, err := cache.GetAccountStateOrNew(*fromAddr)
|
Asset: sc,
|
||||||
|
From: fromAddr,
|
||||||
|
To: toAddr,
|
||||||
|
Block: b.Index,
|
||||||
|
Timestamp: b.Timestamp,
|
||||||
|
Tx: tx.Hash(),
|
||||||
|
}
|
||||||
|
if !fromAddr.Equals(util.Uint160{}) {
|
||||||
|
acc, err := cache.GetAccountStateOrNew(fromAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -779,9 +787,14 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
|
||||||
if err := cache.PutAccountState(acc); err != nil {
|
if err := cache.PutAccountState(acc); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transfer.Amount = -amount
|
||||||
|
if err := cache.AppendNEP5Transfer(fromAddr, transfer); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if toAddr != nil {
|
}
|
||||||
acc, err := cache.GetAccountStateOrNew(*toAddr)
|
if !toAddr.Equals(util.Uint160{}) {
|
||||||
|
acc, err := cache.GetAccountStateOrNew(toAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -795,6 +808,11 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
|
||||||
if err := cache.PutAccountState(acc); err != nil {
|
if err := cache.PutAccountState(acc); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transfer.Amount = amount
|
||||||
|
if err := cache.AppendNEP5Transfer(fromAddr, transfer); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,41 @@ func (dao *dao) DeleteContractState(hash util.Uint160) error {
|
||||||
|
|
||||||
// -- end contracts.
|
// -- end contracts.
|
||||||
|
|
||||||
|
// -- start transfer log.
|
||||||
|
|
||||||
|
// GetNEP5TransferLog retrieves transfer log from the cache.
|
||||||
|
func (dao *dao) GetNEP5TransferLog(acc util.Uint160) (*state.NEP5TransferLog, error) {
|
||||||
|
key := storage.AppendPrefix(storage.STNEP5Transfers, acc.BytesBE())
|
||||||
|
value, err := dao.store.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &state.NEP5TransferLog{Raw: value}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutNEP5TransferLog saves given transfer log in the cache.
|
||||||
|
func (dao *dao) PutNEP5TransferLog(acc util.Uint160, lg *state.NEP5TransferLog) error {
|
||||||
|
key := storage.AppendPrefix(storage.STNEP5Transfers, acc.BytesBE())
|
||||||
|
return dao.store.Put(key, lg.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendNEP5Transfer appends a single NEP5 transfer to a log.
|
||||||
|
func (dao *dao) AppendNEP5Transfer(acc util.Uint160, tr *state.NEP5Transfer) error {
|
||||||
|
lg, err := dao.GetNEP5TransferLog(acc)
|
||||||
|
if err != nil {
|
||||||
|
if err != storage.ErrKeyNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lg = new(state.NEP5TransferLog)
|
||||||
|
}
|
||||||
|
if err := lg.Append(tr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dao.PutNEP5TransferLog(acc, lg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- end transfer log.
|
||||||
|
|
||||||
// -- start unspent coins.
|
// -- start unspent coins.
|
||||||
|
|
||||||
// GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store
|
// GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store
|
||||||
|
|
|
@ -2,6 +2,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
||||||
|
@ -13,6 +14,44 @@ type NEP5Tracker struct {
|
||||||
LastUpdatedBlock uint32
|
LastUpdatedBlock uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEP5TransferLog is a log of NEP5 token transfers for the specific command.
|
||||||
|
type NEP5TransferLog struct {
|
||||||
|
Raw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEP5TransferSize is a size of a marshaled NEP5Transfer struct in bytes.
|
||||||
|
const NEP5TransferSize = util.Uint160Size*3 + 8 + 4 + 4 + util.Uint256Size
|
||||||
|
|
||||||
|
// NEP5Transfer represents a single NEP5 Transfer event.
|
||||||
|
type NEP5Transfer struct {
|
||||||
|
// Asset is a NEP5 contract hash.
|
||||||
|
Asset util.Uint160
|
||||||
|
// Address is the address of the sender.
|
||||||
|
From util.Uint160
|
||||||
|
// To is the address of the receiver.
|
||||||
|
To util.Uint160
|
||||||
|
// Amount is the amount of tokens transferred.
|
||||||
|
// It is negative when tokens are sent and positive if they are received.
|
||||||
|
Amount int64
|
||||||
|
// Block is a number of block when the event occured.
|
||||||
|
Block uint32
|
||||||
|
// Timestamp is the timestamp of the block where transfer occured.
|
||||||
|
Timestamp uint32
|
||||||
|
// Tx is a hash the transaction.
|
||||||
|
Tx util.Uint256
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends single transfer to a log.
|
||||||
|
func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
tr.EncodeBinary(w.BinWriter)
|
||||||
|
if w.Err != nil {
|
||||||
|
return w.Err
|
||||||
|
}
|
||||||
|
lg.Raw = append(lg.Raw, w.Bytes()...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) {
|
func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteU64LE(uint64(t.Balance))
|
w.WriteU64LE(uint64(t.Balance))
|
||||||
|
@ -24,3 +63,26 @@ func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
||||||
t.Balance = int64(r.ReadU64LE())
|
t.Balance = int64(r.ReadU64LE())
|
||||||
t.LastUpdatedBlock = r.ReadU32LE()
|
t.LastUpdatedBlock = r.ReadU32LE()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements io.Serializable interface.
|
||||||
|
// Note: change NEP5TransferSize constant when changing this function.
|
||||||
|
func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteBytes(t.Asset[:])
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinary implements io.Serializable interface.
|
||||||
|
func (t *NEP5Transfer) DecodeBinary(r *io.BinReader) {
|
||||||
|
r.ReadBytes(t.Asset[:])
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,20 @@ func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
||||||
testEncodeDecode(t, expected, new(NEP5Tracker))
|
testEncodeDecode(t, expected, new(NEP5Tracker))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNEP5Transfer_DecodeBinary(t *testing.T) {
|
||||||
|
expected := &NEP5Transfer{
|
||||||
|
Asset: util.Uint160{1, 2, 3},
|
||||||
|
From: util.Uint160{5, 6, 7},
|
||||||
|
To: util.Uint160{8, 9, 10},
|
||||||
|
Amount: 42,
|
||||||
|
Block: 12345,
|
||||||
|
Timestamp: 54321,
|
||||||
|
Tx: util.Uint256{8, 5, 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
testEncodeDecode(t, expected, new(NEP5Transfer))
|
||||||
|
}
|
||||||
|
|
||||||
func testEncodeDecode(t *testing.T, expected, actual io.Serializable) {
|
func testEncodeDecode(t *testing.T, expected, actual io.Serializable) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
expected.EncodeBinary(w.BinWriter)
|
expected.EncodeBinary(w.BinWriter)
|
||||||
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
STNotification KeyPrefix = 0x4d
|
STNotification KeyPrefix = 0x4d
|
||||||
STContract KeyPrefix = 0x50
|
STContract KeyPrefix = 0x50
|
||||||
STStorage KeyPrefix = 0x70
|
STStorage KeyPrefix = 0x70
|
||||||
|
STNEP5Transfers KeyPrefix = 0x72
|
||||||
IXHeaderHashList KeyPrefix = 0x80
|
IXHeaderHashList KeyPrefix = 0x80
|
||||||
IXValidatorsCount KeyPrefix = 0x90
|
IXValidatorsCount KeyPrefix = 0x90
|
||||||
SYSCurrentBlock KeyPrefix = 0xc0
|
SYSCurrentBlock KeyPrefix = 0xc0
|
||||||
|
|
Loading…
Reference in a new issue