zcashd/src/gtest/test_mempoollimit.cpp

167 lines
6.9 KiB
C++

// Copyright (c) 2019-2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include <gtest/gtest.h>
#include <iostream>
#include "arith_uint256.h"
#include "mempool_limit.h"
#include "gtest/utils.h"
#include "util/time.h"
#include "util/test.h"
#include "transaction_builder.h"
const uint256 TX_ID1 = ArithToUint256(1);
const uint256 TX_ID2 = ArithToUint256(2);
const uint256 TX_ID3 = ArithToUint256(3);
TEST(MempoolLimitTests, RecentlyEvictedListAddWrapsAfterMaxSize)
{
FixedClock clock(std::chrono::seconds(1));
RecentlyEvictedList recentlyEvicted(&clock, 2, 100);
recentlyEvicted.add(TX_ID1);
recentlyEvicted.add(TX_ID2);
recentlyEvicted.add(TX_ID3);
// tx 1 should be overwritten by tx 3 due to maxSize 2
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
}
TEST(MempoolLimitTests, RecentlyEvictedListDoesNotContainAfterExpiry)
{
FixedClock clock(std::chrono::seconds(1));
// maxSize=3, timeToKeep=1
RecentlyEvictedList recentlyEvicted(&clock, 3, 1);
recentlyEvicted.add(TX_ID1);
clock.Set(std::chrono::seconds(2));
recentlyEvicted.add(TX_ID2);
recentlyEvicted.add(TX_ID3);
// After 1 second the txId will still be there
EXPECT_TRUE(recentlyEvicted.contains(TX_ID1));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
clock.Set(std::chrono::seconds(3));
// After 2 seconds it is gone
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
clock.Set(std::chrono::seconds(4));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID2));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID3));
}
TEST(MempoolLimitTests, RecentlyEvictedDropOneAtATime)
{
FixedClock clock(std::chrono::seconds(1));
RecentlyEvictedList recentlyEvicted(&clock, 3, 2);
recentlyEvicted.add(TX_ID1);
clock.Set(std::chrono::seconds(2));
recentlyEvicted.add(TX_ID2);
clock.Set(std::chrono::seconds(3));
recentlyEvicted.add(TX_ID3);
EXPECT_TRUE(recentlyEvicted.contains(TX_ID1));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
clock.Set(std::chrono::seconds(4));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
clock.Set(std::chrono::seconds(5));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID2));
EXPECT_TRUE(recentlyEvicted.contains(TX_ID3));
clock.Set(std::chrono::seconds(6));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID1));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID2));
EXPECT_FALSE(recentlyEvicted.contains(TX_ID3));
}
TEST(MempoolLimitTests, WeightedTxTreeCheckSizeAfterDropping)
{
std::set<uint256> testedDropping;
// Run the test until we have tested dropping each of the elements
int trialNum = 0;
while (testedDropping.size() < 3) {
WeightedTxTree tree(MIN_TX_COST * 2);
EXPECT_EQ(0, tree.getTotalWeight().cost);
EXPECT_EQ(0, tree.getTotalWeight().evictionWeight);
tree.add(WeightedTxInfo(TX_ID1, TxWeight(MIN_TX_COST, MIN_TX_COST)));
EXPECT_EQ(4000, tree.getTotalWeight().cost);
EXPECT_EQ(4000, tree.getTotalWeight().evictionWeight);
tree.add(WeightedTxInfo(TX_ID2, TxWeight(MIN_TX_COST, MIN_TX_COST)));
EXPECT_EQ(8000, tree.getTotalWeight().cost);
EXPECT_EQ(8000, tree.getTotalWeight().evictionWeight);
EXPECT_FALSE(tree.maybeDropRandom().has_value());
tree.add(WeightedTxInfo(TX_ID3, TxWeight(MIN_TX_COST, MIN_TX_COST + LOW_FEE_PENALTY)));
EXPECT_EQ(12000, tree.getTotalWeight().cost);
EXPECT_EQ(12000 + LOW_FEE_PENALTY, tree.getTotalWeight().evictionWeight);
std::optional<uint256> drop = tree.maybeDropRandom();
ASSERT_TRUE(drop.has_value());
uint256 txid = drop.value();
testedDropping.insert(txid);
// Do not continue to test if a particular trial fails
ASSERT_EQ(8000, tree.getTotalWeight().cost);
ASSERT_EQ(txid == TX_ID3 ? 8000 : 8000 + LOW_FEE_PENALTY, tree.getTotalWeight().evictionWeight);
}
}
TEST(MempoolLimitTests, WeightedTxInfoFromTx)
{
LoadProofParameters();
// The transaction creation is based on the test:
// test_transaction_builder.cpp/TEST(TransactionBuilder, SetFee)
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
auto testNote = GetTestSaplingNote(sk.default_address(), 50000);
// Default fee
{
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {});
WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), DEFAULT_FEE);
EXPECT_EQ(MIN_TX_COST, info.txWeight.cost);
EXPECT_EQ(MIN_TX_COST, info.txWeight.evictionWeight);
}
// Lower than standard fee
{
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {});
static_assert(DEFAULT_FEE == 1000);
builder.SetFee(DEFAULT_FEE-1);
WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), DEFAULT_FEE-1);
EXPECT_EQ(MIN_TX_COST, info.txWeight.cost);
EXPECT_EQ(MIN_TX_COST + LOW_FEE_PENALTY, info.txWeight.evictionWeight);
}
// Larger Tx
{
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
auto result = builder.Build();
if (result.IsError()) {
std::cerr << result.GetError() << std::endl;
}
WeightedTxInfo info = WeightedTxInfo::from(result.GetTxOrThrow(), DEFAULT_FEE);
EXPECT_EQ(5168, info.txWeight.cost);
EXPECT_EQ(5168, info.txWeight.evictionWeight);
}
RegtestDeactivateSapling();
}