0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-02 09:46:52 -05:00

Merge bitcoin/bitcoin#28830: [refactor] Check CTxMemPool options in ctor

09ef322acc [[refactor]] Check CTxMemPool options in constructor (TheCharlatan)

Pull request description:

  The tests should run the same checks on the mempool options that the init code also applies. The downside to this patch is that the log line may now be printed more than once in the for loop.

  This was originally noticed here https://github.com/bitcoin/bitcoin/pull/25290#discussion_r900272797.

ACKs for top commit:
  stickies-v:
    re-ACK 09ef322acc . Fixed unreachable assert and updated docstring, and also added an exception for "-maxmempool must be at least " in the `tx_pool` fuzz test, which makes sense when looking at how the mempool options are constructed in `SetMempoolConstraints`.
  achow101:
    ACK 09ef322acc
  ryanofsky:
    Code review ACK 09ef322acc. Just fuzz test error checking fix and updated comment since last review

Tree-SHA512: eb3361411c2db70be17f912e3b14d9cb9c60fb0697a1eded952c3b7e8675b7d783780d45c52e091931d1d80fe0f0280cee98dd57a3100def13af20259d9d1b9e
This commit is contained in:
Ava Chow 2024-06-11 15:24:49 -04:00
commit 2251460f3e
No known key found for this signature in database
GPG key ID: 17565732E08E5E41
11 changed files with 83 additions and 30 deletions

View file

@ -1522,19 +1522,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!result) {
return InitError(util::ErrorString(result));
}
mempool_opts.check_ratio = std::clamp<int>(mempool_opts.check_ratio, 0, 1'000'000);
int64_t descendant_limit_bytes = mempool_opts.limits.descendant_size_vbytes * 40;
if (mempool_opts.max_size_bytes < 0 || mempool_opts.max_size_bytes < descendant_limit_bytes) {
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0)));
}
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
bool do_reindex{args.GetBoolArg("-reindex", false)};
const bool do_reindex_chainstate{args.GetBoolArg("-reindex-chainstate", false)};
for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
bilingual_str mempool_error;
node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
if (!mempool_error.empty()) {
return InitError(mempool_error);
}
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
ChainstateManager& chainman = *node.chainman;

View file

@ -7,11 +7,13 @@
#include <test/util/txmempool.h>
#include <test/util/mining.h>
#include <node/mini_miner.h>
#include <node/miner.h>
#include <node/mini_miner.h>
#include <primitives/transaction.h>
#include <random.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/translation.h>
#include <deque>
#include <vector>
@ -33,7 +35,9 @@ void initialize_miner()
FUZZ_TARGET(mini_miner, .init = initialize_miner)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CTxMemPool pool{CTxMemPool::Options{}};
bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error};
Assert(error.empty());
std::vector<COutPoint> outpoints;
std::deque<COutPoint> available_coins = g_available_coins;
LOCK2(::cs_main, pool.cs);
@ -109,7 +113,9 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CTxMemPool pool{CTxMemPool::Options{}};
bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error};
Assert(error.empty());
// Make a copy to preserve determinism.
std::deque<COutPoint> available_coins = g_available_coins;
std::vector<CTransactionRef> transactions;

View file

@ -15,7 +15,9 @@
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <util/check.h>
#include <util/rbf.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
@ -107,7 +109,7 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
SetMockTime(time);
}
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
{
// Take the default options for tests...
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
@ -126,8 +128,13 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
mempool_opts.check_ratio = 1;
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
bilingual_str error;
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
// ... ignore the error since it might be beneficial to fuzz even when the
// mempool size is unreasonably small
Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
return mempool;
}
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
@ -149,8 +156,8 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
chainstate.SetMempool(&tx_pool);

View file

@ -10,6 +10,8 @@
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/translation.h>
#include <cstddef>
#include <cstdint>
@ -52,7 +54,9 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
CBlockHeaderAndShortTxIDs cmpctblock{*block};
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
bilingual_str error;
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
Assert(error.empty());
PartiallyDownloadedBlock pdb{&pool};
// Set of available transactions (mempool or extra_txn)

View file

@ -13,6 +13,8 @@
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/translation.h>
#include <cstdint>
#include <optional>
@ -56,7 +58,9 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
return;
}
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
bilingual_str error;
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
Assert(error.empty());
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
{
@ -90,7 +94,9 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
std::optional<CMutableTransaction> child = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
if (!child) return;
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
bilingual_str error;
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
Assert(error.empty());
// Add a bunch of parent-child pairs to the mempool, and remember them.
std::vector<CTransaction> mempool_txs;

View file

@ -15,7 +15,9 @@
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <util/check.h>
#include <util/rbf.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
@ -116,7 +118,7 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
SetMockTime(time);
}
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
{
// Take the default options for tests...
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
@ -126,7 +128,12 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
bilingual_str error;
auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
// ... ignore the error since it might be beneficial to fuzz even when the
// mempool size is unreasonably small
Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
return mempool;
}
void CheckATMPInvariants(const MempoolAcceptResult& res, bool txid_in_mempool, bool wtxid_in_mempool)
@ -198,8 +205,8 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN};
SetMempoolConstraints(*node.args, fuzzed_data_provider);
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
chainstate.SetMempool(&tx_pool);
@ -376,8 +383,8 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
}
SetMempoolConstraints(*node.args, fuzzed_data_provider);
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
chainstate.SetMempool(&tx_pool);

View file

@ -13,7 +13,9 @@
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <cstdint>
@ -40,7 +42,9 @@ FUZZ_TARGET(validation_load_mempool, .init = initialize_validation_load_mempool)
SetMockTime(ConsumeTime(fuzzed_data_provider));
FuzzedFileProvider fuzzed_file_provider{fuzzed_data_provider};
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
bilingual_str error;
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
Assert(error.empty());
auto& chainstate{static_cast<DummyChainState&>(g_setup->m_node.chainman->ActiveChainstate())};
chainstate.SetMempool(&pool);

View file

@ -14,8 +14,10 @@
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <versionbits.h>
@ -46,7 +48,9 @@ struct MinerTestingSetup : public TestingSetup {
// pointer is not accessed, when the new one should be accessed
// instead.
m_node.mempool.reset();
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
bilingual_str error;
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
Assert(error.empty());
return *m_node.mempool;
}
BlockAssembler AssemblerForTest(CTxMemPool& tx_mempool);

View file

@ -227,7 +227,9 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
bilingual_str error{};
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
Assert(error.empty());
m_cache_sizes = CalculateCacheSizes(m_args);

View file

@ -16,6 +16,7 @@
#include <policy/settings.h>
#include <random.h>
#include <reverse_iterator.h>
#include <tinyformat.h>
#include <util/check.h>
#include <util/feefrac.h>
#include <util/moneystr.h>
@ -26,6 +27,7 @@
#include <util/translation.h>
#include <validationinterface.h>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <optional>
@ -395,8 +397,19 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee,
assert(int(nSigOpCostWithAncestors) >= 0);
}
CTxMemPool::CTxMemPool(const Options& opts)
: m_opts{opts}
//! Clamp option values and populate the error if options are not valid.
static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
{
opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
int64_t descendant_limit_bytes = opts.limits.descendant_size_vbytes * 40;
if (opts.max_size_bytes < 0 || opts.max_size_bytes < descendant_limit_bytes) {
error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0));
}
return std::move(opts);
}
CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
: m_opts{Flatten(std::move(opts), error)}
{
}

View file

@ -43,6 +43,8 @@
class CChain;
class ValidationSignals;
struct bilingual_str;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF;
@ -442,7 +444,7 @@ public:
* accepting transactions becomes O(N^2) where N is the number of transactions
* in the pool.
*/
explicit CTxMemPool(const Options& opts);
explicit CTxMemPool(Options opts, bilingual_str& error);
/**
* If sanity-checking is turned on, check makes sure the pool is