mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-04 19:19:44 +00:00
state: use big.Int for NEP5 balances and transfer amounts
In general, NEP5 contracts are not limited to int64. And we have an example of pnWETH Flamingo token now (with 18 decimals) that easily overflows int64, so for correctness we need to store big.Int. And as TransferLog is shared for different purposes I've decided to not make it variable-length on Neo 2.
This commit is contained in:
parent
38842531ca
commit
eb1986d2fc
4 changed files with 51 additions and 24 deletions
|
@ -3,6 +3,7 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -944,20 +945,23 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, transfer *state.NEP
|
|||
return
|
||||
}
|
||||
bs := balances.Trackers[transfer.Asset]
|
||||
bs.Balance -= transfer.Amount
|
||||
if bs.Balance != 0 {
|
||||
if bs.Balance == nil {
|
||||
return
|
||||
}
|
||||
bs.Balance.Sub(bs.Balance, transfer.Amount)
|
||||
if bs.Balance.Sign() > 0 {
|
||||
bs.LastUpdatedBlock = transfer.Block
|
||||
balances.Trackers[transfer.Asset] = bs
|
||||
} else {
|
||||
delete(balances.Trackers, transfer.Asset)
|
||||
}
|
||||
|
||||
transfer.Amount = -transfer.Amount
|
||||
transfer.Amount.Neg(transfer.Amount)
|
||||
isBig, err := cache.AppendNEP5Transfer(transfer.From, balances.NextTransferBatch, transfer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
transfer.Amount = -transfer.Amount
|
||||
transfer.Amount.Neg(transfer.Amount)
|
||||
if isBig {
|
||||
balances.NextTransferBatch++
|
||||
}
|
||||
|
@ -971,7 +975,10 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, transfer *state.NEP
|
|||
return
|
||||
}
|
||||
bs := balances.Trackers[transfer.Asset]
|
||||
bs.Balance += transfer.Amount
|
||||
if bs.Balance == nil {
|
||||
bs.Balance = new(big.Int)
|
||||
}
|
||||
bs.Balance.Add(bs.Balance, transfer.Amount)
|
||||
bs.LastUpdatedBlock = transfer.Block
|
||||
balances.Trackers[transfer.Asset] = bs
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
||||
type NEP5Tracker struct {
|
||||
// Balance is the current balance of the account.
|
||||
Balance int64
|
||||
Balance *big.Int
|
||||
// LastUpdatedBlock is a number of block when last `transfer` to or from the
|
||||
// account occured.
|
||||
LastUpdatedBlock uint32
|
||||
|
@ -25,7 +25,7 @@ type TransferLog struct {
|
|||
}
|
||||
|
||||
// NEP5TransferSize is a size of a marshaled NEP5Transfer struct in bytes.
|
||||
const NEP5TransferSize = util.Uint160Size*3 + 8 + 4 + 4 + util.Uint256Size + 4
|
||||
const NEP5TransferSize = util.Uint160Size*3 + amountSize + 4 + 4 + util.Uint256Size + 4
|
||||
|
||||
// NEP5Transfer represents a single NEP5 Transfer event.
|
||||
type NEP5Transfer struct {
|
||||
|
@ -37,7 +37,7 @@ type NEP5Transfer struct {
|
|||
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
|
||||
Amount *big.Int
|
||||
// Block is a number of block when the event occured.
|
||||
Block uint32
|
||||
// Timestamp is the timestamp of the block where transfer occured.
|
||||
|
@ -48,6 +48,8 @@ type NEP5Transfer struct {
|
|||
Index uint32
|
||||
}
|
||||
|
||||
const amountSize = 32
|
||||
|
||||
// NEP5Balances is a map of the NEP5 contract hashes
|
||||
// to the corresponding structures.
|
||||
type NEP5Balances struct {
|
||||
|
@ -143,13 +145,13 @@ func (lg *TransferLog) Size() int {
|
|||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) {
|
||||
w.WriteU64LE(uint64(t.Balance))
|
||||
w.WriteVarBytes(emit.IntToBytes(t.Balance))
|
||||
w.WriteU32LE(t.LastUpdatedBlock)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
||||
t.Balance = int64(r.ReadU64LE())
|
||||
t.Balance = emit.BytesToInt(r.ReadVarBytes(amountSize))
|
||||
t.LastUpdatedBlock = r.ReadU32LE()
|
||||
}
|
||||
|
||||
|
@ -194,7 +196,7 @@ func NEP5TransferFromNotification(ne NotificationEvent, txHash util.Uint256, hei
|
|||
Asset: ne.ScriptHash,
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
Amount: amount.Int64(),
|
||||
Amount: amount,
|
||||
Block: height,
|
||||
Timestamp: time,
|
||||
Tx: txHash,
|
||||
|
@ -212,7 +214,19 @@ func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
|||
w.WriteBytes(t.To[:])
|
||||
w.WriteU32LE(t.Block)
|
||||
w.WriteU32LE(t.Timestamp)
|
||||
w.WriteU64LE(uint64(t.Amount))
|
||||
am := emit.IntToBytes(t.Amount)
|
||||
if len(am) > amountSize {
|
||||
panic("bad integer length")
|
||||
}
|
||||
fillerLen := amountSize - len(am)
|
||||
w.WriteBytes(am)
|
||||
var filler byte
|
||||
if t.Amount.Sign() < 0 {
|
||||
filler = 0xff
|
||||
}
|
||||
for i := 0; i < fillerLen; i++ {
|
||||
w.WriteB(filler)
|
||||
}
|
||||
w.WriteU32LE(t.Index)
|
||||
}
|
||||
|
||||
|
@ -224,6 +238,8 @@ func (t *NEP5Transfer) DecodeBinary(r *io.BinReader) {
|
|||
r.ReadBytes(t.To[:])
|
||||
t.Block = r.ReadU32LE()
|
||||
t.Timestamp = r.ReadU32LE()
|
||||
t.Amount = int64(r.ReadU64LE())
|
||||
amount := make([]byte, amountSize)
|
||||
r.ReadBytes(amount)
|
||||
t.Amount = emit.BytesToInt(amount)
|
||||
t.Index = r.ReadU32LE()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -41,7 +42,7 @@ func TestNEP5TransferLog_Append(t *testing.T) {
|
|||
|
||||
func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
||||
expected := &NEP5Tracker{
|
||||
Balance: int64(rand.Uint64()),
|
||||
Balance: big.NewInt(int64(rand.Uint64())),
|
||||
LastUpdatedBlock: rand.Uint32(),
|
||||
}
|
||||
|
||||
|
@ -53,7 +54,7 @@ func TestNEP5Transfer_DecodeBinary(t *testing.T) {
|
|||
Asset: util.Uint160{1, 2, 3},
|
||||
From: util.Uint160{5, 6, 7},
|
||||
To: util.Uint160{8, 9, 10},
|
||||
Amount: 42,
|
||||
Amount: big.NewInt(42),
|
||||
Block: 12345,
|
||||
Timestamp: 54321,
|
||||
Tx: util.Uint256{8, 5, 3},
|
||||
|
@ -70,7 +71,7 @@ func TestNEP5TransferSize(t *testing.T) {
|
|||
|
||||
func randomTransfer(r *rand.Rand) *NEP5Transfer {
|
||||
return &NEP5Transfer{
|
||||
Amount: int64(r.Uint64()),
|
||||
Amount: big.NewInt(int64(r.Uint64())),
|
||||
Block: r.Uint32(),
|
||||
Asset: random.Uint160(),
|
||||
From: random.Uint160(),
|
||||
|
|
|
@ -771,7 +771,7 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, *response.Erro
|
|||
}
|
||||
if as != nil {
|
||||
for h, bal := range as.Trackers {
|
||||
amount := strconv.FormatInt(bal.Balance, 10)
|
||||
amount := bal.Balance.String()
|
||||
bs.Balances = append(bs.Balances, result.NEP5Balance{
|
||||
Asset: h,
|
||||
Amount: amount,
|
||||
|
@ -824,14 +824,15 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
|
||||
NotifyIndex: tr.Index,
|
||||
}
|
||||
if tr.Amount > 0 { // token was received
|
||||
transfer.Amount = strconv.FormatInt(tr.Amount, 10)
|
||||
if tr.Amount.Sign() > 0 { // token was received
|
||||
transfer.Amount = tr.Amount.String()
|
||||
if !tr.From.Equals(util.Uint160{}) {
|
||||
transfer.Address = address.Uint160ToString(tr.From)
|
||||
}
|
||||
bs.Received = append(bs.Received, transfer)
|
||||
} else {
|
||||
transfer.Amount = strconv.FormatInt(-tr.Amount, 10)
|
||||
tr.Amount.Neg(tr.Amount)
|
||||
transfer.Amount = tr.Amount.String()
|
||||
if !tr.To.Equals(util.Uint160{}) {
|
||||
transfer.Address = address.Uint160ToString(tr.To)
|
||||
}
|
||||
|
@ -888,12 +889,14 @@ func uint160ToString(u util.Uint160) string {
|
|||
func appendNEP5ToTransferTx(transfer *result.TransferTx, nepTr *state.NEP5Transfer) {
|
||||
var event result.TransferTxEvent
|
||||
event.Asset = nepTr.Asset.StringLE()
|
||||
if nepTr.Amount > 0 { // token was received
|
||||
event.Value = strconv.FormatInt(nepTr.Amount, 10)
|
||||
if nepTr.Amount.Sign() > 0 { // token was received
|
||||
event.Value = nepTr.Amount.String()
|
||||
event.Type = "receive"
|
||||
event.Address = uint160ToString(nepTr.From)
|
||||
} else {
|
||||
event.Value = strconv.FormatInt(-nepTr.Amount, 10)
|
||||
nepTr.Amount.Neg(nepTr.Amount)
|
||||
event.Value = nepTr.Amount.String()
|
||||
nepTr.Amount.Neg(nepTr.Amount)
|
||||
event.Type = "send"
|
||||
event.Address = uint160ToString(nepTr.To)
|
||||
}
|
||||
|
@ -1327,7 +1330,7 @@ func (s *Server) getBlockTransferTx(ps request.Params) (interface{}, *response.E
|
|||
Asset: nepTr.Asset.StringLE(),
|
||||
From: uint160ToString(nepTr.From),
|
||||
To: uint160ToString(nepTr.To),
|
||||
Value: strconv.FormatInt(nepTr.Amount, 10),
|
||||
Value: nepTr.Amount.String(),
|
||||
})
|
||||
index++
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue