2022-12-24 23:49:50 +00:00
|
|
|
// Copyright (c) 2021-2022 The Bitcoin Core developers
|
2021-01-14 21:26:19 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#include <consensus/validation.h>
|
pool: Add and use MemPoolOptions, ApplyArgsManOptions
Reviewers: Note that CTxMemPool now requires a non-defaulted
CTxMemPool::Options for its constructor. Meaning that there's no need to
worry about a stray CTxMemPool constructor somewhere defaulting to
something incorrect. All instances of CTxMemPool construction are
addressed here in this commit.
We set options for CTxMemPool and construct it in many different ways. A
good example can be seen in how we determine CTxMemPool's check_ratio in
AppInitMain(...).
1. We first set the default based on chainparams's
DefaultConsistencyChecks()
2. Then, we apply the ArgsManager option on top of that default
3. Finally, we clamp the result of that between 0 and 1 Million
With this patch, most CTxMemPool construction are along the lines of:
MemPoolOptions mempool_opts{...default overrides...};
ApplyArgsManOptions(argsman, mempool_opts);
...hard overrides...
CTxMemPool pool{mempool_opts};
This "compositional" style of building options means that we can omit
unnecessary/irrelevant steps wherever we want but also maintain full
customizability.
For example:
- For users of libbitcoinkernel, where we eventually want to remove
ArgsManager, they simply won't call (or even know about)
ApplyArgsManOptions.
- See src/init.cpp to see how the check_ratio CTxMemPool option works
after this change.
A MemPoolOptionsForTest helper was also added and used by tests/fuzz
tests where a local CTxMemPool needed to be created.
The change in src/test/fuzz/tx_pool.cpp seemingly changes behaviour by
applying ArgsManager options on top of the CTxMemPool::Options defaults.
However, in future commits where we introduce flags like -maxmempool,
the call to ApplyArgsManOptions is actually what preserves the existing
behaviour. Previously, although it wasn't obvious, our CTxMemPool would
consult gArgs for flags like -maxmempool when it needed it, so it
already relied on ArgsManager information. This patchset just laid bare
the obfuscatory perils of globals.
[META] As this patchset progresses, we will move more and more
CTxMemPool-relevant options into MemPoolOptions and add their
ArgsMan-related logic to ApplyArgsManOptions.
2022-03-18 13:51:37 -04:00
|
|
|
#include <node/context.h>
|
2022-08-02 15:21:47 +02:00
|
|
|
#include <node/mempool_args.h>
|
2021-11-15 13:42:00 +01:00
|
|
|
#include <node/miner.h>
|
2021-01-14 21:26:19 +01:00
|
|
|
#include <test/fuzz/FuzzedDataProvider.h>
|
|
|
|
#include <test/fuzz/fuzz.h>
|
|
|
|
#include <test/fuzz/util.h>
|
2022-09-30 17:55:10 +01:00
|
|
|
#include <test/fuzz/util/mempool.h>
|
2021-01-14 21:26:19 +01:00
|
|
|
#include <test/util/mining.h>
|
|
|
|
#include <test/util/script.h>
|
|
|
|
#include <test/util/setup_common.h>
|
2022-10-10 14:27:31 +02:00
|
|
|
#include <test/util/txmempool.h>
|
2021-01-14 21:26:19 +01:00
|
|
|
#include <util/rbf.h>
|
|
|
|
#include <validation.h>
|
|
|
|
#include <validationinterface.h>
|
|
|
|
|
2021-11-12 10:06:00 -05:00
|
|
|
using node::BlockAssembler;
|
pool: Add and use MemPoolOptions, ApplyArgsManOptions
Reviewers: Note that CTxMemPool now requires a non-defaulted
CTxMemPool::Options for its constructor. Meaning that there's no need to
worry about a stray CTxMemPool constructor somewhere defaulting to
something incorrect. All instances of CTxMemPool construction are
addressed here in this commit.
We set options for CTxMemPool and construct it in many different ways. A
good example can be seen in how we determine CTxMemPool's check_ratio in
AppInitMain(...).
1. We first set the default based on chainparams's
DefaultConsistencyChecks()
2. Then, we apply the ArgsManager option on top of that default
3. Finally, we clamp the result of that between 0 and 1 Million
With this patch, most CTxMemPool construction are along the lines of:
MemPoolOptions mempool_opts{...default overrides...};
ApplyArgsManOptions(argsman, mempool_opts);
...hard overrides...
CTxMemPool pool{mempool_opts};
This "compositional" style of building options means that we can omit
unnecessary/irrelevant steps wherever we want but also maintain full
customizability.
For example:
- For users of libbitcoinkernel, where we eventually want to remove
ArgsManager, they simply won't call (or even know about)
ApplyArgsManOptions.
- See src/init.cpp to see how the check_ratio CTxMemPool option works
after this change.
A MemPoolOptionsForTest helper was also added and used by tests/fuzz
tests where a local CTxMemPool needed to be created.
The change in src/test/fuzz/tx_pool.cpp seemingly changes behaviour by
applying ArgsManager options on top of the CTxMemPool::Options defaults.
However, in future commits where we introduce flags like -maxmempool,
the call to ApplyArgsManOptions is actually what preserves the existing
behaviour. Previously, although it wasn't obvious, our CTxMemPool would
consult gArgs for flags like -maxmempool when it needed it, so it
already relied on ArgsManager information. This patchset just laid bare
the obfuscatory perils of globals.
[META] As this patchset progresses, we will move more and more
CTxMemPool-relevant options into MemPoolOptions and add their
ArgsMan-related logic to ApplyArgsManOptions.
2022-03-18 13:51:37 -04:00
|
|
|
using node::NodeContext;
|
2021-11-12 10:06:00 -05:00
|
|
|
|
2021-01-14 21:26:19 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
const TestingSetup* g_setup;
|
2021-03-23 10:58:33 +01:00
|
|
|
std::vector<COutPoint> g_outpoints_coinbase_init_mature;
|
|
|
|
std::vector<COutPoint> g_outpoints_coinbase_init_immature;
|
2021-01-14 21:26:19 +01:00
|
|
|
|
|
|
|
struct MockedTxPool : public CTxMemPool {
|
2021-05-19 21:24:33 +10:00
|
|
|
void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)
|
2021-01-14 21:26:19 +01:00
|
|
|
{
|
2021-05-19 21:24:33 +10:00
|
|
|
LOCK(cs);
|
2021-01-14 21:26:19 +01:00
|
|
|
lastRollingFeeUpdate = GetTime();
|
|
|
|
blockSinceLastRollingFeeBump = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void initialize_tx_pool()
|
|
|
|
{
|
|
|
|
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
|
|
|
|
g_setup = testing_setup.get();
|
|
|
|
|
|
|
|
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
|
2023-05-02 17:17:10 +02:00
|
|
|
COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_OP_TRUE)};
|
2021-04-06 21:44:36 +01:00
|
|
|
// Remember the txids to avoid expensive disk access later on
|
2021-03-23 10:58:33 +01:00
|
|
|
auto& outpoints = i < COINBASE_MATURITY ?
|
|
|
|
g_outpoints_coinbase_init_mature :
|
|
|
|
g_outpoints_coinbase_init_immature;
|
2023-05-02 17:17:10 +02:00
|
|
|
outpoints.push_back(prevout);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
SyncWithValidationInterfaceQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TransactionsDelta final : public CValidationInterface {
|
|
|
|
std::set<CTransactionRef>& m_removed;
|
|
|
|
std::set<CTransactionRef>& m_added;
|
|
|
|
|
|
|
|
explicit TransactionsDelta(std::set<CTransactionRef>& r, std::set<CTransactionRef>& a)
|
|
|
|
: m_removed{r}, m_added{a} {}
|
|
|
|
|
|
|
|
void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t /* mempool_sequence */) override
|
|
|
|
{
|
|
|
|
Assert(m_added.insert(tx).second);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
|
|
|
|
{
|
|
|
|
Assert(m_removed.insert(tx).second);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider)
|
|
|
|
{
|
|
|
|
args.ForceSetArg("-limitancestorcount",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50)));
|
|
|
|
args.ForceSetArg("-limitancestorsize",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202)));
|
|
|
|
args.ForceSetArg("-limitdescendantcount",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50)));
|
|
|
|
args.ForceSetArg("-limitdescendantsize",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202)));
|
|
|
|
args.ForceSetArg("-maxmempool",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200)));
|
|
|
|
args.ForceSetArg("-mempoolexpiry",
|
|
|
|
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)));
|
|
|
|
}
|
|
|
|
|
2022-03-09 12:37:19 -05:00
|
|
|
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Chainstate& chainstate)
|
2021-04-28 21:04:14 +02:00
|
|
|
{
|
2021-09-29 19:36:01 +01:00
|
|
|
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
|
2021-04-28 21:04:14 +02:00
|
|
|
{
|
|
|
|
BlockAssembler::Options options;
|
|
|
|
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
|
2021-09-14 16:56:34 +02:00
|
|
|
options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
|
2022-05-25 22:06:23 -04:00
|
|
|
auto assembler = BlockAssembler{chainstate, &tx_pool, options};
|
2021-04-28 21:04:14 +02:00
|
|
|
auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
|
|
|
|
Assert(block_template->block.vtx.size() >= 1);
|
|
|
|
}
|
|
|
|
const auto info_all = tx_pool.infoAll();
|
|
|
|
if (!info_all.empty()) {
|
|
|
|
const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
|
2021-09-15 11:10:51 +02:00
|
|
|
WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, MemPoolRemovalReason::BLOCK /* dummy */));
|
2021-04-28 21:04:14 +02:00
|
|
|
std::vector<uint256> all_txids;
|
|
|
|
tx_pool.queryHashes(all_txids);
|
|
|
|
assert(all_txids.size() < info_all.size());
|
2021-09-29 19:36:01 +01:00
|
|
|
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
|
2021-04-28 21:04:14 +02:00
|
|
|
}
|
|
|
|
SyncWithValidationInterfaceQueue();
|
|
|
|
}
|
|
|
|
|
2022-03-09 12:37:19 -05:00
|
|
|
void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)
|
2021-04-28 20:53:18 +02:00
|
|
|
{
|
|
|
|
const auto time = ConsumeTime(fuzzed_data_provider,
|
|
|
|
chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
|
|
|
|
std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());
|
|
|
|
SetMockTime(time);
|
|
|
|
}
|
|
|
|
|
2022-07-20 15:59:03 +02:00
|
|
|
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
|
pool: Add and use MemPoolOptions, ApplyArgsManOptions
Reviewers: Note that CTxMemPool now requires a non-defaulted
CTxMemPool::Options for its constructor. Meaning that there's no need to
worry about a stray CTxMemPool constructor somewhere defaulting to
something incorrect. All instances of CTxMemPool construction are
addressed here in this commit.
We set options for CTxMemPool and construct it in many different ways. A
good example can be seen in how we determine CTxMemPool's check_ratio in
AppInitMain(...).
1. We first set the default based on chainparams's
DefaultConsistencyChecks()
2. Then, we apply the ArgsManager option on top of that default
3. Finally, we clamp the result of that between 0 and 1 Million
With this patch, most CTxMemPool construction are along the lines of:
MemPoolOptions mempool_opts{...default overrides...};
ApplyArgsManOptions(argsman, mempool_opts);
...hard overrides...
CTxMemPool pool{mempool_opts};
This "compositional" style of building options means that we can omit
unnecessary/irrelevant steps wherever we want but also maintain full
customizability.
For example:
- For users of libbitcoinkernel, where we eventually want to remove
ArgsManager, they simply won't call (or even know about)
ApplyArgsManOptions.
- See src/init.cpp to see how the check_ratio CTxMemPool option works
after this change.
A MemPoolOptionsForTest helper was also added and used by tests/fuzz
tests where a local CTxMemPool needed to be created.
The change in src/test/fuzz/tx_pool.cpp seemingly changes behaviour by
applying ArgsManager options on top of the CTxMemPool::Options defaults.
However, in future commits where we introduce flags like -maxmempool,
the call to ApplyArgsManOptions is actually what preserves the existing
behaviour. Previously, although it wasn't obvious, our CTxMemPool would
consult gArgs for flags like -maxmempool when it needed it, so it
already relied on ArgsManager information. This patchset just laid bare
the obfuscatory perils of globals.
[META] As this patchset progresses, we will move more and more
CTxMemPool-relevant options into MemPoolOptions and add their
ArgsMan-related logic to ApplyArgsManOptions.
2022-03-18 13:51:37 -04:00
|
|
|
{
|
|
|
|
// Take the default options for tests...
|
|
|
|
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
|
|
|
|
|
|
|
|
// ...override specific options for this specific fuzz suite
|
|
|
|
mempool_opts.estimator = nullptr;
|
|
|
|
mempool_opts.check_ratio = 1;
|
2022-07-20 15:59:03 +02:00
|
|
|
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
|
pool: Add and use MemPoolOptions, ApplyArgsManOptions
Reviewers: Note that CTxMemPool now requires a non-defaulted
CTxMemPool::Options for its constructor. Meaning that there's no need to
worry about a stray CTxMemPool constructor somewhere defaulting to
something incorrect. All instances of CTxMemPool construction are
addressed here in this commit.
We set options for CTxMemPool and construct it in many different ways. A
good example can be seen in how we determine CTxMemPool's check_ratio in
AppInitMain(...).
1. We first set the default based on chainparams's
DefaultConsistencyChecks()
2. Then, we apply the ArgsManager option on top of that default
3. Finally, we clamp the result of that between 0 and 1 Million
With this patch, most CTxMemPool construction are along the lines of:
MemPoolOptions mempool_opts{...default overrides...};
ApplyArgsManOptions(argsman, mempool_opts);
...hard overrides...
CTxMemPool pool{mempool_opts};
This "compositional" style of building options means that we can omit
unnecessary/irrelevant steps wherever we want but also maintain full
customizability.
For example:
- For users of libbitcoinkernel, where we eventually want to remove
ArgsManager, they simply won't call (or even know about)
ApplyArgsManOptions.
- See src/init.cpp to see how the check_ratio CTxMemPool option works
after this change.
A MemPoolOptionsForTest helper was also added and used by tests/fuzz
tests where a local CTxMemPool needed to be created.
The change in src/test/fuzz/tx_pool.cpp seemingly changes behaviour by
applying ArgsManager options on top of the CTxMemPool::Options defaults.
However, in future commits where we introduce flags like -maxmempool,
the call to ApplyArgsManOptions is actually what preserves the existing
behaviour. Previously, although it wasn't obvious, our CTxMemPool would
consult gArgs for flags like -maxmempool when it needed it, so it
already relied on ArgsManager information. This patchset just laid bare
the obfuscatory perils of globals.
[META] As this patchset progresses, we will move more and more
CTxMemPool-relevant options into MemPoolOptions and add their
ArgsMan-related logic to ApplyArgsManOptions.
2022-03-18 13:51:37 -04:00
|
|
|
|
|
|
|
// ...and construct a CTxMemPool from it
|
|
|
|
return CTxMemPool{mempool_opts};
|
|
|
|
}
|
|
|
|
|
2023-10-16 12:20:22 -04:00
|
|
|
void CheckATMPInvariants(const MempoolAcceptResult& res, bool txid_in_mempool, bool wtxid_in_mempool)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (res.m_result_type) {
|
|
|
|
case MempoolAcceptResult::ResultType::VALID:
|
|
|
|
{
|
|
|
|
Assert(txid_in_mempool);
|
|
|
|
Assert(wtxid_in_mempool);
|
|
|
|
Assert(res.m_state.IsValid());
|
|
|
|
Assert(!res.m_state.IsInvalid());
|
|
|
|
Assert(res.m_replaced_transactions);
|
|
|
|
Assert(res.m_vsize);
|
|
|
|
Assert(res.m_base_fees);
|
|
|
|
Assert(res.m_effective_feerate);
|
|
|
|
Assert(res.m_wtxids_fee_calculations);
|
|
|
|
Assert(!res.m_other_wtxid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MempoolAcceptResult::ResultType::INVALID:
|
|
|
|
{
|
|
|
|
// It may be already in the mempool since in ATMP cases we don't set MEMPOOL_ENTRY or DIFFERENT_WITNESS
|
|
|
|
Assert(!res.m_state.IsValid());
|
|
|
|
Assert(res.m_state.IsInvalid());
|
2023-10-04 10:06:22 +01:00
|
|
|
|
|
|
|
const bool is_reconsiderable{res.m_state.GetResult() == TxValidationResult::TX_RECONSIDERABLE};
|
2023-10-16 12:20:22 -04:00
|
|
|
Assert(!res.m_replaced_transactions);
|
|
|
|
Assert(!res.m_vsize);
|
|
|
|
Assert(!res.m_base_fees);
|
2023-10-04 10:06:22 +01:00
|
|
|
// Fee information is provided if the failure is TX_RECONSIDERABLE.
|
|
|
|
// In other cases, validation may be unable or unwilling to calculate the fees.
|
|
|
|
Assert(res.m_effective_feerate.has_value() == is_reconsiderable);
|
|
|
|
Assert(res.m_wtxids_fee_calculations.has_value() == is_reconsiderable);
|
2023-10-16 12:20:22 -04:00
|
|
|
Assert(!res.m_other_wtxid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
|
|
|
|
{
|
|
|
|
// ATMP never sets this; only set in package settings
|
|
|
|
Assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
|
|
|
|
{
|
|
|
|
// ATMP never sets this; only set in package settings
|
|
|
|
Assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-11 14:33:31 +02:00
|
|
|
FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
|
2021-01-14 21:26:19 +01:00
|
|
|
{
|
|
|
|
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
|
|
|
const auto& node = g_setup->m_node;
|
2021-12-02 01:50:27 -03:00
|
|
|
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
|
2021-01-14 21:26:19 +01:00
|
|
|
|
2021-04-28 20:53:18 +02:00
|
|
|
MockTime(fuzzed_data_provider, chainstate);
|
2021-01-14 21:26:19 +01:00
|
|
|
|
|
|
|
// All RBF-spendable outpoints
|
|
|
|
std::set<COutPoint> outpoints_rbf;
|
|
|
|
// All outpoints counting toward the total supply (subset of outpoints_rbf)
|
|
|
|
std::set<COutPoint> outpoints_supply;
|
2021-03-23 10:58:33 +01:00
|
|
|
for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
|
2021-01-14 21:26:19 +01:00
|
|
|
Assert(outpoints_supply.insert(outpoint).second);
|
|
|
|
}
|
|
|
|
outpoints_rbf = outpoints_supply;
|
|
|
|
|
|
|
|
// The sum of the values of all spendable outpoints
|
|
|
|
constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN};
|
|
|
|
|
2022-07-24 16:41:38 +02:00
|
|
|
SetMempoolConstraints(*node.args, fuzzed_data_provider);
|
2022-07-20 15:59:03 +02:00
|
|
|
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
|
2021-03-23 11:04:18 +01:00
|
|
|
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
|
2021-01-14 21:26:19 +01:00
|
|
|
|
2021-12-02 01:50:27 -03:00
|
|
|
chainstate.SetMempool(&tx_pool);
|
|
|
|
|
2021-01-14 21:26:19 +01:00
|
|
|
// Helper to query an amount
|
|
|
|
const CCoinsViewMemPool amount_view{WITH_LOCK(::cs_main, return &chainstate.CoinsTip()), tx_pool};
|
|
|
|
const auto GetAmount = [&](const COutPoint& outpoint) {
|
|
|
|
Coin c;
|
2021-03-23 11:04:18 +01:00
|
|
|
Assert(amount_view.GetCoin(outpoint, c));
|
2021-01-14 21:26:19 +01:00
|
|
|
return c.out.nValue;
|
|
|
|
};
|
|
|
|
|
2021-08-21 19:34:13 +02:00
|
|
|
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
|
|
|
|
{
|
2021-01-14 21:26:19 +01:00
|
|
|
{
|
|
|
|
// Total supply is the mempool fee + all outpoints
|
|
|
|
CAmount supply_now{WITH_LOCK(tx_pool.cs, return tx_pool.GetTotalFee())};
|
|
|
|
for (const auto& op : outpoints_supply) {
|
|
|
|
supply_now += GetAmount(op);
|
|
|
|
}
|
|
|
|
Assert(supply_now == SUPPLY_TOTAL);
|
|
|
|
}
|
|
|
|
Assert(!outpoints_supply.empty());
|
|
|
|
|
|
|
|
// Create transaction to add to the mempool
|
|
|
|
const CTransactionRef tx = [&] {
|
|
|
|
CMutableTransaction tx_mut;
|
|
|
|
tx_mut.nVersion = CTransaction::CURRENT_VERSION;
|
|
|
|
tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
|
|
|
|
const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size());
|
|
|
|
const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size() * 2);
|
|
|
|
|
|
|
|
CAmount amount_in{0};
|
|
|
|
for (int i = 0; i < num_in; ++i) {
|
|
|
|
// Pop random outpoint
|
|
|
|
auto pop = outpoints_rbf.begin();
|
|
|
|
std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints_rbf.size() - 1));
|
|
|
|
const auto outpoint = *pop;
|
|
|
|
outpoints_rbf.erase(pop);
|
|
|
|
amount_in += GetAmount(outpoint);
|
|
|
|
|
|
|
|
// Create input
|
|
|
|
const auto sequence = ConsumeSequence(fuzzed_data_provider);
|
|
|
|
const auto script_sig = CScript{};
|
|
|
|
const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
|
|
|
|
CTxIn in;
|
|
|
|
in.prevout = outpoint;
|
|
|
|
in.nSequence = sequence;
|
|
|
|
in.scriptSig = script_sig;
|
|
|
|
in.scriptWitness.stack = script_wit_stack;
|
|
|
|
|
|
|
|
tx_mut.vin.push_back(in);
|
|
|
|
}
|
|
|
|
const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1000, amount_in);
|
|
|
|
const auto amount_out = (amount_in - amount_fee) / num_out;
|
|
|
|
for (int i = 0; i < num_out; ++i) {
|
|
|
|
tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
|
|
|
|
}
|
2022-12-27 15:25:51 +00:00
|
|
|
auto tx = MakeTransactionRef(tx_mut);
|
2021-01-14 21:26:19 +01:00
|
|
|
// Restore previously removed outpoints
|
|
|
|
for (const auto& in : tx->vin) {
|
|
|
|
Assert(outpoints_rbf.insert(in.prevout).second);
|
|
|
|
}
|
|
|
|
return tx;
|
|
|
|
}();
|
|
|
|
|
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
2021-04-28 20:53:18 +02:00
|
|
|
MockTime(fuzzed_data_provider, chainstate);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
|
|
|
tx_pool.RollingFeeUpdate();
|
|
|
|
}
|
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
|
|
|
const auto& txid = fuzzed_data_provider.ConsumeBool() ?
|
2023-07-19 15:37:29 +02:00
|
|
|
tx->GetHash().ToUint256() :
|
2021-01-14 21:26:19 +01:00
|
|
|
PickValue(fuzzed_data_provider, outpoints_rbf).hash;
|
|
|
|
const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
|
|
|
|
tx_pool.PrioritiseTransaction(txid, delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember all removed and added transactions
|
|
|
|
std::set<CTransactionRef> removed;
|
|
|
|
std::set<CTransactionRef> added;
|
|
|
|
auto txr = std::make_shared<TransactionsDelta>(removed, added);
|
|
|
|
RegisterSharedValidationInterface(txr);
|
|
|
|
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
2021-04-10 15:37:47 -07:00
|
|
|
|
2021-07-20 13:26:26 +01:00
|
|
|
// Make sure ProcessNewPackage on one transaction works.
|
2021-04-10 15:37:47 -07:00
|
|
|
// The result is not guaranteed to be the same as what is returned by ATMP.
|
|
|
|
const auto result_package = WITH_LOCK(::cs_main,
|
2021-11-11 16:36:44 -03:00
|
|
|
return ProcessNewPackage(chainstate, tx_pool, {tx}, true));
|
2021-07-20 13:26:26 +01:00
|
|
|
// If something went wrong due to a package-specific policy, it might not return a
|
|
|
|
// validation result for the transaction.
|
|
|
|
if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
|
|
|
|
auto it = result_package.m_tx_results.find(tx->GetWitnessHash());
|
|
|
|
Assert(it != result_package.m_tx_results.end());
|
|
|
|
Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
|
|
|
|
it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
|
|
|
}
|
2021-04-10 15:37:47 -07:00
|
|
|
|
2021-12-02 01:50:27 -03:00
|
|
|
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
2021-01-14 21:26:19 +01:00
|
|
|
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
|
|
|
SyncWithValidationInterfaceQueue();
|
|
|
|
UnregisterSharedValidationInterface(txr);
|
|
|
|
|
2023-10-16 12:20:22 -04:00
|
|
|
bool txid_in_mempool = tx_pool.exists(GenTxid::Txid(tx->GetHash()));
|
|
|
|
bool wtxid_in_mempool = tx_pool.exists(GenTxid::Wtxid(tx->GetWitnessHash()));
|
|
|
|
CheckATMPInvariants(res, txid_in_mempool, wtxid_in_mempool);
|
|
|
|
|
2021-01-14 21:26:19 +01:00
|
|
|
Assert(accepted != added.empty());
|
|
|
|
if (accepted) {
|
|
|
|
Assert(added.size() == 1); // For now, no package acceptance
|
|
|
|
Assert(tx == *added.begin());
|
|
|
|
} else {
|
|
|
|
// Do not consider rejected transaction removed
|
|
|
|
removed.erase(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper to insert spent and created outpoints of a tx into collections
|
|
|
|
using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>;
|
|
|
|
const auto insert_tx = [](Sets created_by_tx, Sets consumed_by_tx, const auto& tx) {
|
|
|
|
for (size_t i{0}; i < tx.vout.size(); ++i) {
|
|
|
|
for (auto& set : created_by_tx) {
|
|
|
|
Assert(set.get().emplace(tx.GetHash(), i).second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto& in : tx.vin) {
|
|
|
|
for (auto& set : consumed_by_tx) {
|
|
|
|
Assert(set.get().insert(in.prevout).second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Add created outpoints, remove spent outpoints
|
|
|
|
{
|
|
|
|
// Outpoints that no longer exist at all
|
|
|
|
std::set<COutPoint> consumed_erased;
|
|
|
|
// Outpoints that no longer count toward the total supply
|
|
|
|
std::set<COutPoint> consumed_supply;
|
|
|
|
for (const auto& removed_tx : removed) {
|
2021-09-14 16:56:34 +02:00
|
|
|
insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*removed_tx);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
for (const auto& added_tx : added) {
|
2021-09-14 16:56:34 +02:00
|
|
|
insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*added_tx);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
for (const auto& p : consumed_erased) {
|
|
|
|
Assert(outpoints_supply.erase(p) == 1);
|
|
|
|
Assert(outpoints_rbf.erase(p) == 1);
|
|
|
|
}
|
|
|
|
for (const auto& p : consumed_supply) {
|
|
|
|
Assert(outpoints_supply.erase(p) == 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 21:04:14 +02:00
|
|
|
Finish(fuzzed_data_provider, tx_pool, chainstate);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
|
2023-07-11 14:33:31 +02:00
|
|
|
FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
|
2021-01-14 21:26:19 +01:00
|
|
|
{
|
|
|
|
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
|
|
|
const auto& node = g_setup->m_node;
|
2023-01-20 12:09:48 +01:00
|
|
|
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
|
2021-04-28 20:53:18 +02:00
|
|
|
|
|
|
|
MockTime(fuzzed_data_provider, chainstate);
|
2021-01-14 21:26:19 +01:00
|
|
|
|
|
|
|
std::vector<uint256> txids;
|
2023-03-26 20:17:55 +01:00
|
|
|
txids.reserve(g_outpoints_coinbase_init_mature.size());
|
2021-03-23 10:58:33 +01:00
|
|
|
for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
|
2021-01-14 21:26:19 +01:00
|
|
|
txids.push_back(outpoint.hash);
|
|
|
|
}
|
|
|
|
for (int i{0}; i <= 3; ++i) {
|
|
|
|
// Add some immature and non-existent outpoints
|
2021-03-23 10:58:33 +01:00
|
|
|
txids.push_back(g_outpoints_coinbase_init_immature.at(i).hash);
|
2021-01-14 21:26:19 +01:00
|
|
|
txids.push_back(ConsumeUInt256(fuzzed_data_provider));
|
|
|
|
}
|
|
|
|
|
2022-07-24 16:41:38 +02:00
|
|
|
SetMempoolConstraints(*node.args, fuzzed_data_provider);
|
2022-07-20 15:59:03 +02:00
|
|
|
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
|
2021-04-28 20:53:18 +02:00
|
|
|
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
|
2021-01-14 21:26:19 +01:00
|
|
|
|
2023-01-20 12:09:48 +01:00
|
|
|
chainstate.SetMempool(&tx_pool);
|
|
|
|
|
2021-08-21 19:34:13 +02:00
|
|
|
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
|
|
|
|
{
|
2021-01-14 21:26:19 +01:00
|
|
|
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
|
|
|
|
|
2021-04-28 20:53:18 +02:00
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
|
|
|
MockTime(fuzzed_data_provider, chainstate);
|
|
|
|
}
|
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
|
|
|
tx_pool.RollingFeeUpdate();
|
|
|
|
}
|
|
|
|
if (fuzzed_data_provider.ConsumeBool()) {
|
2023-10-27 12:38:21 +02:00
|
|
|
const auto txid = fuzzed_data_provider.ConsumeBool() ?
|
2023-07-19 15:37:29 +02:00
|
|
|
mut_tx.GetHash().ToUint256() :
|
2021-04-28 20:53:18 +02:00
|
|
|
PickValue(fuzzed_data_provider, txids);
|
|
|
|
const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
|
|
|
|
tx_pool.PrioritiseTransaction(txid, delta);
|
|
|
|
}
|
|
|
|
|
2021-01-14 21:26:19 +01:00
|
|
|
const auto tx = MakeTransactionRef(mut_tx);
|
|
|
|
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
2021-12-02 01:50:27 -03:00
|
|
|
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
2021-01-14 21:26:19 +01:00
|
|
|
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
|
|
|
if (accepted) {
|
|
|
|
txids.push_back(tx->GetHash());
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 21:04:14 +02:00
|
|
|
Finish(fuzzed_data_provider, tx_pool, chainstate);
|
2021-01-14 21:26:19 +01:00
|
|
|
}
|
|
|
|
} // namespace
|