Skip to content

Commit 3a7702f

Browse files
committed
init: add pause_load_mempool test hook
A new -test=pause_load_mempool argument lets the test framework pause node initialization right before loading the mempool. When set, the node checks for the existence of a pause_load_mempool file and waits until that file is removed. Use this mechanism to test that savemempool fails before the mempool is loaded, and that getmempoolinfo's "loaded" field is false. Also add a test to ensure the pause can be interrupted.
1 parent 5d7d4f0 commit 3a7702f

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

src/common/args.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ const std::vector<std::string> TEST_OPTIONS_DOC{
769769
"addrman (use deterministic addrman)",
770770
"reindex_after_failure_noninteractive_yes (When asked for a reindex after failure interactively, simulate as-if answered with 'yes')",
771771
"bip94 (enforce BIP94 consensus rules)",
772+
"pause_load_mempool (pause startup before loading mempool until regtest/pause_load_mempool is removed)",
772773
};
773774

774775
bool HasTestOption(const ArgsManager& args, const std::string& test_option)

src/init.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,17 @@ static void StartupNotify(const ArgsManager& args)
745745
}
746746
#endif
747747

748+
static void MaybeWaitForTestPause(const ArgsManager& args, const util::SignalInterrupt& interrupt, const std::string& test_option)
749+
{
750+
if (!HasTestOption(args, test_option)) return;
751+
const fs::path pause_file{args.GetDataDirNet() / fs::PathFromString(test_option)};
752+
753+
LogInfo("Waiting for test pause file %s to be removed\n", fs::PathToString(pause_file));
754+
while (!interrupt && fs::exists(pause_file)) {
755+
UninterruptibleSleep(std::chrono::milliseconds{10});
756+
}
757+
}
758+
748759
static bool AppInitServers(NodeContext& node)
749760
{
750761
const ArgsManager& args = *Assert(node.args);
@@ -2044,6 +2055,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
20442055
}
20452056
// Load mempool from disk
20462057
if (auto* pool{chainman.ActiveChainstate().GetMempool()}) {
2058+
MaybeWaitForTestPause(args, chainman.m_interrupt, "pause_load_mempool");
20472059
LoadMempool(*pool, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}, chainman.ActiveChainstate(), {});
20482060
pool->SetLoadTried(!chainman.m_interrupt);
20492061
}

test/functional/feature_init.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,28 @@ def init_empty_test(self):
292292
for option in options:
293293
self.restart_node(1, option)
294294

295+
def init_pause_load_mempool_interrupt_test(self):
296+
self.log.info("Test interrupting startup while pause_load_mempool is active")
297+
node = self.nodes[0]
298+
pause_file = node.chain_path / "pause_load_mempool"
299+
pause_file.touch()
300+
301+
with node.busy_wait_for_debug_log([b"Waiting for test pause file"]):
302+
self.start_breakable(node, extra_args=["-test=pause_load_mempool"], wait_for_import=False)
303+
304+
self.sigterm_node(node)
305+
pause_file.unlink()
306+
307+
self.check_clean_start(node, [])
308+
self.stop_node(0)
309+
295310
def run_test(self):
296311
self.init_pid_test()
297312
self.init_stress_test_interrupt()
298313
self.init_stress_test_removals()
299314
self.break_wait_test()
300315
self.init_empty_test()
316+
self.init_pause_load_mempool_interrupt_test()
301317

302318

303319
if __name__ == '__main__':

test/functional/mempool_persist.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
class MempoolPersistTest(BitcoinTestFramework):
5353
def set_test_params(self):
5454
self.num_nodes = 3
55-
self.extra_args = [[], ["-persistmempool=0"], []]
55+
self.extra_args = [["-test=pause_load_mempool"], ["-persistmempool=0"], []]
5656
self.uses_wallet = None
5757

5858
def run_test(self):
@@ -114,7 +114,15 @@ def run_test(self):
114114
# Give this node a head-start, so we can be "extra-sure" that it didn't load anything later
115115
# Also don't store the mempool, to keep the datadir clean
116116
self.start_node(1, extra_args=["-persistmempool=0"])
117-
self.start_node(0)
117+
# Pause this node during init, in order to check that we can't save
118+
# the mempool before it's loaded.
119+
pause_file = self.nodes[0].chain_path / "pause_load_mempool"
120+
pause_file.touch()
121+
self.start_node(0, wait_for_import=False)
122+
assert not self.nodes[0].getmempoolinfo()["loaded"]
123+
assert_raises_rpc_error(-1, "The mempool was not loaded yet", self.nodes[0].savemempool)
124+
pause_file.unlink()
125+
self.nodes[0].wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
118126
self.start_node(2)
119127
assert self.nodes[0].getmempoolinfo()["loaded"] # start_node is blocking on the mempool being loaded
120128
assert self.nodes[2].getmempoolinfo()["loaded"]

0 commit comments

Comments
 (0)