30std::vector<COutPoint> g_outpoints_coinbase_init_mature;
36 lastRollingFeeUpdate =
GetTime();
37 blockSinceLastRollingFeeBump =
true;
41void initialize_tx_pool()
43 static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
44 g_setup = testing_setup.get();
47 BlockAssembler::Options options;
54 g_outpoints_coinbase_init_mature.push_back(prevout);
61 std::set<COutPoint>& m_mempool_outpoints;
63 explicit OutpointsUpdater(std::set<COutPoint>& r)
64 : m_mempool_outpoints{r} {}
71 for (uint32_t index{0}; index < tx.
info.
m_tx->vout.size(); ++index) {
79 for (
const auto& input : tx->vin) {
81 m_mempool_outpoints.insert(input.prevout);
84 for (uint32_t index{0}; index < tx->vout.size(); ++index) {
85 m_mempool_outpoints.erase(
COutPoint{tx->GetHash(), index});
91 std::set<CTransactionRef>& m_added;
93 explicit TransactionsDelta(std::set<CTransactionRef>& a)
111 const auto time =
ConsumeTime(fuzzed_data_provider,
113 std::numeric_limits<
decltype(chainstate.
m_chain.
Tip()->
nTime)>::max());
125 mempool_opts.limits.ancestor_size_vbytes = fuzzed_data_provider.
ConsumeIntegralInRange<
unsigned>(0, 202) * 1'000;
127 mempool_opts.limits.descendant_size_vbytes = fuzzed_data_provider.
ConsumeIntegralInRange<
unsigned>(0, 202) * 1'000;
133 mempool_opts.check_ratio = 1;
134 mempool_opts.require_standard = fuzzed_data_provider.
ConsumeBool();
138 auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
145std::unique_ptr<CTxMemPool> MakeEphemeralMempool(
const NodeContext&
node)
153 mempool_opts.require_standard =
true;
156 mempool_opts.min_relay_feerate =
CFeeRate(0);
160 auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
170std::optional<COutPoint> GetChildEvictingPrevout(
const CTxMemPool& tx_pool)
173 for (
const auto& tx_info : tx_pool.
infoAll()) {
174 const auto& entry = *
Assert(tx_pool.
GetEntry(tx_info.tx->GetHash()));
176 if (!dust_indexes.empty()) {
177 const auto& children = entry.GetMemPoolChildrenConst();
178 if (!children.empty()) {
179 Assert(children.size() == 1);
181 const auto& only_child = children.begin()->get().GetTx();
182 for (
const auto& tx_input : only_child.vin) {
183 if (tx_input.prevout.hash != tx_info.tx->GetHash()) {
184 return tx_input.prevout;
201 MockTime(fuzzed_data_provider, chainstate);
204 std::set<COutPoint> mempool_outpoints;
205 std::unordered_map<COutPoint, CAmount, SaltedOutpointHasher> outpoints_value;
206 for (
const auto& outpoint : g_outpoints_coinbase_init_mature) {
207 Assert(mempool_outpoints.insert(outpoint).second);
208 outpoints_value[outpoint] = 50 *
COIN;
211 auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
212 node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
214 auto tx_pool_{MakeEphemeralMempool(
node)};
215 MockedTxPool& tx_pool = *
static_cast<MockedTxPool*
>(tx_pool_.get());
217 chainstate.SetMempool(&tx_pool);
221 Assert(!mempool_outpoints.empty());
223 std::vector<CTransactionRef> txs;
226 std::optional<COutPoint> outpoint_to_rbf{fuzzed_data_provider.
ConsumeBool() ? GetChildEvictingPrevout(tx_pool) :
std::nullopt};
231 std::set<COutPoint> package_outpoints;
232 while (txs.size() < num_txs) {
234 txs.emplace_back([&] {
241 bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
242 const auto num_in = outpoint_to_rbf ? 2 :
243 last_tx ? fuzzed_data_provider.
ConsumeIntegralInRange<
int>(package_outpoints.size()/2 + 1, package_outpoints.size()) :
247 auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
249 Assert((
int)outpoints.size() >= num_in && num_in > 0);
252 for (
int i = 0; i < num_in; ++i) {
255 auto pop = outpoints.begin();
257 auto outpoint = *pop;
259 if (i == 0 && outpoint_to_rbf) {
260 outpoint = *outpoint_to_rbf;
261 outpoints.erase(outpoint);
263 outpoints.erase(pop);
266 amount_in += outpoints_value.at(outpoint);
273 tx_mut.
vin.push_back(in);
277 const auto amount_out = (amount_in - amount_fee) / num_out;
278 for (
int i = 0; i < num_out; ++i) {
283 if (!outpoint_to_rbf && fuzzed_data_provider.
ConsumeBool()) {
291 for (
const auto& in : tx->vin) {
295 for (
size_t i = 0; i < tx->vout.size(); ++i) {
296 package_outpoints.emplace(tx->GetHash(), i);
300 for (
size_t i = 0; i < tx->vout.size(); ++i) {
301 outpoints_value[
COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
308 const auto& txid = fuzzed_data_provider.
ConsumeBool() ?
309 txs.back()->GetHash() :
310 PickValue(fuzzed_data_provider, mempool_outpoints).hash;
314 if (tx_pool.exists(txid)) {
315 const auto tx_info{tx_pool.info(txid)};
316 if (
GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate).empty()) {
317 tx_pool.PrioritiseTransaction(txid, delta);
322 auto single_submit = txs.size() == 1;
328 fuzzed_data_provider.
ConsumeBool(), !single_submit));
334 const bool expect_valid{result_package.m_state.IsValid()};
338 node.validation_signals->SyncWithValidationInterfaceQueue();
343 node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
345 WITH_LOCK(
::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
356 MockTime(fuzzed_data_provider, chainstate);
359 std::set<COutPoint> mempool_outpoints;
360 std::unordered_map<COutPoint, CAmount, SaltedOutpointHasher> outpoints_value;
361 for (
const auto& outpoint : g_outpoints_coinbase_init_mature) {
362 Assert(mempool_outpoints.insert(outpoint).second);
363 outpoints_value[outpoint] = 50 *
COIN;
366 auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
367 node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
369 auto tx_pool_{MakeMempool(fuzzed_data_provider,
node)};
370 MockedTxPool& tx_pool = *
static_cast<MockedTxPool*
>(tx_pool_.get());
372 chainstate.SetMempool(&tx_pool);
376 Assert(!mempool_outpoints.empty());
378 std::vector<CTransactionRef> txs;
382 std::set<COutPoint> package_outpoints;
383 while (txs.size() < num_txs) {
385 txs.emplace_back([&] {
392 bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
393 const auto num_in = last_tx ? package_outpoints.size() : fuzzed_data_provider.
ConsumeIntegralInRange<
int>(1, mempool_outpoints.size());
396 auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
398 Assert(!outpoints.empty());
401 for (
size_t i = 0; i < num_in; ++i) {
404 auto pop = outpoints.begin();
406 const auto outpoint = *pop;
407 outpoints.erase(pop);
409 amount_in += outpoints_value.at(outpoint);
413 const auto script_sig =
CScript{};
422 tx_mut.
vin.push_back(in);
426 bool dup_input = fuzzed_data_provider.
ConsumeBool();
428 tx_mut.
vin.push_back(tx_mut.
vin.back());
433 tx_mut.
vin.emplace_back();
437 if (last_tx && amount_in > 1000 && fuzzed_data_provider.
ConsumeBool()) {
445 const auto amount_out = (amount_in - amount_fee) / num_out;
446 for (
int i = 0; i < num_out; ++i) {
452 for (
const auto& in : tx->vin) {
457 for (
size_t i = 0; i < tx->vout.size(); ++i) {
458 package_outpoints.emplace(tx->GetHash(), i);
462 for (
size_t i = 0; i < tx->vout.size(); ++i) {
463 outpoints_value[
COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
470 MockTime(fuzzed_data_provider, chainstate);
473 tx_pool.RollingFeeUpdate();
476 const auto& txid = fuzzed_data_provider.
ConsumeBool() ?
477 txs.back()->GetHash() :
478 PickValue(fuzzed_data_provider, mempool_outpoints).hash;
480 tx_pool.PrioritiseTransaction(txid, delta);
484 std::set<CTransactionRef> added;
485 auto txr = std::make_shared<TransactionsDelta>(added);
486 node.validation_signals->RegisterSharedValidationInterface(txr);
491 auto single_submit = txs.size() == 1 && fuzzed_data_provider.
ConsumeBool();
494 std::optional<CFeeRate> client_maxfeerate{};
500 return ProcessNewPackage(chainstate, tx_pool, txs, single_submit, client_maxfeerate));
505 false, !single_submit));
508 node.validation_signals->SyncWithValidationInterfaceQueue();
509 node.validation_signals->UnregisterSharedValidationInterface(txr);
513 Assert(passed != added.empty());
514 Assert(passed == res.m_state.IsValid());
516 Assert(added.size() == 1);
517 Assert(txs.back() == *added.begin());
523 const bool expect_valid{result_package.m_state.IsValid()};
527 Assert(result_package.m_tx_results.size() == txs.size() || result_package.m_tx_results.empty());
533 if (tx_pool.m_opts.require_standard) {
538 node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
540 WITH_LOCK(
::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
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.
int64_t GetMedianTimePast() const
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
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 const 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 ...
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
std::vector< TxMempoolInfo > infoAll() const
const CTxMemPoolEntry * GetEntry(const Txid &txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs)
An output of a transaction.
Implement this to subscribe to events generated in validation and mempool.
virtual void TransactionRemovedFromMempool(const CTransactionRef &tx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
Notifies listeners of a transaction leaving mempool.
virtual void TransactionAddedToMempool(const NewMempoolTransactionInfo &tx, uint64_t mempool_sequence)
Notifies listeners of a transaction having been added to mempool.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
CChain m_chain
The current chain of blockheaders we consult and build on.
T ConsumeIntegralInRange(T min, T max)
Generate a new block, without valid proof-of-work.
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (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.
@ PCKG_POLICY
The package itself is invalid (e.g. too many transactions).
unsigned int nBytesPerSigOp
std::vector< uint32_t > GetDust(const CTransaction &tx, CFeeRate dust_relay_rate)
Get the vout index numbers of all dust outputs.
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
A mutable version of CTransaction.
std::vector< CTxOut > vout
std::vector< std::vector< unsigned char > > stack
Testing setup that configures a complete environment.
const CTransactionRef m_tx
int64_t ancestor_count
The maximum allowed number of transactions in a package including the entry and its ancestors.
Options struct containing options for constructing a CTxMemPool.
CFeeRate dust_relay_feerate
NodeContext struct containing references to chain state and connection state.
std::unique_ptr< ValidationSignals > validation_signals
Issues calls about blocks and transactions.
std::unique_ptr< ChainstateManager > chainman
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
uint32_t ConsumeSequence(FuzzedDataProvider &fuzzed_data_provider) noexcept
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
COutPoint MineBlock(const NodeContext &node, const node::BlockAssembler::Options &assembler_options)
Returns the generated coin.
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
@ ZEROS
Seed with a compile time constant of zeros.
static const std::vector< std::vector< uint8_t > > P2WSH_EMPTY_TRUE_STACK
static const std::vector< std::vector< uint8_t > > P2WSH_EMPTY_TWO_STACK
static const CScript P2WSH_EMPTY
void CheckMempoolTRUCInvariants(const CTxMemPool &tx_pool)
For every transaction in tx_pool, check TRUC invariants:
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node)
std::optional< std::string > CheckPackageMempoolAcceptResult(const Package &txns, const PackageMempoolAcceptResult &result, bool expect_valid, const CTxMemPool *mempool)
Check expected properties for every PackageMempoolAcceptResult, regardless of value.
void CheckMempoolEphemeralInvariants(const CTxMemPool &tx_pool)
Check that we never get into a state where an ephemeral dust transaction would be mined without the s...
#define EXCLUSIVE_LOCKS_REQUIRED(...)
static constexpr decltype(CTransaction::version) TRUC_VERSION
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
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.