diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1644f101b..511044fc6 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 diff --git a/pkg/core/state/nep5.go b/pkg/core/state/nep5.go index d289ec00e..a87685293 100644 --- a/pkg/core/state/nep5.go +++ b/pkg/core/state/nep5.go @@ -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() } diff --git a/pkg/core/state/nep5_test.go b/pkg/core/state/nep5_test.go index 19f055d1c..a2c8ea764 100644 --- a/pkg/core/state/nep5_test.go +++ b/pkg/core/state/nep5_test.go @@ -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(), diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 623a49e4d..b1ba0636c 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -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++ }