Skip to content

Commit 898fe2b

Browse files
committed
Save transaction priority deltas to mempool-knots.dat
1 parent 069415f commit 898fe2b

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-1
lines changed

src/init.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1795,7 +1795,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17951795
}
17961796
// Load mempool from disk
17971797
if (auto* pool{chainman.ActiveChainstate().GetMempool()}) {
1798-
LoadMempool(*pool, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}, chainman.ActiveChainstate(), {});
1798+
LoadMempool(*pool, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}, chainman.ActiveChainstate(), {
1799+
.load_knots_data = true,
1800+
});
17991801
pool->SetLoadTried(!chainman.m_interrupt);
18001802
}
18011803
});

src/node/mempool_persist.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <uint256.h>
1717
#include <util/fs.h>
1818
#include <util/fs_helpers.h>
19+
#include <util/serfloat.h>
1920
#include <util/signalinterrupt.h>
2021
#include <util/syserror.h>
2122
#include <util/time.h>
@@ -38,6 +39,39 @@ namespace node {
3839

3940
static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1};
4041
static const uint64_t MEMPOOL_DUMP_VERSION{2};
42+
static constexpr uint64_t MEMPOOL_KNOTS_DUMP_VERSION = 0;
43+
44+
bool LoadMempoolKnots(CTxMemPool& pool, const fs::path& knots_filepath, FopenFn mockable_fopen_function)
45+
{
46+
AutoFile file{mockable_fopen_function(knots_filepath, "rb")};
47+
if (file.IsNull()) {
48+
// Typically missing if there's nothing to save
49+
return false;
50+
}
51+
52+
try {
53+
uint64_t version;
54+
file >> version;
55+
if (version != MEMPOOL_KNOTS_DUMP_VERSION) {
56+
return false;
57+
}
58+
59+
const unsigned int priority_deltas_count = ReadCompactSize(file);
60+
uint256 txid;
61+
uint64_t encoded_priority;
62+
for (unsigned int i = 0; i < priority_deltas_count; ++i) {
63+
Unserialize(file, txid);
64+
Unserialize(file, encoded_priority);
65+
const double priority = DecodeDouble(encoded_priority);
66+
pool.PrioritiseTransaction(txid, priority, 0);
67+
}
68+
} catch (const std::exception& e) {
69+
LogInfo("Failed to deserialize mempool-knots data on file: %s. Continuing anyway.\n", e.what());
70+
return false;
71+
}
72+
73+
return true;
74+
}
4175

4276
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
4377
{
@@ -143,6 +177,12 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
143177
return false;
144178
}
145179

180+
if (opts.load_knots_data) {
181+
auto knots_filepath = load_path;
182+
knots_filepath.replace_filename("mempool-knots.dat");
183+
LoadMempoolKnots(pool, knots_filepath, opts.mockable_fopen_function);
184+
}
185+
146186
LogInfo("Imported mempool transactions from file: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
147187
return true;
148188
}
@@ -152,6 +192,7 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
152192
auto start = SteadyClock::now();
153193

154194
std::map<uint256, CAmount> mapDeltas;
195+
std::map<uint256, double> priority_deltas;
155196
std::vector<TxMempoolInfo> vinfo;
156197
std::set<uint256> unbroadcast_txids;
157198

@@ -161,6 +202,9 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
161202
{
162203
LOCK(pool.cs);
163204
for (const auto &i : pool.mapDeltas) {
205+
if (i.second.first) { // priority delta
206+
priority_deltas[i.first] = i.second.first;
207+
}
164208
if (i.second.second) { // fee delta
165209
mapDeltas[i.first] = i.second.second;
166210
}
@@ -209,6 +253,39 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
209253
throw std::runtime_error(
210254
strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));
211255
}
256+
257+
auto knots_filepath = dump_path;
258+
knots_filepath.replace_filename("mempool-knots.dat");
259+
LogInfo("Writing %u mempool prioritizations to file...\n", priority_deltas.size());
260+
if (priority_deltas.size()) {
261+
auto knots_tmppath = knots_filepath;
262+
knots_tmppath += ".new";
263+
264+
AutoFile file{mockable_fopen_function(knots_tmppath, "wb")};
265+
if (file.IsNull()) return false;
266+
267+
uint64_t version = MEMPOOL_KNOTS_DUMP_VERSION;
268+
file << version;
269+
270+
WriteCompactSize(file, priority_deltas.size());
271+
for (const auto& [txid, priority] : priority_deltas) {
272+
Serialize(file, txid);
273+
const uint64_t encoded_priority = EncodeDouble(priority);
274+
Serialize(file, encoded_priority);
275+
}
276+
277+
if (!file.Commit()) throw std::runtime_error("Commit failed");
278+
if (file.fclose() != 0) {
279+
throw std::runtime_error(
280+
strprintf("Error closing %s: %s", fs::PathToString(knots_tmppath), SysErrorString(errno)));
281+
}
282+
if (!RenameOver(knots_tmppath, knots_filepath)) {
283+
throw std::runtime_error("Rename failed (mempool-knots.dat)");
284+
}
285+
} else {
286+
fs::remove(knots_filepath);
287+
}
288+
212289
if (!RenameOver(dump_path + ".new", dump_path)) {
213290
throw std::runtime_error("Rename failed");
214291
}
@@ -217,6 +294,7 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
217294
LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",
218295
Ticks<SecondsDouble>(mid - start),
219296
Ticks<SecondsDouble>(last - mid),
297+
(priority_deltas.empty() ? 0 : fs::file_size(knots_filepath)) +
220298
fs::file_size(dump_path));
221299
} catch (const std::exception& e) {
222300
LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());

src/node/mempool_persist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct ImportMempoolOptions {
2222
bool use_current_time{false};
2323
bool apply_fee_delta_priority{true};
2424
bool apply_unbroadcast_set{true};
25+
bool load_knots_data{false};
2526
};
2627
/** Import the file and attempt to add its contents to the mempool. */
2728
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,

src/test/fuzz/validation_load_mempool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ FUZZ_TARGET(validation_load_mempool, .init = initialize_validation_load_mempool)
5555
(void)LoadMempool(pool, MempoolPath(g_setup->m_args), chainstate,
5656
{
5757
.mockable_fopen_function = fuzzed_fopen,
58+
.load_knots_data = true,
5859
});
5960
pool.SetLoadTried(true);
6061
(void)DumpMempool(pool, MempoolPath(g_setup->m_args), fuzzed_fopen, true);

0 commit comments

Comments
 (0)