Skip to content

Commit 821f5c8

Browse files
committed
Merge bitcoin/bitcoin#25487: [kernel 3b/n] Decouple {Dump,Load}Mempool from ArgsManager
cb3e9a1 Move {Load,Dump}Mempool to kernel namespace (Carl Dong) aa30676 Move DEFAULT_PERSIST_MEMPOOL out of libbitcoinkernel (Carl Dong) 06b88ff LoadMempool: Pass in load_path, stop using gArgs (Carl Dong) b857ac6 test/fuzz: Invoke LoadMempool via CChainState (Carl Dong) b326725 Move FopenFn to fsbridge namespace (Carl Dong) ae1e8e3 mempool: Use NodeClock+friends for LoadMempool (Carl Dong) f9e8e57 mempool: Improve comments for [GS]etLoadTried (Carl Dong) 813962d scripted-diff: Rename m_is_loaded -> m_load_tried (Carl Dong) 413f4bb DumpMempool: Pass in dump_path, stop using gArgs (Carl Dong) bd44078 DumpMempool: Use std::chrono instead of weird int64_t arthmetics (Carl Dong) c84390b test/mempool_persist: Test manual savemempool when -persistmempool=0 (Carl Dong) Pull request description: This is part of the `libbitcoinkernel` project: #24303, https://github.com/bitcoin/bitcoin/projects/18 ----- This PR moves `{Dump,Load}Mempool` into its own `kernel/mempool_persist` module and introduces `ArgsManager` `node::` helpers in `node/mempool_persist_args`to remove the scattered calls to `GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)`. More context can be gleaned from the commit messages. ----- One thing I was reflecting on as I wrote this was that in the long run, I think we should probably invert the validation <-> mempool relationship. Instead of mempool not depending on validation, it might make more sense to have validation not depend on mempool. Not super urgent since `libbitcoinkernel` will include both validation and mempool, but perhaps something for the future. ACKs for top commit: glozow: re ACK cb3e9a1 via `git range-diff 7ae032e...cb3e9a1` MarcoFalke: ACK cb3e9a1 🔒 ryanofsky: Code review ACK cb3e9a1 Tree-SHA512: 979d7237c3abb5a1dd9b5ad3dbf3b954f906a6d8320ed7b923557f41a4472deccae3e8a6bca0018c8e7a3c4a93afecc502acd1e26756f2054f157f1c0edd939d
2 parents c395c8d + cb3e9a1 commit 821f5c8

23 files changed

+373
-196
lines changed

ci/test/06_script_b.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
4141
CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/iwyu_tool.py"\
4242
" src/compat"\
4343
" src/init"\
44+
" src/kernel/mempool_persist.cpp"\
4445
" src/policy/feerate.cpp"\
4546
" src/policy/packages.cpp"\
4647
" src/policy/settings.cpp"\

contrib/devtools/iwyu/bitcoin.core.imp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
{ include: [ "<bits/termios-c_lflag.h>", private, "<termios.h>", public ] },
44
{ include: [ "<bits/termios-struct.h>", private, "<termios.h>", public ] },
55
{ include: [ "<bits/termios-tcflow.h>", private, "<termios.h>", public ] },
6+
{ include: [ "<bits/chrono.h>", private, "<chrono>", public ] },
67
]

src/Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ BITCOIN_CORE_H = \
177177
kernel/context.h \
178178
kernel/mempool_limits.h \
179179
kernel/mempool_options.h \
180+
kernel/mempool_persist.h \
180181
key.h \
181182
key_io.h \
182183
logging.h \
@@ -198,6 +199,7 @@ BITCOIN_CORE_H = \
198199
node/chainstate.h \
199200
node/coin.h \
200201
node/context.h \
202+
node/mempool_persist_args.h \
201203
node/miner.h \
202204
node/minisketchwrapper.h \
203205
node/psbt.h \
@@ -366,6 +368,7 @@ libbitcoin_node_a_SOURCES = \
366368
kernel/checks.cpp \
367369
kernel/coinstats.cpp \
368370
kernel/context.cpp \
371+
kernel/mempool_persist.cpp \
369372
mapport.cpp \
370373
mempool_args.cpp \
371374
net.cpp \
@@ -379,6 +382,7 @@ libbitcoin_node_a_SOURCES = \
379382
node/context.cpp \
380383
node/eviction.cpp \
381384
node/interfaces.cpp \
385+
node/mempool_persist_args.cpp \
382386
node/miner.cpp \
383387
node/minisketchwrapper.cpp \
384388
node/psbt.cpp \
@@ -881,6 +885,7 @@ libbitcoinkernel_la_SOURCES = \
881885
kernel/checks.cpp \
882886
kernel/coinstats.cpp \
883887
kernel/context.cpp \
888+
kernel/mempool_persist.cpp \
884889
key.cpp \
885890
logging.cpp \
886891
node/blockstorage.cpp \

src/Makefile.test_fuzz.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ EXTRA_LIBRARIES += \
1010
TEST_FUZZ_H = \
1111
test/fuzz/fuzz.h \
1212
test/fuzz/FuzzedDataProvider.h \
13+
test/fuzz/mempool_utils.h \
1314
test/fuzz/util.h
1415

1516
libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)

src/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <cstdio>
1111
#include <filesystem>
12+
#include <functional>
1213
#include <iomanip>
1314
#include <ios>
1415
#include <ostream>
@@ -199,6 +200,7 @@ bool create_directories(const std::filesystem::path& p, std::error_code& ec) = d
199200

200201
/** Bridge operations to C stdio */
201202
namespace fsbridge {
203+
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
202204
FILE *fopen(const fs::path& p, const char *mode);
203205

204206
/**

src/init.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <init.h>
1111

1212
#include <kernel/checks.h>
13+
#include <kernel/mempool_persist.h>
1314

1415
#include <addrman.h>
1516
#include <banman.h>
@@ -41,6 +42,7 @@
4142
#include <node/chainstate.h>
4243
#include <node/context.h>
4344
#include <node/interface_ui.h>
45+
#include <node/mempool_persist_args.h>
4446
#include <node/miner.h>
4547
#include <policy/feerate.h>
4648
#include <policy/fees.h>
@@ -102,14 +104,19 @@
102104
#include <zmq/zmqrpc.h>
103105
#endif
104106

107+
using kernel::DumpMempool;
108+
105109
using node::CacheSizes;
106110
using node::CalculateCacheSizes;
107111
using node::ChainstateLoadVerifyError;
108112
using node::ChainstateLoadingError;
109113
using node::CleanupBlockRevFiles;
114+
using node::DEFAULT_PERSIST_MEMPOOL;
110115
using node::DEFAULT_PRINTPRIORITY;
111116
using node::DEFAULT_STOPAFTERBLOCKIMPORT;
112117
using node::LoadChainstate;
118+
using node::MempoolPath;
119+
using node::ShouldPersistMempool;
113120
using node::NodeContext;
114121
using node::ThreadImport;
115122
using node::VerifyLoadedChainstate;
@@ -245,8 +252,8 @@ void Shutdown(NodeContext& node)
245252
node.addrman.reset();
246253
node.netgroupman.reset();
247254

248-
if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
249-
DumpMempool(*node.mempool);
255+
if (node.mempool && node.mempool->GetLoadTried() && ShouldPersistMempool(*node.args)) {
256+
DumpMempool(*node.mempool, MempoolPath(*node.args));
250257
}
251258

252259
// Drop transactions we were still watching, and record fee estimations.
@@ -1669,7 +1676,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16691676
}
16701677

16711678
chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
1672-
ThreadImport(chainman, vImportFiles, args);
1679+
ThreadImport(chainman, vImportFiles, args, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{});
16731680
});
16741681

16751682
// Wait for genesis block to be processed

src/kernel/mempool_persist.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <kernel/mempool_persist.h>
6+
7+
#include <clientversion.h>
8+
#include <consensus/amount.h>
9+
#include <fs.h>
10+
#include <logging.h>
11+
#include <primitives/transaction.h>
12+
#include <serialize.h>
13+
#include <shutdown.h>
14+
#include <streams.h>
15+
#include <sync.h>
16+
#include <txmempool.h>
17+
#include <uint256.h>
18+
#include <util/system.h>
19+
#include <util/time.h>
20+
#include <validation.h>
21+
22+
#include <chrono>
23+
#include <cstdint>
24+
#include <cstdio>
25+
#include <exception>
26+
#include <functional>
27+
#include <map>
28+
#include <memory>
29+
#include <set>
30+
#include <stdexcept>
31+
#include <utility>
32+
#include <vector>
33+
34+
using fsbridge::FopenFn;
35+
36+
namespace kernel {
37+
38+
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
39+
40+
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, CChainState& active_chainstate, FopenFn mockable_fopen_function)
41+
{
42+
if (load_path.empty()) return false;
43+
44+
FILE* filestr{mockable_fopen_function(load_path, "rb")};
45+
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
46+
if (file.IsNull()) {
47+
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
48+
return false;
49+
}
50+
51+
int64_t count = 0;
52+
int64_t expired = 0;
53+
int64_t failed = 0;
54+
int64_t already_there = 0;
55+
int64_t unbroadcast = 0;
56+
auto now = NodeClock::now();
57+
58+
try {
59+
uint64_t version;
60+
file >> version;
61+
if (version != MEMPOOL_DUMP_VERSION) {
62+
return false;
63+
}
64+
uint64_t num;
65+
file >> num;
66+
while (num) {
67+
--num;
68+
CTransactionRef tx;
69+
int64_t nTime;
70+
int64_t nFeeDelta;
71+
file >> tx;
72+
file >> nTime;
73+
file >> nFeeDelta;
74+
75+
CAmount amountdelta = nFeeDelta;
76+
if (amountdelta) {
77+
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
78+
}
79+
if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
80+
LOCK(cs_main);
81+
const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
82+
if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
83+
++count;
84+
} else {
85+
// mempool may contain the transaction already, e.g. from
86+
// wallet(s) having loaded it while we were processing
87+
// mempool transactions; consider these as valid, instead of
88+
// failed, but mark them as 'already there'
89+
if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
90+
++already_there;
91+
} else {
92+
++failed;
93+
}
94+
}
95+
} else {
96+
++expired;
97+
}
98+
if (ShutdownRequested())
99+
return false;
100+
}
101+
std::map<uint256, CAmount> mapDeltas;
102+
file >> mapDeltas;
103+
104+
for (const auto& i : mapDeltas) {
105+
pool.PrioritiseTransaction(i.first, i.second);
106+
}
107+
108+
std::set<uint256> unbroadcast_txids;
109+
file >> unbroadcast_txids;
110+
unbroadcast = unbroadcast_txids.size();
111+
for (const auto& txid : unbroadcast_txids) {
112+
// Ensure transactions were accepted to mempool then add to
113+
// unbroadcast set.
114+
if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
115+
}
116+
} catch (const std::exception& e) {
117+
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
118+
return false;
119+
}
120+
121+
LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
122+
return true;
123+
}
124+
125+
bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
126+
{
127+
auto start = SteadyClock::now();
128+
129+
std::map<uint256, CAmount> mapDeltas;
130+
std::vector<TxMempoolInfo> vinfo;
131+
std::set<uint256> unbroadcast_txids;
132+
133+
static Mutex dump_mutex;
134+
LOCK(dump_mutex);
135+
136+
{
137+
LOCK(pool.cs);
138+
for (const auto &i : pool.mapDeltas) {
139+
mapDeltas[i.first] = i.second;
140+
}
141+
vinfo = pool.infoAll();
142+
unbroadcast_txids = pool.GetUnbroadcastTxs();
143+
}
144+
145+
auto mid = SteadyClock::now();
146+
147+
try {
148+
FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")};
149+
if (!filestr) {
150+
return false;
151+
}
152+
153+
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
154+
155+
uint64_t version = MEMPOOL_DUMP_VERSION;
156+
file << version;
157+
158+
file << (uint64_t)vinfo.size();
159+
for (const auto& i : vinfo) {
160+
file << *(i.tx);
161+
file << int64_t{count_seconds(i.m_time)};
162+
file << int64_t{i.nFeeDelta};
163+
mapDeltas.erase(i.tx->GetHash());
164+
}
165+
166+
file << mapDeltas;
167+
168+
LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
169+
file << unbroadcast_txids;
170+
171+
if (!skip_file_commit && !FileCommit(file.Get()))
172+
throw std::runtime_error("FileCommit failed");
173+
file.fclose();
174+
if (!RenameOver(dump_path + ".new", dump_path)) {
175+
throw std::runtime_error("Rename failed");
176+
}
177+
auto last = SteadyClock::now();
178+
179+
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
180+
Ticks<SecondsDouble>(mid - start),
181+
Ticks<SecondsDouble>(last - mid));
182+
} catch (const std::exception& e) {
183+
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
184+
return false;
185+
}
186+
return true;
187+
}
188+
189+
} // namespace kernel

src/kernel/mempool_persist.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_KERNEL_MEMPOOL_PERSIST_H
6+
#define BITCOIN_KERNEL_MEMPOOL_PERSIST_H
7+
8+
#include <fs.h>
9+
10+
class CChainState;
11+
class CTxMemPool;
12+
13+
namespace kernel {
14+
15+
/** Dump the mempool to disk. */
16+
bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path,
17+
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen,
18+
bool skip_file_commit = false);
19+
20+
/** Load the mempool from disk. */
21+
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,
22+
CChainState& active_chainstate,
23+
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
24+
25+
} // namespace kernel
26+
27+
28+
#endif // BITCOIN_KERNEL_MEMPOOL_PERSIST_H

src/node/blockstorage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ struct CImportingNow {
823823
}
824824
};
825825

826-
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
826+
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args, const fs::path& mempool_path)
827827
{
828828
SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS);
829829
ScheduleBatchPriority();
@@ -893,6 +893,6 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
893893
return;
894894
}
895895
} // End scope of CImportingNow
896-
chainman.ActiveChainstate().LoadMempool(args);
896+
chainman.ActiveChainstate().LoadMempool(mempool_path);
897897
}
898898
} // namespace node

src/node/blockstorage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
211211

212212
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
213213

214-
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args);
214+
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args, const fs::path& mempool_path);
215215
} // namespace node
216216

217217
#endif // BITCOIN_NODE_BLOCKSTORAGE_H

0 commit comments

Comments
 (0)