Skip to content

Commit 2251460

Browse files
committed
Merge bitcoin/bitcoin#28830: [refactor] Check CTxMemPool options in ctor
09ef322 [[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 bitcoin/bitcoin#25290 (comment). ACKs for top commit: stickies-v: re-ACK 09ef322 . 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 09ef322 ryanofsky: Code review ACK 09ef322. Just fuzz test error checking fix and updated comment since last review Tree-SHA512: eb3361411c2db70be17f912e3b14d9cb9c60fb0697a1eded952c3b7e8675b7d783780d45c52e091931d1d80fe0f0280cee98dd57a3100def13af20259d9d1b9e
2 parents 337f9d4 + 09ef322 commit 2251460

11 files changed

+83
-30
lines changed

src/init.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,19 +1522,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15221522
if (!result) {
15231523
return InitError(util::ErrorString(result));
15241524
}
1525-
mempool_opts.check_ratio = std::clamp<int>(mempool_opts.check_ratio, 0, 1'000'000);
1526-
1527-
int64_t descendant_limit_bytes = mempool_opts.limits.descendant_size_vbytes * 40;
1528-
if (mempool_opts.max_size_bytes < 0 || mempool_opts.max_size_bytes < descendant_limit_bytes) {
1529-
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0)));
1530-
}
1531-
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));
15321525

15331526
bool do_reindex{args.GetBoolArg("-reindex", false)};
15341527
const bool do_reindex_chainstate{args.GetBoolArg("-reindex-chainstate", false)};
15351528

15361529
for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
1537-
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
1530+
bilingual_str mempool_error;
1531+
node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
1532+
if (!mempool_error.empty()) {
1533+
return InitError(mempool_error);
1534+
}
1535+
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));
15381536

15391537
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
15401538
ChainstateManager& chainman = *node.chainman;

src/test/fuzz/mini_miner.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
#include <test/util/txmempool.h>
88
#include <test/util/mining.h>
99

10-
#include <node/mini_miner.h>
1110
#include <node/miner.h>
11+
#include <node/mini_miner.h>
1212
#include <primitives/transaction.h>
1313
#include <random.h>
1414
#include <txmempool.h>
15+
#include <util/check.h>
16+
#include <util/translation.h>
1517

1618
#include <deque>
1719
#include <vector>
@@ -33,7 +35,9 @@ void initialize_miner()
3335
FUZZ_TARGET(mini_miner, .init = initialize_miner)
3436
{
3537
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
36-
CTxMemPool pool{CTxMemPool::Options{}};
38+
bilingual_str error;
39+
CTxMemPool pool{CTxMemPool::Options{}, error};
40+
Assert(error.empty());
3741
std::vector<COutPoint> outpoints;
3842
std::deque<COutPoint> available_coins = g_available_coins;
3943
LOCK2(::cs_main, pool.cs);
@@ -109,7 +113,9 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
109113
FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
110114
{
111115
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
112-
CTxMemPool pool{CTxMemPool::Options{}};
116+
bilingual_str error;
117+
CTxMemPool pool{CTxMemPool::Options{}, error};
118+
Assert(error.empty());
113119
// Make a copy to preserve determinism.
114120
std::deque<COutPoint> available_coins = g_available_coins;
115121
std::vector<CTransactionRef> transactions;

src/test/fuzz/package_eval.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include <test/util/script.h>
1616
#include <test/util/setup_common.h>
1717
#include <test/util/txmempool.h>
18+
#include <util/check.h>
1819
#include <util/rbf.h>
20+
#include <util/translation.h>
1921
#include <validation.h>
2022
#include <validationinterface.h>
2123

@@ -107,7 +109,7 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
107109
SetMockTime(time);
108110
}
109111

110-
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
112+
std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
111113
{
112114
// Take the default options for tests...
113115
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
@@ -126,8 +128,13 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
126128
mempool_opts.check_ratio = 1;
127129
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
128130

131+
bilingual_str error;
129132
// ...and construct a CTxMemPool from it
130-
return CTxMemPool{mempool_opts};
133+
auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
134+
// ... ignore the error since it might be beneficial to fuzz even when the
135+
// mempool size is unreasonably small
136+
Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
137+
return mempool;
131138
}
132139

133140
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
@@ -149,8 +156,8 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
149156
auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
150157
node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
151158

152-
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
153-
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
159+
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
160+
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
154161

155162
chainstate.SetMempool(&tx_pool);
156163

src/test/fuzz/partially_downloaded_block.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <test/util/setup_common.h>
1111
#include <test/util/txmempool.h>
1212
#include <txmempool.h>
13+
#include <util/check.h>
14+
#include <util/translation.h>
1315

1416
#include <cstddef>
1517
#include <cstdint>
@@ -52,7 +54,9 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
5254

5355
CBlockHeaderAndShortTxIDs cmpctblock{*block};
5456

55-
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
57+
bilingual_str error;
58+
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
59+
Assert(error.empty());
5660
PartiallyDownloadedBlock pdb{&pool};
5761

5862
// Set of available transactions (mempool or extra_txn)

src/test/fuzz/rbf.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <test/util/setup_common.h>
1414
#include <test/util/txmempool.h>
1515
#include <txmempool.h>
16+
#include <util/check.h>
17+
#include <util/translation.h>
1618

1719
#include <cstdint>
1820
#include <optional>
@@ -56,7 +58,9 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
5658
return;
5759
}
5860

59-
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
61+
bilingual_str error;
62+
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
63+
Assert(error.empty());
6064

6165
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
6266
{
@@ -90,7 +94,9 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
9094
std::optional<CMutableTransaction> child = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
9195
if (!child) return;
9296

93-
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
97+
bilingual_str error;
98+
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
99+
Assert(error.empty());
94100

95101
// Add a bunch of parent-child pairs to the mempool, and remember them.
96102
std::vector<CTransaction> mempool_txs;

src/test/fuzz/tx_pool.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include <test/util/script.h>
1616
#include <test/util/setup_common.h>
1717
#include <test/util/txmempool.h>
18+
#include <util/check.h>
1819
#include <util/rbf.h>
20+
#include <util/translation.h>
1921
#include <validation.h>
2022
#include <validationinterface.h>
2123

@@ -116,7 +118,7 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
116118
SetMockTime(time);
117119
}
118120

119-
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
121+
std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
120122
{
121123
// Take the default options for tests...
122124
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
@@ -126,7 +128,12 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
126128
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
127129

128130
// ...and construct a CTxMemPool from it
129-
return CTxMemPool{mempool_opts};
131+
bilingual_str error;
132+
auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
133+
// ... ignore the error since it might be beneficial to fuzz even when the
134+
// mempool size is unreasonably small
135+
Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
136+
return mempool;
130137
}
131138

132139
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)
198205
constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN};
199206

200207
SetMempoolConstraints(*node.args, fuzzed_data_provider);
201-
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
202-
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
208+
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
209+
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
203210

204211
chainstate.SetMempool(&tx_pool);
205212

@@ -376,8 +383,8 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
376383
}
377384

378385
SetMempoolConstraints(*node.args, fuzzed_data_provider);
379-
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
380-
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
386+
auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
387+
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
381388

382389
chainstate.SetMempool(&tx_pool);
383390

src/test/fuzz/validation_load_mempool.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#include <test/util/setup_common.h>
1414
#include <test/util/txmempool.h>
1515
#include <txmempool.h>
16+
#include <util/check.h>
1617
#include <util/time.h>
18+
#include <util/translation.h>
1719
#include <validation.h>
1820

1921
#include <cstdint>
@@ -40,7 +42,9 @@ FUZZ_TARGET(validation_load_mempool, .init = initialize_validation_load_mempool)
4042
SetMockTime(ConsumeTime(fuzzed_data_provider));
4143
FuzzedFileProvider fuzzed_file_provider{fuzzed_data_provider};
4244

43-
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
45+
bilingual_str error;
46+
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
47+
Assert(error.empty());
4448

4549
auto& chainstate{static_cast<DummyChainState&>(g_setup->m_node.chainman->ActiveChainstate())};
4650
chainstate.SetMempool(&pool);

src/test/miner_tests.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
#include <test/util/txmempool.h>
1515
#include <txmempool.h>
1616
#include <uint256.h>
17+
#include <util/check.h>
1718
#include <util/strencodings.h>
1819
#include <util/time.h>
20+
#include <util/translation.h>
1921
#include <validation.h>
2022
#include <versionbits.h>
2123

@@ -46,7 +48,9 @@ struct MinerTestingSetup : public TestingSetup {
4648
// pointer is not accessed, when the new one should be accessed
4749
// instead.
4850
m_node.mempool.reset();
49-
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
51+
bilingual_str error;
52+
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
53+
Assert(error.empty());
5054
return *m_node.mempool;
5155
}
5256
BlockAssembler AssemblerForTest(CTxMemPool& tx_mempool);

src/test/util/setup_common.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
227227
m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
228228

229229
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
230-
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
230+
bilingual_str error{};
231+
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
232+
Assert(error.empty());
231233

232234
m_cache_sizes = CalculateCacheSizes(m_args);
233235

src/txmempool.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <policy/settings.h>
1717
#include <random.h>
1818
#include <reverse_iterator.h>
19+
#include <tinyformat.h>
1920
#include <util/check.h>
2021
#include <util/feefrac.h>
2122
#include <util/moneystr.h>
@@ -26,6 +27,7 @@
2627
#include <util/translation.h>
2728
#include <validationinterface.h>
2829

30+
#include <algorithm>
2931
#include <cmath>
3032
#include <numeric>
3133
#include <optional>
@@ -395,8 +397,19 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee,
395397
assert(int(nSigOpCostWithAncestors) >= 0);
396398
}
397399

398-
CTxMemPool::CTxMemPool(const Options& opts)
399-
: m_opts{opts}
400+
//! Clamp option values and populate the error if options are not valid.
401+
static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
402+
{
403+
opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
404+
int64_t descendant_limit_bytes = opts.limits.descendant_size_vbytes * 40;
405+
if (opts.max_size_bytes < 0 || opts.max_size_bytes < descendant_limit_bytes) {
406+
error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0));
407+
}
408+
return std::move(opts);
409+
}
410+
411+
CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
412+
: m_opts{Flatten(std::move(opts), error)}
400413
{
401414
}
402415

0 commit comments

Comments
 (0)