13#include <boost/test/unit_test.hpp>
23 tx.
vin.resize(inputs.size());
24 tx.
vout.resize(output_values.size());
25 for (
size_t i = 0; i < inputs.size(); ++i) {
26 tx.
vin[i].prevout.hash = inputs[i]->GetHash();
27 tx.
vin[i].prevout.n = 0;
30 witness.
stack.emplace_back(i + 10);
31 tx.
vin[i].scriptWitness = witness;
33 for (
size_t i = 0; i < output_values.size(); ++i) {
35 tx.
vout[i].nValue = output_values[i];
43 const std::vector<CAmount>& output_values)
45 assert(parent->vout.size() >= 2);
50 tx1.
vout.resize(output_values.size());
52 tx1.
vin[0].prevout.hash = parent->GetHash();
53 tx1.
vin[0].prevout.n = 0;
56 witness.
stack.emplace_back(10);
57 tx1.
vin[0].scriptWitness = witness;
59 for (
size_t i = 0; i < output_values.size(); ++i) {
61 tx1.
vout[i].nValue = output_values[i];
66 tx2.
vin[0].prevout.n = 1;
78 auto tx_to_spend = tx;
79 for (int32_t i{0}; i < num_descendants; ++i) {
80 auto next_tx =
make_tx({tx_to_spend}, {(50 - i) *
CENT});
82 tx_to_spend = next_tx;
126 const auto tx1 =
make_tx( {m_coinbase_txns[0]}, {10 *
COIN});
132 const auto tx3 =
make_tx( {m_coinbase_txns[1]}, {1099 *
CENT});
138 const auto tx5 =
make_tx( {m_coinbase_txns[2]}, {1099 *
CENT});
147 const auto tx7 =
make_tx( {m_coinbase_txns[3]}, {999 *
CENT});
149 const auto tx8 =
make_tx( {m_coinbase_txns[4]}, {999 *
CENT});
153 const auto tx9 =
make_tx( {m_coinbase_txns[5]}, {995 *
CENT});
155 const auto tx10 =
make_tx( {m_coinbase_txns[6]}, {995 *
CENT});
159 const auto tx11 =
make_tx( {m_coinbase_txns[7]}, {995 *
CENT});
161 const auto tx12 =
make_tx( {m_coinbase_txns[8]}, {995 *
CENT});
165 const auto tx13 =
make_tx( {m_coinbase_txns[9]}, {995 *
CENT, 995 *
CENT});
168 const auto entry1_normal = pool.
GetIter(tx1->GetHash()).value();
169 const auto entry2_normal = pool.
GetIter(tx2->GetHash()).value();
170 const auto entry3_low = pool.
GetIter(tx3->GetHash()).value();
171 const auto entry4_high = pool.
GetIter(tx4->GetHash()).value();
172 const auto entry5_low = pool.
GetIter(tx5->GetHash()).value();
173 const auto entry6_low_prioritised = pool.
GetIter(tx6->GetHash()).value();
174 const auto entry7_high = pool.
GetIter(tx7->GetHash()).value();
175 const auto entry8_high = pool.
GetIter(tx8->GetHash()).value();
176 const auto entry9_unchained = pool.
GetIter(tx9->GetHash()).value();
177 const auto entry10_unchained = pool.
GetIter(tx10->GetHash()).value();
178 const auto entry11_unchained = pool.
GetIter(tx11->GetHash()).value();
179 const auto entry12_unchained = pool.
GetIter(tx12->GetHash()).value();
180 const auto entry13_unchained = pool.
GetIter(tx13->GetHash()).value();
196 entry5_low, entry6_low_prioritised, entry7_high, entry8_high};
204 CFeeRate(entry1_normal->GetModifiedFee() + 1, entry1_normal->GetTxSize() + 2),
205 unused_txid).has_value());
251 const std::vector<CTransactionRef> parent_inputs({m_coinbase_txns[0], m_coinbase_txns[1], m_coinbase_txns[2],
252 m_coinbase_txns[3], m_coinbase_txns[4]});
253 const auto conflicts_with_parents =
make_tx(parent_inputs, {50 *
CENT});
258 all_conflicts) == std::nullopt);
260 auto conflicts_size = all_conflicts.size();
261 all_conflicts.clear();
265 conflicts_size += 23;
267 all_conflicts.clear();
271 conflicts_size += 23;
273 all_conflicts.clear();
277 conflicts_size += 23;
279 all_conflicts.clear();
283 conflicts_size += 23;
286 all_conflicts.clear();
293 const auto spends_unconfirmed =
make_tx({tx1}, {36 *
CENT});
294 for (
const auto& input : spends_unconfirmed->vin) {
300 all_entries) == std::nullopt);
304 const auto spends_new_unconfirmed =
make_tx({tx1, tx8}, {36 *
CENT});
308 const auto spends_conflicting_confirmed =
make_tx({m_coinbase_txns[0], m_coinbase_txns[1]}, {45 *
CENT});
328 const auto entry10_child = pool.
GetIter(child_tx->GetHash()).value();
334 const auto entry10_grand_child = pool.
GetIter(grand_child_tx->GetHash()).value();
341 const auto entry_two_parent_child = pool.
GetIter(two_parent_child_tx->GetHash()).value();
342 BOOST_CHECK_EQUAL(pool.
CheckConflictTopology({entry11_unchained}).value(),
strprintf(
"%s is not the only parent of child %s", entry11_unchained->GetSharedTx()->GetHash().ToString(), entry_two_parent_child->GetSharedTx()->GetHash().ToString()));
343 BOOST_CHECK_EQUAL(pool.
CheckConflictTopology({entry12_unchained}).value(),
strprintf(
"%s is not the only parent of child %s", entry12_unchained->GetSharedTx()->GetHash().ToString(), entry_two_parent_child->GetSharedTx()->GetHash().ToString()));
348 const auto entry_sibling_1 = pool.
GetIter(two_siblings.first->GetHash()).value();
349 const auto entry_sibling_2 = pool.
GetIter(two_siblings.second->GetHash()).value();
350 BOOST_CHECK_EQUAL(pool.
CheckConflictTopology({entry_sibling_1}).value(),
strprintf(
"%s is not the only child of parent %s", entry_sibling_1->GetSharedTx()->GetHash().ToString(), entry13_unchained->GetSharedTx()->GetHash().ToString()));
351 BOOST_CHECK_EQUAL(pool.
CheckConflictTopology({entry_sibling_2}).value(),
strprintf(
"%s is not the only child of parent %s", entry_sibling_2->GetSharedTx()->GetHash().ToString(), entry13_unchained->GetSharedTx()->GetHash().ToString()));
365 const auto tx1 =
make_tx( {m_coinbase_txns[0], m_coinbase_txns[1]}, {10 *
COIN});
370 const auto entry1 = pool.
GetIter(tx1->GetHash()).value();
371 const auto tx1_fee = entry1->GetModifiedFee();
372 const auto entry2 = pool.
GetIter(tx2->GetHash()).value();
373 const auto tx2_fee = entry2->GetModifiedFee();
376 const auto tx1_conflict =
make_tx( {m_coinbase_txns[0], m_coinbase_txns[2]}, {10 *
COIN});
377 const auto tx3 =
make_tx( {tx1_conflict}, {995 *
CENT});
378 auto entry3 = entry.
FromTx(tx3);
384 changeset->StageRemoval(entry1);
385 changeset->StageRemoval(entry2);
386 changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0,
false, 4,
LockPoints());
387 changeset->StageAddition(tx3, tx2_fee, 0, 1, 0,
false, 4,
LockPoints());
391 BOOST_CHECK(res1.value().second ==
"insufficient feerate: does not improve feerate diagram");
396 changeset->StageRemoval(entry1);
397 changeset->StageRemoval(entry2);
398 changeset->StageAddition(tx1_conflict, tx1_fee+1, 0, 1, 0,
false, 4,
LockPoints());
399 changeset->StageAddition(tx3, tx2_fee, 0, 1, 0,
false, 4,
LockPoints());
406 changeset->StageRemoval(entry1);
407 changeset->StageRemoval(entry2);
408 changeset->StageAddition(tx1_conflict, tx1_fee+1, 0, 1, 0,
false, 4,
LockPoints());
409 changeset->StageAddition(tx3, tx2_fee, 0, 1, 0,
false, 4,
LockPoints());
413 BOOST_CHECK(res2.value().second ==
"insufficient feerate: does not improve feerate diagram");
423 changeset->StageRemoval(entry1);
424 changeset->StageRemoval(entry2);
425 changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0,
false, 4,
LockPoints());
426 changeset->StageAddition(entry4.GetSharedTx(), tx2_fee, 0, 1, 0,
false, 4,
LockPoints());
433 const auto entry5 = pool.
GetIter(tx5->GetHash()).value();
436 changeset->StageRemoval(entry1);
437 changeset->StageRemoval(entry2);
438 changeset->StageRemoval(entry5);
439 changeset->StageAddition(tx1_conflict, tx1_fee, 0, 1, 0,
false, 4,
LockPoints());
440 changeset->StageAddition(entry4.GetSharedTx(), tx2_fee + entry5->GetModifiedFee() + 1, 0, 1, 0,
false, 4,
LockPoints());
444 BOOST_CHECK_MESSAGE(res3.value().second ==
strprintf(
"%s has 2 descendants, max 1 allowed", tx1->GetHash().GetHex()), res3.value().second);
459 const auto low_tx =
make_tx( {m_coinbase_txns[0]}, {10 *
COIN});
462 const auto entry_low = pool.
GetIter(low_tx->GetHash()).value();
463 const auto low_size = entry_low->GetTxSize();
465 const auto replacement_tx =
make_tx( {m_coinbase_txns[0]}, {9 *
COIN});
466 auto entry_replacement = entry.
FromTx(replacement_tx);
471 changeset->StageRemoval(entry_low);
472 changeset->StageAddition(replacement_tx, 0, 0, 1, 0,
false, 4,
LockPoints());
473 const auto replace_one{changeset->CalculateChunksForRBF()};
475 std::vector<FeeFrac> expected_old_chunks{{
low_fee, low_size}};
476 BOOST_CHECK(replace_one->first == expected_old_chunks);
477 std::vector<FeeFrac> expected_new_chunks{{0, int32_t(entry_replacement.GetTxSize())}};
478 BOOST_CHECK(replace_one->second == expected_new_chunks);
484 changeset->StageRemoval(entry_low);
486 const auto replace_one_fee{changeset->CalculateChunksForRBF()};
488 std::vector<FeeFrac> expected_old_diagram{{
low_fee, low_size}};
489 BOOST_CHECK(replace_one_fee->first == expected_old_diagram);
490 std::vector<FeeFrac> expected_new_diagram{{
high_fee, low_size}};
491 BOOST_CHECK(replace_one_fee->second == expected_new_diagram);
495 const auto high_tx =
make_tx( {low_tx}, {995 *
CENT});
497 const auto entry_high = pool.
GetIter(high_tx->GetHash()).value();
498 const auto high_size = entry_high->GetTxSize();
502 changeset->StageRemoval(entry_low);
503 changeset->StageRemoval(entry_high);
505 const auto replace_single_chunk{changeset->CalculateChunksForRBF()};
507 std::vector<FeeFrac> expected_old_chunks{{
low_fee +
high_fee, low_size + high_size}};
508 BOOST_CHECK(replace_single_chunk->first == expected_old_chunks);
509 std::vector<FeeFrac> expected_new_chunks{{
high_fee, low_size}};
510 BOOST_CHECK(replace_single_chunk->second == expected_new_chunks);
516 changeset->StageRemoval(entry_high);
518 const auto replace_cpfp_child{changeset->CalculateChunksForRBF()};
520 std::vector<FeeFrac> expected_old_chunks{{
low_fee +
high_fee, low_size + high_size}};
521 BOOST_CHECK(replace_cpfp_child->first == expected_old_chunks);
522 std::vector<FeeFrac> expected_new_chunks{{
high_fee, low_size}, {
low_fee, low_size}};
523 BOOST_CHECK(replace_cpfp_child->second == expected_new_chunks);
527 const auto normal_tx =
make_tx( {high_tx}, {995 *
CENT});
529 const auto entry_normal = pool.
GetIter(normal_tx->GetHash()).value();
533 changeset->StageRemoval(entry_low);
534 changeset->StageRemoval(entry_high);
535 changeset->StageRemoval(entry_normal);
537 const auto replace_too_large{changeset->CalculateChunksForRBF()};
543 const auto high_tx_2 =
make_tx( {m_coinbase_txns[1]}, {10 *
COIN});
545 const auto entry_high_2 = pool.
GetIter(high_tx_2->GetHash()).value();
546 const auto high_size_2 = entry_high_2->GetTxSize();
548 const auto low_tx_2 =
make_tx( {high_tx_2}, {9 *
COIN});
550 const auto entry_low_2 = pool.
GetIter(low_tx_2->GetHash()).value();
551 const auto low_size_2 = entry_low_2->GetTxSize();
555 changeset->StageRemoval(entry_high_2);
556 changeset->StageRemoval(entry_low_2);
558 const auto replace_two_chunks_single_cluster{changeset->CalculateChunksForRBF()};
559 BOOST_CHECK(replace_two_chunks_single_cluster.has_value());
560 std::vector<FeeFrac> expected_old_chunks{{
high_fee, high_size_2}, {
low_fee, low_size_2}};
561 BOOST_CHECK(replace_two_chunks_single_cluster->first == expected_old_chunks);
562 std::vector<FeeFrac> expected_new_chunks{{
high_fee, low_size_2}};
563 BOOST_CHECK(replace_two_chunks_single_cluster->second == expected_new_chunks);
567 const auto conflict_1 =
make_tx( {m_coinbase_txns[2]}, {10 *
COIN});
569 const auto conflict_1_entry = pool.
GetIter(conflict_1->GetHash()).value();
571 const auto conflict_2 =
make_tx( {m_coinbase_txns[3]}, {10 *
COIN});
573 const auto conflict_2_entry = pool.
GetIter(conflict_2->GetHash()).value();
575 const auto conflict_3 =
make_tx( {m_coinbase_txns[4]}, {10 *
COIN});
577 const auto conflict_3_entry = pool.
GetIter(conflict_3->GetHash()).value();
581 changeset->StageRemoval(conflict_1_entry);
582 changeset->StageRemoval(conflict_2_entry);
583 changeset->StageRemoval(conflict_3_entry);
585 const auto replace_multiple_clusters{changeset->CalculateChunksForRBF()};
586 BOOST_CHECK(replace_multiple_clusters.has_value());
587 BOOST_CHECK(replace_multiple_clusters->first.size() == 3);
588 BOOST_CHECK(replace_multiple_clusters->second.size() == 1);
592 const auto conflict_1_child =
make_tx({conflict_1}, {995 *
CENT});
594 const auto conflict_1_child_entry = pool.
GetIter(conflict_1_child->GetHash()).value();
598 changeset->StageRemoval(conflict_1_entry);
599 changeset->StageRemoval(conflict_2_entry);
600 changeset->StageRemoval(conflict_3_entry);
601 changeset->StageRemoval(conflict_1_child_entry);
603 const auto replace_multiple_clusters_2{changeset->CalculateChunksForRBF()};
605 BOOST_CHECK(replace_multiple_clusters_2.has_value());
606 BOOST_CHECK(replace_multiple_clusters_2->first.size() == 4);
607 BOOST_CHECK(replace_multiple_clusters_2->second.size() == 1);
611 const auto conflict_1_grand_child =
make_tx({conflict_1_child}, {995 *
CENT});
613 const auto conflict_1_grand_child_entry = pool.
GetIter(conflict_1_child->GetHash()).value();
617 changeset->StageRemoval(conflict_1_entry);
618 changeset->StageRemoval(conflict_2_entry);
619 changeset->StageRemoval(conflict_3_entry);
620 changeset->StageRemoval(conflict_1_child_entry);
621 changeset->StageRemoval(conflict_1_grand_child_entry);
623 const auto replace_cluster_size_3{changeset->CalculateChunksForRBF()};
635 std::vector<FeeFrac> old_chunks{{{950, 300}, {100, 100}}};
636 std::vector<FeeFrac> new_chunks{{{1000, 300}, {50, 100}}};
642 old_chunks = {{950, 300}, {100, 100}};
643 new_chunks = {{1000, 300}, {0, 100}};
649 old_chunks = {{950, 300}, {100, 100}};
650 new_chunks = {{1100, 300}};
657 old_chunks = {{950, 300}, {100, 100}};
658 new_chunks = {{1100, 100}, {0, 100}};
664 old_chunks = {{950, 300}, {100, 100}};
665 new_chunks = {{750, 100}, {249, 250}, {151, 650}};
671 old_chunks = {{950, 300}, {100, 100}};
672 new_chunks = {{750, 100}, {250, 250}, {150, 150}};
678 old_chunks = {{950, 300}, {100, 100}};
679 new_chunks = {{950, 300}, {100, 100}};
685 old_chunks = {{950, 300}, {100, 99}};
686 new_chunks = {{950, 300}, {100, 100}};
693 old_chunks = {{950, 300}, {100, 99}};
694 new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}};
700 new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}, {1, 1}};
int64_t CAmount
Amount in satoshis (Can be negative)
static constexpr CAmount COIN
The amount of satoshis in one BTC.
AddToMempool(pool, CTxMemPoolEntry(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.
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.
std::unique_ptr< ChangeSet > GetChangeSet() EXCLUSIVE_LOCKS_REQUIRED(cs)
std::optional< txiter > GetIter(const Txid &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
bool exists(const Txid &txid) const
std::set< txiter, CompareIteratorByHash > setEntries
std::optional< std::string > CheckConflictTopology(const setEntries &direct_conflicts)
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()
bilingual_str ErrorString(const Result< T > &result)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
std::optional< std::string > HasNoNewUnconfirmed(const CTransaction &tx, const CTxMemPool &pool, const CTxMemPool::setEntries &iters_conflicting)
The replacement transaction may only include an unconfirmed input if that input was included in one o...
std::optional< std::pair< DiagramCheckError, std::string > > ImprovesFeerateDiagram(CTxMemPool::ChangeSet &changeset)
The replacement transaction must improve the feerate diagram of the mempool.
std::optional< std::string > PaysForRBF(CAmount original_fees, CAmount replacement_fees, size_t replacement_vsize, CFeeRate relay_fee, const Txid &txid)
The replacement transaction must pay more fees than the original transactions.
std::optional< std::string > PaysMoreThanConflicts(const CTxMemPool::setEntries &iters_conflicting, CFeeRate replacement_feerate, const Txid &txid)
Check that the feerate of the replacement transaction(s) is higher than the feerate of each of the tr...
std::optional< std::string > EntriesAndTxidsDisjoint(const CTxMemPool::setEntries &ancestors, const std::set< Txid > &direct_conflicts, const Txid &txid)
Check the intersection between two sets of transactions (a set of mempool entries and a set of txids)...
std::optional< std::string > GetEntriesForConflicts(const CTransaction &tx, CTxMemPool &pool, const CTxMemPool::setEntries &iters_conflicting, CTxMemPool::setEntries &all_conflicts)
Get all descendants of iters_conflicting.
@ FAILURE
New diagram wasn't strictly superior
@ UNCALCULABLE
Unable to calculate due to topology or other reason.
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE
Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or rep...
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
uint256 GetRandHash() noexcept
Generate a random uint256.
static std::pair< CTransactionRef, CTransactionRef > make_two_siblings(const CTransactionRef parent, const std::vector< CAmount > &output_values)
static CTransactionRef make_tx(const std::vector< CTransactionRef > &inputs, const std::vector< CAmount > &output_values)
static CTransactionRef add_descendants(const CTransactionRef &tx, int32_t num_descendants, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(
static std::pair< CTransactionRef, CTransactionRef > add_children_to_parent(const CTransactionRef parent, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(
BOOST_AUTO_TEST_CASE(feerate_chunks_utilities)
static CTransactionRef add_descendant_to_parents(const std::vector< CTransactionRef > &parents, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(
BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
static constexpr CAmount CENT
A mutable version of CTransaction.
std::vector< CTxOut > vout
std::vector< std::vector< unsigned char > > stack
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
#define EXCLUSIVE_LOCKS_REQUIRED(...)
std::partial_ordering CompareChunks(std::span< const FeeFrac > chunks0, std::span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.