11 #include <boost/test/unit_test.hpp>
20 tx.
vin.resize(inputs.size());
21 tx.
vout.resize(num_outputs);
22 for (
size_t i = 0; i < inputs.size(); ++i) {
23 tx.
vin[i].prevout = inputs[i];
25 for (
size_t i = 0; i < num_outputs; ++i) {
34 static inline bool sanity_check(
const std::vector<CTransactionRef>& transactions,
35 const std::map<COutPoint, CAmount>& bumpfees)
38 for (
const auto& [outpoint, fee] : bumpfees) {
39 if (fee < 0)
return false;
40 if (fee == 0)
continue;
41 auto outpoint_ = outpoint;
42 const bool found = std::any_of(transactions.cbegin(), transactions.cend(), [&](
const auto& tx) {
43 return outpoint_.hash == tx->GetHash() && outpoint_.n < tx->vout.size();
45 if (!found)
return false;
47 for (
const auto& tx : transactions) {
49 if (tx->vout.size() > 1) {
50 std::set<CAmount> distinct_bumpfees;
51 for (
size_t i{0}; i < tx->vout.size(); ++i) {
52 const auto bumpfee = bumpfees.find(
COutPoint{tx->GetHash(),
static_cast<uint32_t
>(i)});
53 if (
bumpfee != bumpfees.end()) distinct_bumpfees.insert(
bumpfee->second);
55 if (distinct_bumpfees.size() > 1)
return false;
61 template <
typename Key,
typename Value>
62 Value
Find(
const std::map<Key, Value>& map,
const Key& key)
64 auto it = map.find(key);
65 BOOST_CHECK_MESSAGE(it != map.end(),
strprintf(
"Cannot find %s", key.ToString()));
106 std::vector<COutPoint> all_unspent_outpoints({
116 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
118 std::vector<COutPoint> all_spent_outpoints({
126 std::vector<COutPoint> all_parent_outputs({
138 std::vector<CTransactionRef> all_transactions{tx1, tx2, tx3, tx4, tx5, tx6, tx7, tx8};
139 struct TxDimensions {
142 std::map<uint256, TxDimensions> tx_dims;
143 for (
const auto& tx : all_transactions) {
144 const auto it = pool.
GetIter(tx->GetHash()).value();
145 tx_dims.emplace(tx->GetHash(), TxDimensions{it->GetTxSize(), it->GetModifiedFee(),
146 CFeeRate(it->GetModifiedFee(), it->GetTxSize())});
156 for (
const auto& outpoint : nonexistent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
157 for (
const auto& feerate : various_normal_feerates) {
163 BOOST_CHECK(bump_fees.size() == nonexistent_outpoints.size());
164 for (
const auto& outpoint: nonexistent_outpoints) {
165 auto it = bump_fees.find(outpoint);
172 for (
const auto& target_feerate : various_normal_feerates) {
181 const TxDimensions& tx1_dimensions = tx_dims.find(tx1->GetHash())->second;
183 if (target_feerate <= tx1_dimensions.feerate) {
187 BOOST_CHECK_EQUAL(bumpfee1, target_feerate.GetFee(tx1_dimensions.vsize) - tx1_dimensions.mod_fee);
191 const TxDimensions& tx3_dimensions = tx_dims.find(tx3->GetHash())->second;
192 const TxDimensions& tx4_dimensions = tx_dims.find(tx4->GetHash())->second;
193 const CFeeRate tx3_feerate =
CFeeRate(tx3_dimensions.mod_fee + tx4_dimensions.mod_fee, tx3_dimensions.vsize + tx4_dimensions.vsize);
195 if (target_feerate <= tx3_feerate) {
200 BOOST_CHECK_EQUAL(bumpfee3, target_feerate.GetFee(tx3_dimensions.vsize) - tx3_dimensions.mod_fee);
211 const TxDimensions& tx5_dimensions = tx_dims.find(tx5->GetHash())->second;
212 const TxDimensions& tx6_dimensions = tx_dims.find(tx6->GetHash())->second;
213 const CFeeRate tx5_feerate =
CFeeRate(tx5_dimensions.mod_fee + tx6_dimensions.mod_fee, tx5_dimensions.vsize + tx6_dimensions.vsize);
215 if (target_feerate <= tx5_feerate) {
220 BOOST_CHECK_EQUAL(bumpfee5, target_feerate.GetFee(tx5_dimensions.vsize) - tx5_dimensions.mod_fee);
226 for (
const auto& target_feerate : various_normal_feerates) {
229 auto bump_fees_all_spent = mini_miner_all_spent.
CalculateBumpFees(target_feerate);
234 auto bump_fees_all_parents = mini_miner_all_parents.
CalculateBumpFees(target_feerate);
237 for (
auto& bump_fees : {bump_fees_all_parents, bump_fees_all_spent}) {
243 const TxDimensions& tx1_dimensions = tx_dims.find(tx1->GetHash())->second;
245 if (target_feerate <= tx1_dimensions.feerate) {
249 BOOST_CHECK_EQUAL(it1_spent, target_feerate.GetFee(tx1_dimensions.vsize) - tx1_dimensions.mod_fee);
253 const TxDimensions& tx3_dimensions = tx_dims.find(tx3->GetHash())->second;
254 const CFeeRate tx3_feerate_unbumped = tx3_dimensions.feerate;
255 auto it3_spent =
Find(bump_fees,
COutPoint{tx3->GetHash(), 0});
256 if (target_feerate <= tx3_feerate_unbumped) {
260 BOOST_CHECK_EQUAL(it3_spent, target_feerate.GetFee(tx3_dimensions.vsize) - tx3_dimensions.mod_fee);
264 const TxDimensions& tx5_dimensions = tx_dims.find(tx5->GetHash())->second;
265 const CFeeRate tx5_feerate_unbumped = tx5_dimensions.feerate;
266 auto it5_spent =
Find(bump_fees,
COutPoint{tx5->GetHash(), 0});
267 if (target_feerate <= tx5_feerate_unbumped) {
271 BOOST_CHECK_EQUAL(it5_spent, target_feerate.GetFee(tx5_dimensions.vsize) - tx5_dimensions.mod_fee);
307 std::vector<CTransactionRef> all_transactions{tx1, tx2, tx3, tx4, tx5, tx6, tx7, tx8};
308 std::vector<int64_t> tx_vsizes;
309 tx_vsizes.reserve(all_transactions.size());
312 std::vector<COutPoint> all_unspent_outpoints({
324 for (
const auto& outpoint : all_unspent_outpoints)
BOOST_CHECK(!pool.
isSpent(outpoint));
326 const auto tx3_feerate =
CFeeRate(high_fee, tx_vsizes[2]);
327 const auto tx4_feerate =
CFeeRate(high_fee, tx_vsizes[3]);
330 const auto tx4_anc_feerate =
CFeeRate(low_fee + med_fee + high_fee, tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[3]);
331 const auto tx5_feerate =
CFeeRate(high_fee, tx_vsizes[4]);
332 const auto tx7_anc_feerate =
CFeeRate(low_fee + med_fee, tx_vsizes[5] + tx_vsizes[6]);
333 const auto tx8_anc_feerate =
CFeeRate(low_fee + high_fee, tx_vsizes[5] + tx_vsizes[7]);
347 const auto tx1_bumpfee = bump_fees.find(
COutPoint{tx1->GetHash(), 1});
350 const auto tx4_bumpfee = bump_fees.find(
COutPoint{tx4->GetHash(), 0});
353 very_high_feerate.
GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));
354 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
357 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]) - (high_fee + low_fee + med_fee));
358 const auto tx8_bumpfee = bump_fees.find(
COutPoint{tx8->GetHash(), 0});
361 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]) - (high_fee + low_fee + high_fee));
364 BOOST_CHECK(mini_miner_total_tx4.IsReadyToCalculate());
365 const auto tx4_bump_fee = mini_miner_total_tx4.CalculateTotalBumpFees(very_high_feerate);
366 BOOST_CHECK(!mini_miner_total_tx4.IsReadyToCalculate());
369 very_high_feerate.
GetFee(tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]) - (low_fee + med_fee + high_fee + high_fee));
372 BOOST_CHECK(mini_miner_tx7_tx8.IsReadyToCalculate());
373 const auto tx7_tx8_bumpfee = mini_miner_tx7_tx8.CalculateTotalBumpFees(very_high_feerate);
374 BOOST_CHECK(!mini_miner_tx7_tx8.IsReadyToCalculate());
377 very_high_feerate.
GetFee(tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6] + tx_vsizes[7]) - (high_fee + low_fee + med_fee + high_fee));
381 const auto just_below_tx5 =
CFeeRate(tx5_feerate.GetFeePerK() - 5);
388 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
390 BOOST_CHECK_EQUAL(tx7_bumpfee->second, just_below_tx5.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));
391 const auto tx8_bumpfee = bump_fees.find(
COutPoint{tx8->GetHash(), 0});
393 BOOST_CHECK_EQUAL(tx8_bumpfee->second, just_below_tx5.GetFee(tx_vsizes[5] + tx_vsizes[7]) - (low_fee + high_fee));
396 BOOST_CHECK(mini_miner_tx7_tx8.IsReadyToCalculate());
397 const auto tx7_tx8_bumpfee = mini_miner_tx7_tx8.CalculateTotalBumpFees(just_below_tx5);
398 BOOST_CHECK(!mini_miner_tx7_tx8.IsReadyToCalculate());
400 BOOST_CHECK_EQUAL(tx7_tx8_bumpfee.value(), just_below_tx5.GetFee(tx_vsizes[5] + tx_vsizes[6]) - (low_fee + med_fee));
404 const auto just_above_tx7 =
CFeeRate(med_fee + 10, tx_vsizes[6]);
412 const auto tx7_bumpfee = bump_fees.find(
COutPoint{tx7->GetHash(), 0});
414 BOOST_CHECK_EQUAL(tx7_bumpfee->second, just_above_tx7.GetFee(tx_vsizes[6]) - (med_fee));
415 const auto tx8_bumpfee = bump_fees.find(
COutPoint{tx8->GetHash(), 0});
427 std::vector<uint256> chain_txids;
428 auto& lasttx = m_coinbase_txns[0];
429 for (
auto i{0}; i < 500; ++i) {
432 chain_txids.push_back(tx->GetHash());
435 const auto cluster_500tx = pool.
GatherClusters({lasttx->GetHash()});
438 const auto vec_iters_500 = pool.
GetIterVec(chain_txids);
439 for (
const auto& iter : vec_iters_500)
BOOST_CHECK(cluster_500tx_set.count(iter));
444 const auto cluster_501 = pool.
GatherClusters({tx_501->GetHash()});
453 std::vector<uint256> zigzag_txids;
454 for (
auto p{0}; p < 50; ++p) {
457 zigzag_txids.push_back(txp->GetHash());
459 for (
auto c{0}; c < 49; ++c) {
462 zigzag_txids.push_back(txc->GetHash());
464 const auto vec_iters_zigzag = pool.
GetIterVec(zigzag_txids);
466 const std::vector<size_t> indeces{0, 22, 72, zigzag_txids.size() - 1};
467 for (
const auto index : indeces) {
472 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