27 std::vector<COutPoint> g_outpoints_coinbase_init_mature;
33 lastRollingFeeUpdate =
GetTime();
34 blockSinceLastRollingFeeBump =
true;
38 void initialize_tx_pool()
40 static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
41 g_setup = testing_setup.get();
47 g_outpoints_coinbase_init_mature.push_back(prevout);
54 std::set<COutPoint>& m_mempool_outpoints;
56 explicit OutpointsUpdater(std::set<COutPoint>& r)
57 : m_mempool_outpoints{r} {}
64 for (uint32_t index{0}; index < tx.
info.
m_tx->vout.size(); ++index) {
72 for (
const auto& input : tx->vin) {
74 m_mempool_outpoints.insert(input.prevout);
77 for (uint32_t index{0}; index < tx->vout.size(); ++index) {
78 m_mempool_outpoints.erase(
COutPoint{tx->GetHash(), index});
84 std::set<CTransactionRef>& m_added;
86 explicit TransactionsDelta(std::set<CTransactionRef>& a)
104 const auto time =
ConsumeTime(fuzzed_data_provider,
106 std::numeric_limits<decltype(chainstate.
m_chain.
Tip()->
nTime)>::max());
118 mempool_opts.limits.ancestor_size_vbytes = fuzzed_data_provider.
ConsumeIntegralInRange<
unsigned>(0, 202) * 1
'000; 119 mempool_opts.limits.descendant_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50); 120 mempool_opts.limits.descendant_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000;
126 mempool_opts.check_ratio = 1; 127 mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool(); 129 // ...and construct a CTxMemPool from it 130 return CTxMemPool{mempool_opts}; 133 FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool) 135 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 136 const auto& node = g_setup->m_node; 137 auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())}; 139 MockTime(fuzzed_data_provider, chainstate); 141 // All RBF-spendable outpoints outside of the unsubmitted package 142 std::set<COutPoint> mempool_outpoints; 143 std::map<COutPoint, CAmount> outpoints_value; 144 for (const auto& outpoint : g_outpoints_coinbase_init_mature) { 145 Assert(mempool_outpoints.insert(outpoint).second); 146 outpoints_value[outpoint] = 50 * COIN; 149 auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints); 150 RegisterSharedValidationInterface(outpoints_updater); 152 CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)}; 153 MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_); 155 chainstate.SetMempool(&tx_pool); 157 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300) 159 Assert(!mempool_outpoints.empty()); 161 std::vector<CTransactionRef> txs; 163 // Make packages of 1-to-26 transactions 164 const auto num_txs = (size_t) fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 26); 165 std::set<COutPoint> package_outpoints; 166 while (txs.size() < num_txs) { 168 // Last transaction in a package needs to be a child of parents to get further in validation 169 // so the last transaction to be generated(in a >1 package) must spend all package-made outputs 170 // Note that this test currently only spends package outputs in last transaction. 171 bool last_tx = num_txs > 1 && txs.size() == num_txs - 1; 173 // Create transaction to add to the mempool 174 const CTransactionRef tx = [&] { 175 CMutableTransaction tx_mut; 176 tx_mut.nVersion = fuzzed_data_provider.ConsumeBool() ? 3 : CTransaction::CURRENT_VERSION; 177 tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>(); 178 // Last tx will sweep all outpoints in package 179 const auto num_in = last_tx ? package_outpoints.size() : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size()); 180 auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size() * 2); 182 auto& outpoints = last_tx ? package_outpoints : mempool_outpoints; 184 Assert(!outpoints.empty()); 186 CAmount amount_in{0}; 187 for (size_t i = 0; i < num_in; ++i) { 188 // Pop random outpoint 189 auto pop = outpoints.begin(); 190 std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints.size() - 1)); 191 const auto outpoint = *pop; 192 outpoints.erase(pop); 193 // no need to update or erase from outpoints_value 194 amount_in += outpoints_value.at(outpoint); 197 const auto sequence = ConsumeSequence(fuzzed_data_provider); 198 const auto script_sig = CScript{}; 199 const auto script_wit_stack = fuzzed_data_provider.ConsumeBool() ? P2WSH_EMPTY_TRUE_STACK : P2WSH_EMPTY_TWO_STACK; 202 in.prevout = outpoint; 203 in.nSequence = sequence; 204 in.scriptSig = script_sig; 205 in.scriptWitness.stack = script_wit_stack; 207 tx_mut.vin.push_back(in); 210 // Duplicate an input 211 bool dup_input = fuzzed_data_provider.ConsumeBool(); 213 tx_mut.vin.push_back(tx_mut.vin.back()); 216 // Refer to a non-existent input 217 if (fuzzed_data_provider.ConsumeBool()) { 218 tx_mut.vin.emplace_back(); 221 // Make a p2pk output to make sigops adjusted vsize to violate v3, potentially, which is never spent 222 if (last_tx && amount_in > 1000 && fuzzed_data_provider.ConsumeBool()) { 223 tx_mut.vout.emplace_back(1000, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG); 224 // Don't add any other outputs.
230 const auto amount_out = (amount_in - amount_fee) / num_out;
231 for (
int i = 0; i < num_out; ++i) {
237 for (
const auto& in : tx->vin) {
239 Assert(in ==
CTxIn() || outpoints.insert(in.prevout).second || dup_input);
242 for (
size_t i = 0; i < tx->vout.size(); ++i) {
243 package_outpoints.emplace(tx->GetHash(), i);
247 for (
size_t i = 0; i < tx->vout.size(); ++i) {
248 outpoints_value[
COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
256 MockTime(fuzzed_data_provider, chainstate);
259 tx_pool.RollingFeeUpdate();
262 const auto& txid = fuzzed_data_provider.
ConsumeBool() ?
263 txs.back()->GetHash() :
264 PickValue(fuzzed_data_provider, mempool_outpoints).hash;
266 tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);
270 std::set<CTransactionRef> added;
271 auto txr = std::make_shared<TransactionsDelta>(added);
277 auto single_submit = txs.size() == 1 && fuzzed_data_provider.
ConsumeBool();
285 false, !single_submit));
293 Assert(passed != added.empty());
294 Assert(passed == res.m_state.IsValid());
296 Assert(added.size() == 1);
297 Assert(txs.back() == *added.begin());
303 const bool expect_valid{result_package.m_state.IsValid()};
307 Assert(result_package.m_tx_results.size() == txs.size() || result_package.m_tx_results.empty());
std::shared_ptr< const CTransaction > CTransactionRef
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
virtual void TransactionRemovedFromMempool(const CTransactionRef &tx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
Notifies listeners of a transaction leaving mempool.
The package itself is invalid (e.g. too many transactions).
MemPoolRemovalReason
Reason why a transaction was removed from the mempool, this is passed to the notification signal...
int Height() const
Return the maximal height in the chain.
static const int COINBASE_MATURITY
Coinbase transaction outputs can only be spent after this number of new blocks (network rule) ...
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.
CChain m_chain
The current chain of blockheaders we consult and build on.
virtual void TransactionAddedToMempool(const NewMempoolTransactionInfo &tx, uint64_t mempool_sequence)
Notifies listeners of a transaction having been added to mempool.
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
Implement this to subscribe to events generated in validation and mempool.
int64_t CAmount
Amount in satoshis (Can be negative)
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(
Try to add a transaction to the mempool.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
NodeContext struct containing references to chain state and connection state.
Chainstate stores and provides an API to update our local knowledge of the current best chain...
An input of a transaction.
void CheckMempoolV3Invariants(const CTxMemPool &tx_pool)
For every transaction in tx_pool, check v3 invariants:
PackageMempoolAcceptResult ProcessNewPackage(Chainstate &active_chainstate, CTxMemPool &pool, const Package &package, bool test_accept)
Validate (and maybe submit) a package to the mempool.
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.
An outpoint - a combination of a transaction hash and an index n into its vout.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
int64_t GetMedianTimePast() const
static CTransactionRef MakeTransactionRef(Tx &&txIn)
COutPoint MineBlock(const NodeContext &node, const CScript &coinbase_scriptPubKey)
Returns the generated coin.
static const CScript P2WSH_EMPTY
#define EXCLUSIVE_LOCKS_REQUIRED(...)
unsigned int nBytesPerSigOp
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
const CTransactionRef m_tx
Options struct containing options for constructing a CTxMemPool.
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
int64_t ancestor_count
The maximum allowed number of transactions in a package including the entry and its ancestors...
T ConsumeIntegralInRange(T min, T max)
int64_t GetTime()
DEPRECATED, see GetTime.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Testing setup that configures a complete environment.
#define Assert(val)
Identity function.
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node)
static constexpr CAmount COIN
The amount of satoshis in one BTC.