12#include <boost/test/unit_test.hpp>
26 tx.
vin.resize(inputs.size());
27 tx.
vout.resize(num_outputs);
28 for (
size_t i = 0; i < inputs.size(); ++i) {
29 tx.
vin[i].prevout = inputs[i];
31 for (
size_t i = 0; i < num_outputs; ++i) {
40static inline bool sanity_check(
const std::vector<CTransactionRef>& transactions,
41 const std::map<COutPoint, CAmount>& bumpfees)
44 for (
const auto& [outpoint,
fee] : bumpfees) {
45 if (
fee < 0)
return false;
46 if (
fee == 0)
continue;
47 auto outpoint_ = outpoint;
48 const bool found = std::any_of(transactions.cbegin(), transactions.cend(), [&](
const auto& tx) {
49 return outpoint_.hash == tx->GetHash() && outpoint_.n < tx->vout.size();
51 if (!found)
return false;
53 for (
const auto& tx : transactions) {
55 if (tx->vout.size() > 1) {
56 std::set<CAmount> distinct_bumpfees;
57 for (
size_t i{0}; i < tx->vout.size(); ++i) {
58 const auto bumpfee = bumpfees.find(
COutPoint{tx->GetHash(),
static_cast<uint32_t
>(i)});
59 if (
bumpfee != bumpfees.end()) distinct_bumpfees.insert(
bumpfee->second);
61 if (distinct_bumpfees.size() > 1)
return false;
67template <
typename Key,
typename Value>
68Value
Find(
const std::map<Key, Value>& map,
const Key& key)
70 auto it = map.find(key);
71 BOOST_CHECK_MESSAGE(it != map.end(),
strprintf(
"Cannot find %s", key.ToString()));
82 const CAmount positive_base_fee{1000};
83 const CAmount negative_fee_delta{-50000};
84 const CAmount negative_modified_fees{positive_base_fee + negative_fee_delta};
86 const auto tx_mod_negative =
make_tx({
COutPoint{m_coinbase_txns[4]->GetHash(), 0}}, 1);
89 const COutPoint only_outpoint{tx_mod_negative->GetHash(), 0};
93 BOOST_CHECK(mini_miner_target0.IsReadyToCalculate());
95 mini_miner_target0.BuildMockTemplate(feerate_zero);
98 BOOST_CHECK(mini_miner_target0.GetMockTemplateTxids().empty());
102 BOOST_CHECK(mini_miner_no_target.IsReadyToCalculate());
103 mini_miner_no_target.BuildMockTemplate(std::nullopt);
104 const auto template_txids{mini_miner_no_target.GetMockTemplateTxids()};
106 BOOST_CHECK(template_txids.count(tx_mod_negative->GetHash()) > 0);
144 std::vector<COutPoint> all_unspent_outpoints({
154 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
156 std::vector<COutPoint> all_spent_outpoints({
164 std::vector<COutPoint> all_parent_outputs({
176 std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};
177 struct TxDimensions {
180 std::map<Txid, TxDimensions> tx_dims;
181 for (
const auto& tx : all_transactions) {
183 tx_dims.emplace(tx->GetHash(), TxDimensions{entry.GetTxSize(), entry.GetModifiedFee(),
184 CFeeRate(entry.GetModifiedFee(), entry.GetTxSize())});
194 for (
const auto& outpoint : nonexistent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
195 for (
const auto& feerate : various_normal_feerates) {
201 BOOST_CHECK(bump_fees.size() == nonexistent_outpoints.size());
202 for (
const auto& outpoint: nonexistent_outpoints) {
203 auto it = bump_fees.find(outpoint);
210 for (
const auto& target_feerate : various_normal_feerates) {
219 const TxDimensions& tx0_dimensions = tx_dims.find(tx0->GetHash())->second;
221 if (target_feerate <= tx0_dimensions.feerate) {
225 BOOST_CHECK_EQUAL(bumpfee0, target_feerate.GetFee(tx0_dimensions.vsize) - tx0_dimensions.mod_fee);
229 const TxDimensions& tx2_dimensions = tx_dims.find(tx2->GetHash())->second;
230 const TxDimensions& tx3_dimensions = tx_dims.find(tx3->GetHash())->second;
231 const CFeeRate tx2_feerate =
CFeeRate(tx2_dimensions.mod_fee + tx3_dimensions.mod_fee, tx2_dimensions.vsize + tx3_dimensions.vsize);
233 if (target_feerate <= tx2_feerate) {
238 BOOST_CHECK_EQUAL(bumpfee2, target_feerate.GetFee(tx2_dimensions.vsize) - tx2_dimensions.mod_fee);
249 const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;
250 const TxDimensions& tx5_dimensions = tx_dims.find(tx5->GetHash())->second;
251 const CFeeRate tx4_feerate =
CFeeRate(tx4_dimensions.mod_fee + tx5_dimensions.mod_fee, tx4_dimensions.vsize + tx5_dimensions.vsize);
253 if (target_feerate <= tx4_feerate) {
258 BOOST_CHECK_EQUAL(bumpfee4, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);
264 for (
const auto& target_feerate : various_normal_feerates) {
267 auto bump_fees_all_spent = mini_miner_all_spent.
CalculateBumpFees(target_feerate);
272 auto bump_fees_all_parents = mini_miner_all_parents.
CalculateBumpFees(target_feerate);
275 for (
auto& bump_fees : {bump_fees_all_parents, bump_fees_all_spent}) {
281 const TxDimensions& tx0_dimensions = tx_dims.find(tx0->GetHash())->second;
283 if (target_feerate <= tx0_dimensions.feerate) {
287 BOOST_CHECK_EQUAL(it0_spent, target_feerate.GetFee(tx0_dimensions.vsize) - tx0_dimensions.mod_fee);
291 const TxDimensions& tx2_dimensions = tx_dims.find(tx2->GetHash())->second;
292 const CFeeRate tx2_feerate_unbumped = tx2_dimensions.feerate;
293 auto it2_spent =
Find(bump_fees,
COutPoint{tx2->GetHash(), 0});
294 if (target_feerate <= tx2_feerate_unbumped) {
298 BOOST_CHECK_EQUAL(it2_spent, target_feerate.GetFee(tx2_dimensions.vsize) - tx2_dimensions.mod_fee);
302 const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;
303 const CFeeRate tx4_feerate_unbumped = tx4_dimensions.feerate;
304 auto it4_spent =
Find(bump_fees,
COutPoint{tx4->GetHash(), 0});
305 if (target_feerate <= tx4_feerate_unbumped) {
309 BOOST_CHECK_EQUAL(it4_spent, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);
317 std::vector<node::MiniMinerMempoolEntry> miniminer_info;
319 const int32_t tx0_vsize{tx_dims.at(tx0->GetHash()).vsize};
320 const int32_t tx1_vsize{tx_dims.at(tx1->GetHash()).vsize};
321 const int32_t tx2_vsize{tx_dims.at(tx2->GetHash()).vsize};
322 const int32_t tx3_vsize{tx_dims.at(tx3->GetHash()).vsize};
323 const int32_t tx4_vsize{tx_dims.at(tx4->GetHash()).vsize};
324 const int32_t tx5_vsize{tx_dims.at(tx5->GetHash()).vsize};
325 const int32_t tx6_vsize{tx_dims.at(tx6->GetHash()).vsize};
326 const int32_t tx7_vsize{tx_dims.at(tx7->GetHash()).vsize};
328 miniminer_info.emplace_back(tx0,tx0_vsize,tx0_vsize,
med_fee,
med_fee);
329 miniminer_info.emplace_back(tx1, tx1_vsize, tx0_vsize + tx1_vsize,
med_fee, 2*
med_fee);
330 miniminer_info.emplace_back(tx2, tx2_vsize, tx2_vsize,
low_fee,
low_fee);
332 miniminer_info.emplace_back(tx4, tx4_vsize, tx4_vsize,
low_fee,
low_fee);
333 miniminer_info.emplace_back(tx5, tx5_vsize, tx4_vsize + tx5_vsize, tx5_mod_fee,
low_fee + tx5_mod_fee);
337 std::map<Txid, std::set<Txid>> descendant_caches;
338 descendant_caches.emplace(tx0->GetHash(), std::set<Txid>{tx0->GetHash(), tx1->GetHash()});
339 descendant_caches.emplace(tx1->GetHash(), std::set<Txid>{tx1->GetHash()});
340 descendant_caches.emplace(tx2->GetHash(), std::set<Txid>{tx2->GetHash(), tx3->GetHash()});
341 descendant_caches.emplace(tx3->GetHash(), std::set<Txid>{tx3->GetHash()});
342 descendant_caches.emplace(tx4->GetHash(), std::set<Txid>{tx4->GetHash(), tx5->GetHash()});
343 descendant_caches.emplace(tx5->GetHash(), std::set<Txid>{tx5->GetHash()});
344 descendant_caches.emplace(tx6->GetHash(), std::set<Txid>{tx6->GetHash(), tx7->GetHash()});
345 descendant_caches.emplace(tx7->GetHash(), std::set<Txid>{tx7->GetHash()});
352 for (
const auto& sequences : {miniminer_manual.
Linearize(), miniminer_pool.
Linearize()}) {
426 std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};
427 std::vector<int64_t> tx_vsizes;
428 tx_vsizes.reserve(all_transactions.size());
431 std::vector<COutPoint> all_unspent_outpoints({
443 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
480 const auto tx0_bumpfee = bump_fees.find(
COutPoint{tx0->GetHash(), 1});
483 const auto tx3_bumpfee = bump_fees.find(
COutPoint{tx3->GetHash(), 0});
487 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
491 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
497 BOOST_CHECK(mini_miner_total_tx3.IsReadyToCalculate());
498 const auto tx3_bump_fee = mini_miner_total_tx3.CalculateTotalBumpFees(very_high_feerate);
499 BOOST_CHECK(!mini_miner_total_tx3.IsReadyToCalculate());
505 BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());
506 const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(very_high_feerate);
507 BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());
514 const auto just_below_tx4 =
CFeeRate(tx4_feerate.GetFeePerK() - 5);
521 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
524 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
529 BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());
530 const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(just_below_tx4);
531 BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());
545 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
548 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
553 std::vector<node::MiniMinerMempoolEntry> miniminer_info;
554 miniminer_info.emplace_back(tx0,tx_vsizes[0], tx_vsizes[0],
low_fee,
low_fee);
555 miniminer_info.emplace_back(tx1, tx_vsizes[1], tx_vsizes[1],
med_fee,
med_fee);
556 miniminer_info.emplace_back(tx2, tx_vsizes[2], tx_vsizes[2],
high_fee,
high_fee);
558 miniminer_info.emplace_back(tx4, tx_vsizes[4], tx_vsizes[4],
high_fee,
high_fee);
563 std::map<Txid, std::set<Txid>> descendant_caches;
564 descendant_caches.emplace(tx0->GetHash(), std::set<Txid>{tx0->GetHash(), tx3->GetHash()});
565 descendant_caches.emplace(tx1->GetHash(), std::set<Txid>{tx1->GetHash(), tx3->GetHash()});
566 descendant_caches.emplace(tx2->GetHash(), std::set<Txid>{tx2->GetHash(), tx3->GetHash()});
567 descendant_caches.emplace(tx3->GetHash(), std::set<Txid>{tx3->GetHash()});
568 descendant_caches.emplace(tx4->GetHash(), std::set<Txid>{tx4->GetHash(), tx5->GetHash(), tx6->GetHash(), tx7->GetHash()});
569 descendant_caches.emplace(tx5->GetHash(), std::set<Txid>{tx5->GetHash(), tx6->GetHash(), tx7->GetHash()});
570 descendant_caches.emplace(tx6->GetHash(), std::set<Txid>{tx6->GetHash()});
571 descendant_caches.emplace(tx7->GetHash(), std::set<Txid>{tx7->GetHash()});
578 for (
const auto& sequences : {miniminer_manual.
Linearize(), miniminer_pool.
Linearize()}) {
602 std::vector<Txid> last_txs;
603 std::vector<Txid> chain_txids;
604 auto& lasttx = m_coinbase_txns[0];
606 for (
int cluster_count=0; cluster_count < 10; ++cluster_count) {
608 lasttx = m_coinbase_txns[cluster_count];
609 for (
auto i{0}; i < 50; ++i) {
612 chain_txids.push_back(tx->GetHash());
615 last_txs.emplace_back(lasttx->GetHash());
620 const auto vec_iters_500 = pool.
GetIterVec(chain_txids);
621 for (
const auto& iter : vec_iters_500)
BOOST_CHECK(cluster_500tx_set.count(iter));
635 std::vector<Txid> zigzag_txids;
636 for (
auto p{0}; p < 32; ++p) {
639 zigzag_txids.push_back(txp->GetHash());
641 for (
auto c{0}; c < 31; ++c) {
644 zigzag_txids.push_back(txc->GetHash());
646 const auto vec_iters_zigzag = pool.
GetIterVec(zigzag_txids);
648 const std::vector<size_t> indices{0, 22, 52, zigzag_txids.size() - 1};
649 for (
const auto index : indices) {
654 for (
const auto& iter : vec_iters_zigzag)
BOOST_CHECK(clusterset.count(iter));
665 auto grandparent_zero_fee =
make_tx({{m_coinbase_txns.at(0)->GetHash(), 0}}, 1);
666 auto parent_high_feerate =
make_tx({{grandparent_zero_fee->GetHash(), 0}}, 1);
668 auto grandparent_double_low_feerate =
make_tx({{m_coinbase_txns.at(2)->GetHash(), 0}}, 1);
669 auto parent_med_feerate =
make_tx({{grandparent_double_low_feerate->GetHash(), 0}}, 1);
671 auto grandparent_low_feerate =
make_tx({{m_coinbase_txns.at(1)->GetHash(), 0}}, 1);
672 auto parent_double_low_feerate =
make_tx({{grandparent_low_feerate->GetHash(), 0}}, 1);
674 auto child =
make_tx({{parent_high_feerate->GetHash(), 0}, {parent_double_low_feerate->GetHash(), 0}, {parent_med_feerate->GetHash(), 0}}, 1);
677 const int64_t tx_vsize{100};
678 const int64_t child_vsize{1000};
680 std::vector<node::MiniMinerMempoolEntry> miniminer_info;
681 miniminer_info.emplace_back(grandparent_zero_fee, tx_vsize,tx_vsize, 0,0);
682 miniminer_info.emplace_back(parent_high_feerate, tx_vsize, 2*tx_vsize,
high_fee,
high_fee);
683 miniminer_info.emplace_back(grandparent_double_low_feerate, tx_vsize, tx_vsize, 2*
low_fee, 2*
low_fee);
685 miniminer_info.emplace_back(grandparent_low_feerate, tx_vsize, tx_vsize,
low_fee,
low_fee);
686 miniminer_info.emplace_back(parent_double_low_feerate, tx_vsize, 2*tx_vsize, 2*
low_fee, 3*
low_fee);
688 std::map<Txid, std::set<Txid>> descendant_caches;
689 descendant_caches.emplace(grandparent_zero_fee->GetHash(), std::set<Txid>{grandparent_zero_fee->GetHash(), parent_high_feerate->GetHash(), child->GetHash()});
690 descendant_caches.emplace(grandparent_low_feerate->GetHash(), std::set<Txid>{grandparent_low_feerate->GetHash(), parent_double_low_feerate->GetHash(), child->GetHash()});
691 descendant_caches.emplace(grandparent_double_low_feerate->GetHash(), std::set<Txid>{grandparent_double_low_feerate->GetHash(), parent_med_feerate->GetHash(), child->GetHash()});
692 descendant_caches.emplace(parent_high_feerate->GetHash(), std::set<Txid>{parent_high_feerate->GetHash(), child->GetHash()});
693 descendant_caches.emplace(parent_med_feerate->GetHash(), std::set<Txid>{parent_med_feerate->GetHash(), child->GetHash()});
694 descendant_caches.emplace(parent_double_low_feerate->GetHash(), std::set<Txid>{parent_double_low_feerate->GetHash(), child->GetHash()});
695 descendant_caches.emplace(child->GetHash(), std::set<Txid>{child->GetHash()});
699 const auto sequences{miniminer_manual.
Linearize()};
int64_t CAmount
Amount in satoshis (Can be negative)
static constexpr CAmount COIN
The amount of satoshis in one BTC.
AddToMempool(pool, CTxMemPoolEntry(TxGraph::Ref(), tx, fee, 0, 1, 0, false, 4, lp))
#define Assert(val)
Identity function.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
CAmount GetFee(int32_t virtual_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
An outpoint - a combination of a transaction hash and an index n into its vout.
Serialized script, used inside transaction inputs and outputs.
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
void PrioritiseTransaction(const Txid &hash, const CAmount &nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
std::vector< txiter > GetIterVec(const std::vector< Txid > &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Translate a list of hashes into a list of mempool iterators to avoid repeated lookups.
std::set< txiter, CompareIteratorByHash > setEntries
std::vector< txiter > GatherClusters(const std::vector< Txid > &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Collect the entire cluster of connected transactions for each transaction in txids.
const CTransaction * GetConflictTx(const COutPoint &prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Get the transaction in the pool that spends the same prevout.
std::tuple< size_t, size_t, CAmount > CalculateAncestorData(const CTxMemPoolEntry &entry) const EXCLUSIVE_LOCKS_REQUIRED(cs)
bool isSpent(const COutPoint &outpoint) const
const CTxMemPoolEntry * GetEntry(const Txid &txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs)
A minimal version of BlockAssembler, using the same ancestor set scoring algorithm.
std::map< Txid, uint32_t > Linearize()
Construct a new block template with all of the transactions and calculate the order in which they are...
std::map< COutPoint, CAmount > CalculateBumpFees(const CFeeRate &target_feerate)
Construct a new block template and, for each outpoint corresponding to a transaction that did not mak...
bool IsReadyToCalculate() const
Returns true if CalculateBumpFees may be called, false if not.
static transaction_identifier FromUint256(const uint256 &id)
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
BOOST_FIXTURE_TEST_CASE(miniminer_negative, TestChain100Setup)
static bool sanity_check(const std::vector< CTransactionRef > &transactions, const std::map< COutPoint, CAmount > &bumpfees)
static CTransactionRef make_tx(const std::vector< COutPoint > &inputs, size_t num_outputs)
Value Find(const std::map< Key, Value > &map, const Key &key)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Compute the virtual transaction size (weight reinterpreted as bytes).
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
uint256 GetRandHash() noexcept
Generate a random uint256.
static constexpr CAmount CENT
A mutable version of CTransaction.
std::vector< CTxOut > vout
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
TestMemPoolEntryHelper & Fee(CAmount _fee)
Testing setup that configures a complete environment.
std::unique_ptr< CTxMemPool > mempool