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
|
||||
}
|
||||
|
||||
func parseUint160(addr []byte) *util.Uint160 {
|
||||
func parseUint160(addr []byte) util.Uint160 {
|
||||
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) {
|
||||
toAddr := parseUint160(to)
|
||||
fromAddr := parseUint160(from)
|
||||
if fromAddr != nil {
|
||||
acc, err := cache.GetAccountStateOrNew(*fromAddr)
|
||||
transfer := &state.NEP5Transfer{
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -779,9 +787,14 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
|
|||
if err := cache.PutAccountState(acc); err != nil {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -795,6 +808,11 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
|
|||
if err := cache.PutAccountState(acc); err != nil {
|
||||
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.
|
||||
|
||||
// -- 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.
|
||||
|
||||
// GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store
|
||||
|
|
|
@ -2,6 +2,7 @@ package state
|
|||
|
||||
import (
|
||||
"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.
|
||||
|
@ -13,6 +14,44 @@ type NEP5Tracker struct {
|
|||
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.
|
||||
func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) {
|
||||
w.WriteU64LE(uint64(t.Balance))
|
||||
|
@ -24,3 +63,26 @@ func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
|||
t.Balance = int64(r.ReadU64LE())
|
||||
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))
|
||||
}
|
||||
|
||||
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) {
|
||||
w := io.NewBufBinWriter()
|
||||
expected.EncodeBinary(w.BinWriter)
|
||||
|
|
|
@ -17,6 +17,7 @@ const (
|
|||
STNotification KeyPrefix = 0x4d
|
||||
STContract KeyPrefix = 0x50
|
||||
STStorage KeyPrefix = 0x70
|
||||
STNEP5Transfers KeyPrefix = 0x72
|
||||
IXHeaderHashList KeyPrefix = 0x80
|
||||
IXValidatorsCount KeyPrefix = 0x90
|
||||
SYSCurrentBlock KeyPrefix = 0xc0
|
||||
|
|
Loading…
Reference in a new issue