Merge pull request #1165 from nspcc-dev/neo3/state/nep5transfer
core: store NEP5 transfers and balances as big.Int
This commit is contained in:
commit
1fa2d4bc6a
15 changed files with 113 additions and 80 deletions
|
@ -694,7 +694,7 @@ func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.C
|
||||||
}
|
}
|
||||||
amount = bigint.FromBytes(bs)
|
amount = bigint.FromBytes(bs)
|
||||||
}
|
}
|
||||||
bc.processNEP5Transfer(d, h, b, note.ScriptHash, from, to, amount.Int64())
|
bc.processNEP5Transfer(d, h, b, note.ScriptHash, from, to, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUint160(addr []byte) util.Uint160 {
|
func parseUint160(addr []byte) util.Uint160 {
|
||||||
|
@ -704,7 +704,7 @@ func parseUint160(addr []byte) util.Uint160 {
|
||||||
return util.Uint160{}
|
return util.Uint160{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, h util.Uint256, 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 *big.Int) {
|
||||||
toAddr := parseUint160(to)
|
toAddr := parseUint160(to)
|
||||||
fromAddr := parseUint160(from)
|
fromAddr := parseUint160(from)
|
||||||
transfer := &state.NEP5Transfer{
|
transfer := &state.NEP5Transfer{
|
||||||
|
@ -721,11 +721,10 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, h util.Uint256, b *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bs := balances.Trackers[sc]
|
bs := balances.Trackers[sc]
|
||||||
bs.Balance -= amount
|
bs.Balance = *new(big.Int).Sub(&bs.Balance, amount)
|
||||||
bs.LastUpdatedBlock = b.Index
|
bs.LastUpdatedBlock = b.Index
|
||||||
balances.Trackers[sc] = bs
|
balances.Trackers[sc] = bs
|
||||||
|
transfer.Amount = *new(big.Int).Sub(&transfer.Amount, amount)
|
||||||
transfer.Amount = -amount
|
|
||||||
isBig, err := cache.AppendNEP5Transfer(fromAddr, balances.NextTransferBatch, transfer)
|
isBig, err := cache.AppendNEP5Transfer(fromAddr, balances.NextTransferBatch, transfer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -743,11 +742,11 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, h util.Uint256, b *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bs := balances.Trackers[sc]
|
bs := balances.Trackers[sc]
|
||||||
bs.Balance += amount
|
bs.Balance = *new(big.Int).Add(&bs.Balance, amount)
|
||||||
bs.LastUpdatedBlock = b.Index
|
bs.LastUpdatedBlock = b.Index
|
||||||
balances.Trackers[sc] = bs
|
balances.Trackers[sc] = bs
|
||||||
|
|
||||||
transfer.Amount = amount
|
transfer.Amount = *amount
|
||||||
isBig, err := cache.AppendNEP5Transfer(toAddr, balances.NextTransferBatch, transfer)
|
isBig, err := cache.AppendNEP5Transfer(toAddr, balances.NextTransferBatch, transfer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -788,23 +787,24 @@ func (bc *Blockchain) GetNEP5Balances(acc util.Uint160) *state.NEP5Balances {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUtilityTokenBalance returns utility token (GAS) balance for the acc.
|
// GetUtilityTokenBalance returns utility token (GAS) balance for the acc.
|
||||||
func (bc *Blockchain) GetUtilityTokenBalance(acc util.Uint160) int64 {
|
func (bc *Blockchain) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
|
||||||
bs, err := bc.dao.GetNEP5Balances(acc)
|
bs, err := bc.dao.GetNEP5Balances(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return big.NewInt(0)
|
||||||
}
|
}
|
||||||
return bs.Trackers[bc.contracts.GAS.Hash].Balance
|
balance := bs.Trackers[bc.contracts.GAS.Hash].Balance
|
||||||
|
return &balance
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGoverningTokenBalance returns governing token (NEO) balance and the height
|
// GetGoverningTokenBalance returns governing token (NEO) balance and the height
|
||||||
// of the last balance change for the account.
|
// of the last balance change for the account.
|
||||||
func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (int64, uint32) {
|
func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32) {
|
||||||
bs, err := bc.dao.GetNEP5Balances(acc)
|
bs, err := bc.dao.GetNEP5Balances(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0
|
return big.NewInt(0), 0
|
||||||
}
|
}
|
||||||
neo := bs.Trackers[bc.contracts.NEO.Hash]
|
neo := bs.Trackers[bc.contracts.NEO.Hash]
|
||||||
return neo.Balance, neo.LastUpdatedBlock
|
return &neo.Balance, neo.LastUpdatedBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastBatch returns last persisted storage batch.
|
// LastBatch returns last persisted storage batch.
|
||||||
|
@ -1062,7 +1062,7 @@ func (bc *Blockchain) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)
|
||||||
// amount of NEO between specified blocks. The amount of NEO being passed is in
|
// amount of NEO between specified blocks. The amount of NEO being passed is in
|
||||||
// its natural non-divisible form (1 NEO as 1, 2 NEO as 2, no multiplication by
|
// its natural non-divisible form (1 NEO as 1, 2 NEO as 2, no multiplication by
|
||||||
// 10⁸ is needed as for Fixed8).
|
// 10⁸ is needed as for Fixed8).
|
||||||
func (bc *Blockchain) CalculateClaimable(value int64, startHeight, endHeight uint32) int64 {
|
func (bc *Blockchain) CalculateClaimable(value *big.Int, startHeight, endHeight uint32) *big.Int {
|
||||||
var amount int64
|
var amount int64
|
||||||
di := uint32(bc.decrementInterval)
|
di := uint32(bc.decrementInterval)
|
||||||
|
|
||||||
|
@ -1088,7 +1088,7 @@ func (bc *Blockchain) CalculateClaimable(value int64, startHeight, endHeight uin
|
||||||
amount += int64(iend-istart) * int64(bc.generationAmount[ustart])
|
amount += int64(iend-istart) * int64(bc.generationAmount[ustart])
|
||||||
}
|
}
|
||||||
|
|
||||||
return amount * value
|
return new(big.Int).Mul(big.NewInt(amount), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FeePerByte returns transaction network fee per byte.
|
// FeePerByte returns transaction network fee per byte.
|
||||||
|
@ -1148,7 +1148,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
||||||
}
|
}
|
||||||
balance := bc.GetUtilityTokenBalance(t.Sender)
|
balance := bc.GetUtilityTokenBalance(t.Sender)
|
||||||
need := t.SystemFee + t.NetworkFee
|
need := t.SystemFee + t.NetworkFee
|
||||||
if balance < need {
|
if balance.Cmp(big.NewInt(need)) < 0 {
|
||||||
return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need)
|
return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need)
|
||||||
}
|
}
|
||||||
size := io.GetVarSize(t)
|
size := io.GetVarSize(t)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -196,23 +197,23 @@ func TestGetClaimable(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("first generation period", func(t *testing.T) {
|
t.Run("first generation period", func(t *testing.T) {
|
||||||
amount := bc.CalculateClaimable(1, 0, 2)
|
amount := bc.CalculateClaimable(big.NewInt(1), 0, 2)
|
||||||
require.EqualValues(t, 8, amount)
|
require.EqualValues(t, big.NewInt(8), amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("a number of full periods", func(t *testing.T) {
|
t.Run("a number of full periods", func(t *testing.T) {
|
||||||
amount := bc.CalculateClaimable(1, 0, 6)
|
amount := bc.CalculateClaimable(big.NewInt(1), 0, 6)
|
||||||
require.EqualValues(t, 4+4+3+3+2+2, amount)
|
require.EqualValues(t, big.NewInt(4+4+3+3+2+2), amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("start from the 2-nd block", func(t *testing.T) {
|
t.Run("start from the 2-nd block", func(t *testing.T) {
|
||||||
amount := bc.CalculateClaimable(1, 1, 7)
|
amount := bc.CalculateClaimable(big.NewInt(1), 1, 7)
|
||||||
require.EqualValues(t, 4+3+3+2+2+1, amount)
|
require.EqualValues(t, big.NewInt(4+3+3+2+2+1), amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("end height after generation has ended", func(t *testing.T) {
|
t.Run("end height after generation has ended", func(t *testing.T) {
|
||||||
amount := bc.CalculateClaimable(1, 1, 10)
|
amount := bc.CalculateClaimable(big.NewInt(1), 1, 10)
|
||||||
require.EqualValues(t, 4+3+3+2+2+1+1, amount)
|
require.EqualValues(t, big.NewInt(4+3+3+2+2+1+1), amount)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package blockchainer
|
package blockchainer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||||
|
@ -19,13 +21,13 @@ type Blockchainer interface {
|
||||||
AddHeaders(...*block.Header) error
|
AddHeaders(...*block.Header) error
|
||||||
AddBlock(*block.Block) error
|
AddBlock(*block.Block) error
|
||||||
BlockHeight() uint32
|
BlockHeight() uint32
|
||||||
CalculateClaimable(value int64, startHeight, endHeight uint32) int64
|
CalculateClaimable(value *big.Int, startHeight, endHeight uint32) *big.Int
|
||||||
Close()
|
Close()
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetContractState(hash util.Uint160) *state.Contract
|
GetContractState(hash util.Uint160) *state.Contract
|
||||||
GetEnrollments() ([]state.Validator, error)
|
GetEnrollments() ([]state.Validator, error)
|
||||||
GetGoverningTokenBalance(acc util.Uint160) (int64, uint32)
|
GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(int) util.Uint256
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
CurrentHeaderHash() util.Uint256
|
CurrentHeaderHash() util.Uint256
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -181,7 +182,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
priv0 := testchain.PrivateKeyByID(0)
|
priv0 := testchain.PrivateKeyByID(0)
|
||||||
priv0ScriptHash := priv0.GetScriptHash()
|
priv0ScriptHash := priv0.GetScriptHash()
|
||||||
|
|
||||||
require.Equal(t, int64(0), bc.GetUtilityTokenBalance(priv0ScriptHash))
|
require.Equal(t, big.NewInt(0), bc.GetUtilityTokenBalance(priv0ScriptHash))
|
||||||
// Move some NEO to one simple account.
|
// Move some NEO to one simple account.
|
||||||
txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount)
|
txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount)
|
||||||
txMoveNeo.ValidUntilBlock = validUntilBlock
|
txMoveNeo.ValidUntilBlock = validUntilBlock
|
||||||
|
@ -211,7 +212,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE())
|
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE())
|
||||||
t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE())
|
t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE())
|
||||||
|
|
||||||
require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash) >= 1000*native.GASFactor)
|
require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash).Cmp(big.NewInt(1000*native.GASFactor)) >= 0)
|
||||||
// info for getblockheader rpc tests
|
// info for getblockheader rpc tests
|
||||||
t.Logf("header hash: %s", b.Hash().StringLE())
|
t.Logf("header hash: %s", b.Hash().StringLE())
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package mempool
|
package mempool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Feer is an interface that abstract the implementation of the fee calculation.
|
// Feer is an interface that abstract the implementation of the fee calculation.
|
||||||
type Feer interface {
|
type Feer interface {
|
||||||
FeePerByte() int64
|
FeePerByte() int64
|
||||||
GetUtilityTokenBalance(util.Uint160) int64
|
GetUtilityTokenBalance(util.Uint160) *big.Int
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mempool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -35,7 +36,7 @@ type items []*item
|
||||||
// utilityBalanceAndFees stores sender's balance and overall fees of
|
// utilityBalanceAndFees stores sender's balance and overall fees of
|
||||||
// sender's transactions which are currently in mempool
|
// sender's transactions which are currently in mempool
|
||||||
type utilityBalanceAndFees struct {
|
type utilityBalanceAndFees struct {
|
||||||
balance int64
|
balance *big.Int
|
||||||
feeSum int64
|
feeSum int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ func (mp *Pool) checkBalanceAndUpdate(tx *transaction.Transaction, feer Feer) bo
|
||||||
mp.fees[tx.Sender] = senderFee
|
mp.fees[tx.Sender] = senderFee
|
||||||
}
|
}
|
||||||
needFee := senderFee.feeSum + tx.SystemFee + tx.NetworkFee
|
needFee := senderFee.feeSum + tx.SystemFee + tx.NetworkFee
|
||||||
if senderFee.balance < needFee {
|
if senderFee.balance.Cmp(big.NewInt(needFee)) < 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package mempool
|
package mempool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -16,13 +17,13 @@ type FeerStub struct {
|
||||||
feePerByte int64
|
feePerByte int64
|
||||||
}
|
}
|
||||||
|
|
||||||
const balance = 10000000
|
var balance = big.NewInt(10000000)
|
||||||
|
|
||||||
func (fs *FeerStub) FeePerByte() int64 {
|
func (fs *FeerStub) FeePerByte() int64 {
|
||||||
return fs.feePerByte
|
return fs.feePerByte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) int64 {
|
func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
|
||||||
return balance
|
return balance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ func TestMemPoolFees(t *testing.T) {
|
||||||
mp := NewMemPool(10)
|
mp := NewMemPool(10)
|
||||||
sender0 := util.Uint160{1, 2, 3}
|
sender0 := util.Uint160{1, 2, 3}
|
||||||
tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
||||||
tx0.NetworkFee = balance + 1
|
tx0.NetworkFee = balance.Int64() + 1
|
||||||
tx0.Sender = sender0
|
tx0.Sender = sender0
|
||||||
// insufficient funds to add transaction, but balance should be stored
|
// insufficient funds to add transaction, but balance should be stored
|
||||||
require.Equal(t, false, mp.Verify(tx0, &FeerStub{}))
|
require.Equal(t, false, mp.Verify(tx0, &FeerStub{}))
|
||||||
|
@ -195,9 +196,10 @@ func TestMemPoolFees(t *testing.T) {
|
||||||
feeSum: 0,
|
feeSum: 0,
|
||||||
}, mp.fees[sender0])
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
balancePart := new(big.Int).Div(balance, big.NewInt(4))
|
||||||
// no problems with adding another transaction with lower fee
|
// no problems with adding another transaction with lower fee
|
||||||
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
||||||
tx1.NetworkFee = balance * 0.7
|
tx1.NetworkFee = balancePart.Int64()
|
||||||
tx1.Sender = sender0
|
tx1.Sender = sender0
|
||||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||||
require.Equal(t, 1, len(mp.fees))
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
@ -208,14 +210,14 @@ func TestMemPoolFees(t *testing.T) {
|
||||||
|
|
||||||
// balance shouldn't change after adding one more transaction
|
// balance shouldn't change after adding one more transaction
|
||||||
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
||||||
tx2.NetworkFee = balance * 0.3
|
tx2.NetworkFee = new(big.Int).Sub(balance, balancePart).Int64()
|
||||||
tx2.Sender = sender0
|
tx2.Sender = sender0
|
||||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||||
require.Equal(t, 2, len(mp.verifiedTxes))
|
require.Equal(t, 2, len(mp.verifiedTxes))
|
||||||
require.Equal(t, 1, len(mp.fees))
|
require.Equal(t, 1, len(mp.fees))
|
||||||
require.Equal(t, utilityBalanceAndFees{
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
balance: balance,
|
balance: balance,
|
||||||
feeSum: balance,
|
feeSum: balance.Int64(),
|
||||||
}, mp.fees[sender0])
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
// can't add more transactions as we don't have enough GAS
|
// can't add more transactions as we don't have enough GAS
|
||||||
|
@ -227,7 +229,7 @@ func TestMemPoolFees(t *testing.T) {
|
||||||
require.Equal(t, 1, len(mp.fees))
|
require.Equal(t, 1, len(mp.fees))
|
||||||
require.Equal(t, utilityBalanceAndFees{
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
balance: balance,
|
balance: balance,
|
||||||
feeSum: balance,
|
feeSum: balance.Int64(),
|
||||||
}, mp.fees[sender0])
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
// check whether sender's fee updates correctly
|
// check whether sender's fee updates correctly
|
||||||
|
|
|
@ -197,9 +197,9 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
|
||||||
if ic.Block == nil || ic.Block.Index == 0 {
|
if ic.Block == nil || ic.Block.Index == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gen := ic.Chain.CalculateClaimable(acc.Balance.Int64(), acc.BalanceHeight, ic.Block.Index)
|
gen := ic.Chain.CalculateClaimable(&acc.Balance, acc.BalanceHeight, ic.Block.Index)
|
||||||
acc.BalanceHeight = ic.Block.Index
|
acc.BalanceHeight = ic.Block.Index
|
||||||
n.GAS.mint(ic, h, big.NewInt(gen))
|
n.GAS.mint(ic, h, gen)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,8 +212,8 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem
|
||||||
}
|
}
|
||||||
tr := bs.Trackers[n.Hash]
|
tr := bs.Trackers[n.Hash]
|
||||||
|
|
||||||
gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end)
|
gen := ic.Chain.CalculateClaimable(&tr.Balance, tr.LastUpdatedBlock, end)
|
||||||
return stackitem.NewBigInteger(big.NewInt(gen))
|
return stackitem.NewBigInteger(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
|
|
@ -214,7 +214,7 @@ func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
balance := bs.Trackers[c.Hash].Balance
|
balance := bs.Trackers[c.Hash].Balance
|
||||||
return stackitem.NewBigInteger(big.NewInt(balance))
|
return stackitem.NewBigInteger(&balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -8,7 +11,7 @@ import (
|
||||||
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
||||||
type NEP5Tracker struct {
|
type NEP5Tracker struct {
|
||||||
// Balance is the current balance of the account.
|
// 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
|
// LastUpdatedBlock is a number of block when last `transfer` to or from the
|
||||||
// account occured.
|
// account occured.
|
||||||
LastUpdatedBlock uint32
|
LastUpdatedBlock uint32
|
||||||
|
@ -17,11 +20,10 @@ type NEP5Tracker struct {
|
||||||
// NEP5TransferLog is a log of NEP5 token transfers for the specific command.
|
// NEP5TransferLog is a log of NEP5 token transfers for the specific command.
|
||||||
type NEP5TransferLog struct {
|
type NEP5TransferLog struct {
|
||||||
Raw []byte
|
Raw []byte
|
||||||
|
// size is the number of NEP5Transfers written into Raw
|
||||||
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEP5TransferSize is a size of a marshaled NEP5Transfer struct in bytes.
|
|
||||||
const NEP5TransferSize = util.Uint160Size*3 + 8 + 4 + 8 + util.Uint256Size
|
|
||||||
|
|
||||||
// NEP5Transfer represents a single NEP5 Transfer event.
|
// NEP5Transfer represents a single NEP5 Transfer event.
|
||||||
type NEP5Transfer struct {
|
type NEP5Transfer struct {
|
||||||
// Asset is a NEP5 contract hash.
|
// Asset is a NEP5 contract hash.
|
||||||
|
@ -32,7 +34,7 @@ type NEP5Transfer struct {
|
||||||
To util.Uint160
|
To util.Uint160
|
||||||
// Amount is the amount of tokens transferred.
|
// Amount is the amount of tokens transferred.
|
||||||
// It is negative when tokens are sent and positive if they are received.
|
// 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 is a number of block when the event occured.
|
||||||
Block uint32
|
Block uint32
|
||||||
// Timestamp is the timestamp of the block where transfer occured.
|
// Timestamp is the timestamp of the block where transfer occured.
|
||||||
|
@ -89,6 +91,7 @@ func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error {
|
||||||
return w.Err
|
return w.Err
|
||||||
}
|
}
|
||||||
lg.Raw = append(lg.Raw, w.Bytes()...)
|
lg.Raw = append(lg.Raw, w.Bytes()...)
|
||||||
|
lg.size++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +101,10 @@ func (lg *NEP5TransferLog) ForEach(f func(*NEP5Transfer) error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
tr := new(NEP5Transfer)
|
tr := new(NEP5Transfer)
|
||||||
for i := 0; i < len(lg.Raw); i += NEP5TransferSize {
|
var bytesRead int
|
||||||
r := io.NewBinReaderFromBuf(lg.Raw[i : i+NEP5TransferSize])
|
for i := 0; i < len(lg.Raw); i += bytesRead {
|
||||||
tr.DecodeBinary(r)
|
r := io.NewBinReaderFromBuf(lg.Raw[i:])
|
||||||
|
bytesRead = tr.DecodeBinaryReturnCount(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return r.Err
|
return r.Err
|
||||||
} else if err := f(tr); err != nil {
|
} else if err := f(tr); err != nil {
|
||||||
|
@ -112,23 +116,22 @@ func (lg *NEP5TransferLog) ForEach(f func(*NEP5Transfer) error) error {
|
||||||
|
|
||||||
// Size returns an amount of transfer written in log.
|
// Size returns an amount of transfer written in log.
|
||||||
func (lg *NEP5TransferLog) Size() int {
|
func (lg *NEP5TransferLog) Size() int {
|
||||||
return len(lg.Raw) / NEP5TransferSize
|
return lg.size
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.WriteVarBytes(bigint.ToBytes(&t.Balance))
|
||||||
w.WriteU32LE(t.LastUpdatedBlock)
|
w.WriteU32LE(t.LastUpdatedBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
||||||
t.Balance = int64(r.ReadU64LE())
|
t.Balance = *bigint.FromBytes(r.ReadVarBytes())
|
||||||
t.LastUpdatedBlock = r.ReadU32LE()
|
t.LastUpdatedBlock = r.ReadU32LE()
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
// Note: change NEP5TransferSize constant when changing this function.
|
|
||||||
func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteBytes(t.Asset[:])
|
w.WriteBytes(t.Asset[:])
|
||||||
w.WriteBytes(t.Tx[:])
|
w.WriteBytes(t.Tx[:])
|
||||||
|
@ -136,16 +139,27 @@ func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteBytes(t.To[:])
|
w.WriteBytes(t.To[:])
|
||||||
w.WriteU32LE(t.Block)
|
w.WriteU32LE(t.Block)
|
||||||
w.WriteU64LE(t.Timestamp)
|
w.WriteU64LE(t.Timestamp)
|
||||||
w.WriteU64LE(uint64(t.Amount))
|
amountBytes := bigint.ToBytes(&t.Amount)
|
||||||
|
w.WriteU64LE(uint64(len(amountBytes)))
|
||||||
|
w.WriteBytes(amountBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (t *NEP5Transfer) DecodeBinary(r *io.BinReader) {
|
func (t *NEP5Transfer) DecodeBinary(r *io.BinReader) {
|
||||||
|
_ = t.DecodeBinaryReturnCount(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinaryReturnCount decodes NEP5Transfer and returns the number of bytes read.
|
||||||
|
func (t *NEP5Transfer) DecodeBinaryReturnCount(r *io.BinReader) int {
|
||||||
r.ReadBytes(t.Asset[:])
|
r.ReadBytes(t.Asset[:])
|
||||||
r.ReadBytes(t.Tx[:])
|
r.ReadBytes(t.Tx[:])
|
||||||
r.ReadBytes(t.From[:])
|
r.ReadBytes(t.From[:])
|
||||||
r.ReadBytes(t.To[:])
|
r.ReadBytes(t.To[:])
|
||||||
t.Block = r.ReadU32LE()
|
t.Block = r.ReadU32LE()
|
||||||
t.Timestamp = r.ReadU64LE()
|
t.Timestamp = r.ReadU64LE()
|
||||||
t.Amount = int64(r.ReadU64LE())
|
amountLen := r.ReadU64LE()
|
||||||
|
amountBytes := make([]byte, amountLen)
|
||||||
|
r.ReadBytes(amountBytes)
|
||||||
|
t.Amount = *bigint.FromBytes(amountBytes)
|
||||||
|
return util.Uint160Size*3 + 8 + 4 + (8 + len(amountBytes)) + +util.Uint256Size
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -40,7 +41,7 @@ func TestNEP5TransferLog_Append(t *testing.T) {
|
||||||
|
|
||||||
func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
||||||
expected := &NEP5Tracker{
|
expected := &NEP5Tracker{
|
||||||
Balance: int64(rand.Uint64()),
|
Balance: *big.NewInt(int64(rand.Uint64())),
|
||||||
LastUpdatedBlock: rand.Uint32(),
|
LastUpdatedBlock: rand.Uint32(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ func TestNEP5Transfer_DecodeBinary(t *testing.T) {
|
||||||
Asset: util.Uint160{1, 2, 3},
|
Asset: util.Uint160{1, 2, 3},
|
||||||
From: util.Uint160{5, 6, 7},
|
From: util.Uint160{5, 6, 7},
|
||||||
To: util.Uint160{8, 9, 10},
|
To: util.Uint160{8, 9, 10},
|
||||||
Amount: 42,
|
Amount: *big.NewInt(42),
|
||||||
Block: 12345,
|
Block: 12345,
|
||||||
Timestamp: 54321,
|
Timestamp: 54321,
|
||||||
Tx: util.Uint256{8, 5, 3},
|
Tx: util.Uint256{8, 5, 3},
|
||||||
|
@ -64,12 +65,18 @@ func TestNEP5Transfer_DecodeBinary(t *testing.T) {
|
||||||
func TestNEP5TransferSize(t *testing.T) {
|
func TestNEP5TransferSize(t *testing.T) {
|
||||||
tr := randomTransfer(rand.New(rand.NewSource(0)))
|
tr := randomTransfer(rand.New(rand.NewSource(0)))
|
||||||
size := io.GetVarSize(tr)
|
size := io.GetVarSize(tr)
|
||||||
require.EqualValues(t, NEP5TransferSize, size)
|
w := io.NewBufBinWriter()
|
||||||
|
tr.EncodeBinary(w.BinWriter)
|
||||||
|
require.NoError(t, w.Err)
|
||||||
|
r := io.NewBinReaderFromBuf(w.Bytes())
|
||||||
|
actualTr := &NEP5Transfer{}
|
||||||
|
actual := actualTr.DecodeBinaryReturnCount(r)
|
||||||
|
require.EqualValues(t, actual, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomTransfer(r *rand.Rand) *NEP5Transfer {
|
func randomTransfer(r *rand.Rand) *NEP5Transfer {
|
||||||
return &NEP5Transfer{
|
return &NEP5Transfer{
|
||||||
Amount: int64(r.Uint64()),
|
Amount: *big.NewInt(int64(r.Uint64())),
|
||||||
Block: r.Uint32(),
|
Block: r.Uint32(),
|
||||||
Asset: random.Uint160(),
|
Asset: random.Uint160(),
|
||||||
From: random.Uint160(),
|
From: random.Uint160(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -31,7 +32,7 @@ func (chain testChain) ApplyPolicyToTxSet([]*transaction.Transaction) []*transac
|
||||||
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
func (chain testChain) CalculateClaimable(int64, uint32, uint32) int64 {
|
func (chain testChain) CalculateClaimable(*big.Int, uint32, uint32) *big.Int {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +124,11 @@ func (chain testChain) GetMemPool() *mempool.Pool {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chain testChain) GetGoverningTokenBalance(acc util.Uint160) (int64, uint32) {
|
func (chain testChain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chain testChain) GetUtilityTokenBalance(uint160 util.Uint160) int64 {
|
func (chain testChain) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -512,7 +513,7 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, *response.Erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
amount := amountToString(bal.Balance, dec)
|
amount := amountToString(&bal.Balance, dec)
|
||||||
bs.Balances = append(bs.Balances, result.NEP5Balance{
|
bs.Balances = append(bs.Balances, result.NEP5Balance{
|
||||||
Asset: h,
|
Asset: h,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
|
@ -547,8 +548,8 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if tr.Amount > 0 { // token was received
|
if tr.Amount.Sign() > 0 { // token was received
|
||||||
transfer.Amount = amountToString(tr.Amount, d)
|
transfer.Amount = amountToString(&tr.Amount, d)
|
||||||
if !tr.From.Equals(util.Uint160{}) {
|
if !tr.From.Equals(util.Uint160{}) {
|
||||||
transfer.Address = address.Uint160ToString(tr.From)
|
transfer.Address = address.Uint160ToString(tr.From)
|
||||||
}
|
}
|
||||||
|
@ -556,7 +557,7 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
transfer.Amount = amountToString(-tr.Amount, d)
|
transfer.Amount = amountToString(new(big.Int).Neg(&tr.Amount), d)
|
||||||
if !tr.To.Equals(util.Uint160{}) {
|
if !tr.To.Equals(util.Uint160{}) {
|
||||||
transfer.Address = address.Uint160ToString(tr.To)
|
transfer.Address = address.Uint160ToString(tr.To)
|
||||||
}
|
}
|
||||||
|
@ -569,15 +570,14 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
||||||
return bs, nil
|
return bs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func amountToString(amount int64, decimals int64) string {
|
func amountToString(amount *big.Int, decimals int64) string {
|
||||||
if decimals == 0 {
|
if decimals == 0 {
|
||||||
return strconv.FormatInt(amount, 10)
|
return amount.String()
|
||||||
}
|
}
|
||||||
pow := int64(math.Pow10(int(decimals)))
|
pow := int64(math.Pow10(int(decimals)))
|
||||||
q := amount / pow
|
q, r := new(big.Int).DivMod(amount, big.NewInt(pow), new(big.Int))
|
||||||
r := amount % pow
|
if r.Sign() == 0 {
|
||||||
if r == 0 {
|
return q.String()
|
||||||
return strconv.FormatInt(q, 10)
|
|
||||||
}
|
}
|
||||||
fs := fmt.Sprintf("%%d.%%0%dd", decimals)
|
fs := fmt.Sprintf("%%d.%%0%dd", decimals)
|
||||||
return fmt.Sprintf(fs, q, r)
|
return fmt.Sprintf(fs, q, r)
|
||||||
|
@ -783,11 +783,11 @@ func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Erro
|
||||||
}
|
}
|
||||||
|
|
||||||
neo, neoHeight := s.chain.GetGoverningTokenBalance(u)
|
neo, neoHeight := s.chain.GetGoverningTokenBalance(u)
|
||||||
if neo == 0 {
|
if neo.Sign() == 0 {
|
||||||
return "0", nil
|
return "0", nil
|
||||||
}
|
}
|
||||||
gas := s.chain.CalculateClaimable(neo, neoHeight, s.chain.BlockHeight()+1) // +1 as in C#, for the next block.
|
gas := s.chain.CalculateClaimable(neo, neoHeight, s.chain.BlockHeight()+1) // +1 as in C#, for the next block.
|
||||||
return strconv.FormatInt(gas, 10), nil
|
return gas.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getValidators returns the current NEO consensus nodes information and voting status.
|
// getValidators returns the current NEO consensus nodes information and voting status.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
@ -90,6 +91,6 @@ func (fs *FeerStub) FeePerByte() int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FeerStub) GetUtilityTokenBalance(acc util.Uint160) int64 {
|
func (fs *FeerStub) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
|
||||||
return 1000000 * native.GASFactor
|
return big.NewInt(1000000 * native.GASFactor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -270,7 +271,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
Timestamp: b.Timestamp,
|
Timestamp: b.Timestamp,
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Address: "", // burn has empty receiver
|
Address: "", // burn has empty receiver
|
||||||
Amount: amountToString(int64(amount), 8),
|
Amount: amountToString(big.NewInt(amount), 8),
|
||||||
Index: b.Index,
|
Index: b.Index,
|
||||||
TxHash: b.Hash(),
|
TxHash: b.Hash(),
|
||||||
})
|
})
|
||||||
|
@ -282,7 +283,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
Timestamp: b.Timestamp,
|
Timestamp: b.Timestamp,
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Address: "", // minted from network fees.
|
Address: "", // minted from network fees.
|
||||||
Amount: amountToString(int64(netFee), 8),
|
Amount: amountToString(big.NewInt(netFee), 8),
|
||||||
Index: b.Index,
|
Index: b.Index,
|
||||||
TxHash: b.Hash(),
|
TxHash: b.Hash(),
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue