12 #include <boost/test/unit_test.hpp>
21 tx.
vin.resize(inputs.size());
22 tx.
vout.resize(num_outputs);
23 for (
size_t i = 0; i < inputs.size(); ++i) {
24 tx.
vin[i].prevout = inputs[i];
26 for (
size_t i = 0; i < num_outputs; ++i) {
35 static inline bool sanity_check(
const std::vector<CTransactionRef>& transactions,
36 const std::map<COutPoint, CAmount>& bumpfees)
39 for (
const auto& [outpoint, fee] : bumpfees) {
40 if (fee < 0)
return false;
41 if (fee == 0)
continue;
42 auto outpoint_ = outpoint;
43 const bool found = std::any_of(transactions.cbegin(), transactions.cend(), [&](
const auto& tx) {
44 return outpoint_.hash == tx->GetHash() && outpoint_.n < tx->vout.size();
46 if (!found)
return false;
48 for (
const auto& tx : transactions) {
50 if (tx->vout.size() > 1) {
51 std::set<CAmount> distinct_bumpfees;
52 for (
size_t i{0}; i < tx->vout.size(); ++i) {
53 const auto bumpfee = bumpfees.find(
COutPoint{tx->GetHash(),
static_cast<uint32_t
>(i)});
54 if (
bumpfee != bumpfees.end()) distinct_bumpfees.insert(
bumpfee->second);
56 if (distinct_bumpfees.size() > 1)
return false;
62 template <
typename Key,
typename Value>
63 Value
Find(
const std::map<Key, Value>& map,
const Key& key)
65 auto it = map.find(key);
66 BOOST_CHECK_MESSAGE(it != map.end(),
strprintf(
"Cannot find %s", key.ToString()));
107 std::vector<COutPoint> all_unspent_outpoints({
117 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
119 std::vector<COutPoint> all_spent_outpoints({
127 std::vector<COutPoint> all_parent_outputs({
139 std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};
140 struct TxDimensions {
143 std::map<uint256, TxDimensions> tx_dims;
144 for (
const auto& tx : all_transactions) {
145 const auto it = pool.
GetIter(tx->GetHash()).value();
146 tx_dims.emplace(tx->GetHash(), TxDimensions{it->GetTxSize(), it->GetModifiedFee(),
147 CFeeRate(it->GetModifiedFee(), it->GetTxSize())});
157 for (
const auto& outpoint : nonexistent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
158 for (
const auto& feerate : various_normal_feerates) {
164 BOOST_CHECK(bump_fees.size() == nonexistent_outpoints.size());
165 for (
const auto& outpoint: nonexistent_outpoints) {
166 auto it = bump_fees.find(outpoint);
173 for (
const auto& target_feerate : various_normal_feerates) {
182 const TxDimensions& tx0_dimensions = tx_dims.find(tx0->GetHash())->second;
184 if (target_feerate <= tx0_dimensions.feerate) {
188 BOOST_CHECK_EQUAL(bumpfee0, target_feerate.GetFee(tx0_dimensions.vsize) - tx0_dimensions.mod_fee);
192 const TxDimensions& tx2_dimensions = tx_dims.find(tx2->GetHash())->second;
193 const TxDimensions& tx3_dimensions = tx_dims.find(tx3->GetHash())->second;
194 const CFeeRate tx2_feerate =
CFeeRate(tx2_dimensions.mod_fee + tx3_dimensions.mod_fee, tx2_dimensions.vsize + tx3_dimensions.vsize);
196 if (target_feerate <= tx2_feerate) {
201 BOOST_CHECK_EQUAL(bumpfee2, target_feerate.GetFee(tx2_dimensions.vsize) - tx2_dimensions.mod_fee);
212 const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;
213 const TxDimensions& tx5_dimensions = tx_dims.find(tx5->GetHash())->second;
214 const CFeeRate tx4_feerate =
CFeeRate(tx4_dimensions.mod_fee + tx5_dimensions.mod_fee, tx4_dimensions.vsize + tx5_dimensions.vsize);
216 if (target_feerate <= tx4_feerate) {
221 BOOST_CHECK_EQUAL(bumpfee4, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);
227 for (
const auto& target_feerate : various_normal_feerates) {
230 auto bump_fees_all_spent = mini_miner_all_spent.
CalculateBumpFees(target_feerate);
235 auto bump_fees_all_parents = mini_miner_all_parents.
CalculateBumpFees(target_feerate);
238 for (
auto& bump_fees : {bump_fees_all_parents, bump_fees_all_spent}) {
244 const TxDimensions& tx0_dimensions = tx_dims.find(tx0->GetHash())->second;
246 if (target_feerate <= tx0_dimensions.feerate) {
250 BOOST_CHECK_EQUAL(it0_spent, target_feerate.GetFee(tx0_dimensions.vsize) - tx0_dimensions.mod_fee);
254 const TxDimensions& tx2_dimensions = tx_dims.find(tx2->GetHash())->second;
255 const CFeeRate tx2_feerate_unbumped = tx2_dimensions.feerate;
256 auto it2_spent =
Find(bump_fees,
COutPoint{tx2->GetHash(), 0});
257 if (target_feerate <= tx2_feerate_unbumped) {
261 BOOST_CHECK_EQUAL(it2_spent, target_feerate.GetFee(tx2_dimensions.vsize) - tx2_dimensions.mod_fee);
265 const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;
266 const CFeeRate tx4_feerate_unbumped = tx4_dimensions.feerate;
267 auto it4_spent =
Find(bump_fees,
COutPoint{tx4->GetHash(), 0});
268 if (target_feerate <= tx4_feerate_unbumped) {
272 BOOST_CHECK_EQUAL(it4_spent, target_feerate.GetFee(tx4_dimensions.vsize) - tx4_dimensions.mod_fee);
335 std::vector<CTransactionRef> all_transactions{tx0, tx1, tx2, tx3, tx4, tx5, tx6, tx7};
336 std::vector<int64_t> tx_vsizes;
337 tx_vsizes.reserve(all_transactions.size());
340 std::vector<COutPoint> all_unspent_outpoints({
352 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
354 const auto tx2_feerate =
CFeeRate(high_fee, tx_vsizes[2]);
355 const auto tx3_feerate =
CFeeRate(high_fee, tx_vsizes[3]);
358 const auto tx3_anc_feerate =
CFeeRate(low_fee + med_fee + high_fee + high_fee, tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]);
359 const auto tx3_iter = pool.
GetIter(tx3->GetHash());
360 BOOST_CHECK(tx3_anc_feerate ==
CFeeRate(tx3_iter.value()->GetModFeesWithAncestors(), tx3_iter.value()->GetSizeWithAncestors()));
361 const auto tx4_feerate =
CFeeRate(high_fee, tx_vsizes[4]);
362 const auto tx6_anc_feerate =
CFeeRate(high_fee + low_fee + med_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]);
363 const auto tx6_iter = pool.
GetIter(tx6->GetHash());
364 BOOST_CHECK(tx6_anc_feerate ==
CFeeRate(tx6_iter.value()->GetModFeesWithAncestors(), tx6_iter.value()->GetSizeWithAncestors()));
365 const auto tx7_anc_feerate =
CFeeRate(high_fee + low_fee + high_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]);
366 const auto tx7_iter = pool.
GetIter(tx7->GetHash());
367 BOOST_CHECK(tx7_anc_feerate ==
CFeeRate(tx7_iter.value()->GetModFeesWithAncestors(), tx7_iter.value()->GetSizeWithAncestors()));
381 const auto tx0_bumpfee = bump_fees.find(
COutPoint{tx0->GetHash(), 1});
384 const auto tx3_bumpfee = bump_fees.find(
COutPoint{tx3->GetHash(), 0});
387 very_high_feerate.
GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));
388 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
391 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]) - (high_fee + low_fee + med_fee));
392 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
395 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]) - (high_fee + low_fee + high_fee));
398 BOOST_CHECK(mini_miner_total_tx3.IsReadyToCalculate());
399 const auto tx3_bump_fee = mini_miner_total_tx3.CalculateTotalBumpFees(very_high_feerate);
400 BOOST_CHECK(!mini_miner_total_tx3.IsReadyToCalculate());
403 very_high_feerate.
GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));
406 BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());
407 const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(very_high_feerate);
408 BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());
411 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6] + tx_vsizes[7]) - (high_fee + low_fee + med_fee + high_fee));
415 const auto just_below_tx4 =
CFeeRate(tx4_feerate.GetFeePerK() - 5);
422 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
424 BOOST_CHECK_EQUAL(tx6_bumpfee->second, just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));
425 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
427 BOOST_CHECK_EQUAL(tx7_bumpfee->second, just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[7]) - (low_fee + high_fee));
430 BOOST_CHECK(mini_miner_tx6_tx7.IsReadyToCalculate());
431 const auto tx6_tx7_bumpfee = mini_miner_tx6_tx7.CalculateTotalBumpFees(just_below_tx4);
432 BOOST_CHECK(!mini_miner_tx6_tx7.IsReadyToCalculate());
434 BOOST_CHECK_EQUAL(tx6_tx7_bumpfee.value(), just_below_tx4.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));
438 const auto just_above_tx6 =
CFeeRate(med_fee + 10, tx_vsizes[6]);
446 const auto tx6_bumpfee = bump_fees.find(
COutPoint{tx6->GetHash(), 0});
448 BOOST_CHECK_EQUAL(tx6_bumpfee->second, just_above_tx6.GetFee(tx_vsizes[6]) - (med_fee));
449 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
461 std::vector<uint256> chain_txids;
462 auto& lasttx = m_coinbase_txns[0];
463 for (
auto i{0}; i < 500; ++i) {
466 chain_txids.push_back(tx->GetHash());
469 const auto cluster_500tx = pool.
GatherClusters({lasttx->GetHash()});
472 const auto vec_iters_500 = pool.
GetIterVec(chain_txids);
473 for (
const auto& iter : vec_iters_500)
BOOST_CHECK(cluster_500tx_set.count(iter));
478 const auto cluster_501 = pool.
GatherClusters({tx_501->GetHash()});
487 std::vector<uint256> zigzag_txids;
488 for (
auto p{0}; p < 50; ++p) {
491 zigzag_txids.push_back(txp->GetHash());
493 for (
auto c{0}; c < 49; ++c) {
496 zigzag_txids.push_back(txc->GetHash());
498 const auto vec_iters_zigzag = pool.
GetIterVec(zigzag_txids);
500 const std::vector<size_t> indices{0, 22, 72, zigzag_txids.size() - 1};
501 for (
const auto index : indices) {
506 for (
const auto& iter : vec_iters_zigzag)
BOOST_CHECK(clusterset.count(iter));
int64_t CAmount
Amount in satoshis (Can be negative)
static constexpr CAmount COIN
The amount of satoshis in one BTC.
#define Assert(val)
Identity function.
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
CAmount GetFee(uint32_t num_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 uint256 &hash, const CAmount &nFeeDelta)
Affect CreateNewBlock prioritisation of transactions.
std::vector< txiter > GetIterVec(const std::vector< uint256 > &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Translate a list of hashes into a list of mempool iterators to avoid repeated lookups.
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimate=true) EXCLUSIVE_LOCKS_REQUIRED(cs
If sanity-checking is turned on, check makes sure the pool is consistent (does not contain two transa...
std::optional< txiter > GetIter(const uint256 &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
std::vector< txiter > GatherClusters(const std::vector< uint256 > &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Collect the entire cluster of connected transactions for each transaction in txids.
std::set< txiter, CompareIteratorByHash > setEntries
const CTransaction * GetConflictTx(const COutPoint &prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Get the transaction in the pool that spends the same prevout.
bool isSpent(const COutPoint &outpoint) const
A minimal version of BlockAssembler.
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.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
BOOST_AUTO_TEST_SUITE_END()
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)
BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
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
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