58std::vector<COutPoint> g_outpoints_coinbase_init_mature;
59std::vector<COutPoint> g_outpoints_coinbase_init_immature;
65 lastRollingFeeUpdate =
GetTime();
66 blockSinceLastRollingFeeBump =
true;
70void initialize_tx_pool()
78 .coinbase_output_script = P2WSH_OP_TRUE,
82 g_outpoints_coinbase_init_mature :
83 g_outpoints_coinbase_init_immature;
84 outpoints.push_back(prevout);
90 std::set<CTransactionRef>& m_removed;
91 std::set<CTransactionRef>& m_added;
93 explicit TransactionsDelta(std::set<CTransactionRef>& r, std::set<CTransactionRef>& a)
94 : m_removed{r}, m_added{a} {}
96 void TransactionAddedToMempool(
const NewMempoolTransactionInfo& tx, uint64_t )
override
103 Assert(m_removed.insert(tx).second);
121std::vector<Wtxid> WtxidsToRelay(
FuzzedDataProvider& fuzzed_data_provider,
const MockedTxPool& tx_pool)
124 std::vector<Wtxid> res;
127 const auto mempool_entries{tx_pool.entryAll()};
129 if (!mempool_entries.empty() && fuzzed_data_provider.
ConsumeBool()) {
131 const auto& entry_ref{
PickValue(fuzzed_data_provider, mempool_entries).get()};
132 res.push_back(entry_ref.GetTx().GetWitnessHash());
155 Assert(block_template->block.vtx.size() >= 1);
159 tx_pool.removeForBlock(block_template->block.vtx);
162 std::vector<Txid> hashes_to_update;
163 for (
const auto& tx : block_template->block.vtx) {
166 hashes_to_update.push_back(tx->GetHash());
171 tx_pool.UpdateTransactionsFromBlock(hashes_to_update);
173 const auto info_all = tx_pool.infoAll();
174 if (!info_all.empty()) {
175 const auto& tx_to_remove = *
PickValue(fuzzed_data_provider, info_all).tx;
177 assert(tx_pool.size() < info_all.size());
181 auto wtxids_to_relay{WtxidsToRelay(fuzzed_data_provider, tx_pool)};
182 const auto wtxids_count_before{wtxids_to_relay.size()};
184 const auto sorted_iter{
WITH_LOCK(tx_pool.cs,
return tx_pool.ExtractBestByMiningScoreWithTopology(wtxids_to_relay, n_to_sort))};
185 const auto expected_count{std::min(n_to_sort, wtxids_count_before)};
187 Assert(wtxids_to_relay.size() <= wtxids_count_before - expected_count);
190 Assert(sorted_iter.size() == expected_count || wtxids_to_relay.empty());
194 const auto is_in_mempool = [&](
const auto& wtxid)
EXCLUSIVE_LOCKS_REQUIRED(tx_pool.cs) {
return tx_pool.GetIter(wtxid).has_value(); };
195 Assert(
WITH_LOCK(tx_pool.cs,
return std::ranges::all_of(wtxids_to_relay, is_in_mempool)));
197 const auto wtxids_count{wtxids_to_relay.size()};
198 const std::set<Wtxid> unique_wtxids{std::make_move_iterator(wtxids_to_relay.begin()), std::make_move_iterator(wtxids_to_relay.end())};
199 Assert(unique_wtxids.size() == wtxids_count);
205 tx_pool.TrimToSize(fuzzed_data_provider.
ConsumeIntegralInRange<
size_t>(0U, tx_pool.DynamicMemoryUsage() * 2));
218 const auto time =
ConsumeTime(fuzzed_data_provider,
220 std::numeric_limits<
decltype(chainstate.
m_chain.
Tip()->
nTime)>::max());
235 auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
242void CheckATMPInvariants(
const MempoolAcceptResult& res,
bool txid_in_mempool,
bool wtxid_in_mempool)
297 MockTime(fuzzed_data_provider, chainstate);
300 std::set<COutPoint> outpoints_rbf;
302 std::set<COutPoint> outpoints_supply;
303 for (
const auto& outpoint : g_outpoints_coinbase_init_mature) {
304 Assert(outpoints_supply.insert(outpoint).second);
306 outpoints_rbf = outpoints_supply;
311 SetMempoolConstraints(*
node.args, fuzzed_data_provider);
312 auto tx_pool_{MakeMempool(fuzzed_data_provider,
node)};
313 MockedTxPool& tx_pool = *
static_cast<MockedTxPool*
>(tx_pool_.get());
315 chainstate.SetMempool(&tx_pool);
319 const auto GetAmount = [&](
const COutPoint& outpoint) {
320 auto coin{amount_view.
GetCoin(outpoint).value()};
321 return coin.out.nValue;
328 for (
const auto& op : outpoints_supply) {
329 supply_now += GetAmount(op);
331 Assert(supply_now == SUPPLY_TOTAL);
333 Assert(!outpoints_supply.empty());
344 for (
int i = 0; i < num_in; ++i) {
346 auto pop = outpoints_rbf.begin();
348 const auto outpoint = *pop;
349 outpoints_rbf.erase(pop);
350 amount_in += GetAmount(outpoint);
354 const auto script_sig =
CScript{};
355 const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
362 tx_mut.
vin.push_back(in);
366 bool add_sigops{fuzzed_data_provider.
ConsumeBool()};
369 const auto amount_out = (amount_in - amount_fee) / num_out;
370 for (
int i = 0; i < num_out; ++i) {
371 if (i == 0 && add_sigops) {
374 tx_mut.
vout.emplace_back(amount_out, P2WSH_OP_TRUE);
378 auto tx = MakeTransactionRef(tx_mut);
380 for (
const auto& in : tx->vin) {
387 MockTime(fuzzed_data_provider, chainstate);
390 tx_pool.RollingFeeUpdate();
393 const auto& txid = fuzzed_data_provider.
ConsumeBool() ?
395 PickValue(fuzzed_data_provider, outpoints_rbf).hash;
397 tx_pool.PrioritiseTransaction(txid, delta);
401 std::set<CTransactionRef> removed;
402 std::set<CTransactionRef> added;
403 auto txr = std::make_shared<TransactionsDelta>(removed, added);
404 node.validation_signals->RegisterSharedValidationInterface(txr);
413 auto it = result_package.m_tx_results.find(tx->GetWitnessHash());
414 Assert(it != result_package.m_tx_results.end());
421 node.validation_signals->SyncWithValidationInterfaceQueue();
422 node.validation_signals->UnregisterSharedValidationInterface(txr);
424 bool txid_in_mempool = tx_pool.exists(tx->GetHash());
425 bool wtxid_in_mempool = tx_pool.exists(tx->GetWitnessHash());
426 CheckATMPInvariants(res, txid_in_mempool, wtxid_in_mempool);
428 Assert(accepted != added.empty());
430 Assert(added.size() == 1);
431 Assert(tx == *added.begin());
439 using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>;
440 const auto insert_tx = [](Sets created_by_tx, Sets consumed_by_tx,
const auto& tx) {
441 for (
size_t i{0}; i < tx.vout.size(); ++i) {
442 for (
auto& set : created_by_tx) {
443 Assert(set.get().emplace(tx.GetHash(), i).second);
446 for (
const auto& in : tx.vin) {
447 for (
auto& set : consumed_by_tx) {
455 std::set<COutPoint> consumed_erased;
457 std::set<COutPoint> consumed_supply;
458 for (
const auto& removed_tx : removed) {
459 insert_tx({consumed_erased}, {outpoints_supply}, *removed_tx);
461 for (
const auto& added_tx : added) {
462 insert_tx({outpoints_supply, outpoints_rbf}, {consumed_supply}, *added_tx);
464 for (
const auto& p : consumed_erased) {
465 Assert(outpoints_supply.erase(p) == 1);
466 Assert(outpoints_rbf.erase(p) == 1);
468 for (
const auto& p : consumed_supply) {
469 Assert(outpoints_supply.erase(p) == 1);
473 Finish(fuzzed_data_provider, tx_pool, chainstate);
483 MockTime(fuzzed_data_provider, chainstate);
485 std::vector<Txid> txids;
486 txids.reserve(g_outpoints_coinbase_init_mature.size());
487 for (
const auto& outpoint : g_outpoints_coinbase_init_mature) {
488 txids.push_back(outpoint.hash);
490 for (
int i{0}; i <= 3; ++i) {
492 txids.push_back(g_outpoints_coinbase_init_immature.at(i).hash);
496 SetMempoolConstraints(*
node.args, fuzzed_data_provider);
497 auto tx_pool_{MakeMempool(fuzzed_data_provider,
node)};
498 MockedTxPool& tx_pool = *
static_cast<MockedTxPool*
>(tx_pool_.get());
500 chainstate.SetMempool(&tx_pool);
503 bool ever_bypassed_limits{
false};
509 MockTime(fuzzed_data_provider, chainstate);
512 tx_pool.RollingFeeUpdate();
515 const auto txid = fuzzed_data_provider.
ConsumeBool() ?
519 tx_pool.PrioritiseTransaction(txid, delta);
522 const bool bypass_limits{fuzzed_data_provider.
ConsumeBool()};
523 ever_bypassed_limits |= bypass_limits;
525 const auto tx = MakeTransactionRef(mut_tx);
529 txids.push_back(tx->GetHash());
530 if (!ever_bypassed_limits) {
535 Finish(fuzzed_data_provider, tx_pool, chainstate);
int64_t CAmount
Amount in satoshis (Can be negative).
constexpr CAmount COIN
The amount of satoshis in one BTC.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
const TestingSetup * g_setup
#define Assert(val)
Identity function.
void ForceSetArg(const std::string &strArg, const std::string &strValue) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
int64_t GetMedianTimePast() const
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
int Height() const
Return the maximal height in the chain.
CCoinsView that brings transactions from a mempool into view.
std::optional< Coin > GetCoin(const COutPoint &outpoint) const override
GetCoin, returning whether it exists and is not spent.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
An outpoint - a combination of a transaction hash and an index n into its vout.
Serialized script, used inside transaction inputs and outputs.
static constexpr uint32_t CURRENT_VERSION
An input of a transaction.
CScriptWitness scriptWitness
Only serialized through CTransaction.
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
kernel::MemPoolOptions Options
Implement this to subscribe to events generated in validation and mempool.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
T ConsumeIntegralInRange(T min, T max)
Generate a new block, without valid proof-of-work.
std::unique_ptr< CBlockTemplate > CreateNewBlock()
Construct a new block template.
static transaction_identifier FromUint256(const uint256 &id)
@ TX_RECONSIDERABLE
fails some policy, but might be acceptable if submitted in a (different) package
constexpr int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule).
constexpr unsigned int MAX_BLOCK_WEIGHT
The maximum allowed weight for a block, see BIP 141 (network rule).
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
MemPoolRemovalReason
Reason why a transaction was removed from the mempool, this is passed to the notification signal.
@ BLOCK
Removed for block.
@ REORG
Removed for reorganization.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
@ PCKG_POLICY
The package itself is invalid (e.g. too many transactions).
constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT
Default for -blockreservedweight.
std::shared_ptr< const CTransaction > CTransactionRef
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
A mutable version of CTransaction.
std::vector< CTxOut > vout
std::vector< std::vector< unsigned char > > stack
Validation result for a transaction evaluated by MemPoolAccept (single or package).
const std::optional< int64_t > m_vsize
Virtual size as used by the mempool, calculated using serialized size and sigops.
const ResultType m_result_type
Result type.
const std::optional< CAmount > m_base_fees
Raw base fees in satoshis.
const TxValidationState m_state
Contains information about why the transaction failed.
const std::optional< CFeeRate > m_effective_feerate
The feerate at which this transaction was considered.
const std::optional< Wtxid > m_other_wtxid
The wtxid of the transaction in the mempool which has the same txid but different witness.
const std::optional< std::vector< Wtxid > > m_wtxids_fee_calculations
Contains the wtxids of the transactions used for fee-related checks.
Testing setup that configures a complete environment.
const CTransactionRef m_tx
Block template creation options.
std::unique_ptr< ValidationSignals > validation_signals
Issues calls about blocks and transactions.
std::unique_ptr< ChainstateManager > chainman
#define WITH_LOCK(cs, code)
SeedRandomStateForTest(SeedRand::ZEROS)
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
uint32_t ConsumeSequence(FuzzedDataProvider &fuzzed_data_provider) noexcept
CMutableTransaction ConsumeTransaction(FuzzedDataProvider &fuzzed_data_provider, const std::optional< std::vector< Txid > > &prevout_txids, const int max_num_in, const int max_num_out) noexcept
CAmount ConsumeMoney(FuzzedDataProvider &fuzzed_data_provider, const std::optional< CAmount > &max) noexcept
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
COutPoint MineBlock(const NodeContext &node, const node::BlockCreateOptions &assembler_options)
Returns the generated coin.
@ ZEROS
Seed with a compile time constant of zeros.
void CheckMempoolTRUCInvariants(const CTxMemPool &tx_pool)
For every transaction in tx_pool, check TRUC invariants:
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
constexpr decltype(CTransaction::version) TRUC_VERSION
void SetMockTime(std::chrono::time_point< NodeClock, std::chrono::seconds > mock)
std::chrono::seconds GetMockTime()
For testing.
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
PackageMempoolAcceptResult ProcessNewPackage(Chainstate &active_chainstate, CTxMemPool &pool, const Package &package, bool test_accept, const std::optional< CFeeRate > &client_maxfeerate)
Validate (and maybe submit) a package to the mempool.
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept)
Try to add a transaction to the mempool.