370 lines
14 KiB
Go
370 lines
14 KiB
Go
package mempool
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/meshplus/bitxhub-kit/types"
|
|
"github.com/meshplus/bitxhub-model/pb"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestGetBlock(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
privKey1 := genPrivKey()
|
|
privKey2 := genPrivKey()
|
|
tx1 := constructTx(uint64(1), &privKey1)
|
|
tx2 := constructTx(uint64(2), &privKey1)
|
|
tx3 := constructTx(uint64(2), &privKey2)
|
|
tx4 := constructTx(uint64(4), &privKey2)
|
|
var txList []*pb.Transaction
|
|
txList = append(txList, tx1, tx2, tx3, tx4)
|
|
// mock follower
|
|
batch := mpi.ProcessTransactions(txList, false, true)
|
|
ast.Nil(batch)
|
|
// mock leader
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.Nil(batch)
|
|
|
|
// mock leader to getBlock
|
|
txList = make([]*pb.Transaction, 0)
|
|
tx5 := constructTx(uint64(1), &privKey2)
|
|
txList = append(txList, tx5)
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.Equal(4, len(batch.TxList))
|
|
}
|
|
|
|
func TestGetPendingNonceByAccount(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
nonce := mpi.GetPendingNonceByAccount(account1.String())
|
|
ast.Equal(uint64(1), nonce)
|
|
|
|
privKey2 := genPrivKey()
|
|
account2, _ := privKey2.PublicKey().Address()
|
|
tx1 := constructTx(uint64(1), &privKey1)
|
|
tx2 := constructTx(uint64(2), &privKey1)
|
|
tx3 := constructTx(uint64(1), &privKey2)
|
|
tx4 := constructTx(uint64(2), &privKey2)
|
|
tx5 := constructTx(uint64(4), &privKey2)
|
|
var txList []*pb.Transaction
|
|
txList = append(txList, tx1, tx2, tx3, tx4, tx5)
|
|
batch := mpi.ProcessTransactions(txList, false, true)
|
|
ast.Nil(batch)
|
|
nonce = mpi.GetPendingNonceByAccount(account1.String())
|
|
ast.Equal(uint64(3), nonce)
|
|
nonce = mpi.GetPendingNonceByAccount(account2.String())
|
|
ast.Equal(uint64(3), nonce, "not 4")
|
|
}
|
|
|
|
func TestCommitTransactions(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, batchC := mockMempoolImpl()
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
nonce := mpi.GetPendingNonceByAccount(account1.String())
|
|
ast.Equal(uint64(1), nonce)
|
|
privKey2 := genPrivKey()
|
|
tx1 := constructTx(uint64(1), &privKey1)
|
|
tx2 := constructTx(uint64(2), &privKey1)
|
|
tx3 := constructTx(uint64(1), &privKey2)
|
|
tx4 := constructTx(uint64(4), &privKey2)
|
|
var txList []*pb.Transaction
|
|
txList = append(txList, tx1, tx2, tx3, tx4)
|
|
batch := mpi.ProcessTransactions(txList, true, true)
|
|
ast.Nil(batch)
|
|
ast.Equal(3, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
|
|
|
go func() {
|
|
<-batchC
|
|
}()
|
|
tx5 := constructTx(uint64(2), &privKey2)
|
|
txList = []*pb.Transaction{}
|
|
txList = append(txList, tx5)
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.Equal(4, len(batch.TxList))
|
|
ast.Equal(4, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(uint64(2), mpi.batchSeqNo)
|
|
|
|
var txHashList []*types.Hash
|
|
txHashList = append(txHashList, tx1.TransactionHash, tx2.TransactionHash, tx3.TransactionHash, tx5.TransactionHash)
|
|
state := &ChainState{
|
|
TxHashList: txHashList,
|
|
Height: uint64(2),
|
|
}
|
|
mpi.CommitTransactions(state)
|
|
time.Sleep(100 * time.Millisecond)
|
|
ast.Equal(0, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
|
}
|
|
|
|
func TestIncreaseChainHeight(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
ast.Equal(uint64(1), mpi.batchSeqNo)
|
|
mpi.batchSeqNo++
|
|
mpi.SetBatchSeqNo(mpi.batchSeqNo)
|
|
ast.Equal(uint64(2), mpi.batchSeqNo)
|
|
}
|
|
|
|
func TestProcessTransactions(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
txList := make([]*pb.Transaction, 0)
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
privKey2 := genPrivKey()
|
|
account2, _ := privKey2.PublicKey().Address()
|
|
tx1 := constructTx(uint64(1), &privKey1)
|
|
tx2 := constructTx(uint64(2), &privKey1)
|
|
tx3 := constructTx(uint64(1), &privKey2)
|
|
tx4 := constructTx(uint64(2), &privKey2)
|
|
tx5 := constructTx(uint64(4), &privKey2)
|
|
txList = append(txList, tx1, tx2, tx3, tx4, tx5)
|
|
batch := mpi.ProcessTransactions(txList, false, true)
|
|
ast.Nil(batch)
|
|
ast.Equal(4, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(5, len(mpi.txStore.txHashMap))
|
|
ast.Equal(2, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(3, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
mpi.batchSize = 4
|
|
tx6 := constructTx(uint64(3), &privKey1)
|
|
tx7 := constructTx(uint64(5), &privKey2)
|
|
txList = make([]*pb.Transaction, 0)
|
|
txList = append(txList, tx6, tx7)
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.Equal(4, len(batch.TxList))
|
|
ast.Equal(uint64(2), batch.Height)
|
|
ast.Equal(uint64(1), mpi.txStore.priorityNonBatchSize)
|
|
ast.Equal(5, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(2, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(7, len(mpi.txStore.txHashMap))
|
|
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(4), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
}
|
|
|
|
func TestForward(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
txList := make([]*pb.Transaction, 0)
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
tx1 := constructTx(uint64(1), &privKey1)
|
|
tx2 := constructTx(uint64(2), &privKey1)
|
|
tx3 := constructTx(uint64(3), &privKey1)
|
|
tx4 := constructTx(uint64(4), &privKey1)
|
|
tx5 := constructTx(uint64(6), &privKey1)
|
|
txList = append(txList, tx1, tx2, tx3, tx4, tx5)
|
|
batch := mpi.ProcessTransactions(txList, false, true)
|
|
ast.Nil(batch)
|
|
list := mpi.txStore.allTxs[account1.String()]
|
|
ast.Equal(5, list.index.size())
|
|
ast.Equal(4, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
|
|
|
removeList := list.forward(uint64(3))
|
|
ast.Equal(1, len(removeList))
|
|
ast.Equal(2, len(removeList[account1.String()]))
|
|
ast.Equal(uint64(1), removeList[account1.String()][0].Nonce)
|
|
ast.Equal(uint64(2), removeList[account1.String()][1].Nonce)
|
|
}
|
|
|
|
func TestUnorderedIncomingTxs(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
mpi.batchSize = 5
|
|
|
|
txList := make([]*pb.Transaction, 0)
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
privKey2 := genPrivKey()
|
|
account2, _ := privKey2.PublicKey().Address()
|
|
nonceArr := []uint64{4, 2, 1}
|
|
for _, i := range nonceArr {
|
|
tx1 := constructTx(i, &privKey1)
|
|
tx2 := constructTx(i, &privKey2)
|
|
txList = append(txList, tx1, tx2)
|
|
}
|
|
batch := mpi.ProcessTransactions(txList, true, true)
|
|
ast.Nil(batch) // not enough for 5 txs to generate batch
|
|
ast.Equal(4, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(2, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(6, len(mpi.txStore.txHashMap))
|
|
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(3, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
tx7 := constructTx(uint64(3), &privKey1)
|
|
tx8 := constructTx(uint64(5), &privKey2)
|
|
txList = make([]*pb.Transaction, 0)
|
|
txList = append(txList, tx7, tx8)
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.NotNil(batch)
|
|
|
|
var hashes []types.Hash
|
|
// this batch will contain tx{1,2,3} for privKey1 and tx{1,2} for privKey2
|
|
ast.Equal(5, len(batch.TxList))
|
|
ast.Equal(uint64(2), batch.Height)
|
|
ast.Equal(uint64(1), mpi.txStore.priorityNonBatchSize)
|
|
ast.Equal(6, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(3, mpi.txStore.parkingLotIndex.size(), "delete parkingLot until finishing executor")
|
|
ast.Equal(8, len(mpi.txStore.txHashMap))
|
|
ast.Equal(4, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(5), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
// process committed txs
|
|
hashList := make([]*types.Hash, 0, len(hashes))
|
|
for _, tx := range batch.TxList {
|
|
hashList = append(hashList, tx.Hash())
|
|
}
|
|
ready := &ChainState{
|
|
TxHashList: hashList,
|
|
Height: 2,
|
|
}
|
|
mpi.processCommitTransactions(ready)
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
ast.Equal(uint64(1), mpi.txStore.priorityNonBatchSize)
|
|
ast.Equal(1, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(3, mpi.txStore.parkingLotIndex.size(), "delete parkingLot until finishing executor")
|
|
ast.Equal(3, len(mpi.txStore.txHashMap))
|
|
ast.Equal(1, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(2, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(5), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
// generate block3
|
|
txList = make([]*pb.Transaction, 0)
|
|
account1NonceArr2 := []uint64{5, 6}
|
|
for _, i := range account1NonceArr2 {
|
|
tx1 := constructTx(i, &privKey1)
|
|
txList = append(txList, tx1)
|
|
}
|
|
account2NonceArr2 := []uint64{3, 6}
|
|
for _, i := range account2NonceArr2 {
|
|
tx1 := constructTx(i, &privKey2)
|
|
txList = append(txList, tx1)
|
|
}
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.NotNil(batch)
|
|
ast.Equal(uint64(2), mpi.txStore.priorityNonBatchSize)
|
|
ast.Equal(7, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(3, mpi.txStore.parkingLotIndex.size(), "delete parkingLot until finishing executor")
|
|
ast.Equal(7, len(mpi.txStore.txHashMap))
|
|
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(4), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
|
|
ast.Equal(uint64(7), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
|
|
ast.Equal(uint64(7), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
}
|
|
|
|
func TestGetTimeoutTransaction(t *testing.T) {
|
|
ast := assert.New(t)
|
|
mpi, _ := mockMempoolImpl()
|
|
mpi.txSliceSize = 3
|
|
allTxHashes := make([]*types.Hash, 0)
|
|
|
|
txList := make([]*pb.Transaction, 0)
|
|
privKey1 := genPrivKey()
|
|
account1, _ := privKey1.PublicKey().Address()
|
|
privKey2 := genPrivKey()
|
|
account2, _ := privKey2.PublicKey().Address()
|
|
nonceArr := []uint64{4, 5}
|
|
for _, i := range nonceArr {
|
|
tx1 := constructTx(i, &privKey1)
|
|
tx2 := constructTx(i, &privKey2)
|
|
txList = append(txList, tx1, tx2)
|
|
allTxHashes = append(allTxHashes, tx1.Hash(), tx2.Hash())
|
|
}
|
|
batch := mpi.ProcessTransactions(txList, true, true)
|
|
ast.Nil(batch)
|
|
|
|
// set another incoming list for timeout
|
|
time.Sleep(150 * time.Millisecond)
|
|
nonceArr = []uint64{1, 2}
|
|
for _, i := range nonceArr {
|
|
tx1 := constructTx(i, &privKey1)
|
|
tx2 := constructTx(i, &privKey2)
|
|
txList = append(txList, tx1, tx2)
|
|
allTxHashes = append(allTxHashes, tx1.Hash(), tx2.Hash())
|
|
}
|
|
batch = mpi.ProcessTransactions(txList, true, false)
|
|
ast.NotNil(batch)
|
|
// tx1,tx2 for account1 and account2 will be batched.
|
|
// all txs for account1 and account2 will be in priorityIndex.
|
|
// And tx4,tx5 for account1 and account2 will be timeout while tx3 will not
|
|
ast.Equal(4, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(4, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(8, len(mpi.txStore.txHashMap))
|
|
ast.Equal(4, mpi.txStore.allTxs[account1.String()].index.size())
|
|
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
|
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
|
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
|
|
|
tx1 := constructTx(3, &privKey1)
|
|
tx2 := constructTx(3, &privKey2)
|
|
txList = append(txList, tx1, tx2)
|
|
allTxHashes = append(allTxHashes, tx1.Hash(), tx2.Hash())
|
|
batch = mpi.ProcessTransactions(txList, true, true)
|
|
ast.NotNil(batch)
|
|
|
|
// tx4,tx5 should be timeout
|
|
timeoutList := mpi.GetTimeoutTransactions(100 * time.Millisecond)
|
|
ast.NotNil(timeoutList)
|
|
ast.Equal(2, len(timeoutList))
|
|
ast.Equal(3, len(timeoutList[0]))
|
|
ast.Equal(1, len(timeoutList[1]))
|
|
ast.Equal(6, mpi.txStore.ttlIndex.index.Len())
|
|
|
|
// wait another 150 millisecond, tx3 be timeout too.
|
|
// though tx1,tx2 has wait a long time, they are not local and they won't be broadcast
|
|
time.Sleep(150 * time.Millisecond)
|
|
timeoutList = mpi.GetTimeoutTransactions(100 * time.Millisecond)
|
|
ast.NotNil(timeoutList)
|
|
ast.Equal(2, len(timeoutList))
|
|
ast.Equal(3, len(timeoutList[0]))
|
|
ast.Equal(3, len(timeoutList[1]))
|
|
ast.Equal(6, mpi.txStore.ttlIndex.index.Len())
|
|
|
|
// check if all indices are normally cleaned after commit
|
|
ast.Equal(10, len(allTxHashes))
|
|
state := &ChainState{
|
|
TxHashList: allTxHashes,
|
|
Height: uint64(2),
|
|
}
|
|
mpi.CommitTransactions(state)
|
|
time.Sleep(100 * time.Millisecond)
|
|
ast.Equal(0, mpi.txStore.ttlIndex.index.Len())
|
|
ast.Equal(0, len(mpi.txStore.ttlIndex.items))
|
|
ast.Equal(int64(math.MaxInt64), mpi.txStore.earliestTimestamp)
|
|
ast.Equal(0, mpi.txStore.priorityIndex.size())
|
|
ast.Equal(0, mpi.txStore.parkingLotIndex.size())
|
|
ast.Equal(0, len(mpi.txStore.batchedTxs))
|
|
ast.Equal(0, len(mpi.txStore.txHashMap))
|
|
ast.Equal(uint64(0), mpi.txStore.priorityNonBatchSize)
|
|
}
|