mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-29 19:27:36 +00:00
5df726db68
Time is not really relevant for us here and we don't use this timestamp in any way. Yet it occupies 24 bytes and we do two clock_gettime calls to get it. Replace it with blockStamp which is going to be used in the future for transaction retransmissions. It allows to improve single-node TPS by another 3%.
299 lines
9.7 KiB
Go
299 lines
9.7 KiB
Go
package mempool
|
|
|
|
import (
|
|
"math/big"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type FeerStub struct {
|
|
feePerByte int64
|
|
}
|
|
|
|
var balance = big.NewInt(10000000)
|
|
|
|
func (fs *FeerStub) FeePerByte() int64 {
|
|
return fs.feePerByte
|
|
}
|
|
|
|
func (fs *FeerStub) BlockHeight() uint32 {
|
|
return 0
|
|
}
|
|
|
|
func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
|
|
return balance
|
|
}
|
|
|
|
func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
|
|
mp := New(10)
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = 0
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
_, ok := mp.TryGetValue(tx.Hash())
|
|
require.Equal(t, false, ok)
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
// Re-adding should fail.
|
|
require.Error(t, mp.Add(tx, fs))
|
|
tx2, ok := mp.TryGetValue(tx.Hash())
|
|
require.Equal(t, true, ok)
|
|
require.Equal(t, tx, tx2)
|
|
mp.Remove(tx.Hash())
|
|
_, ok = mp.TryGetValue(tx.Hash())
|
|
require.Equal(t, false, ok)
|
|
// Make sure nothing left in the mempool after removal.
|
|
assert.Equal(t, 0, len(mp.verifiedMap))
|
|
assert.Equal(t, 0, len(mp.verifiedTxes))
|
|
}
|
|
|
|
func TestMemPoolAddRemove(t *testing.T) {
|
|
var fs = &FeerStub{}
|
|
testMemPoolAddRemoveWithFeer(t, fs)
|
|
}
|
|
|
|
func TestOverCapacity(t *testing.T) {
|
|
var fs = &FeerStub{}
|
|
const mempoolSize = 10
|
|
mp := New(mempoolSize)
|
|
|
|
for i := 0; i < mempoolSize; i++ {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = uint32(i)
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
}
|
|
txcnt := uint32(mempoolSize)
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
|
|
bigScript := make([]byte, 64)
|
|
bigScript[0] = byte(opcode.PUSH1)
|
|
bigScript[1] = byte(opcode.RET)
|
|
// Fees are also prioritized.
|
|
for i := 0; i < mempoolSize; i++ {
|
|
tx := transaction.New(netmode.UnitTestNet, bigScript, 0)
|
|
tx.NetworkFee = 10000
|
|
tx.Nonce = txcnt
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
txcnt++
|
|
// size is ~90, networkFee is 10000 => feePerByte is 119
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
}
|
|
// Less prioritized txes are not allowed anymore.
|
|
tx := transaction.New(netmode.UnitTestNet, bigScript, 0)
|
|
tx.NetworkFee = 100
|
|
tx.Nonce = txcnt
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
txcnt++
|
|
require.Error(t, mp.Add(tx, fs))
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
|
|
// Low net fee, but higher per-byte fee is still a better combination.
|
|
tx = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = txcnt
|
|
tx.NetworkFee = 7000
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
txcnt++
|
|
// size is ~51 (small script), networkFee is 7000 (<10000)
|
|
// => feePerByte is 137 (>119)
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
|
|
// High priority always wins over low priority.
|
|
for i := 0; i < mempoolSize; i++ {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.NetworkFee = 8000
|
|
tx.Nonce = txcnt
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
txcnt++
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
}
|
|
// Good luck with low priority now.
|
|
tx = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = txcnt
|
|
tx.NetworkFee = 7000
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
require.Error(t, mp.Add(tx, fs))
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
|
}
|
|
|
|
func TestGetVerified(t *testing.T) {
|
|
var fs = &FeerStub{}
|
|
const mempoolSize = 10
|
|
mp := New(mempoolSize)
|
|
|
|
txes := make([]*transaction.Transaction, 0, mempoolSize)
|
|
for i := 0; i < mempoolSize; i++ {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = uint32(i)
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
txes = append(txes, tx)
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
}
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
verTxes := mp.GetVerifiedTransactions()
|
|
require.Equal(t, mempoolSize, len(verTxes))
|
|
require.ElementsMatch(t, txes, verTxes)
|
|
for _, tx := range txes {
|
|
mp.Remove(tx.Hash())
|
|
}
|
|
verTxes = mp.GetVerifiedTransactions()
|
|
require.Equal(t, 0, len(verTxes))
|
|
}
|
|
|
|
func TestRemoveStale(t *testing.T) {
|
|
var fs = &FeerStub{}
|
|
const mempoolSize = 10
|
|
mp := New(mempoolSize)
|
|
|
|
txes1 := make([]*transaction.Transaction, 0, mempoolSize/2)
|
|
txes2 := make([]*transaction.Transaction, 0, mempoolSize/2)
|
|
for i := 0; i < mempoolSize; i++ {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = uint32(i)
|
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
|
if i%2 == 0 {
|
|
txes1 = append(txes1, tx)
|
|
} else {
|
|
txes2 = append(txes2, tx)
|
|
}
|
|
require.NoError(t, mp.Add(tx, fs))
|
|
}
|
|
require.Equal(t, mempoolSize, mp.Count())
|
|
mp.RemoveStale(func(t *transaction.Transaction) bool {
|
|
for _, tx := range txes2 {
|
|
if tx == t {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, &FeerStub{})
|
|
require.Equal(t, mempoolSize/2, mp.Count())
|
|
verTxes := mp.GetVerifiedTransactions()
|
|
for _, txf := range verTxes {
|
|
require.NotContains(t, txes1, txf)
|
|
require.Contains(t, txes2, txf)
|
|
}
|
|
}
|
|
|
|
func TestMemPoolFees(t *testing.T) {
|
|
mp := New(10)
|
|
sender0 := util.Uint160{1, 2, 3}
|
|
tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx0.NetworkFee = balance.Int64() + 1
|
|
tx0.Signers = []transaction.Signer{{Account: sender0}}
|
|
// insufficient funds to add transaction, and balance shouldn't be stored
|
|
require.Equal(t, false, mp.Verify(tx0, &FeerStub{}))
|
|
require.Error(t, mp.Add(tx0, &FeerStub{}))
|
|
require.Equal(t, 0, len(mp.fees))
|
|
|
|
balancePart := new(big.Int).Div(balance, big.NewInt(4))
|
|
// no problems with adding another transaction with lower fee
|
|
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx1.NetworkFee = balancePart.Int64()
|
|
tx1.Signers = []transaction.Signer{{Account: sender0}}
|
|
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
|
require.Equal(t, 1, len(mp.fees))
|
|
require.Equal(t, utilityBalanceAndFees{
|
|
balance: balance,
|
|
feeSum: big.NewInt(tx1.NetworkFee),
|
|
}, mp.fees[sender0])
|
|
|
|
// balance shouldn't change after adding one more transaction
|
|
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx2.NetworkFee = new(big.Int).Sub(balance, balancePart).Int64()
|
|
tx2.Signers = []transaction.Signer{{Account: 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: balance,
|
|
feeSum: balance,
|
|
}, mp.fees[sender0])
|
|
|
|
// can't add more transactions as we don't have enough GAS
|
|
tx3 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx3.NetworkFee = 1
|
|
tx3.Signers = []transaction.Signer{{Account: 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: balance,
|
|
feeSum: balance,
|
|
}, 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: balance,
|
|
feeSum: big.NewInt(tx2.NetworkFee),
|
|
}, 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))
|
|
}
|
|
|
|
func TestMempoolItemsOrder(t *testing.T) {
|
|
sender0 := util.Uint160{1, 2, 3}
|
|
|
|
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx1.NetworkFee = new(big.Int).Div(balance, big.NewInt(8)).Int64()
|
|
tx1.Signers = []transaction.Signer{{Account: sender0}}
|
|
tx1.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}
|
|
item1 := item{txn: tx1}
|
|
|
|
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx2.NetworkFee = new(big.Int).Div(balance, big.NewInt(16)).Int64()
|
|
tx2.Signers = []transaction.Signer{{Account: sender0}}
|
|
tx2.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}
|
|
item2 := item{txn: tx2}
|
|
|
|
tx3 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx3.NetworkFee = new(big.Int).Div(balance, big.NewInt(2)).Int64()
|
|
tx3.Signers = []transaction.Signer{{Account: sender0}}
|
|
item3 := item{txn: tx3}
|
|
|
|
tx4 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
tx4.NetworkFee = new(big.Int).Div(balance, big.NewInt(4)).Int64()
|
|
tx4.Signers = []transaction.Signer{{Account: sender0}}
|
|
item4 := item{txn: tx4}
|
|
|
|
require.True(t, item1.CompareTo(item2) > 0)
|
|
require.True(t, item2.CompareTo(item1) < 0)
|
|
require.True(t, item1.CompareTo(item3) > 0)
|
|
require.True(t, item3.CompareTo(item1) < 0)
|
|
require.True(t, item1.CompareTo(item4) > 0)
|
|
require.True(t, item4.CompareTo(item1) < 0)
|
|
require.True(t, item2.CompareTo(item3) > 0)
|
|
require.True(t, item3.CompareTo(item2) < 0)
|
|
require.True(t, item2.CompareTo(item4) > 0)
|
|
require.True(t, item4.CompareTo(item2) < 0)
|
|
require.True(t, item3.CompareTo(item4) > 0)
|
|
require.True(t, item4.CompareTo(item3) < 0)
|
|
}
|