mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-04 15:18:05 +01:00
Merge bitcoin/bitcoin#25487: [kernel 3b/n] Decouple {Dump,Load}Mempool
from ArgsManager
cb3e9a1e3f
Move {Load,Dump}Mempool to kernel namespace (Carl Dong)aa30676541
Move DEFAULT_PERSIST_MEMPOOL out of libbitcoinkernel (Carl Dong)06b88ffb8a
LoadMempool: Pass in load_path, stop using gArgs (Carl Dong)b857ac60d9
test/fuzz: Invoke LoadMempool via CChainState (Carl Dong)b3267258b0
Move FopenFn to fsbridge namespace (Carl Dong)ae1e8e3756
mempool: Use NodeClock+friends for LoadMempool (Carl Dong)f9e8e5719f
mempool: Improve comments for [GS]etLoadTried (Carl Dong)813962da0b
scripted-diff: Rename m_is_loaded -> m_load_tried (Carl Dong)413f4bb52b
DumpMempool: Pass in dump_path, stop using gArgs (Carl Dong)bd4407817e
DumpMempool: Use std::chrono instead of weird int64_t arthmetics (Carl Dong)c84390b741
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 ACKcb3e9a1e3f
via `git range-diff 7ae032e...cb3e9a1` MarcoFalke: ACKcb3e9a1e3f
🔒 ryanofsky: Code review ACKcb3e9a1e3f
Tree-SHA512: 979d7237c3abb5a1dd9b5ad3dbf3b954f906a6d8320ed7b923557f41a4472deccae3e8a6bca0018c8e7a3c4a93afecc502acd1e26756f2054f157f1c0edd939d
This commit is contained in:
commit
821f5c824f
23 changed files with 373 additions and 196 deletions
|
@ -41,6 +41,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
|
|||
CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/iwyu_tool.py"\
|
||||
" src/compat"\
|
||||
" src/init"\
|
||||
" src/kernel/mempool_persist.cpp"\
|
||||
" src/policy/feerate.cpp"\
|
||||
" src/policy/packages.cpp"\
|
||||
" src/policy/settings.cpp"\
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
{ include: [ "<bits/termios-c_lflag.h>", private, "<termios.h>", public ] },
|
||||
{ include: [ "<bits/termios-struct.h>", private, "<termios.h>", public ] },
|
||||
{ include: [ "<bits/termios-tcflow.h>", private, "<termios.h>", public ] },
|
||||
{ include: [ "<bits/chrono.h>", private, "<chrono>", public ] },
|
||||
]
|
||||
|
|
|
@ -177,6 +177,7 @@ BITCOIN_CORE_H = \
|
|||
kernel/context.h \
|
||||
kernel/mempool_limits.h \
|
||||
kernel/mempool_options.h \
|
||||
kernel/mempool_persist.h \
|
||||
key.h \
|
||||
key_io.h \
|
||||
logging.h \
|
||||
|
@ -198,6 +199,7 @@ BITCOIN_CORE_H = \
|
|||
node/chainstate.h \
|
||||
node/coin.h \
|
||||
node/context.h \
|
||||
node/mempool_persist_args.h \
|
||||
node/miner.h \
|
||||
node/minisketchwrapper.h \
|
||||
node/psbt.h \
|
||||
|
@ -366,6 +368,7 @@ libbitcoin_node_a_SOURCES = \
|
|||
kernel/checks.cpp \
|
||||
kernel/coinstats.cpp \
|
||||
kernel/context.cpp \
|
||||
kernel/mempool_persist.cpp \
|
||||
mapport.cpp \
|
||||
mempool_args.cpp \
|
||||
net.cpp \
|
||||
|
@ -379,6 +382,7 @@ libbitcoin_node_a_SOURCES = \
|
|||
node/context.cpp \
|
||||
node/eviction.cpp \
|
||||
node/interfaces.cpp \
|
||||
node/mempool_persist_args.cpp \
|
||||
node/miner.cpp \
|
||||
node/minisketchwrapper.cpp \
|
||||
node/psbt.cpp \
|
||||
|
@ -881,6 +885,7 @@ libbitcoinkernel_la_SOURCES = \
|
|||
kernel/checks.cpp \
|
||||
kernel/coinstats.cpp \
|
||||
kernel/context.cpp \
|
||||
kernel/mempool_persist.cpp \
|
||||
key.cpp \
|
||||
logging.cpp \
|
||||
node/blockstorage.cpp \
|
||||
|
|
|
@ -10,6 +10,7 @@ EXTRA_LIBRARIES += \
|
|||
TEST_FUZZ_H = \
|
||||
test/fuzz/fuzz.h \
|
||||
test/fuzz/FuzzedDataProvider.h \
|
||||
test/fuzz/mempool_utils.h \
|
||||
test/fuzz/util.h
|
||||
|
||||
libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
|
||||
|
|
2
src/fs.h
2
src/fs.h
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <ostream>
|
||||
|
@ -199,6 +200,7 @@ bool create_directories(const std::filesystem::path& p, std::error_code& ec) = d
|
|||
|
||||
/** Bridge operations to C stdio */
|
||||
namespace fsbridge {
|
||||
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
|
||||
FILE *fopen(const fs::path& p, const char *mode);
|
||||
|
||||
/**
|
||||
|
|
13
src/init.cpp
13
src/init.cpp
|
@ -10,6 +10,7 @@
|
|||
#include <init.h>
|
||||
|
||||
#include <kernel/checks.h>
|
||||
#include <kernel/mempool_persist.h>
|
||||
|
||||
#include <addrman.h>
|
||||
#include <banman.h>
|
||||
|
@ -41,6 +42,7 @@
|
|||
#include <node/chainstate.h>
|
||||
#include <node/context.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <node/mempool_persist_args.h>
|
||||
#include <node/miner.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
|
@ -102,14 +104,19 @@
|
|||
#include <zmq/zmqrpc.h>
|
||||
#endif
|
||||
|
||||
using kernel::DumpMempool;
|
||||
|
||||
using node::CacheSizes;
|
||||
using node::CalculateCacheSizes;
|
||||
using node::ChainstateLoadVerifyError;
|
||||
using node::ChainstateLoadingError;
|
||||
using node::CleanupBlockRevFiles;
|
||||
using node::DEFAULT_PERSIST_MEMPOOL;
|
||||
using node::DEFAULT_PRINTPRIORITY;
|
||||
using node::DEFAULT_STOPAFTERBLOCKIMPORT;
|
||||
using node::LoadChainstate;
|
||||
using node::MempoolPath;
|
||||
using node::ShouldPersistMempool;
|
||||
using node::NodeContext;
|
||||
using node::ThreadImport;
|
||||
using node::VerifyLoadedChainstate;
|
||||
|
@ -245,8 +252,8 @@ void Shutdown(NodeContext& node)
|
|||
node.addrman.reset();
|
||||
node.netgroupman.reset();
|
||||
|
||||
if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
||||
DumpMempool(*node.mempool);
|
||||
if (node.mempool && node.mempool->GetLoadTried() && ShouldPersistMempool(*node.args)) {
|
||||
DumpMempool(*node.mempool, MempoolPath(*node.args));
|
||||
}
|
||||
|
||||
// Drop transactions we were still watching, and record fee estimations.
|
||||
|
@ -1669,7 +1676,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
}
|
||||
|
||||
chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
|
||||
ThreadImport(chainman, vImportFiles, args);
|
||||
ThreadImport(chainman, vImportFiles, args, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{});
|
||||
});
|
||||
|
||||
// Wait for genesis block to be processed
|
||||
|
|
189
src/kernel/mempool_persist.cpp
Normal file
189
src/kernel/mempool_persist.cpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <kernel/mempool_persist.h>
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <consensus/amount.h>
|
||||
#include <fs.h>
|
||||
#include <logging.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <serialize.h>
|
||||
#include <shutdown.h>
|
||||
#include <streams.h>
|
||||
#include <sync.h>
|
||||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using fsbridge::FopenFn;
|
||||
|
||||
namespace kernel {
|
||||
|
||||
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
|
||||
|
||||
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, CChainState& active_chainstate, FopenFn mockable_fopen_function)
|
||||
{
|
||||
if (load_path.empty()) return false;
|
||||
|
||||
FILE* filestr{mockable_fopen_function(load_path, "rb")};
|
||||
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull()) {
|
||||
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t count = 0;
|
||||
int64_t expired = 0;
|
||||
int64_t failed = 0;
|
||||
int64_t already_there = 0;
|
||||
int64_t unbroadcast = 0;
|
||||
auto now = NodeClock::now();
|
||||
|
||||
try {
|
||||
uint64_t version;
|
||||
file >> version;
|
||||
if (version != MEMPOOL_DUMP_VERSION) {
|
||||
return false;
|
||||
}
|
||||
uint64_t num;
|
||||
file >> num;
|
||||
while (num) {
|
||||
--num;
|
||||
CTransactionRef tx;
|
||||
int64_t nTime;
|
||||
int64_t nFeeDelta;
|
||||
file >> tx;
|
||||
file >> nTime;
|
||||
file >> nFeeDelta;
|
||||
|
||||
CAmount amountdelta = nFeeDelta;
|
||||
if (amountdelta) {
|
||||
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
||||
}
|
||||
if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
|
||||
LOCK(cs_main);
|
||||
const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
|
||||
if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
++count;
|
||||
} else {
|
||||
// mempool may contain the transaction already, e.g. from
|
||||
// wallet(s) having loaded it while we were processing
|
||||
// mempool transactions; consider these as valid, instead of
|
||||
// failed, but mark them as 'already there'
|
||||
if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
|
||||
++already_there;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++expired;
|
||||
}
|
||||
if (ShutdownRequested())
|
||||
return false;
|
||||
}
|
||||
std::map<uint256, CAmount> mapDeltas;
|
||||
file >> mapDeltas;
|
||||
|
||||
for (const auto& i : mapDeltas) {
|
||||
pool.PrioritiseTransaction(i.first, i.second);
|
||||
}
|
||||
|
||||
std::set<uint256> unbroadcast_txids;
|
||||
file >> unbroadcast_txids;
|
||||
unbroadcast = unbroadcast_txids.size();
|
||||
for (const auto& txid : unbroadcast_txids) {
|
||||
// Ensure transactions were accepted to mempool then add to
|
||||
// unbroadcast set.
|
||||
if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit)
|
||||
{
|
||||
auto start = SteadyClock::now();
|
||||
|
||||
std::map<uint256, CAmount> mapDeltas;
|
||||
std::vector<TxMempoolInfo> vinfo;
|
||||
std::set<uint256> unbroadcast_txids;
|
||||
|
||||
static Mutex dump_mutex;
|
||||
LOCK(dump_mutex);
|
||||
|
||||
{
|
||||
LOCK(pool.cs);
|
||||
for (const auto &i : pool.mapDeltas) {
|
||||
mapDeltas[i.first] = i.second;
|
||||
}
|
||||
vinfo = pool.infoAll();
|
||||
unbroadcast_txids = pool.GetUnbroadcastTxs();
|
||||
}
|
||||
|
||||
auto mid = SteadyClock::now();
|
||||
|
||||
try {
|
||||
FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")};
|
||||
if (!filestr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
uint64_t version = MEMPOOL_DUMP_VERSION;
|
||||
file << version;
|
||||
|
||||
file << (uint64_t)vinfo.size();
|
||||
for (const auto& i : vinfo) {
|
||||
file << *(i.tx);
|
||||
file << int64_t{count_seconds(i.m_time)};
|
||||
file << int64_t{i.nFeeDelta};
|
||||
mapDeltas.erase(i.tx->GetHash());
|
||||
}
|
||||
|
||||
file << mapDeltas;
|
||||
|
||||
LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
|
||||
file << unbroadcast_txids;
|
||||
|
||||
if (!skip_file_commit && !FileCommit(file.Get()))
|
||||
throw std::runtime_error("FileCommit failed");
|
||||
file.fclose();
|
||||
if (!RenameOver(dump_path + ".new", dump_path)) {
|
||||
throw std::runtime_error("Rename failed");
|
||||
}
|
||||
auto last = SteadyClock::now();
|
||||
|
||||
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
|
||||
Ticks<SecondsDouble>(mid - start),
|
||||
Ticks<SecondsDouble>(last - mid));
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
28
src/kernel/mempool_persist.h
Normal file
28
src/kernel/mempool_persist.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_KERNEL_MEMPOOL_PERSIST_H
|
||||
#define BITCOIN_KERNEL_MEMPOOL_PERSIST_H
|
||||
|
||||
#include <fs.h>
|
||||
|
||||
class CChainState;
|
||||
class CTxMemPool;
|
||||
|
||||
namespace kernel {
|
||||
|
||||
/** Dump the mempool to disk. */
|
||||
bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path,
|
||||
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen,
|
||||
bool skip_file_commit = false);
|
||||
|
||||
/** Load the mempool from disk. */
|
||||
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,
|
||||
CChainState& active_chainstate,
|
||||
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
|
||||
|
||||
} // namespace kernel
|
||||
|
||||
|
||||
#endif // BITCOIN_KERNEL_MEMPOOL_PERSIST_H
|
|
@ -823,7 +823,7 @@ struct CImportingNow {
|
|||
}
|
||||
};
|
||||
|
||||
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
|
||||
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args, const fs::path& mempool_path)
|
||||
{
|
||||
SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS);
|
||||
ScheduleBatchPriority();
|
||||
|
@ -893,6 +893,6 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
|
|||
return;
|
||||
}
|
||||
} // End scope of CImportingNow
|
||||
chainman.ActiveChainstate().LoadMempool(args);
|
||||
chainman.ActiveChainstate().LoadMempool(mempool_path);
|
||||
}
|
||||
} // namespace node
|
||||
|
|
|
@ -211,7 +211,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
|
|||
|
||||
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
|
||||
|
||||
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args);
|
||||
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args, const fs::path& mempool_path);
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_BLOCKSTORAGE_H
|
||||
|
|
23
src/node/mempool_persist_args.cpp
Normal file
23
src/node/mempool_persist_args.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <node/mempool_persist_args.h>
|
||||
|
||||
#include <fs.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
bool ShouldPersistMempool(const ArgsManager& argsman)
|
||||
{
|
||||
return argsman.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL);
|
||||
}
|
||||
|
||||
fs::path MempoolPath(const ArgsManager& argsman)
|
||||
{
|
||||
return argsman.GetDataDirNet() / "mempool.dat";
|
||||
}
|
||||
|
||||
} // namespace node
|
25
src/node/mempool_persist_args.h
Normal file
25
src/node/mempool_persist_args.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_NODE_MEMPOOL_PERSIST_ARGS_H
|
||||
#define BITCOIN_NODE_MEMPOOL_PERSIST_ARGS_H
|
||||
|
||||
#include <fs.h>
|
||||
|
||||
class ArgsManager;
|
||||
|
||||
namespace node {
|
||||
|
||||
/**
|
||||
* Default for -persistmempool, indicating whether the node should attempt to
|
||||
* automatically load the mempool on start and save to disk on shutdown
|
||||
*/
|
||||
static constexpr bool DEFAULT_PERSIST_MEMPOOL{true};
|
||||
|
||||
bool ShouldPersistMempool(const ArgsManager& argsman);
|
||||
fs::path MempoolPath(const ArgsManager& argsman);
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_MEMPOOL_PERSIST_ARGS_H
|
|
@ -5,9 +5,12 @@
|
|||
|
||||
#include <rpc/blockchain.h>
|
||||
|
||||
#include <kernel/mempool_persist.h>
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <core_io.h>
|
||||
#include <fs.h>
|
||||
#include <node/mempool_persist_args.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <policy/settings.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
@ -18,7 +21,11 @@
|
|||
#include <univalue.h>
|
||||
#include <util/moneystr.h>
|
||||
|
||||
using kernel::DumpMempool;
|
||||
|
||||
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
|
||||
using node::MempoolPath;
|
||||
using node::ShouldPersistMempool;
|
||||
using node::NodeContext;
|
||||
|
||||
static RPCHelpMan sendrawtransaction()
|
||||
|
@ -653,7 +660,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
|
|||
// Make sure this call is atomic in the pool.
|
||||
LOCK(pool.cs);
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("loaded", pool.IsLoaded());
|
||||
ret.pushKV("loaded", pool.GetLoadTried());
|
||||
ret.pushKV("size", (int64_t)pool.size());
|
||||
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
|
||||
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
|
||||
|
@ -717,16 +724,18 @@ static RPCHelpMan savemempool()
|
|||
const ArgsManager& args{EnsureAnyArgsman(request.context)};
|
||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||
|
||||
if (!mempool.IsLoaded()) {
|
||||
if (!mempool.GetLoadTried()) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
|
||||
}
|
||||
|
||||
if (!DumpMempool(mempool)) {
|
||||
const fs::path& dump_path = MempoolPath(args);
|
||||
|
||||
if (!DumpMempool(mempool, dump_path)) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).u8string());
|
||||
ret.pushKV("filename", dump_path.u8string());
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
|
19
src/test/fuzz/mempool_utils.h
Normal file
19
src/test/fuzz/mempool_utils.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
|
||||
#define BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
|
||||
|
||||
#include <validation.h>
|
||||
|
||||
class DummyChainState final : public CChainState
|
||||
{
|
||||
public:
|
||||
void SetMempool(CTxMemPool* mempool)
|
||||
{
|
||||
m_mempool = mempool;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
|
|
@ -8,6 +8,7 @@
|
|||
#include <node/miner.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/mempool_utils.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/mining.h>
|
||||
#include <test/util/script.h>
|
||||
|
@ -34,15 +35,6 @@ struct MockedTxPool : public CTxMemPool {
|
|||
}
|
||||
};
|
||||
|
||||
class DummyChainState final : public CChainState
|
||||
{
|
||||
public:
|
||||
void SetMempool(CTxMemPool* mempool)
|
||||
{
|
||||
m_mempool = mempool;
|
||||
}
|
||||
};
|
||||
|
||||
void initialize_tx_pool()
|
||||
{
|
||||
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <kernel/mempool_persist.h>
|
||||
|
||||
#include <chainparamsbase.h>
|
||||
#include <mempool_args.h>
|
||||
#include <node/mempool_persist_args.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/mempool_utils.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <txmempool.h>
|
||||
|
@ -15,6 +19,10 @@
|
|||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
using kernel::DumpMempool;
|
||||
|
||||
using node::MempoolPath;
|
||||
|
||||
namespace {
|
||||
const TestingSetup* g_setup;
|
||||
} // namespace
|
||||
|
@ -33,9 +41,12 @@ FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
|
|||
|
||||
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
|
||||
|
||||
auto& chainstate{static_cast<DummyChainState&>(g_setup->m_node.chainman->ActiveChainstate())};
|
||||
chainstate.SetMempool(&pool);
|
||||
|
||||
auto fuzzed_fopen = [&](const fs::path&, const char*) {
|
||||
return fuzzed_file_provider.open();
|
||||
};
|
||||
(void)LoadMempool(pool, g_setup->m_node.chainman->ActiveChainstate(), fuzzed_fopen);
|
||||
(void)DumpMempool(pool, fuzzed_fopen, true);
|
||||
(void)chainstate.LoadMempool(MempoolPath(g_setup->m_args), fuzzed_fopen);
|
||||
(void)DumpMempool(pool, MempoolPath(g_setup->m_args), fuzzed_fopen, true);
|
||||
}
|
||||
|
|
|
@ -1210,14 +1210,14 @@ void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors,
|
|||
}
|
||||
}
|
||||
|
||||
bool CTxMemPool::IsLoaded() const
|
||||
bool CTxMemPool::GetLoadTried() const
|
||||
{
|
||||
LOCK(cs);
|
||||
return m_is_loaded;
|
||||
return m_load_tried;
|
||||
}
|
||||
|
||||
void CTxMemPool::SetIsLoaded(bool loaded)
|
||||
void CTxMemPool::SetLoadTried(bool load_tried)
|
||||
{
|
||||
LOCK(cs);
|
||||
m_is_loaded = loaded;
|
||||
m_load_tried = load_tried;
|
||||
}
|
||||
|
|
|
@ -451,7 +451,7 @@ protected:
|
|||
|
||||
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||
|
||||
bool m_is_loaded GUARDED_BY(cs){false};
|
||||
bool m_load_tried GUARDED_BY(cs){false};
|
||||
|
||||
CFeeRate GetMinFee(size_t sizelimit) const;
|
||||
|
||||
|
@ -728,11 +728,17 @@ public:
|
|||
*/
|
||||
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
|
||||
|
||||
/** @returns true if the mempool is fully loaded */
|
||||
bool IsLoaded() const;
|
||||
/**
|
||||
* @returns true if we've made an attempt to load the mempool regardless of
|
||||
* whether the attempt was successful or not
|
||||
*/
|
||||
bool GetLoadTried() const;
|
||||
|
||||
/** Sets the current loaded state */
|
||||
void SetIsLoaded(bool loaded);
|
||||
/**
|
||||
* Set whether or not we've made an attempt to load the mempool (regardless
|
||||
* of whether the attempt was successful or not)
|
||||
*/
|
||||
void SetLoadTried(bool load_tried);
|
||||
|
||||
unsigned long size() const
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ struct NodeClock : public std::chrono::system_clock {
|
|||
};
|
||||
using NodeSeconds = std::chrono::time_point<NodeClock, std::chrono::seconds>;
|
||||
|
||||
using SteadyClock = std::chrono::steady_clock;
|
||||
using SteadySeconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::seconds>;
|
||||
using SteadyMilliseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::milliseconds>;
|
||||
using SteadyMicroseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds>;
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
#include <validation.h>
|
||||
|
||||
#include <kernel/coinstats.h>
|
||||
#include <kernel/mempool_persist.h>
|
||||
|
||||
#include <arith_uint256.h>
|
||||
#include <chain.h>
|
||||
#include <chainparams.h>
|
||||
|
@ -17,8 +20,8 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <cuckoocache.h>
|
||||
#include <flatfile.h>
|
||||
#include <fs.h>
|
||||
#include <hash.h>
|
||||
#include <kernel/coinstats.h>
|
||||
#include <logging.h>
|
||||
#include <logging/timer.h>
|
||||
#include <node/blockstorage.h>
|
||||
|
@ -47,12 +50,14 @@
|
|||
#include <util/rbf.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
#include <util/trace.h>
|
||||
#include <util/translation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <warnings.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
|
@ -61,7 +66,9 @@
|
|||
using kernel::CCoinsStats;
|
||||
using kernel::CoinStatsHashType;
|
||||
using kernel::ComputeUTXOStats;
|
||||
using kernel::LoadMempool;
|
||||
|
||||
using fsbridge::FopenFn;
|
||||
using node::BLOCKFILE_CHUNK_SIZE;
|
||||
using node::BlockManager;
|
||||
using node::BlockMap;
|
||||
|
@ -3861,13 +3868,11 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
|
|||
}
|
||||
}
|
||||
|
||||
void CChainState::LoadMempool(const ArgsManager& args)
|
||||
void CChainState::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_function)
|
||||
{
|
||||
if (!m_mempool) return;
|
||||
if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
||||
::LoadMempool(*m_mempool, *this);
|
||||
}
|
||||
m_mempool->SetIsLoaded(!ShutdownRequested());
|
||||
::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
|
||||
m_mempool->SetLoadTried(!ShutdownRequested());
|
||||
}
|
||||
|
||||
bool CChainState::LoadChainTip()
|
||||
|
@ -4638,153 +4643,6 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
|
||||
|
||||
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
|
||||
{
|
||||
int64_t nExpiryTimeout = std::chrono::seconds{pool.m_expiry}.count();
|
||||
FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat", "rb")};
|
||||
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull()) {
|
||||
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t count = 0;
|
||||
int64_t expired = 0;
|
||||
int64_t failed = 0;
|
||||
int64_t already_there = 0;
|
||||
int64_t unbroadcast = 0;
|
||||
int64_t nNow = GetTime();
|
||||
|
||||
try {
|
||||
uint64_t version;
|
||||
file >> version;
|
||||
if (version != MEMPOOL_DUMP_VERSION) {
|
||||
return false;
|
||||
}
|
||||
uint64_t num;
|
||||
file >> num;
|
||||
while (num) {
|
||||
--num;
|
||||
CTransactionRef tx;
|
||||
int64_t nTime;
|
||||
int64_t nFeeDelta;
|
||||
file >> tx;
|
||||
file >> nTime;
|
||||
file >> nFeeDelta;
|
||||
|
||||
CAmount amountdelta = nFeeDelta;
|
||||
if (amountdelta) {
|
||||
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
||||
}
|
||||
if (nTime > nNow - nExpiryTimeout) {
|
||||
LOCK(cs_main);
|
||||
const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
|
||||
if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
++count;
|
||||
} else {
|
||||
// mempool may contain the transaction already, e.g. from
|
||||
// wallet(s) having loaded it while we were processing
|
||||
// mempool transactions; consider these as valid, instead of
|
||||
// failed, but mark them as 'already there'
|
||||
if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
|
||||
++already_there;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++expired;
|
||||
}
|
||||
if (ShutdownRequested())
|
||||
return false;
|
||||
}
|
||||
std::map<uint256, CAmount> mapDeltas;
|
||||
file >> mapDeltas;
|
||||
|
||||
for (const auto& i : mapDeltas) {
|
||||
pool.PrioritiseTransaction(i.first, i.second);
|
||||
}
|
||||
|
||||
std::set<uint256> unbroadcast_txids;
|
||||
file >> unbroadcast_txids;
|
||||
unbroadcast = unbroadcast_txids.size();
|
||||
for (const auto& txid : unbroadcast_txids) {
|
||||
// Ensure transactions were accepted to mempool then add to
|
||||
// unbroadcast set.
|
||||
if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit)
|
||||
{
|
||||
int64_t start = GetTimeMicros();
|
||||
|
||||
std::map<uint256, CAmount> mapDeltas;
|
||||
std::vector<TxMempoolInfo> vinfo;
|
||||
std::set<uint256> unbroadcast_txids;
|
||||
|
||||
static Mutex dump_mutex;
|
||||
LOCK(dump_mutex);
|
||||
|
||||
{
|
||||
LOCK(pool.cs);
|
||||
for (const auto &i : pool.mapDeltas) {
|
||||
mapDeltas[i.first] = i.second;
|
||||
}
|
||||
vinfo = pool.infoAll();
|
||||
unbroadcast_txids = pool.GetUnbroadcastTxs();
|
||||
}
|
||||
|
||||
int64_t mid = GetTimeMicros();
|
||||
|
||||
try {
|
||||
FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat.new", "wb")};
|
||||
if (!filestr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
uint64_t version = MEMPOOL_DUMP_VERSION;
|
||||
file << version;
|
||||
|
||||
file << (uint64_t)vinfo.size();
|
||||
for (const auto& i : vinfo) {
|
||||
file << *(i.tx);
|
||||
file << int64_t{count_seconds(i.m_time)};
|
||||
file << int64_t{i.nFeeDelta};
|
||||
mapDeltas.erase(i.tx->GetHash());
|
||||
}
|
||||
|
||||
file << mapDeltas;
|
||||
|
||||
LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
|
||||
file << unbroadcast_txids;
|
||||
|
||||
if (!skip_file_commit && !FileCommit(file.Get()))
|
||||
throw std::runtime_error("FileCommit failed");
|
||||
file.fclose();
|
||||
if (!RenameOver(gArgs.GetDataDirNet() / "mempool.dat.new", gArgs.GetDataDirNet() / "mempool.dat")) {
|
||||
throw std::runtime_error("Rename failed");
|
||||
}
|
||||
int64_t last = GetTimeMicros();
|
||||
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Guess how far we are in the verification process at the given block index
|
||||
//! require cs_main if pindex has not been validated yet (because nChainTx might be unset)
|
||||
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) {
|
||||
|
|
|
@ -68,8 +68,6 @@ static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
|||
static const bool DEFAULT_TXINDEX = false;
|
||||
static constexpr bool DEFAULT_COINSTATSINDEX{false};
|
||||
static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
|
||||
/** Default for -persistmempool */
|
||||
static const bool DEFAULT_PERSIST_MEMPOOL = true;
|
||||
/** Default for -stopatheight */
|
||||
static const int DEFAULT_STOPATHEIGHT = 0;
|
||||
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ActiveChain().Tip() will not be pruned. */
|
||||
|
@ -679,7 +677,7 @@ public:
|
|||
void CheckBlockIndex();
|
||||
|
||||
/** Load the persisted mempool from disk */
|
||||
void LoadMempool(const ArgsManager& args);
|
||||
void LoadMempool(const fs::path& load_path, fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
|
||||
|
||||
/** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */
|
||||
bool LoadChainTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
@ -1014,14 +1012,6 @@ bool DeploymentEnabled(const ChainstateManager& chainman, DEP dep)
|
|||
return DeploymentEnabled(chainman.GetConsensus(), dep);
|
||||
}
|
||||
|
||||
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
|
||||
|
||||
/** Dump the mempool to disk. */
|
||||
bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false);
|
||||
|
||||
/** Load the mempool from disk. */
|
||||
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen);
|
||||
|
||||
/**
|
||||
* Return the expected assumeutxo value for a given height, if one exists.
|
||||
*
|
||||
|
|
|
@ -141,6 +141,16 @@ class MempoolPersistTest(BitcoinTestFramework):
|
|||
self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
|
||||
assert_equal(node2_balance, wallet_watch.getbalance())
|
||||
|
||||
mempooldat0 = os.path.join(self.nodes[0].datadir, self.chain, 'mempool.dat')
|
||||
mempooldat1 = os.path.join(self.nodes[1].datadir, self.chain, 'mempool.dat')
|
||||
|
||||
self.log.debug("Force -persistmempool=0 node1 to savemempool to disk via RPC")
|
||||
assert not os.path.exists(mempooldat1)
|
||||
result1 = self.nodes[1].savemempool()
|
||||
assert os.path.isfile(mempooldat1)
|
||||
assert_equal(result1['filename'], mempooldat1)
|
||||
os.remove(mempooldat1)
|
||||
|
||||
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
|
||||
self.stop_nodes()
|
||||
self.start_node(0, extra_args=["-persistmempool=0"])
|
||||
|
@ -153,8 +163,6 @@ class MempoolPersistTest(BitcoinTestFramework):
|
|||
assert self.nodes[0].getmempoolinfo()["loaded"]
|
||||
assert_equal(len(self.nodes[0].getrawmempool()), 7)
|
||||
|
||||
mempooldat0 = os.path.join(self.nodes[0].datadir, self.chain, 'mempool.dat')
|
||||
mempooldat1 = os.path.join(self.nodes[1].datadir, self.chain, 'mempool.dat')
|
||||
self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it")
|
||||
os.remove(mempooldat0)
|
||||
result0 = self.nodes[0].savemempool()
|
||||
|
|
|
@ -22,6 +22,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES = (
|
|||
"wallet/fees -> wallet/wallet -> wallet/fees",
|
||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet",
|
||||
"kernel/coinstats -> validation -> kernel/coinstats",
|
||||
"kernel/mempool_persist -> validation -> kernel/mempool_persist",
|
||||
)
|
||||
|
||||
CODE_DIR = "src"
|
||||
|
|
Loading…
Add table
Reference in a new issue