forked from TrueCloudLab/neoneo-go
core: add senders' fees monitor to mempool
In order to simplify maintainance of overall current senders` fees in the mempool we are to keep them in a separate map.
This commit is contained in:
parent
c3e08afe34
commit
1afd630169
6 changed files with 160 additions and 20 deletions
|
@ -723,7 +723,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
bc.topBlock.Store(block)
|
bc.topBlock.Store(block)
|
||||||
atomic.StoreUint32(&bc.blockHeight, block.Index)
|
atomic.StoreUint32(&bc.blockHeight, block.Index)
|
||||||
updateBlockHeightMetric(block.Index)
|
updateBlockHeightMetric(block.Index)
|
||||||
bc.memPool.RemoveStale(bc.isTxStillRelevant)
|
bc.memPool.RemoveStale(bc.isTxStillRelevant, bc)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,6 +817,11 @@ func (bc *Blockchain) GetNEP5Balances(acc util.Uint160) *state.NEP5Balances {
|
||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUtilityTokenBalance returns utility token (GAS) balance for the acc.
|
||||||
|
func (bc *Blockchain) GetUtilityTokenBalance(acc util.Uint160) util.Fixed8 {
|
||||||
|
return util.Fixed8FromInt64(bc.GetNEP5Balances(acc).Trackers[bc.contracts.GAS.Hash].Balance)
|
||||||
|
}
|
||||||
|
|
||||||
// LastBatch returns last persisted storage batch.
|
// LastBatch returns last persisted storage batch.
|
||||||
func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
||||||
return bc.lastBatch
|
return bc.lastBatch
|
||||||
|
@ -1171,7 +1176,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
||||||
if t.ValidUntilBlock <= height || t.ValidUntilBlock > height+transaction.MaxValidUntilBlockIncrement {
|
if t.ValidUntilBlock <= height || t.ValidUntilBlock > height+transaction.MaxValidUntilBlockIncrement {
|
||||||
return errors.Errorf("transaction has expired. ValidUntilBlock = %d, current height = %d", t.ValidUntilBlock, height)
|
return errors.Errorf("transaction has expired. ValidUntilBlock = %d, current height = %d", t.ValidUntilBlock, height)
|
||||||
}
|
}
|
||||||
balance := util.Fixed8FromInt64(bc.GetNEP5Balances(t.Sender).Trackers[bc.contracts.GAS.Hash].Balance)
|
balance := bc.GetUtilityTokenBalance(t.Sender)
|
||||||
need := t.SystemFee.Add(t.NetworkFee)
|
need := t.SystemFee.Add(t.NetworkFee)
|
||||||
if balance.LessThan(need) {
|
if balance.LessThan(need) {
|
||||||
return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need)
|
return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need)
|
||||||
|
@ -1189,7 +1194,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
||||||
return errors.New("invalid transaction's inputs")
|
return errors.New("invalid transaction's inputs")
|
||||||
}
|
}
|
||||||
if block == nil {
|
if block == nil {
|
||||||
if ok := bc.memPool.Verify(t); !ok {
|
if ok := bc.memPool.Verify(t, bc); !ok {
|
||||||
return errors.New("invalid transaction due to conflicts with the memory pool")
|
return errors.New("invalid transaction due to conflicts with the memory pool")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,5 @@ import (
|
||||||
type Feer interface {
|
type Feer interface {
|
||||||
IsLowPriority(util.Fixed8) bool
|
IsLowPriority(util.Fixed8) bool
|
||||||
FeePerByte() util.Fixed8
|
FeePerByte() util.Fixed8
|
||||||
|
GetUtilityTokenBalance(util.Uint160) util.Fixed8
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,13 @@ type TxWithFee struct {
|
||||||
Fee util.Fixed8
|
Fee util.Fixed8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// utilityBalanceAndFees stores sender's balance and overall fees of
|
||||||
|
// sender's transactions which are currently in mempool
|
||||||
|
type utilityBalanceAndFees struct {
|
||||||
|
balance util.Fixed8
|
||||||
|
feeSum util.Fixed8
|
||||||
|
}
|
||||||
|
|
||||||
// Pool stores the unconfirms transactions.
|
// Pool stores the unconfirms transactions.
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
@ -46,6 +53,7 @@ type Pool struct {
|
||||||
verifiedTxes items
|
verifiedTxes items
|
||||||
inputs []*transaction.Input
|
inputs []*transaction.Input
|
||||||
claims []*transaction.Input
|
claims []*transaction.Input
|
||||||
|
fees map[util.Uint160]utilityBalanceAndFees
|
||||||
|
|
||||||
capacity int
|
capacity int
|
||||||
}
|
}
|
||||||
|
@ -156,6 +164,38 @@ func dropInputFromSortedSlice(slice *[]*transaction.Input, input *transaction.In
|
||||||
*slice = (*slice)[:len(*slice)-1]
|
*slice = (*slice)[:len(*slice)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tryAddSendersFee tries to add system fee and network fee to the total sender`s fee in mempool
|
||||||
|
// and returns false if sender has not enough GAS to pay
|
||||||
|
func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer) bool {
|
||||||
|
if !mp.checkBalanceAndUpdate(tx, feer) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mp.addSendersFee(tx)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkBalanceAndUpdate returns true in case when sender has enough GAS to pay for
|
||||||
|
// the transaction and sets sender's balance value in mempool in case if it was not set
|
||||||
|
func (mp *Pool) checkBalanceAndUpdate(tx *transaction.Transaction, feer Feer) bool {
|
||||||
|
senderFee, ok := mp.fees[tx.Sender]
|
||||||
|
if !ok {
|
||||||
|
senderFee.balance = feer.GetUtilityTokenBalance(tx.Sender)
|
||||||
|
mp.fees[tx.Sender] = senderFee
|
||||||
|
}
|
||||||
|
needFee := senderFee.feeSum + tx.SystemFee + tx.NetworkFee
|
||||||
|
if senderFee.balance < needFee {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSendersFee adds system fee and network fee to the total sender`s fee in mempool
|
||||||
|
func (mp *Pool) addSendersFee(tx *transaction.Transaction) {
|
||||||
|
senderFee := mp.fees[tx.Sender]
|
||||||
|
senderFee.feeSum += tx.SystemFee + tx.NetworkFee
|
||||||
|
mp.fees[tx.Sender] = senderFee
|
||||||
|
}
|
||||||
|
|
||||||
// Add tries to add given transaction to the Pool.
|
// Add tries to add given transaction to the Pool.
|
||||||
func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
||||||
var pItem = &item{
|
var pItem = &item{
|
||||||
|
@ -164,7 +204,7 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
||||||
}
|
}
|
||||||
pItem.isLowPrio = fee.IsLowPriority(pItem.txn.NetworkFee)
|
pItem.isLowPrio = fee.IsLowPriority(pItem.txn.NetworkFee)
|
||||||
mp.lock.Lock()
|
mp.lock.Lock()
|
||||||
if !mp.checkTxConflicts(t) {
|
if !mp.checkTxConflicts(t, fee) {
|
||||||
mp.lock.Unlock()
|
mp.lock.Unlock()
|
||||||
return ErrConflict
|
return ErrConflict
|
||||||
}
|
}
|
||||||
|
@ -202,6 +242,7 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
||||||
copy(mp.verifiedTxes[n+1:], mp.verifiedTxes[n:])
|
copy(mp.verifiedTxes[n+1:], mp.verifiedTxes[n:])
|
||||||
mp.verifiedTxes[n] = pItem
|
mp.verifiedTxes[n] = pItem
|
||||||
}
|
}
|
||||||
|
mp.addSendersFee(pItem.txn)
|
||||||
|
|
||||||
// For lots of inputs it might be easier to push them all and sort
|
// For lots of inputs it might be easier to push them all and sort
|
||||||
// afterwards, but that requires benchmarking.
|
// afterwards, but that requires benchmarking.
|
||||||
|
@ -237,6 +278,9 @@ func (mp *Pool) Remove(hash util.Uint256) {
|
||||||
} else if num == len(mp.verifiedTxes)-1 {
|
} else if num == len(mp.verifiedTxes)-1 {
|
||||||
mp.verifiedTxes = mp.verifiedTxes[:num]
|
mp.verifiedTxes = mp.verifiedTxes[:num]
|
||||||
}
|
}
|
||||||
|
senderFee := mp.fees[it.txn.Sender]
|
||||||
|
senderFee.feeSum -= it.txn.SystemFee + it.txn.NetworkFee
|
||||||
|
mp.fees[it.txn.Sender] = senderFee
|
||||||
for i := range it.txn.Inputs {
|
for i := range it.txn.Inputs {
|
||||||
dropInputFromSortedSlice(&mp.inputs, &it.txn.Inputs[i])
|
dropInputFromSortedSlice(&mp.inputs, &it.txn.Inputs[i])
|
||||||
}
|
}
|
||||||
|
@ -254,15 +298,16 @@ func (mp *Pool) Remove(hash util.Uint256) {
|
||||||
// RemoveStale filters verified transactions through the given function keeping
|
// RemoveStale filters verified transactions through the given function keeping
|
||||||
// only the transactions for which it returns a true result. It's used to quickly
|
// only the transactions for which it returns a true result. It's used to quickly
|
||||||
// drop part of the mempool that is now invalid after the block acceptance.
|
// drop part of the mempool that is now invalid after the block acceptance.
|
||||||
func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool) {
|
func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer) {
|
||||||
mp.lock.Lock()
|
mp.lock.Lock()
|
||||||
// We can reuse already allocated slice
|
// We can reuse already allocated slice
|
||||||
// because items are iterated one-by-one in increasing order.
|
// because items are iterated one-by-one in increasing order.
|
||||||
newVerifiedTxes := mp.verifiedTxes[:0]
|
newVerifiedTxes := mp.verifiedTxes[:0]
|
||||||
newInputs := mp.inputs[:0]
|
newInputs := mp.inputs[:0]
|
||||||
newClaims := mp.claims[:0]
|
newClaims := mp.claims[:0]
|
||||||
|
mp.fees = make(map[util.Uint160]utilityBalanceAndFees) // it'd be nice to reuse existing map, but we can't easily clear it
|
||||||
for _, itm := range mp.verifiedTxes {
|
for _, itm := range mp.verifiedTxes {
|
||||||
if isOK(itm.txn) {
|
if isOK(itm.txn) && mp.tryAddSendersFee(itm.txn, feer) {
|
||||||
newVerifiedTxes = append(newVerifiedTxes, itm)
|
newVerifiedTxes = append(newVerifiedTxes, itm)
|
||||||
for i := range itm.txn.Inputs {
|
for i := range itm.txn.Inputs {
|
||||||
newInputs = append(newInputs, &itm.txn.Inputs[i])
|
newInputs = append(newInputs, &itm.txn.Inputs[i])
|
||||||
|
@ -295,6 +340,7 @@ func NewMemPool(capacity int) Pool {
|
||||||
verifiedMap: make(map[util.Uint256]*item),
|
verifiedMap: make(map[util.Uint256]*item),
|
||||||
verifiedTxes: make([]*item, 0, capacity),
|
verifiedTxes: make([]*item, 0, capacity),
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
|
fees: make(map[util.Uint160]utilityBalanceAndFees),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,10 +384,13 @@ func areInputsInPool(inputs []transaction.Input, pool []*transaction.Input) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTxConflicts is an internal unprotected version of Verify.
|
// checkTxConflicts is an internal unprotected version of Verify.
|
||||||
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction) bool {
|
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool {
|
||||||
if areInputsInPool(tx.Inputs, mp.inputs) {
|
if areInputsInPool(tx.Inputs, mp.inputs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !mp.checkBalanceAndUpdate(tx, fee) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
switch tx.Type {
|
switch tx.Type {
|
||||||
case transaction.ClaimType:
|
case transaction.ClaimType:
|
||||||
claim := tx.Data.(*transaction.ClaimTX)
|
claim := tx.Data.(*transaction.ClaimTX)
|
||||||
|
@ -364,8 +413,8 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction) bool {
|
||||||
// Verify verifies if the inputs of a transaction tx are already used in any other transaction in the memory pool.
|
// Verify verifies if the inputs of a transaction tx are already used in any other transaction in the memory pool.
|
||||||
// If yes, the transaction tx is not a valid transaction and the function return false.
|
// If yes, the transaction tx is not a valid transaction and the function return false.
|
||||||
// If no, the transaction tx is a valid transaction and the function return true.
|
// If no, the transaction tx is a valid transaction and the function return true.
|
||||||
func (mp *Pool) Verify(tx *transaction.Transaction) bool {
|
func (mp *Pool) Verify(tx *transaction.Transaction, feer Feer) bool {
|
||||||
mp.lock.RLock()
|
mp.lock.RLock()
|
||||||
defer mp.lock.RUnlock()
|
defer mp.lock.RUnlock()
|
||||||
return mp.checkTxConflicts(tx)
|
return mp.checkTxConflicts(tx, feer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ func (fs *FeerStub) FeePerByte() util.Fixed8 {
|
||||||
return fs.feePerByte
|
return fs.feePerByte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) util.Fixed8 {
|
||||||
|
return util.Fixed8FromInt64(10000)
|
||||||
|
}
|
||||||
|
|
||||||
func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
|
func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
|
||||||
mp := NewMemPool(10)
|
mp := NewMemPool(10)
|
||||||
tx := transaction.NewContractTX()
|
tx := transaction.NewContractTX()
|
||||||
|
@ -111,7 +115,7 @@ func TestMemPoolAddRemoveWithInputsAndClaims(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
}, &FeerStub{})
|
||||||
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
|
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
|
||||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||||
assert.Equal(t, len(claim2.Claims), len(mp.claims))
|
assert.Equal(t, len(claim2.Claims), len(mp.claims))
|
||||||
|
@ -124,24 +128,24 @@ func TestMemPoolVerifyInputs(t *testing.T) {
|
||||||
tx.Nonce = 1
|
tx.Nonce = 1
|
||||||
inhash1 := random.Uint256()
|
inhash1 := random.Uint256()
|
||||||
tx.Inputs = append(tx.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 0})
|
tx.Inputs = append(tx.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 0})
|
||||||
require.Equal(t, true, mp.Verify(tx))
|
require.Equal(t, true, mp.Verify(tx, &FeerStub{}))
|
||||||
require.NoError(t, mp.Add(tx, &FeerStub{}))
|
require.NoError(t, mp.Add(tx, &FeerStub{}))
|
||||||
|
|
||||||
tx2 := transaction.NewContractTX()
|
tx2 := transaction.NewContractTX()
|
||||||
tx2.Nonce = 2
|
tx2.Nonce = 2
|
||||||
inhash2 := random.Uint256()
|
inhash2 := random.Uint256()
|
||||||
tx2.Inputs = append(tx2.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
tx2.Inputs = append(tx2.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||||
require.Equal(t, true, mp.Verify(tx2))
|
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
|
||||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||||
|
|
||||||
tx3 := transaction.NewContractTX()
|
tx3 := transaction.NewContractTX()
|
||||||
tx3.Nonce = 3
|
tx3.Nonce = 3
|
||||||
// Different index number, but the same PrevHash as in tx1.
|
// Different index number, but the same PrevHash as in tx1.
|
||||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 1})
|
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 1})
|
||||||
require.Equal(t, true, mp.Verify(tx3))
|
require.Equal(t, true, mp.Verify(tx3, &FeerStub{}))
|
||||||
// The same input as in tx2.
|
// The same input as in tx2.
|
||||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||||
require.Equal(t, false, mp.Verify(tx3))
|
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||||
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,30 +160,30 @@ func TestMemPoolVerifyClaims(t *testing.T) {
|
||||||
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||||
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||||
}
|
}
|
||||||
require.Equal(t, true, mp.Verify(tx1))
|
require.Equal(t, true, mp.Verify(tx1, &FeerStub{}))
|
||||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||||
|
|
||||||
tx2, claim2 := newClaimTX()
|
tx2, claim2 := newClaimTX()
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i + 10)})
|
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i + 10)})
|
||||||
}
|
}
|
||||||
require.Equal(t, true, mp.Verify(tx2))
|
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
|
||||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||||
|
|
||||||
tx3, claim3 := newClaimTX()
|
tx3, claim3 := newClaimTX()
|
||||||
claim3.Claims = append(claim3.Claims, transaction.Input{PrevHash: hash1, PrevIndex: 0})
|
claim3.Claims = append(claim3.Claims, transaction.Input{PrevHash: hash1, PrevIndex: 0})
|
||||||
require.Equal(t, false, mp.Verify(tx3))
|
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||||
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemPoolVerifyIssue(t *testing.T) {
|
func TestMemPoolVerifyIssue(t *testing.T) {
|
||||||
mp := NewMemPool(50)
|
mp := NewMemPool(50)
|
||||||
tx1 := newIssueTX()
|
tx1 := newIssueTX()
|
||||||
require.Equal(t, true, mp.Verify(tx1))
|
require.Equal(t, true, mp.Verify(tx1, &FeerStub{}))
|
||||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||||
|
|
||||||
tx2 := newIssueTX()
|
tx2 := newIssueTX()
|
||||||
require.Equal(t, false, mp.Verify(tx2))
|
require.Equal(t, false, mp.Verify(tx2, &FeerStub{}))
|
||||||
require.Error(t, mp.Add(tx2, &FeerStub{}))
|
require.Error(t, mp.Add(tx2, &FeerStub{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +339,7 @@ func TestRemoveStale(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
}, &FeerStub{})
|
||||||
require.Equal(t, mempoolSize/2, mp.Count())
|
require.Equal(t, mempoolSize/2, mp.Count())
|
||||||
verTxes := mp.GetVerifiedTransactions()
|
verTxes := mp.GetVerifiedTransactions()
|
||||||
for _, txf := range verTxes {
|
for _, txf := range verTxes {
|
||||||
|
@ -343,3 +347,76 @@ func TestRemoveStale(t *testing.T) {
|
||||||
require.Contains(t, txes2, txf.Tx)
|
require.Contains(t, txes2, txf.Tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMemPoolFees(t *testing.T) {
|
||||||
|
mp := NewMemPool(10)
|
||||||
|
sender0 := util.Uint160{1, 2, 3}
|
||||||
|
tx0 := transaction.NewContractTX()
|
||||||
|
tx0.NetworkFee = util.Fixed8FromInt64(11000)
|
||||||
|
tx0.Sender = sender0
|
||||||
|
// insufficient funds to add transaction, but balance should be stored
|
||||||
|
require.Equal(t, false, mp.Verify(tx0, &FeerStub{}))
|
||||||
|
require.Error(t, mp.Add(tx0, &FeerStub{}))
|
||||||
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
|
balance: util.Fixed8FromInt64(10000),
|
||||||
|
feeSum: 0,
|
||||||
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
// no problems with adding another transaction with lower fee
|
||||||
|
tx1 := transaction.NewContractTX()
|
||||||
|
tx1.NetworkFee = util.Fixed8FromInt64(7000)
|
||||||
|
tx1.Sender = sender0
|
||||||
|
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||||
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
|
balance: util.Fixed8FromInt64(10000),
|
||||||
|
feeSum: util.Fixed8FromInt64(7000),
|
||||||
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
// balance shouldn't change after adding one more transaction
|
||||||
|
tx2 := transaction.NewContractTX()
|
||||||
|
tx2.NetworkFee = util.Fixed8FromFloat(3000)
|
||||||
|
tx2.Sender = sender0
|
||||||
|
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||||
|
require.Equal(t, 2, len(mp.verifiedTxes))
|
||||||
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
|
balance: util.Fixed8FromInt64(10000),
|
||||||
|
feeSum: util.Fixed8FromInt64(10000),
|
||||||
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
// can't add more transactions as we don't have enough GAS
|
||||||
|
tx3 := transaction.NewContractTX()
|
||||||
|
tx3.NetworkFee = util.Fixed8FromFloat(0.5)
|
||||||
|
tx3.Sender = sender0
|
||||||
|
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||||
|
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||||
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
|
balance: util.Fixed8FromInt64(10000),
|
||||||
|
feeSum: util.Fixed8FromInt64(10000),
|
||||||
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
// check whether sender's fee updates correctly
|
||||||
|
mp.RemoveStale(func(t *transaction.Transaction) bool {
|
||||||
|
if t == tx2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, &FeerStub{})
|
||||||
|
require.Equal(t, 1, len(mp.fees))
|
||||||
|
require.Equal(t, utilityBalanceAndFees{
|
||||||
|
balance: util.Fixed8FromInt64(10000),
|
||||||
|
feeSum: util.Fixed8FromFloat(3000),
|
||||||
|
}, mp.fees[sender0])
|
||||||
|
|
||||||
|
// there should be nothing left
|
||||||
|
mp.RemoveStale(func(t *transaction.Transaction) bool {
|
||||||
|
if t == tx3 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, &FeerStub{})
|
||||||
|
require.Equal(t, 0, len(mp.fees))
|
||||||
|
}
|
||||||
|
|
|
@ -137,6 +137,10 @@ func (chain testChain) IsLowPriority(util.Fixed8) bool {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (chain testChain) GetUtilityTokenBalance(uint160 util.Uint160) util.Fixed8 {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
|
|
||||||
func (chain testChain) PoolTx(*transaction.Transaction) error {
|
func (chain testChain) PoolTx(*transaction.Transaction) error {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,3 +71,7 @@ func (fs *FeerStub) IsLowPriority(util.Fixed8) bool {
|
||||||
func (fs *FeerStub) FeePerByte() util.Fixed8 {
|
func (fs *FeerStub) FeePerByte() util.Fixed8 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *FeerStub) GetUtilityTokenBalance(acc util.Uint160) util.Fixed8 {
|
||||||
|
return util.Fixed8FromInt64(1000000)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue