Skip to content

Commit

Permalink
Add Unit Test for MemoryPool sort order. Fixed sort order to return d…
Browse files Browse the repository at this point in the history
…escending. (neo-project#559)

* Add unit test to verify memory pool sort order and reverification order. Fixed sort order bug.

* VerifyCanTransactionFitInPool works as intended. Also inadvertantly verified GetLowestFeeTransaction() works.
  • Loading branch information
jsolman authored and rodoufu committed Mar 3, 2019
1 parent 14b9cdb commit 66bd45b
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 19 deletions.
123 changes: 110 additions & 13 deletions neo.UnitTests/UT_MemoryPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,28 +113,34 @@ long LongRandom(long min, long max, Random rand)
return longRand % (max - min) + min;
}

private Transaction CreateMockHighPriorityTransaction()
private Transaction CreateMockTransactionWithFee(long fee)
{
var mockTx = CreateRandomHashInvocationMockTransaction();
long rNetFee = LongRandom(100000, 100000000, _random); // [0.001,1]) GAS (enough to be a high priority TX)
mockTx.SetupGet(p => p.NetworkFee).Returns(new Fixed8(rNetFee));
mockTx.SetupGet(p => p.NetworkFee).Returns(new Fixed8(fee));
var tx = mockTx.Object;
tx.Inputs = new CoinReference[1];
// Any input will trigger reading the transaction output and get our mocked transaction output.
tx.Inputs[0] = new CoinReference
if (fee > 0)
{
PrevHash = UInt256.Zero,
PrevIndex = 0
};
tx.Inputs = new CoinReference[1];
// Any input will trigger reading the transaction output and get our mocked transaction output.
tx.Inputs[0] = new CoinReference
{
PrevHash = UInt256.Zero,
PrevIndex = 0
};
}
return tx;
}

private Transaction CreateMockHighPriorityTransaction()
{
return CreateMockTransactionWithFee(LongRandom(100000, 100000000, _random));
}

private Transaction CreateMockLowPriorityTransaction()
{
var mockTx = CreateRandomHashInvocationMockTransaction();
long rNetFee = LongRandom(0, 100000, _random); // [0,0.001] GAS a fee lower than the threshold of 0.001 GAS (not enough to be a high priority TX)
mockTx.SetupGet(p => p.NetworkFee).Returns(new Fixed8(rNetFee));
return mockTx.Object;
long rNetFee = LongRandom(0, 100000, _random);
// [0,0.001] GAS a fee lower than the threshold of 0.001 GAS (not enough to be a high priority TX)
return CreateMockTransactionWithFee(rNetFee);
}

private void AddTransactions(int count, bool isHighPriority=false)
Expand Down Expand Up @@ -280,6 +286,97 @@ public void BlockPersistMovesTxToUnverifiedAndReverification()
_unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0);
}

private void verifyTransactionsSortedDescending(IEnumerable<Transaction> transactions)
{
Transaction lastTransaction = null;
foreach (var tx in transactions)
{
if (lastTransaction != null)
{
if (lastTransaction.FeePerByte == tx.FeePerByte)
{
if (lastTransaction.NetworkFee == tx.NetworkFee)
lastTransaction.Hash.Should().BeGreaterThan(tx.Hash);
else
lastTransaction.NetworkFee.Should().BeGreaterThan(tx.NetworkFee);
}
else
{
lastTransaction.FeePerByte.Should().BeGreaterThan(tx.FeePerByte);
}
}
lastTransaction = tx;
}
}

[TestMethod]
public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst()
{
AddLowPriorityTransactions(50);
AddHighPriorityTransactions(50);

var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList();
// verify all 100 transactions are returned in sorted order
sortedVerifiedTxs.Count.ShouldBeEquivalentTo(100);
verifyTransactionsSortedDescending(sortedVerifiedTxs);

// move all to unverified
var block = new Block { Transactions = new Transaction[0] };
_unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot());

_unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0);
_unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0);
_unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(50);
_unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(50);

// We can verify the order they are re-verified by reverifying 2 at a time
while (_unit.UnVerifiedCount > 0)
{
_unit.GetVerifiedAndUnverifiedTransactions(out IEnumerable<Transaction> sortedVerifiedTransactions,
out IEnumerable<Transaction> sortedUnverifiedTransactions);
sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0);
var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray();
verifyTransactionsSortedDescending(sortedUnverifiedArray);
var maxHighPriorityTransaction = sortedUnverifiedArray.First();
var maxLowPriorityTransaction = sortedUnverifiedArray.First(tx => tx.IsLowPriority);

// reverify 1 high priority and 1 low priority transaction
_unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot());
var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray();
verifiedTxs.Length.ShouldBeEquivalentTo(2);
verifiedTxs[0].ShouldBeEquivalentTo(maxHighPriorityTransaction);
verifiedTxs[1].ShouldBeEquivalentTo(maxLowPriorityTransaction);
var blockWith2Tx = new Block { Transactions = new Transaction[2] { maxHighPriorityTransaction, maxLowPriorityTransaction }};
// verify and remove the 2 transactions from the verified pool
_unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot());
_unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0);
_unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0);
}
_unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0);
_unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0);
}

void VerifyCapacityThresholdForAttemptingToAddATransaction()
{
var sortedVerified = _unit.GetSortedVerifiedTransactions().ToArray();

var txBarelyWontFit = CreateMockTransactionWithFee(sortedVerified.Last().NetworkFee.GetData() - 1);
_unit.CanTransactionFitInPool(txBarelyWontFit).ShouldBeEquivalentTo(false);
var txBarelyFits = CreateMockTransactionWithFee(sortedVerified.Last().NetworkFee.GetData() + 1);
_unit.CanTransactionFitInPool(txBarelyFits).ShouldBeEquivalentTo(true);
}

[TestMethod]
public void VerifyCanTransactionFitInPoolWorksAsIntended()
{
AddLowPriorityTransactions(100);
VerifyCapacityThresholdForAttemptingToAddATransaction();
AddHighPriorityTransactions(50);
VerifyCapacityThresholdForAttemptingToAddATransaction();
AddHighPriorityTransactions(50);
VerifyCapacityThresholdForAttemptingToAddATransaction();
}

[TestMethod]
public void CapacityTestWithUnverifiedHighProirtyTransactions()
{
Expand Down
13 changes: 7 additions & 6 deletions neo/Ledger/MemoryPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ public IEnumerable<Transaction> GetVerifiedTransactions()
_txRwLock.EnterReadLock();
try
{
verifiedTransactions = _sortedHighPrioTransactions.Select(p => p.Transaction)
.Concat(_sortedLowPrioTransactions.Select(p => p.Transaction)).ToArray();
unverifiedTransactions = _unverifiedTransactions.Select(p => p.Value.Transaction).ToArray();
verifiedTransactions = _sortedHighPrioTransactions.Reverse().Select(p => p.Transaction)
.Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Transaction)).ToArray();
unverifiedTransactions = _unverifiedSortedHighPriorityTransactions.Reverse().Select(p => p.Transaction)
.Concat(_unverifiedSortedLowPriorityTransactions.Reverse().Select(p => p.Transaction)).ToArray();
}
finally
{
Expand All @@ -241,8 +242,8 @@ public IEnumerable<Transaction> GetSortedVerifiedTransactions()
_txRwLock.EnterReadLock();
try
{
return _sortedHighPrioTransactions.Select(p => p.Transaction)
.Concat(_sortedLowPrioTransactions.Select(p => p.Transaction))
return _sortedHighPrioTransactions.Reverse().Select(p => p.Transaction)
.Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Transaction))
.ToArray();
}
finally
Expand Down Expand Up @@ -429,7 +430,7 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot)
DateTime reverifyCutOffTimeStamp = DateTime.UtcNow.AddSeconds(secondsTimeout);
List<PoolItem> reverifiedItems = new List<PoolItem>(count);
List<PoolItem> invalidItems = new List<PoolItem>();

// Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end.
foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count))
{
Expand Down

0 comments on commit 66bd45b

Please sign in to comment.