aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.test.include21
-rw-r--r--src/base58.cpp4
-rw-r--r--src/base58.h13
-rw-r--r--src/bench/bench.cpp5
-rw-r--r--src/bench/checkqueue.cpp9
-rw-r--r--src/bitcoind.cpp43
-rw-r--r--src/chain.h6
-rw-r--r--src/dummywallet.cpp35
-rw-r--r--src/init.cpp343
-rw-r--r--src/init.h14
-rw-r--r--src/interfaces/chain.h13
-rw-r--r--src/interfaces/node.cpp69
-rw-r--r--src/interfaces/node.h63
-rw-r--r--src/interfaces/wallet.cpp43
-rw-r--r--src/interfaces/wallet.h35
-rw-r--r--src/key.cpp4
-rw-r--r--src/net.cpp4
-rw-r--r--src/net.h11
-rw-r--r--src/net_processing.cpp290
-rw-r--r--src/net_processing.h2
-rw-r--r--src/netaddress.cpp475
-rw-r--r--src/netaddress.h156
-rw-r--r--src/netbase.cpp5
-rw-r--r--src/node/context.h5
-rw-r--r--src/protocol.h20
-rw-r--r--src/pubkey.cpp2
-rw-r--r--src/qt/bitcoin.cpp81
-rw-r--r--src/qt/bitcoin.h10
-rw-r--r--src/qt/bitcoingui.cpp6
-rw-r--r--src/qt/forms/debugwindow.ui2
-rw-r--r--src/qt/forms/receivecoinsdialog.ui6
-rw-r--r--src/qt/intro.cpp9
-rw-r--r--src/qt/intro.h2
-rw-r--r--src/qt/optionsmodel.cpp26
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/paymentserver.cpp6
-rw-r--r--src/qt/paymentserver.h2
-rw-r--r--src/qt/receivecoinsdialog.cpp16
-rw-r--r--src/qt/receivecoinsdialog.h3
-rw-r--r--src/qt/splashscreen.cpp45
-rw-r--r--src/qt/splashscreen.h13
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/test_main.cpp12
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/utilitydialog.h6
-rw-r--r--src/qt/walletcontroller.cpp10
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/rpc/mining.cpp141
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/server.cpp6
-rw-r--r--src/rpc/server.h4
-rw-r--r--src/sync.cpp5
-rw-r--r--src/sync.h10
-rw-r--r--src/test/denialofservice_tests.cpp20
-rw-r--r--src/test/fuzz/asmap.cpp14
-rw-r--r--src/test/fuzz/net.cpp156
-rw-r--r--src/test/fuzz/netaddress.cpp5
-rw-r--r--src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp38
-rw-r--r--src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp33
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/net_tests.cpp74
-rw-r--r--src/test/netbase_tests.cpp17
-rw-r--r--src/test/util/setup_common.cpp46
-rw-r--r--src/test/util/setup_common.h13
-rw-r--r--src/test/validation_chainstate_tests.cpp3
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp11
-rw-r--r--src/test/validation_flush_tests.cpp3
-rw-r--r--src/threadsafety.h6
-rw-r--r--src/util/message.cpp2
-rw-r--r--src/util/strencodings.cpp28
-rw-r--r--src/util/strencodings.h11
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/validation.cpp51
-rw-r--r--src/validation.h17
-rw-r--r--src/wallet/init.cpp27
-rw-r--r--src/wallet/rpcdump.cpp110
-rw-r--r--src/wallet/rpcwallet.cpp34
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/test/init_test_fixture.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.h3
-rw-r--r--src/wallet/test/init_tests.cpp14
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp12
-rw-r--r--src/wallet/wallet.cpp153
-rw-r--r--src/wallet/wallet.h27
-rw-r--r--src/wallet/walletdb.cpp17
-rw-r--r--src/wallet/walletdb.h1
-rw-r--r--src/zmq/zmqpublishnotifier.cpp8
-rw-r--r--src/zmq/zmqrpc.cpp10
92 files changed, 1758 insertions, 1360 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 0068c94070..91cdecad40 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -70,6 +70,7 @@ FUZZ_TARGETS = \
test/fuzz/message \
test/fuzz/messageheader_deserialize \
test/fuzz/multiplication_overflow \
+ test/fuzz/net \
test/fuzz/net_permissions \
test/fuzz/netaddr_deserialize \
test/fuzz/netaddress \
@@ -132,6 +133,8 @@ FUZZ_TARGETS = \
test/fuzz/script_sigcache \
test/fuzz/script_sign \
test/fuzz/scriptnum_ops \
+ test/fuzz/secp256k1_ec_seckey_import_export_der \
+ test/fuzz/secp256k1_ecdsa_signature_parse_der_lax \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
test/fuzz/snapshotmetadata_deserialize \
@@ -722,6 +725,12 @@ test_fuzz_multiplication_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_multiplication_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_multiplication_overflow_SOURCES = test/fuzz/multiplication_overflow.cpp
+test_fuzz_net_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_net_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_net_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_net_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_net_SOURCES = test/fuzz/net.cpp
+
test_fuzz_net_permissions_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_net_permissions_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_net_permissions_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -1094,6 +1103,18 @@ test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_scriptnum_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_scriptnum_ops_SOURCES = test/fuzz/scriptnum_ops.cpp
+test_fuzz_secp256k1_ec_seckey_import_export_der_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ec_seckey_import_export_der_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_secp256k1_ec_seckey_import_export_der_SOURCES = test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
+
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_SOURCES = test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
+
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/base58.cpp b/src/base58.cpp
index 18cd2090e0..0dc6044145 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -35,7 +35,7 @@ static const int8_t mapBase58[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
+NODISCARD static bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && IsSpace(*psz))
@@ -141,7 +141,7 @@ std::string EncodeBase58Check(Span<const unsigned char> input)
return EncodeBase58(vch);
}
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
+NODISCARD static bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
{
if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) ||
(vchRet.size() < 4)) {
diff --git a/src/base58.h b/src/base58.h
index b87664b78b..468c3e2589 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -26,13 +26,6 @@
std::string EncodeBase58(Span<const unsigned char> input);
/**
- * Decode a base58-encoded string (psz) into a byte vector (vchRet).
- * return true if decoding is successful.
- * psz cannot be nullptr.
- */
-NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
-
-/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
@@ -44,12 +37,6 @@ NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>&
std::string EncodeBase58Check(Span<const unsigned char> input);
/**
- * Decode a base58-encoded string (psz) that includes a checksum into a byte
- * vector (vchRet), return true if decoding is successful
- */
-NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
-
-/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 01466d0b6f..012057e792 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -70,7 +70,10 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
std::cout << bench.complexityBigO() << std::endl;
}
- benchmarkResults.push_back(bench.results().back());
+
+ if (!bench.results().empty()) {
+ benchmarkResults.push_back(bench.results().back());
+ }
}
GenerateTemplateResults(benchmarkResults, args.output_csv, "# Benchmark, evals, iterations, total, min, max, median\n"
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 19d7bc0dbc..ffa772d8c1 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -14,8 +14,6 @@
#include <vector>
-
-static const int MIN_CORES = 2;
static const size_t BATCHES = 101;
static const size_t BATCH_SIZE = 30;
static const int PREVECTOR_SIZE = 28;
@@ -26,6 +24,9 @@ static const unsigned int QUEUE_BATCH_SIZE = 128;
// and there is a little bit of work done between calls to Add.
static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
{
+ // We shouldn't ever be running with the checkqueue on a single core machine.
+ if (GetNumCores() <= 1) return;
+
const ECCVerifyHandle verify_handle;
ECC_Start();
@@ -44,7 +45,9 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
};
CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
boost::thread_group tg;
- for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) {
+ // The main thread should be counted to prevent thread oversubscription, and
+ // to decrease the variance of benchmark results.
+ for (auto x = 0; x < GetNumCores() - 1; ++x) {
tg.create_thread([&]{queue.Thread();});
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b04cc12059..02074f820a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -44,34 +44,28 @@ static void WaitForShutdown(NodeContext& node)
static bool AppInit(int argc, char* argv[])
{
NodeContext node;
- node.chain = interfaces::MakeChain(node);
bool fRet = false;
util::ThreadSetInternalName("init");
- //
- // Parameters
- //
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
SetupServerArgs(node);
+ ArgsManager& args = *Assert(node.args);
std::string error;
- if (!gArgs.ParseParameters(argc, argv, error)) {
+ if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
}
// Process help and version before taking care about datadir
- if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
- if (gArgs.IsArgSet("-version"))
- {
+ if (args.IsArgSet("-version")) {
strUsage += FormatParagraph(LicenseInfo()) + "\n";
- }
- else
- {
+ } else {
strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n";
- strUsage += "\n" + gArgs.GetHelpMessage();
+ strUsage += "\n" + args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
@@ -82,14 +76,14 @@ static bool AppInit(int argc, char* argv[])
try
{
if (!CheckDataDirOption()) {
- return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))));
+ return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""))));
}
- if (!gArgs.ReadConfigFiles(error, true)) {
+ if (!args.ReadConfigFiles(error, true)) {
return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error)));
}
// Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
- SelectParams(gArgs.GetChainName());
+ SelectParams(args.GetChainName());
} catch (const std::exception& e) {
return InitError(Untranslated(strprintf("%s\n", e.what())));
}
@@ -101,23 +95,21 @@ static bool AppInit(int argc, char* argv[])
}
}
- if (!gArgs.InitSettings(error)) {
+ if (!args.InitSettings(error)) {
InitError(Untranslated(error));
return false;
}
// -server defaults to true for bitcoind but not for the GUI so do this here
- gArgs.SoftSetBoolArg("-server", true);
+ args.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
- InitLogging();
- InitParameterInteraction();
- if (!AppInitBasicSetup())
- {
+ InitLogging(args);
+ InitParameterInteraction(args);
+ if (!AppInitBasicSetup(args)) {
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (!AppInitParameterInteraction())
- {
+ if (!AppInitParameterInteraction(args)) {
// InitError will have been called with detailed error, which ends up on console
return false;
}
@@ -126,8 +118,7 @@ static bool AppInit(int argc, char* argv[])
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (gArgs.GetBoolArg("-daemon", false))
- {
+ if (args.GetBoolArg("-daemon", false)) {
#if HAVE_DECL_DAEMON
#if defined(MAC_OSX)
#pragma GCC diagnostic push
@@ -152,7 +143,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitMain(context, node);
+ fRet = AppInitInterfaces(node) && AppInitMain(context, node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
diff --git a/src/chain.h b/src/chain.h
index 802e23f775..43e8a39f36 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -398,12 +398,6 @@ public:
return vChain[nHeight];
}
- /** Compare two chains efficiently. */
- friend bool operator==(const CChain &a, const CChain &b) {
- return a.vChain.size() == b.vChain.size() &&
- a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
- }
-
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
return (*this)[pindex->nHeight] == pindex;
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 380d4eb8ac..8d2dcd0279 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -4,11 +4,8 @@
#include <util/system.h>
#include <walletinitinterface.h>
-#include <support/allocators/secure.h>
class CWallet;
-enum class WalletCreationStatus;
-struct bilingual_str;
namespace interfaces {
class Chain;
@@ -49,7 +46,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-walletdir=<dir>",
"-walletnotify=<cmd>",
"-walletrbf",
- "-zapwallettxes=<mode>",
"-dblogsize=<n>",
"-flushwallet",
"-privdb",
@@ -59,37 +55,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
-fs::path GetWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<fs::path> ListWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<std::shared_ptr<CWallet>> GetWallets()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
namespace interfaces {
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
diff --git a/src/init.cpp b/src/init.cpp
index ebea6037d6..bb93f5c797 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -107,14 +107,14 @@ static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
*/
static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
-static fs::path GetPidFile()
+static fs::path GetPidFile(const ArgsManager& args)
{
- return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
+ return AbsPathForConfigVal(fs::path(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
}
-NODISCARD static bool CreatePidFile()
+NODISCARD static bool CreatePidFile(const ArgsManager& args)
{
- fsbridge::ofstream file{GetPidFile()};
+ fsbridge::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
@@ -123,7 +123,7 @@ NODISCARD static bool CreatePidFile()
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile(args).string(), std::strerror(errno)));
}
}
@@ -180,6 +180,7 @@ void Shutdown(NodeContext& node)
TRY_LOCK(g_shutdown_mutex, lock_shutdown);
if (!lock_shutdown) return;
LogPrintf("%s: In progress...\n", __func__);
+ Assert(node.args);
/// Note: Shutdown() must be able to handle cases in which initialization failed part of the way,
/// for example if the data directory was found to be locked.
@@ -230,7 +231,7 @@ void Shutdown(NodeContext& node)
node.connman.reset();
node.banman.reset();
- if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (::mempool.IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(::mempool);
}
@@ -301,19 +302,19 @@ void Shutdown(NodeContext& node)
GetMainSignals().UnregisterBackgroundSignalScheduler();
globalVerifyHandle.reset();
ECC_Stop();
- node.args = nullptr;
node.mempool = nullptr;
node.chainman = nullptr;
node.scheduler.reset();
try {
- if (!fs::remove(GetPidFile())) {
+ if (!fs::remove(GetPidFile(*node.args))) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
}
} catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
+ node.args = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -372,7 +373,7 @@ void SetupServerArgs(NodeContext& node)
node.args = &gArgs;
ArgsManager& argsman = *node.args;
- SetupHelpOptions(gArgs);
+ SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
@@ -599,21 +600,6 @@ std::string LicenseInfo()
"\n";
}
-#if HAVE_SYSTEM
-static void BlockNotifyCallback(SynchronizationState sync_state, const CBlockIndex* pBlockIndex)
-{
- if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex)
- return;
-
- std::string strCmd = gArgs.GetArg("-blocknotify", "");
- if (!strCmd.empty()) {
- boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
- std::thread t(runCommand, strCmd);
- t.detach(); // thread runs free
- }
-}
-#endif
-
static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
@@ -684,7 +670,7 @@ static void CleanupBlockRevFiles()
}
}
-static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles)
+static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
{
const CChainParams& chainparams = Params();
ScheduleBatchPriority();
@@ -746,13 +732,13 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
}
}
- if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
+ if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
return;
}
} // End scope of CImportingNow
- if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
LoadMempool(::mempool);
}
::mempool.SetIsLoaded(!ShutdownRequested());
@@ -780,6 +766,7 @@ static bool InitSanityCheck()
static bool AppInitServers(const util::Ref& context, NodeContext& node)
{
+ const ArgsManager& args = *Assert(node.args);
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
@@ -788,71 +775,71 @@ static bool AppInitServers(const util::Ref& context, NodeContext& node)
node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(context))
return false;
- if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
+ if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
StartHTTPServer();
return true;
}
// Parameter interaction based on rules
-void InitParameterInteraction()
+void InitParameterInteraction(ArgsManager& args)
{
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
- if (gArgs.IsArgSet("-bind")) {
- if (gArgs.SoftSetBoolArg("-listen", true))
+ if (args.IsArgSet("-bind")) {
+ if (args.SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
}
- if (gArgs.IsArgSet("-whitebind")) {
- if (gArgs.SoftSetBoolArg("-listen", true))
+ if (args.IsArgSet("-whitebind")) {
+ if (args.SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
}
- if (gArgs.IsArgSet("-connect")) {
+ if (args.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
- if (gArgs.SoftSetBoolArg("-dnsseed", false))
+ if (args.SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-listen", false))
+ if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
}
- if (gArgs.IsArgSet("-proxy")) {
+ if (args.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy server is specified
- if (gArgs.SoftSetBoolArg("-listen", false))
+ if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
// to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
// to listen locally, so don't rely on this happening through -listen below.
- if (gArgs.SoftSetBoolArg("-upnp", false))
+ if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
// to protect privacy, do not discover addresses by default
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
}
- if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
+ if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening (pointless)
- if (gArgs.SoftSetBoolArg("-upnp", false))
+ if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-listenonion", false))
+ if (args.SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
}
- if (gArgs.IsArgSet("-externalip")) {
+ if (args.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
// disable whitelistrelay in blocksonly mode
- if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
- if (gArgs.SoftSetBoolArg("-whitelistrelay", false))
+ if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
+ if (args.SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
}
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
- if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
- if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
+ if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ if (args.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
}
@@ -863,18 +850,18 @@ void InitParameterInteraction()
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
-void InitLogging()
+void InitLogging(const ArgsManager& args)
{
- LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
- LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
- LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
- LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
- LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
+ LogInstance().m_file_path = AbsPathForConfigVal(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+ LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
+ LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
+ LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
#ifdef HAVE_THREAD_LOCAL
- LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
+ LogInstance().m_log_threadnames = args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
#endif
- fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
+ fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
std::string version_string = FormatFullVersion();
#ifdef DEBUG
@@ -909,7 +896,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
std::terminate();
};
-bool AppInitBasicSetup()
+bool AppInitBasicSetup(ArgsManager& args)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
@@ -929,7 +916,7 @@ bool AppInitBasicSetup()
}
#ifndef WIN32
- if (!gArgs.GetBoolArg("-sysperms", false)) {
+ if (!args.GetBoolArg("-sysperms", false)) {
umask(077);
}
@@ -951,7 +938,7 @@ bool AppInitBasicSetup()
return true;
}
-bool AppInitParameterInteraction()
+bool AppInitParameterInteraction(const ArgsManager& args)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 2: parameter interactions
@@ -961,9 +948,9 @@ bool AppInitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
- std::string network = gArgs.GetChainName();
+ std::string network = args.GetChainName();
bilingual_str errors;
- for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ for (const auto& arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
}
@@ -973,7 +960,7 @@ bool AppInitParameterInteraction()
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
- for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ for (const auto& section : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
}
@@ -982,15 +969,15 @@ bool AppInitParameterInteraction()
}
if (!fs::is_directory(GetBlocksDir())) {
- return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "")));
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
- std::string blockfilterindex_value = gArgs.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
+ std::string blockfilterindex_value = args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
- const std::vector<std::string> names = gArgs.GetArgs("-blockfilterindex");
+ const std::vector<std::string> names = args.GetArgs("-blockfilterindex");
for (const auto& name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
@@ -1001,7 +988,7 @@ bool AppInitParameterInteraction()
}
// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index are both enabled.
- if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
+ if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
return InitError(_("Cannot set -peerblockfilters without -blockfilterindex."));
}
@@ -1010,8 +997,8 @@ bool AppInitParameterInteraction()
}
// if using block pruning, then disallow txindex
- if (gArgs.GetArg("-prune", 0)) {
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
+ if (args.GetArg("-prune", 0)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (!g_enabled_filter_types.empty()) {
return InitError(_("Prune mode is incompatible with -blockfilterindex."));
@@ -1019,14 +1006,14 @@ bool AppInitParameterInteraction()
}
// -bind and -whitebind can't be set when not listening
- size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
- if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
+ size_t nUserBind = args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
+ if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
- nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
+ nUserMaxConnections = args.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
@@ -1046,9 +1033,9 @@ bool AppInitParameterInteraction()
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
- if (gArgs.IsArgSet("-debug")) {
+ if (args.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
- const std::vector<std::string> categories = gArgs.GetArgs("-debug");
+ const std::vector<std::string> categories = args.GetArgs("-debug");
if (std::none_of(categories.begin(), categories.end(),
[](std::string cat){return cat == "0" || cat == "none";})) {
@@ -1061,28 +1048,28 @@ bool AppInitParameterInteraction()
}
// Now remove the logging categories which were explicitly excluded
- for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
+ for (const std::string& cat : args.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
}
}
// Checkmempool and checkblockindex default to true in regtest mode
- int ratio = std::min<int>(std::max<int>(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
+ int ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
if (ratio != 0) {
mempool.setSanityCheck(1.0 / ratio);
}
- fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
- fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
+ fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
+ fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
- hashAssumeValid = uint256S(gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
+ hashAssumeValid = uint256S(args.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());
else
LogPrintf("Validating signatures for all blocks.\n");
- if (gArgs.IsArgSet("-minimumchainwork")) {
- const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
+ if (args.IsArgSet("-minimumchainwork")) {
+ const std::string minChainWorkStr = args.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr));
}
@@ -1096,22 +1083,21 @@ bool AppInitParameterInteraction()
}
// mempool limits
- int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
+ int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMin = args.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
- if (gArgs.IsArgSet("-incrementalrelayfee"))
- {
+ if (args.IsArgSet("-incrementalrelayfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
- return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
+ if (!ParseMoney(args.GetArg("-incrementalrelayfee", ""), n))
+ return InitError(AmountErrMsg("incrementalrelayfee", args.GetArg("-incrementalrelayfee", "")));
incrementalRelayFee = CFeeRate(n);
}
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
- int64_t nPruneArg = gArgs.GetArg("-prune", 0);
+ int64_t nPruneArg = args.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
@@ -1128,20 +1114,20 @@ bool AppInitParameterInteraction()
fPruneMode = true;
}
- nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
+ nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
- peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
+ peer_connect_timeout = args.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
}
- if (gArgs.IsArgSet("-minrelaytxfee")) {
+ if (args.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
- return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
+ if (!ParseMoney(args.GetArg("-minrelaytxfee", ""), n)) {
+ return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in CWallet::CreateWalletFromFile()
::minRelayTxFee = CFeeRate(n);
@@ -1153,48 +1139,46 @@ bool AppInitParameterInteraction()
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that happens
- if (gArgs.IsArgSet("-blockmintxfee"))
- {
+ if (args.IsArgSet("-blockmintxfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
- return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
+ if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n))
+ return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions
- if (gArgs.IsArgSet("-dustrelayfee"))
- {
+ if (args.IsArgSet("-dustrelayfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n))
- return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
+ if (!ParseMoney(args.GetArg("-dustrelayfee", ""), n))
+ return InitError(AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
- fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
+ fRequireStandard = !args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
}
- nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
+ nBytesPerSigOp = args.GetArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
- fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
- fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
- nMaxDatacarrierBytes = gArgs.GetArg("-datacarriersize", nMaxDatacarrierBytes);
+ fIsBareMultisigStd = args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
+ fAcceptDatacarrier = args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
+ nMaxDatacarrierBytes = args.GetArg("-datacarriersize", nMaxDatacarrierBytes);
// Option to startup with mocktime set (used for regression testing):
- SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+ SetMockTime(args.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
- if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
+ if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
+ if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError(Untranslated("rpcserialversion must be non-negative."));
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError(Untranslated("Unknown rpcserialversion requested."));
- nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
+ nMaxTipAge = args.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
return true;
}
@@ -1245,16 +1229,28 @@ bool AppInitLockDataDirectory()
return true;
}
+bool AppInitInterfaces(NodeContext& node)
+{
+ node.chain = interfaces::MakeChain(node);
+ // Create client interfaces for wallets that are supposed to be loaded
+ // according to -wallet and -disablewallet options. This only constructs
+ // the interfaces, it doesn't load wallet data. Wallets actually get loaded
+ // when load() and start() interface methods are called below.
+ g_wallet_init_interface.Construct(node);
+ return true;
+}
+
bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{
+ const ArgsManager& args = *Assert(node.args);
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
- if (!CreatePidFile()) {
+ if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
}
if (LogInstance().m_print_to_file) {
- if (gArgs.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
+ if (args.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
// Do this first since it both loads a bunch of debug.log into memory,
// and because this needs to happen before any other debug.log printing
LogInstance().ShrinkDebugFile();
@@ -1271,10 +1267,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
LogPrintf("Using data directory %s\n", GetDataDir().string());
// Only log conf file usage message if conf file actually exists.
- fs::path config_file_path = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ fs::path config_file_path = GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME));
if (fs::exists(config_file_path)) {
LogPrintf("Config file: %s\n", config_file_path.string());
- } else if (gArgs.IsArgSet("-conf")) {
+ } else if (args.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string()));
} else {
@@ -1283,23 +1279,23 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
// Log the config arguments to debug.log
- gArgs.LogArgs();
+ args.LogArgs();
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
// Warn about relative -datadir path.
- if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) {
+ if (args.IsArgSet("-datadir") && !fs::path(args.GetArg("-datadir", "")).is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
"current working directory '%s'. This is fragile, because if bitcoin is started in the future "
"from a different location, it will be unable to locate the current data files. There could "
"also be data loss if bitcoin is started while in a temporary directory.\n",
- gArgs.GetArg("-datadir", ""), fs::current_path().string());
+ args.GetArg("-datadir", ""), fs::current_path().string());
}
InitSignatureCache();
InitScriptExecutionCache();
- int script_threads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
+ int script_threads = args.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script threads)
@@ -1333,12 +1329,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
- // Create client interfaces for wallets that are supposed to be loaded
- // according to -wallet and -disablewallet options. This only constructs
- // the interfaces, it doesn't load wallet data. Wallets actually get loaded
- // when load() and start() interface methods are called below.
- g_wallet_init_interface.Construct(node);
-
/* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
@@ -1355,8 +1345,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
* that the server is there and will be ready later). Warmup mode will
* be disabled when initialisation is finished.
*/
- if (gArgs.GetBoolArg("-server", false))
- {
+ if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers(context, node))
return InitError(_("Unable to start HTTP server. See debug log for details."));
@@ -1376,9 +1365,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// need to reindex later.
assert(!node.banman);
- node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
- node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), gArgs.GetBoolArg("-networkactive", true));
+ node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true));
// Make mempool generally available in the node context. For example the connection manager, wallet, or RPC threads,
// which are all started after this, may use it from the node context.
assert(!node.mempool);
@@ -1392,7 +1381,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
- for (const std::string& cmt : gArgs.GetArgs("-uacomment")) {
+ for (const std::string& cmt : args.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
uacomments.push_back(cmt);
@@ -1403,9 +1392,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
- if (gArgs.IsArgSet("-onlynet")) {
+ if (args.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
- for (const std::string& snet : gArgs.GetArgs("-onlynet")) {
+ for (const std::string& snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
@@ -1419,12 +1408,12 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
// Check for host lookup allowed before parsing any network related parameters
- fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
+ fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
- bool proxyRandomize = gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
+ bool proxyRandomize = args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
- std::string proxyArg = gArgs.GetArg("-proxy", "");
+ std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
@@ -1446,7 +1435,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
// An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none)
- std::string onionArg = gArgs.GetArg("-onion", "");
+ std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
@@ -1464,11 +1453,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
// see Step 2: parameter interactions for more information about these
- fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
- fDiscover = gArgs.GetBoolArg("-discover", true);
- g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
+ fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
+ fDiscover = args.GetBoolArg("-discover", true);
+ g_relay_txes = !args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
- for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
+ for (const std::string& strAddr : args.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
@@ -1477,8 +1466,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
// Read asmap file if configured
- if (gArgs.IsArgSet("-asmap")) {
- fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", ""));
+ if (args.IsArgSet("-asmap")) {
+ fs::path asmap_path = fs::path(args.GetArg("-asmap", ""));
if (asmap_path.empty()) {
asmap_path = DEFAULT_ASMAP_FILENAME;
}
@@ -1511,22 +1500,22 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set
uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
- if (gArgs.IsArgSet("-maxuploadtarget")) {
- nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024;
+ if (args.IsArgSet("-maxuploadtarget")) {
+ nMaxOutboundLimit = args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024;
}
// ********************************************************* Step 7: load block chain
- fReindex = gArgs.GetBoolArg("-reindex", false);
- bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
+ fReindex = args.GetBoolArg("-reindex", false);
+ bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
- int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
+ int64_t nTotalCache = (args.GetArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
nTotalCache -= nBlockTreeDBCache;
- int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
+ int64_t nTxIndexCache = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
nTotalCache -= nTxIndexCache;
int64_t filter_index_cache = 0;
if (!g_enabled_filter_types.empty()) {
@@ -1539,10 +1528,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
int64_t nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
- int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
@@ -1566,7 +1555,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
- chainman.InitializeChainstate();
+ chainman.InitializeChainstate(*Assert(node.mempool));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
@@ -1707,7 +1696,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
- if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
+ if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
@@ -1725,10 +1714,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// Only verify the DB of the active chainstate. This is fixed in later
// work when we allow VerifyDB to be parameterized by chainstate.
if (&::ChainstateActive() == chainstate &&
- !CVerifyDB().VerifyDB(
+ !CVerifyDB().VerifyDB(
chainparams, &chainstate->CoinsDB(),
- gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
- gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
+ args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
failed_verification = true;
break;
@@ -1784,7 +1773,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
fFeeEstimatesInitialized = true;
// ********************************************************* Step 8: start indexers
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex);
g_txindex->Start();
}
@@ -1844,16 +1833,26 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
#if HAVE_SYSTEM
- if (gArgs.IsArgSet("-blocknotify"))
- uiInterface.NotifyBlockTip_connect(BlockNotifyCallback);
+ const std::string block_notify = args.GetArg("-blocknotify", "");
+ if (!block_notify.empty()) {
+ uiInterface.NotifyBlockTip_connect([block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) {
+ if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) return;
+ std::string command = block_notify;
+ boost::replace_all(command, "%s", pBlockIndex->GetBlockHash().GetHex());
+ std::thread t(runCommand, command);
+ t.detach(); // thread runs free
+ });
+ }
#endif
std::vector<fs::path> vImportFiles;
- for (const std::string& strFile : gArgs.GetArgs("-loadblock")) {
+ for (const std::string& strFile : args.GetArgs("-loadblock")) {
vImportFiles.push_back(strFile);
}
- g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman]{ ThreadImport(chainman, vImportFiles); });
+ g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman, &args] {
+ ThreadImport(chainman, vImportFiles, args);
+ });
// Wait for genesis block to be processed
{
@@ -1892,13 +1891,13 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
- if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
+ if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl();
Discover();
// Map ports with UPnP
- if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
+ if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) {
StartMapPort();
}
@@ -1913,41 +1912,41 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc = node.peer_logic.get();
- connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
- connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
- connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
+ connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
+ connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
connOptions.m_peer_connect_timeout = peer_connect_timeout;
- for (const std::string& strBind : gArgs.GetArgs("-bind")) {
+ for (const std::string& strBind : args.GetArgs("-bind")) {
CService addrBind;
if (!Lookup(strBind, addrBind, GetListenPort(), false)) {
return InitError(ResolveErrMsg("bind", strBind));
}
connOptions.vBinds.push_back(addrBind);
}
- for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
+ for (const std::string& strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}
- for (const auto& net : gArgs.GetArgs("-whitelist")) {
+ for (const auto& net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
- connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
+ connOptions.vSeedNodes = args.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
- connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
+ connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
- const auto connect = gArgs.GetArgs("-connect");
+ const auto connect = args.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
diff --git a/src/init.h b/src/init.h
index 20008ba5be..679e875da1 100644
--- a/src/init.h
+++ b/src/init.h
@@ -8,8 +8,8 @@
#include <memory>
#include <string>
-#include <util/system.h>
+class ArgsManager;
struct NodeContext;
namespace interfaces {
struct BlockAndHeaderTipInfo;
@@ -25,21 +25,21 @@ class Ref;
void Interrupt(NodeContext& node);
void Shutdown(NodeContext& node);
//!Initialize the logging infrastructure
-void InitLogging();
+void InitLogging(const ArgsManager& args);
//!Parameter interaction: change current parameters depending on various rules
-void InitParameterInteraction();
+void InitParameterInteraction(ArgsManager& args);
/** Initialize bitcoin core: Basic context setup.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
-bool AppInitBasicSetup();
+bool AppInitBasicSetup(ArgsManager& args);
/**
* Initialization: parameter interaction.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
*/
-bool AppInitParameterInteraction();
+bool AppInitParameterInteraction(const ArgsManager& args);
/**
* Initialization sanity checks: ecc init, sanity checks, dir lock.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
@@ -53,6 +53,10 @@ bool AppInitSanityChecks();
*/
bool AppInitLockDataDirectory();
/**
+ * Initialize node and wallet interface pointers. Has no prerequisites or side effects besides allocating memory.
+ */
+bool AppInitInterfaces(NodeContext& node);
+/**
* Bitcoin core main initialization.
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 053d40335f..6e50ccb27a 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -314,24 +314,11 @@ public:
//! Set mock time.
virtual void setMockTime(int64_t time) = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr<Chain> MakeChain(NodeContext& node);
-//! Return implementation of ChainClient interface for a wallet client. This
-//! function will be undefined in builds where ENABLE_WALLET is false.
-//!
-//! Currently, wallets are the only chain clients. But in the future, other
-//! types of chain clients could be added, such as tools for monitoring,
-//! analysis, or fee estimation. These clients need to expose their own
-//! MakeXXXClient functions returning their implementations of the ChainClient
-//! interface.
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
-
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 21400d00f8..2c5f8627e6 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -27,6 +27,7 @@
#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
+#include <util/check.h>
#include <util/ref.h>
#include <util/system.h>
#include <util/translation.h>
@@ -41,48 +42,24 @@
#include <boost/signals2/signal.hpp>
-class CWallet;
-fs::path GetWalletDir();
-std::vector<fs::path> ListWalletDir();
-std::vector<std::shared_ptr<CWallet>> GetWallets();
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings);
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet);
-
namespace interfaces {
-
namespace {
class NodeImpl : public Node
{
public:
NodeImpl(NodeContext* context) { setContext(context); }
- void initError(const bilingual_str& message) override { InitError(message); }
- bool parseParameters(int argc, const char* const argv[], std::string& error) override
- {
- return gArgs.ParseParameters(argc, argv, error);
- }
- bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); }
- void forceSetArg(const std::string& arg, const std::string& value) override { gArgs.ForceSetArg(arg, value); }
- bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); }
- bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); }
- void selectParams(const std::string& network) override { SelectParams(network); }
- bool initSettings(std::string& error) override { return gArgs.InitSettings(error); }
- uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); }
- uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); }
- std::string getNetwork() override { return Params().NetworkIDString(); }
- void initLogging() override { InitLogging(); }
- void initParameterInteraction() override { InitParameterInteraction(); }
+ void initLogging() override { InitLogging(*Assert(m_context->args)); }
+ void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); }
bilingual_str getWarnings() override { return GetWarnings(true); }
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
- return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
- AppInitLockDataDirectory();
+ return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() &&
+ AppInitLockDataDirectory() && AppInitInterfaces(*m_context);
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
- m_context->chain = MakeChain(*m_context);
return AppInitMain(m_context_ref, *m_context, tip_info);
}
void appShutdown() override
@@ -109,7 +86,6 @@ public:
StopMapPort();
}
}
- void setupServerArgs() override { return SetupServerArgs(*m_context); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(CConnman::NumConnections flags) override
{
@@ -255,36 +231,9 @@ public:
LOCK(::cs_main);
return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
}
- std::string getWalletDir() override
- {
- return GetWalletDir().string();
- }
- std::vector<std::string> listWalletDir() override
- {
- std::vector<std::string> paths;
- for (auto& path : ListWalletDir()) {
- paths.push_back(path.string());
- }
- return paths;
- }
- std::vector<std::unique_ptr<Wallet>> getWallets() override
+ WalletClient& walletClient() override
{
- std::vector<std::unique_ptr<Wallet>> wallets;
- for (auto& client : m_context->chain_clients) {
- auto client_wallets = client->getWallets();
- std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets));
- }
- return wallets;
- }
- std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
- {
- return MakeWallet(LoadWallet(*m_context->chain, name, error, warnings));
- }
- std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) override
- {
- std::shared_ptr<CWallet> wallet;
- status = CreateWallet(*m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
- return MakeWallet(wallet);
+ return *Assert(m_context->wallet_client);
}
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
@@ -302,10 +251,6 @@ public:
{
return MakeHandler(::uiInterface.ShowProgress_connect(fn));
}
- std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
- {
- return HandleLoadWallet(std::move(fn));
- }
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn));
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 753f3e6b13..5079be038e 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -29,14 +29,13 @@ class RPCTimerInterface;
class UniValue;
class proxyType;
enum class SynchronizationState;
-enum class WalletCreationStatus;
struct CNodeStateStats;
struct NodeContext;
struct bilingual_str;
namespace interfaces {
class Handler;
-class Wallet;
+class WalletClient;
struct BlockTip;
//! Block and header tip information
@@ -55,41 +54,6 @@ class Node
public:
virtual ~Node() {}
- //! Send init error.
- virtual void initError(const bilingual_str& message) = 0;
-
- //! Set command line arguments.
- virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
-
- //! Set a command line argument
- virtual void forceSetArg(const std::string& arg, const std::string& value) = 0;
-
- //! Set a command line argument if it doesn't already have a value
- virtual bool softSetArg(const std::string& arg, const std::string& value) = 0;
-
- //! Set a command line boolean argument if it doesn't already have a value
- virtual bool softSetBoolArg(const std::string& arg, bool value) = 0;
-
- //! Load settings from configuration file.
- virtual bool readConfigFiles(std::string& error) = 0;
-
- //! Choose network parameters.
- virtual void selectParams(const std::string& network) = 0;
-
- //! Read and update <datadir>/settings.json file with saved settings. This
- //! needs to be called after selectParams() because the settings file
- //! location is network-specific.
- virtual bool initSettings(std::string& error) = 0;
-
- //! Get the (assumed) blockchain size.
- virtual uint64_t getAssumedBlockchainSize() = 0;
-
- //! Get the (assumed) chain state size.
- virtual uint64_t getAssumedChainStateSize() = 0;
-
- //! Get network name.
- virtual std::string getNetwork() = 0;
-
//! Init logging.
virtual void initLogging() = 0;
@@ -117,9 +81,6 @@ public:
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
- //! Setup arguments
- virtual void setupServerArgs() = 0;
-
//! Map port.
virtual void mapPort(bool use_upnp) = 0;
@@ -211,22 +172,8 @@ public:
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
- //! Return default wallet directory.
- virtual std::string getWalletDir() = 0;
-
- //! Return available wallets in wallet directory.
- virtual std::vector<std::string> listWalletDir() = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
-
- //! Attempts to load a wallet from file or directory.
- //! The loaded wallet is also notified to handlers previously registered
- //! with handleLoadWallet.
- virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
-
- //! Create a wallet from file
- virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) = 0;
+ //! Get wallet client.
+ virtual WalletClient& walletClient() = 0;
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
@@ -248,10 +195,6 @@ public:
using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
- //! Register handler for load wallet messages.
- using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
- virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
-
//! Register handler for number of connections changed messages.
using NotifyNumConnectionsChangedFn = std::function<void(int new_num_connections)>;
virtual std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 7fd24425cf..63c109658e 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -37,6 +37,7 @@ namespace {
//! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{
+ LOCK(wallet.cs_wallet);
WalletTx result;
result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size());
@@ -132,7 +133,11 @@ public:
{
return m_wallet->SignMessage(message, pkhash, str_sig);
}
- bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
+ bool isSpendable(const CTxDestination& dest) override
+ {
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
+ }
bool haveWatchOnly() override
{
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();
@@ -480,7 +485,7 @@ public:
std::shared_ptr<CWallet> m_wallet;
};
-class WalletClientImpl : public ChainClient
+class WalletClientImpl : public WalletClient
{
public:
WalletClientImpl(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
@@ -489,6 +494,9 @@ public:
m_context.chain = &chain;
m_context.args = &args;
}
+ ~WalletClientImpl() override { UnloadWallets(); }
+
+ //! ChainClient methods
void registerRpcs() override
{
for (const CRPCCommand& command : GetWalletRPCCommands()) {
@@ -504,6 +512,30 @@ public:
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
+
+ //! WalletClient methods
+ std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ std::shared_ptr<CWallet> wallet;
+ status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
+ return MakeWallet(std::move(wallet));
+ }
+ std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ return MakeWallet(LoadWallet(*m_context.chain, WalletLocation(name), error, warnings));
+ }
+ std::string getWalletDir() override
+ {
+ return GetWalletDir().string();
+ }
+ std::vector<std::string> listWalletDir() override
+ {
+ std::vector<std::string> paths;
+ for (auto& path : ListWalletDir()) {
+ paths.push_back(path.string());
+ }
+ return paths;
+ }
std::vector<std::unique_ptr<Wallet>> getWallets() override
{
std::vector<std::unique_ptr<Wallet>> wallets;
@@ -512,7 +544,10 @@ public:
}
return wallets;
}
- ~WalletClientImpl() override { UnloadWallets(); }
+ std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
+ {
+ return HandleLoadWallet(std::move(fn));
+ }
WalletContext m_context;
const std::vector<std::string> m_wallet_filenames;
@@ -524,7 +559,7 @@ public:
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; }
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
{
return MakeUnique<WalletClientImpl>(chain, args, std::move(wallet_filenames));
}
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 3cdadbc72e..186f5d81a5 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <amount.h> // For CAmount
+#include <interfaces/chain.h> // For ChainClient
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
@@ -28,9 +29,11 @@ class CWallet;
enum class FeeReason;
enum class OutputType;
enum class TransactionError;
+enum class WalletCreationStatus;
enum isminetype : unsigned int;
struct CRecipient;
struct PartiallySignedTransaction;
+struct WalletContext;
struct bilingual_str;
typedef uint8_t isminefilter;
@@ -301,6 +304,34 @@ public:
virtual CWallet* wallet() { return nullptr; }
};
+//! Wallet chain client that in addition to having chain client methods for
+//! starting up, shutting down, and registering RPCs, also has additional
+//! methods (called by the GUI) to load and create wallets.
+class WalletClient : public ChainClient
+{
+public:
+ //! Create new wallet.
+ virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Load existing wallet.
+ virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Return default wallet directory.
+ virtual std::string getWalletDir() = 0;
+
+ //! Return available wallets in wallet directory.
+ virtual std::vector<std::string> listWalletDir() = 0;
+
+ //! Return interfaces for accessing wallets (if any).
+ virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+
+ //! Register handler for load wallet messages. This callback is triggered by
+ //! createWallet and loadWallet above, and also triggered when wallets are
+ //! loaded at startup or by RPC.
+ using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
+ virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
+};
+
//! Information about one wallet address.
struct WalletAddress
{
@@ -379,6 +410,10 @@ struct WalletTxOut
//! dummywallet.cpp and throws if the wallet component is not compiled.
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet);
+//! Return implementation of ChainClient interface for a wallet client. This
+//! function will be undefined in builds where ENABLE_WALLET is false.
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
+
} // namespace interfaces
#endif // BITCOIN_INTERFACES_WALLET_H
diff --git a/src/key.cpp b/src/key.cpp
index 4ed74a39b1..868a8b9b0e 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -31,7 +31,7 @@ static secp256k1_context* secp256k1_context_sign = nullptr;
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
-static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
const unsigned char *end = seckey + seckeylen;
memset(out32, 0, 32);
/* sequence header */
@@ -88,7 +88,7 @@ static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
+int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
assert(*seckeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
diff --git a/src/net.cpp b/src/net.cpp
index 883e57bdf0..dc34743c5b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -904,10 +904,10 @@ bool CConnman::AttemptToEvictConnection()
// Protect the 8 nodes with the lowest minimum ping time.
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
- // Protect 4 nodes that most recently sent us transactions.
+ // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
- // Protect 4 nodes that most recently sent us blocks.
+ // Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
// Protect the half of the remaining nodes which have been connected the longest.
diff --git a/src/net.h b/src/net.h
index c72eada3ff..0b5ab2390e 100644
--- a/src/net.h
+++ b/src/net.h
@@ -932,8 +932,17 @@ public:
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
- // Block and TXN accept times
+ /** UNIX epoch time of the last block received from this peer that we had
+ * not yet seen (e.g. not already received from another peer), that passed
+ * preliminary validity checks and was saved to disk, even if we don't
+ * connect the block or it eventually fails connection. Used as an inbound
+ * peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastBlockTime{0};
+
+ /** UNIX epoch time of the last transaction received from this peer that we
+ * had not yet seen (e.g. not already received from another peer) and that
+ * was accepted into our mempool. Used as an inbound peer eviction criterium
+ * in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
// Ping time measurement:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 60bdfbe9f5..0e049bd666 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -278,12 +278,6 @@ struct CNodeState {
const CService address;
//! Whether we have a fully established connection.
bool fCurrentlyConnected;
- //! Accumulated misbehaviour score for this peer.
- int nMisbehavior;
- //! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
- bool m_should_discourage;
- //! String name of this peer (debugging/logging purposes).
- const std::string name;
//! The best known block we know this peer has announced.
const CBlockIndex *pindexBestKnownBlock;
//! The hash of the last unknown block this peer has announced.
@@ -432,13 +426,10 @@ struct CNodeState {
//! Whether this peer relays txs via wtxid
bool m_wtxid_relay{false};
- CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
- address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
- m_is_manual_connection (is_manual)
+ CNodeState(CAddress addrIn, bool is_inbound, bool is_manual)
+ : address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual)
{
fCurrentlyConnected = false;
- nMisbehavior = 0;
- m_should_discourage = false;
pindexBestKnownBlock = nullptr;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = nullptr;
@@ -476,6 +467,50 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
+/**
+ * Data structure for an individual peer. This struct is not protected by
+ * cs_main since it does not contain validation-critical data.
+ *
+ * Memory is owned by shared pointers and this object is destructed when
+ * the refcount drops to zero.
+ *
+ * TODO: move most members from CNodeState to this structure.
+ * TODO: move remaining application-layer data members from CNode to this structure.
+ */
+struct Peer {
+ /** Same id as the CNode object for this peer */
+ const NodeId m_id{0};
+
+ /** Protects misbehavior data members */
+ Mutex m_misbehavior_mutex;
+ /** Accumulated misbehavior score for this peer */
+ int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0};
+ /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
+ bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false};
+
+ Peer(NodeId id) : m_id(id) {}
+};
+
+using PeerRef = std::shared_ptr<Peer>;
+
+/**
+ * Map of all Peer objects, keyed by peer id. This map is protected
+ * by the global g_peer_mutex. Once a shared pointer reference is
+ * taken, the lock may be released. Individual fields are protected by
+ * their own locks.
+ */
+Mutex g_peer_mutex;
+static std::map<NodeId, PeerRef> g_peer_map GUARDED_BY(g_peer_mutex);
+
+/** Get a shared pointer to the Peer object.
+ * May return nullptr if the Peer object can't be found. */
+static PeerRef GetPeerRef(NodeId id)
+{
+ LOCK(g_peer_mutex);
+ auto it = g_peer_map.find(id);
+ return it != g_peer_map.end() ? it->second : nullptr;
+}
+
static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
@@ -628,13 +663,12 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
}
}
connman.ForNode(nodeid, [&connman](CNode* pfrom){
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
- AssertLockHeld(cs_main);
connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
@@ -841,7 +875,12 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->IsInboundConn(), pnode->IsManualConn()));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn(), pnode->IsManualConn()));
+ }
+ {
+ PeerRef peer = std::make_shared<Peer>(nodeid);
+ LOCK(g_peer_mutex);
+ g_peer_map.emplace_hint(g_peer_map.end(), nodeid, std::move(peer));
}
if(!pnode->IsInboundConn())
PushNodeVersion(*pnode, m_connman, GetTime());
@@ -870,13 +909,21 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
fUpdateConnectionTime = false;
LOCK(cs_main);
+ int misbehavior{0};
+ {
+ PeerRef peer = GetPeerRef(nodeid);
+ assert(peer != nullptr);
+ misbehavior = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+ LOCK(g_peer_mutex);
+ g_peer_map.erase(nodeid);
+ }
CNodeState *state = State(nodeid);
assert(state != nullptr);
if (state->fSyncStarted)
nSyncStarted--;
- if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ if (misbehavior == 0 && state->fCurrentlyConnected) {
fUpdateConnectionTime = true;
}
@@ -906,17 +953,23 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
}
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
- if (state == nullptr)
- return false;
- stats.nMisbehavior = state->nMisbehavior;
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
- for (const QueuedBlock& queue : state->vBlocksInFlight) {
- if (queue.pindex)
- stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ {
+ LOCK(cs_main);
+ CNodeState* state = State(nodeid);
+ if (state == nullptr)
+ return false;
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
+ for (const QueuedBlock& queue : state->vBlocksInFlight) {
+ if (queue.pindex)
+ stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ }
}
+
+ PeerRef peer = GetPeerRef(nodeid);
+ if (peer == nullptr) return false;
+ stats.m_misbehavior_score = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+
return true;
}
@@ -1060,21 +1113,21 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
* Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
* to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
*/
-void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
{
assert(howmuch > 0);
- CNodeState* const state = State(pnode);
- if (state == nullptr) return;
+ PeerRef peer = GetPeerRef(pnode);
+ if (peer == nullptr) return;
- state->nMisbehavior += howmuch;
+ LOCK(peer->m_misbehavior_mutex);
+ peer->m_misbehavior_score += howmuch;
const std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
- {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
- state->m_should_discourage = true;
+ if (peer->m_misbehavior_score >= DISCOURAGEMENT_THRESHOLD && peer->m_misbehavior_score - howmuch < DISCOURAGEMENT_THRESHOLD) {
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
+ peer->m_should_discourage = true;
} else {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
}
}
@@ -1096,7 +1149,6 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
- LOCK(cs_main);
Misbehaving(nodeid, 100, message);
return true;
}
@@ -1120,18 +1172,12 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_INVALID_HEADER:
case BlockValidationResult::BLOCK_CHECKPOINT:
case BlockValidationResult::BLOCK_INVALID_PREV:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- }
+ Misbehaving(nodeid, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case BlockValidationResult::BLOCK_MISSING_PREV:
- {
- // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
- LOCK(cs_main);
- Misbehaving(nodeid, 10, message);
- }
+ // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
+ Misbehaving(nodeid, 10, message);
return true;
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1155,11 +1201,8 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
break;
// The node is providing invalid data:
case TxValidationResult::TX_CONSENSUS:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- return true;
- }
+ Misbehaving(nodeid, 100, message);
+ return true;
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
@@ -1327,7 +1370,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// TODO: Avoid the repeated-serialization here
if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
@@ -1422,54 +1465,48 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
//
-bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- switch (inv.type)
- {
- case MSG_TX:
- case MSG_WITNESS_TX:
- case MSG_WTX:
- {
- assert(recentRejects);
- if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
- {
- // If the chain tip has changed previously rejected transactions
- // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
- // or a double-spend. Reset the rejects filter and give those
- // txs a second chance.
- hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
- recentRejects->reset();
- }
-
- {
- LOCK(g_cs_orphans);
- if (!inv.IsMsgWtx() && mapOrphanTransactions.count(inv.hash)) {
- return true;
- } else if (inv.IsMsgWtx() && g_orphans_by_wtxid.count(inv.hash)) {
- return true;
- }
- }
+ assert(recentRejects);
+ if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
+ // If the chain tip has changed previously rejected transactions
+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
+ // or a double-spend. Reset the rejects filter and give those
+ // txs a second chance.
+ hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
+ recentRejects->reset();
+ }
- {
- LOCK(g_cs_recent_confirmed_transactions);
- if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
- }
+ const uint256& hash = gtxid.GetHash();
- return recentRejects->contains(inv.hash) || mempool.exists(ToGenTxid(inv));
+ {
+ LOCK(g_cs_orphans);
+ if (!gtxid.IsWtxid() && mapOrphanTransactions.count(hash)) {
+ return true;
+ } else if (gtxid.IsWtxid() && g_orphans_by_wtxid.count(hash)) {
+ return true;
}
- case MSG_BLOCK:
- case MSG_WITNESS_BLOCK:
- return LookupBlockIndex(inv.hash) != nullptr;
}
- // Don't know what it is, just say we already got one
- return true;
+
+ {
+ LOCK(g_cs_recent_confirmed_transactions);
+ if (g_recent_confirmed_transactions->contains(hash)) return true;
+ }
+
+ return recentRejects->contains(hash) || mempool.exists(gtxid);
+}
+
+bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ return LookupBlockIndex(block_hash) != nullptr;
}
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
connman.ForEachNode([&txid, &wtxid](CNode* pnode)
{
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
+
CNodeState &state = *State(pnode->GetId());
if (state.m_wtxid_relay) {
pnode->PushTxInventory(wtxid);
@@ -1564,7 +1601,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
// disconnect node in case we have reached the outbound limit for serving historical blocks
if (send &&
connman.OutboundTargetReached(true) &&
- (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
@@ -1590,7 +1627,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
std::shared_ptr<const CBlock> pblock;
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
pblock = a_recent_block;
- } else if (inv.type == MSG_WITNESS_BLOCK) {
+ } else if (inv.IsMsgWitnessBlk()) {
// Fast-path: in this case it is possible to serve the block directly from disk,
// as the network format matches the format on disk
std::vector<uint8_t> block_data;
@@ -1607,12 +1644,11 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
pblock = pblockRead;
}
if (pblock) {
- if (inv.type == MSG_BLOCK)
+ if (inv.IsMsgBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_WITNESS_BLOCK)
+ } else if (inv.IsMsgWitnessBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_FILTERED_BLOCK)
- {
+ } else if (inv.IsMsgFilteredBlk()) {
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
if (pfrom.m_tx_relay != nullptr) {
@@ -1636,9 +1672,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
// else
// no response
- }
- else if (inv.type == MSG_CMPCT_BLOCK)
- {
+ } else if (inv.IsMsgCmpctBlk()) {
// If a peer is asking for old blocks, we're almost guaranteed
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
@@ -1766,7 +1800,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// expensive to process.
if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
+ if (inv.IsGenBlkMsg()) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
}
// else: If the first item on the queue is an unknown type, we erase it
@@ -1806,7 +1840,6 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
return;
}
@@ -2318,7 +2351,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Each connection can only send one version message
if (pfrom.nVersion != 0)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "redundant version message");
return;
}
@@ -2478,7 +2510,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (pfrom.nVersion == 0) {
// Must have a version message before anything else
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
return;
}
@@ -2545,7 +2576,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
return;
}
@@ -2559,7 +2589,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
if (vAddr.size() > MAX_ADDR_TO_SEND)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
return;
}
@@ -2638,7 +2667,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
return;
}
@@ -2654,14 +2682,11 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
LOCK(cs_main);
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
uint256* best_block{nullptr};
- for (CInv &inv : vInv)
- {
- if (interruptMsgProc)
- return;
+ for (CInv& inv : vInv) {
+ if (interruptMsgProc) return;
// Ignore INVs that don't match wtxidrelay setting.
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
@@ -2672,14 +2697,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (inv.IsMsgWtx()) continue;
}
- bool fAlreadyHave = AlreadyHave(inv, m_mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
+ if (inv.IsMsgBlk()) {
+ const bool fAlreadyHave = AlreadyHaveBlock(inv.hash);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
- if (inv.IsMsgTx()) {
- inv.type |= nFetchFlags;
- }
-
- if (inv.type == MSG_BLOCK) {
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
@@ -2689,15 +2710,21 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// then fetch the blocks we need to catch up.
best_block = &inv.hash;
}
- } else {
+ } else if (inv.IsGenTxMsg()) {
+ const GenTxid gtxid = ToGenTxid(inv);
+ const bool fAlreadyHave = AlreadyHaveTx(gtxid, mempool);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
+
pfrom.AddKnownTx(inv.hash);
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
return;
} else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
- RequestTx(State(pfrom.GetId()), ToGenTxid(inv), current_time);
+ RequestTx(State(pfrom.GetId()), gtxid, current_time);
}
+ } else {
+ LogPrint(BCLog::NET, "Unknown inv type \"%s\" received from peer=%d\n", inv.ToString(), pfrom.GetId());
}
}
@@ -2714,7 +2741,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
return;
}
@@ -2969,7 +2995,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (!AlreadyHave(CInv(MSG_WTX, wtxid), m_mempool) &&
+ if (!AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool) &&
AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
m_mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
@@ -3013,7 +3039,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
}
if (!fRejectedParents) {
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
for (const uint256& parent_txid : unique_parents) {
@@ -3022,9 +3047,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// wtxidrelay peers.
// Eventually we should replace this with an improved
// protocol for getting all unconfirmed parents.
- CInv _inv(MSG_TX | nFetchFlags, parent_txid);
+ const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
pfrom.AddKnownTx(parent_txid);
- if (!AlreadyHave(_inv, m_mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time);
+ if (!AlreadyHaveTx(gtxid, m_mempool)) RequestTx(State(pfrom.GetId()), gtxid, current_time);
}
AddOrphanTx(ptx, pfrom.GetId());
@@ -3439,7 +3464,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
return;
}
@@ -3641,7 +3665,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
}
else if (pfrom.m_tx_relay != nullptr)
@@ -3675,7 +3698,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
}
if (bad) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
}
return;
@@ -3761,15 +3783,17 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
{
const NodeId peer_id{pnode.GetId()};
+ PeerRef peer = GetPeerRef(peer_id);
+ if (peer == nullptr) return false;
+
{
- LOCK(cs_main);
- CNodeState& state = *State(peer_id);
+ LOCK(peer->m_misbehavior_mutex);
// There's nothing to do if the m_should_discourage flag isn't set
- if (!state.m_should_discourage) return false;
+ if (!peer->m_should_discourage) return false;
- state.m_should_discourage = false;
- } // cs_main
+ peer->m_should_discourage = false;
+ } // peer.m_misbehavior_mutex
if (pnode.HasPermission(PF_NOBAN)) {
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
@@ -3957,7 +3981,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
m_connman.ForEachNode([&](CNode* pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return;
@@ -3974,7 +3998,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
});
if (worst_peer != -1) {
bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
@@ -4575,7 +4599,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// processing at a later time, see below)
tx_process_time.erase(tx_process_time.begin());
CInv inv(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
- if (!AlreadyHave(inv, m_mempool)) {
+ if (!AlreadyHaveTx(ToGenTxid(inv), m_mempool)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
const auto last_request_time = GetTxRequestTime(gtxid);
diff --git a/src/net_processing.h b/src/net_processing.h
index 74d6603747..4f1c52fc17 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -97,7 +97,7 @@ private:
};
struct CNodeStateStats {
- int nMisbehavior = 0;
+ int m_misbehavior_score = 0;
int nSyncHeight = -1;
int nCommonHeight = -1;
std::vector<int> vHeightInFlight;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index d29aed6c8b..b50cf74069 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -3,79 +3,91 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <cstdint>
#include <netaddress.h>
+
#include <hash.h>
+#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/asmap.h>
-#include <tinyformat.h>
-static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
-static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <iterator>
+#include <tuple>
-// 0xFD + sha256("bitcoin")[0:5]
-static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
+constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE;
/**
* Construct an unspecified IPv6 network address (::/128).
*
* @note This address is considered invalid by CNetAddr::IsValid()
*/
-CNetAddr::CNetAddr()
-{
- memset(ip, 0, sizeof(ip));
-}
+CNetAddr::CNetAddr() {}
void CNetAddr::SetIP(const CNetAddr& ipIn)
{
+ // Size check.
+ switch (ipIn.m_net) {
+ case NET_IPV4:
+ assert(ipIn.m_addr.size() == ADDR_IPV4_SIZE);
+ break;
+ case NET_IPV6:
+ assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE);
+ break;
+ case NET_ONION:
+ assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE);
+ break;
+ case NET_INTERNAL:
+ assert(ipIn.m_addr.size() == ADDR_INTERNAL_SIZE);
+ break;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
m_net = ipIn.m_net;
- memcpy(ip, ipIn.ip, sizeof(ip));
+ m_addr = ipIn.m_addr;
+}
+
+template <typename T1, size_t PREFIX_LEN>
+inline bool HasPrefix(const T1& obj, const std::array<uint8_t, PREFIX_LEN>& prefix)
+{
+ return obj.size() >= PREFIX_LEN &&
+ std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
}
-void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16])
+void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
{
- if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) {
+ assert(ipv6.size() == ADDR_IPV6_SIZE);
+
+ size_t skip{0};
+
+ if (HasPrefix(ipv6, IPV4_IN_IPV6_PREFIX)) {
+ // IPv4-in-IPv6
m_net = NET_IPV4;
- } else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) {
+ skip = sizeof(IPV4_IN_IPV6_PREFIX);
+ } else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) {
+ // TORv2-in-IPv6
m_net = NET_ONION;
- } else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == 0) {
+ skip = sizeof(TORV2_IN_IPV6_PREFIX);
+ } else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) {
+ // Internal-in-IPv6
m_net = NET_INTERNAL;
+ skip = sizeof(INTERNAL_IN_IPV6_PREFIX);
} else {
+ // IPv6
m_net = NET_IPV6;
}
- memcpy(ip, ipv6, 16);
-}
-void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
-{
- switch(network)
- {
- case NET_IPV4:
- m_net = NET_IPV4;
- memcpy(ip, pchIPv4, 12);
- memcpy(ip+12, ip_in, 4);
- break;
- case NET_IPV6:
- SetLegacyIPv6(ip_in);
- break;
- default:
- assert(!"invalid network");
- }
+ m_addr.assign(ipv6.begin() + skip, ipv6.end());
}
/**
- * Try to make this a dummy address that maps the specified name into IPv6 like
- * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy
- * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be
- * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to
- * unique-local addresses.
- *
- * CAddrMan uses these fake addresses to keep track of which DNS seeds were
- * used.
- *
+ * Create an "internal" address that represents a name or FQDN. CAddrMan uses
+ * these fake addresses to keep track of which DNS seeds were used.
* @returns Whether or not the operation was successful.
- *
- * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
+ * @see NET_INTERNAL, INTERNAL_IN_IPV6_PREFIX, CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
*/
bool CNetAddr::SetInternal(const std::string &name)
{
@@ -85,31 +97,26 @@ bool CNetAddr::SetInternal(const std::string &name)
m_net = NET_INTERNAL;
unsigned char hash[32] = {};
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
- memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
- memcpy(ip + sizeof(g_internal_prefix), hash, sizeof(ip) - sizeof(g_internal_prefix));
+ m_addr.assign(hash, hash + ADDR_INTERNAL_SIZE);
return true;
}
/**
- * Try to make this a dummy address that maps the specified onion address into
- * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix
- * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they
- * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+ * Parse a TORv2 address and set this object to it.
*
* @returns Whether or not the operation was successful.
*
- * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193()
+ * @see CNetAddr::IsTor()
*/
bool CNetAddr::SetSpecial(const std::string &strName)
{
if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
- if (vchAddr.size() != 16-sizeof(pchOnionCat))
+ if (vchAddr.size() != ADDR_TORV2_SIZE) {
return false;
+ }
m_net = NET_ONION;
- memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
- for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
- ip[i + sizeof(pchOnionCat)] = vchAddr[i];
+ m_addr.assign(vchAddr.begin(), vchAddr.end());
return true;
}
return false;
@@ -117,28 +124,23 @@ bool CNetAddr::SetSpecial(const std::string &strName)
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
- SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
+ m_net = NET_IPV4;
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&ipv4Addr);
+ m_addr.assign(ptr, ptr + ADDR_IPV4_SIZE);
}
CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope)
{
- SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr);
+ SetLegacyIPv6(Span<const uint8_t>(reinterpret_cast<const uint8_t*>(&ipv6Addr), sizeof(ipv6Addr)));
scopeId = scope;
}
-unsigned int CNetAddr::GetByte(int n) const
-{
- return ip[15-n];
-}
-
bool CNetAddr::IsBindAny() const
{
- const int cmplen = IsIPv4() ? 4 : 16;
- for (int i = 0; i < cmplen; ++i) {
- if (GetByte(i)) return false;
+ if (!IsIPv4() && !IsIPv6()) {
+ return false;
}
-
- return true;
+ return std::all_of(m_addr.begin(), m_addr.end(), [](uint8_t b) { return b == 0; });
}
bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; }
@@ -148,88 +150,88 @@ bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; }
bool CNetAddr::IsRFC1918() const
{
return IsIPv4() && (
- GetByte(3) == 10 ||
- (GetByte(3) == 192 && GetByte(2) == 168) ||
- (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31)));
+ m_addr[0] == 10 ||
+ (m_addr[0] == 192 && m_addr[1] == 168) ||
+ (m_addr[0] == 172 && m_addr[1] >= 16 && m_addr[1] <= 31));
}
bool CNetAddr::IsRFC2544() const
{
- return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19);
+ return IsIPv4() && m_addr[0] == 198 && (m_addr[1] == 18 || m_addr[1] == 19);
}
bool CNetAddr::IsRFC3927() const
{
- return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254);
+ return IsIPv4() && HasPrefix(m_addr, std::array<uint8_t, 2>{169, 254});
}
bool CNetAddr::IsRFC6598() const
{
- return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127;
+ return IsIPv4() && m_addr[0] == 100 && m_addr[1] >= 64 && m_addr[1] <= 127;
}
bool CNetAddr::IsRFC5737() const
{
- return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) ||
- (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) ||
- (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113));
+ return IsIPv4() && (HasPrefix(m_addr, std::array<uint8_t, 3>{192, 0, 2}) ||
+ HasPrefix(m_addr, std::array<uint8_t, 3>{198, 51, 100}) ||
+ HasPrefix(m_addr, std::array<uint8_t, 3>{203, 0, 113}));
}
bool CNetAddr::IsRFC3849() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x0D && GetByte(12) == 0xB8;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x0D, 0xB8});
}
bool CNetAddr::IsRFC3964() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 2>{0x20, 0x02});
}
bool CNetAddr::IsRFC6052() const
{
- static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
- return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0;
+ return IsIPv6() &&
+ HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x64, 0xFF, 0x9B, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
}
bool CNetAddr::IsRFC4380() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 &&
- GetByte(12) == 0;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x00, 0x00});
}
bool CNetAddr::IsRFC4862() const
{
- static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
- return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 8>{0xFE, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00});
}
bool CNetAddr::IsRFC4193() const
{
- return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC;
+ return IsIPv6() && (m_addr[0] & 0xFE) == 0xFC;
}
bool CNetAddr::IsRFC6145() const
{
- static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
- return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0;
+ return IsIPv6() &&
+ HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00});
}
bool CNetAddr::IsRFC4843() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) &&
+ (m_addr[3] & 0xF0) == 0x10;
}
bool CNetAddr::IsRFC7343() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) &&
+ (m_addr[3] & 0xF0) == 0x20;
}
bool CNetAddr::IsHeNet() const
{
- return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70);
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x04, 0x70});
}
/**
@@ -243,13 +245,15 @@ bool CNetAddr::IsTor() const { return m_net == NET_ONION; }
bool CNetAddr::IsLocal() const
{
// IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8)
- if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
+ if (IsIPv4() && (m_addr[0] == 127 || m_addr[0] == 0)) {
return true;
+ }
// IPv6 loopback (::1/128)
static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
- if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0)
+ if (IsIPv6() && memcmp(m_addr.data(), pchLocal, sizeof(pchLocal)) == 0) {
return true;
+ }
return false;
}
@@ -266,19 +270,11 @@ bool CNetAddr::IsLocal() const
*/
bool CNetAddr::IsValid() const
{
- // Cleanup 3-byte shifted addresses caused by garbage in size field
- // of addr messages from versions before 0.2.9 checksum.
- // Two consecutive addr messages look like this:
- // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
- // so if the first length field is garbled, it reads the second batch
- // of addr misaligned by 3 bytes.
- if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
- return false;
-
// unspecified IPv6 address (::/128)
unsigned char ipNone6[16] = {};
- if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0)
+ if (IsIPv6() && memcmp(m_addr.data(), ipNone6, sizeof(ipNone6)) == 0) {
return false;
+ }
// documentation IPv6 address
if (IsRFC3849())
@@ -287,17 +283,11 @@ bool CNetAddr::IsValid() const
if (IsInternal())
return false;
- if (IsIPv4())
- {
- // INADDR_NONE
- uint32_t ipNone = INADDR_NONE;
- if (memcmp(ip+12, &ipNone, 4) == 0)
- return false;
-
- // 0
- ipNone = 0;
- if (memcmp(ip+12, &ipNone, 4) == 0)
+ if (IsIPv4()) {
+ const uint32_t addr = ReadBE32(m_addr.data());
+ if (addr == INADDR_ANY || addr == INADDR_NONE) {
return false;
+ }
}
return true;
@@ -318,7 +308,7 @@ bool CNetAddr::IsRoutable() const
}
/**
- * @returns Whether or not this is a dummy address that maps a name into IPv6.
+ * @returns Whether or not this is a dummy address that represents a name.
*
* @see CNetAddr::SetInternal(const std::string &)
*/
@@ -341,9 +331,9 @@ enum Network CNetAddr::GetNetwork() const
std::string CNetAddr::ToStringIP() const
{
if (IsTor())
- return EncodeBase32(&ip[6], 10) + ".onion";
+ return EncodeBase32(m_addr) + ".onion";
if (IsInternal())
- return EncodeBase32(ip + sizeof(g_internal_prefix), sizeof(ip) - sizeof(g_internal_prefix)) + ".internal";
+ return EncodeBase32(m_addr) + ".internal";
CService serv(*this, 0);
struct sockaddr_storage sockaddr;
socklen_t socklen = sizeof(sockaddr);
@@ -353,13 +343,13 @@ std::string CNetAddr::ToStringIP() const
return std::string(name);
}
if (IsIPv4())
- return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
- else
- return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12),
- GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8),
- GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4),
- GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0));
+ return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]);
+ assert(IsIPv6());
+ return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
+ m_addr[0] << 8 | m_addr[1], m_addr[2] << 8 | m_addr[3],
+ m_addr[4] << 8 | m_addr[5], m_addr[6] << 8 | m_addr[7],
+ m_addr[8] << 8 | m_addr[9], m_addr[10] << 8 | m_addr[11],
+ m_addr[12] << 8 | m_addr[13], m_addr[14] << 8 | m_addr[15]);
}
std::string CNetAddr::ToString() const
@@ -369,12 +359,12 @@ std::string CNetAddr::ToString() const
bool operator==(const CNetAddr& a, const CNetAddr& b)
{
- return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0;
+ return a.m_net == b.m_net && a.m_addr == b.m_addr;
}
bool operator<(const CNetAddr& a, const CNetAddr& b)
{
- return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0);
+ return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr);
}
/**
@@ -391,7 +381,8 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
{
if (!IsIPv4())
return false;
- memcpy(pipv4Addr, ip+12, 4);
+ assert(sizeof(*pipv4Addr) == m_addr.size());
+ memcpy(pipv4Addr, m_addr.data(), m_addr.size());
return true;
}
@@ -410,7 +401,8 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
if (!IsIPv6()) {
return false;
}
- memcpy(pipv6Addr, ip, 16);
+ assert(sizeof(*pipv6Addr) == m_addr.size());
+ memcpy(pipv6Addr, m_addr.data(), m_addr.size());
return true;
}
@@ -421,15 +413,17 @@ bool CNetAddr::HasLinkedIPv4() const
uint32_t CNetAddr::GetLinkedIPv4() const
{
- if (IsIPv4() || IsRFC6145() || IsRFC6052()) {
- // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
- return ReadBE32(ip + 12);
+ if (IsIPv4()) {
+ return ReadBE32(m_addr.data());
+ } else if (IsRFC6052() || IsRFC6145()) {
+ // mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
+ return ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data());
} else if (IsRFC3964()) {
// 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6
- return ReadBE32(ip + 2);
+ return ReadBE32(MakeSpan(m_addr).subspan(2, ADDR_IPV4_SIZE).data());
} else if (IsRFC4380()) {
// Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped
- return ~ReadBE32(ip + 12);
+ return ~ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data());
}
assert(false);
}
@@ -458,10 +452,10 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
}
std::vector<bool> ip_bits(128);
if (HasLinkedIPv4()) {
- // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits)
+ // For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
- ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1;
+ ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
}
}
uint32_t ipv4 = GetLinkedIPv4();
@@ -470,8 +464,9 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
}
} else {
// Use all 128 bits of the IPv6 address otherwise
+ assert(IsIPv6());
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
- uint8_t cur_byte = GetByte(15 - byte_i);
+ uint8_t cur_byte = m_addr[byte_i];
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
}
@@ -507,19 +502,15 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
}
vchRet.push_back(net_class);
- int nStartByte = 0;
- int nBits = 16;
+ int nBits{0};
if (IsLocal()) {
// all local addresses belong to the same group
- nBits = 0;
} else if (IsInternal()) {
// all internal-usage addresses get their own group
- nStartByte = sizeof(g_internal_prefix);
- nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8;
+ nBits = ADDR_INTERNAL_SIZE * 8;
} else if (!IsRoutable()) {
// all other unroutable addresses belong to the same group
- nBits = 0;
} else if (HasLinkedIPv4()) {
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
uint32_t ipv4 = GetLinkedIPv4();
@@ -527,7 +518,6 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
} else if (IsTor()) {
- nStartByte = 6;
nBits = 4;
} else if (IsHeNet()) {
// for he.net, use /36 groups
@@ -537,23 +527,29 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
nBits = 32;
}
- // push our ip onto vchRet byte by byte...
- while (nBits >= 8)
- {
- vchRet.push_back(GetByte(15 - nStartByte));
- nStartByte++;
- nBits -= 8;
- }
+ // Push our address onto vchRet.
+ const size_t num_bytes = nBits / 8;
+ vchRet.insert(vchRet.end(), m_addr.begin(), m_addr.begin() + num_bytes);
+ nBits %= 8;
// ...for the last byte, push nBits and for the rest of the byte push 1's
- if (nBits > 0)
- vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
+ if (nBits > 0) {
+ assert(num_bytes < m_addr.size());
+ vchRet.push_back(m_addr[num_bytes] | ((1 << (8 - nBits)) - 1));
+ }
return vchRet;
}
+std::vector<unsigned char> CNetAddr::GetAddrBytes() const
+{
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+ SerializeV1Array(serialized);
+ return {std::begin(serialized), std::end(serialized)};
+}
+
uint64_t CNetAddr::GetHash() const
{
- uint256 hash = Hash(ip);
+ uint256 hash = Hash(m_addr);
uint64_t nRet;
memcpy(&nRet, &hash, sizeof(nRet));
return nRet;
@@ -764,53 +760,89 @@ CSubNet::CSubNet():
memset(netmask, 0, sizeof(netmask));
}
-CSubNet::CSubNet(const CNetAddr &addr, int32_t mask)
+CSubNet::CSubNet(const CNetAddr& addr, uint8_t mask) : CSubNet()
{
- valid = true;
+ valid = (addr.IsIPv4() && mask <= ADDR_IPV4_SIZE * 8) ||
+ (addr.IsIPv6() && mask <= ADDR_IPV6_SIZE * 8);
+ if (!valid) {
+ return;
+ }
+
+ assert(mask <= sizeof(netmask) * 8);
+
network = addr;
- // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
- memset(netmask, 255, sizeof(netmask));
-
- // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- const int astartofs = network.IsIPv4() ? 12 : 0;
-
- int32_t n = mask;
- if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address
- {
- n += astartofs*8;
- // Clear bits [n..127]
- for (; n < 128; ++n)
- netmask[n>>3] &= ~(1<<(7-(n&7)));
- } else
- valid = false;
- // Normalize network according to netmask
- for(int x=0; x<16; ++x)
- network.ip[x] &= netmask[x];
+ uint8_t n = mask;
+ for (size_t i = 0; i < network.m_addr.size(); ++i) {
+ const uint8_t bits = n < 8 ? n : 8;
+ netmask[i] = (uint8_t)((uint8_t)0xFF << (8 - bits)); // Set first bits.
+ network.m_addr[i] &= netmask[i]; // Normalize network according to netmask.
+ n -= bits;
+ }
+}
+
+/**
+ * @returns The number of 1-bits in the prefix of the specified subnet mask. If
+ * the specified subnet mask is not a valid one, -1.
+ */
+static inline int NetmaskBits(uint8_t x)
+{
+ switch(x) {
+ case 0x00: return 0;
+ case 0x80: return 1;
+ case 0xc0: return 2;
+ case 0xe0: return 3;
+ case 0xf0: return 4;
+ case 0xf8: return 5;
+ case 0xfc: return 6;
+ case 0xfe: return 7;
+ case 0xff: return 8;
+ default: return -1;
+ }
}
-CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask)
+CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet()
{
- valid = true;
- network = addr;
- // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
- memset(netmask, 255, sizeof(netmask));
+ valid = (addr.IsIPv4() || addr.IsIPv6()) && addr.m_net == mask.m_net;
+ if (!valid) {
+ return;
+ }
+ // Check if `mask` contains 1-bits after 0-bits (which is an invalid netmask).
+ bool zeros_found = false;
+ for (auto b : mask.m_addr) {
+ const int num_bits = NetmaskBits(b);
+ if (num_bits == -1 || (zeros_found && num_bits != 0)) {
+ valid = false;
+ return;
+ }
+ if (num_bits < 8) {
+ zeros_found = true;
+ }
+ }
+
+ assert(mask.m_addr.size() <= sizeof(netmask));
- // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- const int astartofs = network.IsIPv4() ? 12 : 0;
+ memcpy(netmask, mask.m_addr.data(), mask.m_addr.size());
- for(int x=astartofs; x<16; ++x)
- netmask[x] = mask.ip[x];
+ network = addr;
// Normalize network according to netmask
- for(int x=0; x<16; ++x)
- network.ip[x] &= netmask[x];
+ for (size_t x = 0; x < network.m_addr.size(); ++x) {
+ network.m_addr[x] &= netmask[x];
+ }
}
-CSubNet::CSubNet(const CNetAddr &addr):
- valid(addr.IsValid())
+CSubNet::CSubNet(const CNetAddr& addr) : CSubNet()
{
- memset(netmask, 255, sizeof(netmask));
+ valid = addr.IsIPv4() || addr.IsIPv6();
+ if (!valid) {
+ return;
+ }
+
+ assert(addr.m_addr.size() <= sizeof(netmask));
+
+ memset(netmask, 0xFF, addr.m_addr.size());
+
network = addr;
}
@@ -822,68 +854,29 @@ bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
return false;
- for(int x=0; x<16; ++x)
- if ((addr.ip[x] & netmask[x]) != network.ip[x])
+ assert(network.m_addr.size() == addr.m_addr.size());
+ for (size_t x = 0; x < addr.m_addr.size(); ++x) {
+ if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) {
return false;
- return true;
-}
-
-/**
- * @returns The number of 1-bits in the prefix of the specified subnet mask. If
- * the specified subnet mask is not a valid one, -1.
- */
-static inline int NetmaskBits(uint8_t x)
-{
- switch(x) {
- case 0x00: return 0;
- case 0x80: return 1;
- case 0xc0: return 2;
- case 0xe0: return 3;
- case 0xf0: return 4;
- case 0xf8: return 5;
- case 0xfc: return 6;
- case 0xfe: return 7;
- case 0xff: return 8;
- default: return -1;
+ }
}
+ return true;
}
std::string CSubNet::ToString() const
{
- /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
- int cidr = 0;
- bool valid_cidr = true;
- int n = network.IsIPv4() ? 12 : 0;
- for (; n < 16 && netmask[n] == 0xff; ++n)
- cidr += 8;
- if (n < 16) {
- int bits = NetmaskBits(netmask[n]);
- if (bits < 0)
- valid_cidr = false;
- else
- cidr += bits;
- ++n;
- }
- for (; n < 16 && valid_cidr; ++n)
- if (netmask[n] != 0x00)
- valid_cidr = false;
-
- /* Format output */
- std::string strNetmask;
- if (valid_cidr) {
- strNetmask = strprintf("%u", cidr);
- } else {
- if (network.IsIPv4())
- strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
- else
- strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
- netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
- netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
- netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
+ assert(network.m_addr.size() <= sizeof(netmask));
+
+ uint8_t cidr = 0;
+
+ for (size_t i = 0; i < network.m_addr.size(); ++i) {
+ if (netmask[i] == 0x00) {
+ break;
+ }
+ cidr += NetmaskBits(netmask[i]);
}
- return network.ToString() + "/" + strNetmask;
+ return network.ToString() + strprintf("/%u", cidr);
}
bool CSubNet::IsValid() const
diff --git a/src/netaddress.h b/src/netaddress.h
index 0365907d44..d00f5a6f55 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,9 +9,12 @@
#include <config/bitcoin-config.h>
#endif
+#include <attributes.h>
#include <compat.h>
+#include <prevector.h>
#include <serialize.h>
+#include <array>
#include <cstdint>
#include <string>
#include <vector>
@@ -39,16 +42,49 @@ enum Network
/// TORv2
NET_ONION,
- /// A set of dummy addresses that map a name to an IPv6 address. These
- /// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses).
- /// We use them to map a string or FQDN to an IPv6 address in CAddrMan to
- /// keep track of which DNS seeds were used.
+ /// A set of addresses that represent the hash of a string or FQDN. We use
+ /// them in CAddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
NET_MAX,
};
+/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
+};
+
+/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
+ 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
+};
+
+/// Prefix of an IPv6 address when it contains an embedded "internal" address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
+ 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
+};
+
+/// Size of IPv4 address (in bytes).
+static constexpr size_t ADDR_IPV4_SIZE = 4;
+
+/// Size of IPv6 address (in bytes).
+static constexpr size_t ADDR_IPV6_SIZE = 16;
+
+/// Size of TORv2 address (in bytes).
+static constexpr size_t ADDR_TORV2_SIZE = 10;
+
+/// Size of "internal" (NET_INTERNAL) address (in bytes).
+static constexpr size_t ADDR_INTERNAL_SIZE = 10;
+
/**
* Network address.
*/
@@ -56,11 +92,16 @@ class CNetAddr
{
protected:
/**
+ * Raw representation of the network address.
+ * In network byte order (big endian) for IPv4 and IPv6.
+ */
+ prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
+
+ /**
* Network to which this address belongs.
*/
Network m_net{NET_IPV6};
- unsigned char ip[16]; // in network byte order
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
public:
@@ -74,13 +115,7 @@ class CNetAddr
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
* `addr` encoding.
*/
- void SetLegacyIPv6(const uint8_t ipv6[16]);
-
- /**
- * Set raw IPv4 or IPv6 address (in network byte order)
- * @note Only NET_IPV4 and NET_IPV6 are allowed for network.
- */
- void SetRaw(Network network, const uint8_t *data);
+ void SetLegacyIPv6(Span<const uint8_t> ipv6);
bool SetInternal(const std::string& name);
@@ -111,7 +146,6 @@ class CNetAddr
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
- unsigned int GetByte(int n) const;
uint64_t GetHash() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
uint32_t GetNetClass() const;
@@ -127,7 +161,7 @@ class CNetAddr
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
- std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; }
+ std::vector<unsigned char> GetAddrBytes() const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
@@ -143,7 +177,7 @@ class CNetAddr
template <typename Stream>
void Serialize(Stream& s) const
{
- s << ip;
+ SerializeV1Stream(s);
}
/**
@@ -152,14 +186,92 @@ class CNetAddr
template <typename Stream>
void Unserialize(Stream& s)
{
- unsigned char ip_temp[sizeof(ip)];
- s >> ip_temp;
+ UnserializeV1Stream(s);
+ }
+
+ friend class CSubNet;
+
+ private:
+ /**
+ * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
+ */
+ static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to an array.
+ * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
+ */
+ void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
+ {
+ size_t prefix_size;
+
+ switch (m_net) {
+ case NET_IPV6:
+ assert(m_addr.size() == sizeof(arr));
+ memcpy(arr, m_addr.data(), m_addr.size());
+ return;
+ case NET_IPV4:
+ prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_ONION:
+ prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_INTERNAL:
+ prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+ }
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to a stream.
+ * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
+ */
+ template <typename Stream>
+ void SerializeV1Stream(Stream& s) const
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ SerializeV1Array(serialized);
+
+ s << serialized;
+ }
+
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from an array.
+ */
+ void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
+ {
// Use SetLegacyIPv6() so that m_net is set correctly. For example
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
- SetLegacyIPv6(ip_temp);
+ SetLegacyIPv6(arr);
}
- friend class CSubNet;
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from a stream.
+ */
+ template <typename Stream>
+ void UnserializeV1Stream(Stream& s)
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ s >> serialized;
+
+ UnserializeV1Array(serialized);
+ }
};
class CSubNet
@@ -174,11 +286,11 @@ class CSubNet
public:
CSubNet();
- CSubNet(const CNetAddr &addr, int32_t mask);
- CSubNet(const CNetAddr &addr, const CNetAddr &mask);
+ CSubNet(const CNetAddr& addr, uint8_t mask);
+ CSubNet(const CNetAddr& addr, const CNetAddr& mask);
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
- explicit CSubNet(const CNetAddr &addr);
+ explicit CSubNet(const CNetAddr& addr);
bool Match(const CNetAddr &addr) const;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 3a3b5f3e66..0273839017 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cstdint>
+#include <limits>
#ifndef WIN32
#include <fcntl.h>
@@ -838,8 +839,8 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
if (slash != strSubnet.npos)
{
std::string strNetmask = strSubnet.substr(slash + 1);
- int32_t n;
- if (ParseInt32(strNetmask, &n)) {
+ uint8_t n;
+ if (ParseUInt8(strNetmask, &n)) {
// If valid number, assume CIDR variable-length subnet masking
ret = CSubNet(network, n);
return ret.IsValid();
diff --git a/src/node/context.h b/src/node/context.h
index be568cba36..793c9dfc34 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -20,6 +20,7 @@ class PeerLogicValidation;
namespace interfaces {
class Chain;
class ChainClient;
+class WalletClient;
} // namespace interfaces
//! NodeContext struct containing references to chain state and connection
@@ -40,7 +41,11 @@ struct NodeContext {
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
+ //! List of all chain clients (wallet processes or other client) connected to node.
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
+ //! Reference to chain client that should used to load or create wallets
+ //! opened by the gui.
+ interfaces::WalletClient* wallet_client{nullptr};
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};
diff --git a/src/protocol.h b/src/protocol.h
index 2e6c767cdd..a059bf482a 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -247,7 +247,7 @@ extern const char* CFCHECKPT;
* txid.
* @since protocol version 70016 as described by BIP 339.
*/
-extern const char *WTXIDRELAY;
+extern const char* WTXIDRELAY;
}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
@@ -418,12 +418,22 @@ public:
std::string ToString() const;
// Single-message helper methods
- bool IsMsgTx() const { return type == MSG_TX; }
- bool IsMsgWtx() const { return type == MSG_WTX; }
- bool IsMsgWitnessTx() const { return type == MSG_WITNESS_TX; }
+ bool IsMsgTx() const { return type == MSG_TX; }
+ bool IsMsgBlk() const { return type == MSG_BLOCK; }
+ bool IsMsgWtx() const { return type == MSG_WTX; }
+ bool IsMsgFilteredBlk() const { return type == MSG_FILTERED_BLOCK; }
+ bool IsMsgCmpctBlk() const { return type == MSG_CMPCT_BLOCK; }
+ bool IsMsgWitnessBlk() const { return type == MSG_WITNESS_BLOCK; }
// Combined-message helper methods
- bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
+ bool IsGenTxMsg() const
+ {
+ return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX;
+ }
+ bool IsGenBlkMsg() const
+ {
+ return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK;
+ }
int type;
uint256 hash;
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index ef42aa5bc7..fc14f41a0c 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -24,7 +24,7 @@ secp256k1_context* secp256k1_context_verify = nullptr;
* strict DER before being passed to this module, and we know it supports all
* violations present in the blockchain before that point.
*/
-static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index f53fcc41f3..eaaaaeb904 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -27,9 +27,11 @@
#include <qt/walletmodel.h>
#endif // ENABLE_WALLET
+#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <noui.h>
#include <uint256.h>
#include <util/system.h>
@@ -37,6 +39,7 @@
#include <util/translation.h>
#include <validation.h>
+#include <boost/signals2/connection.hpp>
#include <memory>
#include <QApplication>
@@ -193,10 +196,9 @@ void BitcoinCore::shutdown()
static int qt_argc = 1;
static const char* qt_argv = "bitcoin-qt";
-BitcoinApplication::BitcoinApplication(interfaces::Node& node):
+BitcoinApplication::BitcoinApplication():
QApplication(qt_argc, const_cast<char **>(&qt_argv)),
coreThread(nullptr),
- m_node(node),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
@@ -204,6 +206,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node):
returnValue(0),
platformStyle(nullptr)
{
+ // Qt runs setlocale(LC_ALL, "") on initialization.
RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
@@ -246,12 +249,12 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(m_node, this, resetSettings);
+ optionsModel = new OptionsModel(this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
- window = new BitcoinGUI(m_node, platformStyle, networkStyle, nullptr);
+ window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -259,17 +262,27 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
- SplashScreen *splash = new SplashScreen(m_node, nullptr, networkStyle);
+ assert(!m_splash);
+ m_splash = new SplashScreen(nullptr, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when finish() happens.
- splash->show();
- connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
- connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
+ m_splash->show();
+ connect(this, &BitcoinApplication::requestedInitialize, m_splash, &SplashScreen::handleLoadWallet);
+ connect(this, &BitcoinApplication::splashFinished, m_splash, &SplashScreen::finish);
+ connect(this, &BitcoinApplication::requestedShutdown, m_splash, &QWidget::close);
+}
+
+void BitcoinApplication::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ if (optionsModel) optionsModel->setNode(*m_node);
+ if (m_splash) m_splash->setNode(*m_node);
}
bool BitcoinApplication::baseInitialize()
{
- return m_node.baseInitialize();
+ return node().baseInitialize();
}
void BitcoinApplication::startThread()
@@ -277,7 +290,7 @@ void BitcoinApplication::startThread()
if(coreThread)
return;
coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore(m_node);
+ BitcoinCore *executor = new BitcoinCore(node());
executor->moveToThread(coreThread);
/* communication to and from thread */
@@ -298,8 +311,8 @@ void BitcoinApplication::parameterSetup()
// print to the console unnecessarily.
gArgs.SoftSetBoolArg("-printtoconsole", false);
- m_node.initLogging();
- m_node.initParameterInteraction();
+ InitLogging(gArgs);
+ InitParameterInteraction(gArgs);
}
void BitcoinApplication::InitializePruneSetting(bool prune)
@@ -331,7 +344,7 @@ void BitcoinApplication::requestShutdown()
window->unsubscribeFromCoreSignals();
// Request node shutdown, which can interrupt long operations, like
// rescanning a wallet.
- m_node.startShutdown();
+ node().startShutdown();
// Unsetting the client model can cause the current thread to wait for node
// to complete an operation, like wait for a RPC execution to complete.
window->setClientModel(nullptr);
@@ -353,7 +366,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qInfo() << "Platform customization:" << platformStyle->getName();
- clientModel = new ClientModel(m_node, optionsModel);
+ clientModel = new ClientModel(node(), optionsModel);
window->setClientModel(clientModel, &tip_info);
#ifdef ENABLE_WALLET
if (WalletModel::isWalletEnabled()) {
@@ -437,9 +450,9 @@ int GuiMain(int argc, char* argv[])
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
// Subscribe to global signals from core
- std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox);
- std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion);
- std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage);
+ boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
+ boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
+ boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
@@ -453,15 +466,15 @@ int GuiMain(int argc, char* argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
- BitcoinApplication app(*node);
+ BitcoinApplication app;
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- node->setupServerArgs();
+ SetupServerArgs(node_context);
SetupUIArgs(gArgs);
std::string error;
- if (!node->parseParameters(argc, argv, error)) {
- node->initError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
+ if (!gArgs.ParseParameters(argc, argv, error)) {
+ InitError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
// Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
// message can not be translated because translations have not been initialized
@@ -487,7 +500,7 @@ int GuiMain(int argc, char* argv[])
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
- HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
+ HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
@@ -497,18 +510,18 @@ int GuiMain(int argc, char* argv[])
bool did_show_intro = false;
bool prune = false; // Intro dialog prune check box
// Gracefully exit if the user cancels
- if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS;
+ if (!Intro::showIfNeeded(did_show_intro, prune)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!CheckDataDirOption()) {
- node->initError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
+ InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
- if (!node->readConfigFiles(error)) {
- node->initError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
+ if (!gArgs.ReadConfigFiles(error, true)) {
+ InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
@@ -522,18 +535,18 @@ int GuiMain(int argc, char* argv[])
// Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
- node->selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- node->initError(Untranslated(strprintf("%s\n", e.what())));
+ InitError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
- PaymentServer::ipcParseCommandLine(*node, argc, argv);
+ PaymentServer::ipcParseCommandLine(argc, argv);
#endif
- if (!node->initSettings(error)) {
- node->initError(Untranslated(error));
+ if (!gArgs.InitSettings(error)) {
+ InitError(Untranslated(error));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
@@ -587,6 +600,8 @@ int GuiMain(int argc, char* argv[])
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
+ app.setNode(*node);
+
int rv = EXIT_SUCCESS;
try
{
@@ -609,10 +624,10 @@ int GuiMain(int argc, char* argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
}
return rv;
}
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 20c6dfc047..69e0a5921e 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -10,6 +10,7 @@
#endif
#include <QApplication>
+#include <assert.h>
#include <memory>
#include <interfaces/node.h>
@@ -20,6 +21,7 @@ class NetworkStyle;
class OptionsModel;
class PaymentServer;
class PlatformStyle;
+class SplashScreen;
class WalletController;
class WalletModel;
@@ -54,7 +56,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(interfaces::Node& node);
+ explicit BitcoinApplication();
~BitcoinApplication();
#ifdef ENABLE_WALLET
@@ -88,6 +90,9 @@ public:
/// Setup platform style
void setupPlatformStyle();
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node);
+
public Q_SLOTS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
@@ -102,7 +107,6 @@ Q_SIGNALS:
private:
QThread *coreThread;
- interfaces::Node& m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
@@ -114,6 +118,8 @@ private:
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
+ SplashScreen* m_splash = nullptr;
+ interfaces::Node* m_node = nullptr;
void startThread();
};
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 56adbf249a..aa58c0b10e 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -95,7 +95,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
updateWindowTitle();
rpcConsole = new RPCConsole(node, _platformStyle, nullptr);
- helpMessageDialog = new HelpMessageDialog(node, this, false);
+ helpMessageDialog = new HelpMessageDialog(this, false);
#ifdef ENABLE_WALLET
if(enableWallet)
{
@@ -821,7 +821,7 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(m_node, this, true);
+ HelpMessageDialog dlg(this, true);
dlg.exec();
}
@@ -1189,7 +1189,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
- if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
+ if (m_node.walletClient().getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 93840b4169..d210faec03 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -290,7 +290,7 @@
<item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>Current number of blocks</string>
+ <string>Current block height</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 7dbee6d689..06d39426c9 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -114,6 +114,12 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset>
</property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index ad21dfc3ef..235722d091 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <chainparams.h>
#include <fs.h>
#include <qt/intro.h>
#include <qt/forms/ui_intro.h>
@@ -181,7 +182,7 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune)
+bool Intro::showIfNeeded(bool& did_show_intro, bool& prune)
{
did_show_intro = false;
@@ -199,13 +200,13 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
{
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
- node.selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch (const std::exception&) {
return false;
}
/* If current default data directory does not exist, let the user choose one */
- Intro intro(0, node.getAssumedBlockchainSize(), node.getAssumedChainStateSize());
+ Intro intro(0, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/bitcoin"));
did_show_intro = true;
@@ -242,7 +243,7 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
* (to be consistent with bitcoind behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
- node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 732393246e..51f42de7ac 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -47,7 +47,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune);
+ static bool showIfNeeded(bool& did_show_intro, bool& prune);
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 58a7591c95..7e089b4f95 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -27,8 +27,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
- QAbstractListModel(parent), m_node(node)
+OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent)
{
Init(resetSettings);
}
@@ -97,12 +97,12 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
- if (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
addOverriddenOption("-dbcache");
if (!settings.contains("nThreadsScriptVerif"))
settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
- if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
@@ -112,19 +112,19 @@ void OptionsModel::Init(bool resetSettings)
#ifdef ENABLE_WALLET
if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true);
- if (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
+ if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
#endif
// Network
if (!settings.contains("fUseUPnP"))
settings.setValue("fUseUPnP", DEFAULT_UPNP);
- if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
+ if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN);
- if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
+ if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
if (!settings.contains("fUseProxy"))
@@ -132,7 +132,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrProxy"))
settings.setValue("addrProxy", GetDefaultProxyAddress());
// Only try to set -proxy, if user has enabled fUseProxy
- if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
+ if ((settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())))
addOverriddenOption("-proxy");
else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
addOverriddenOption("-proxy");
@@ -142,7 +142,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrSeparateProxyTor"))
settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
// Only try to set -onion, if user has enabled fUseSeparateProxyTor
- if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
+ if ((settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())))
addOverriddenOption("-onion");
else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
addOverriddenOption("-onion");
@@ -150,7 +150,7 @@ void OptionsModel::Init(bool resetSettings)
// Display
if (!settings.contains("language"))
settings.setValue("language", "");
- if (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
addOverriddenOption("-lang");
language = settings.value("language").toString();
@@ -244,10 +244,10 @@ void OptionsModel::SetPruneEnabled(bool prune, bool force)
const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
std::string prune_val = prune ? ToString(prune_target_mib) : "0";
if (force) {
- m_node.forceSetArg("-prune", prune_val);
+ gArgs.ForceSetArg("-prune", prune_val);
return;
}
- if (!m_node.softSetArg("-prune", prune_val)) {
+ if (!gArgs.SoftSetArg("-prune", prune_val)) {
addOverriddenOption("-prune");
}
}
@@ -353,7 +353,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- m_node.mapPort(value.toBool());
+ node().mapPort(value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 14fdf9046e..3d9e7bbb80 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -11,6 +11,8 @@
#include <QAbstractListModel>
+#include <assert.h>
+
namespace interfaces {
class Node;
}
@@ -39,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -92,10 +94,11 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { return m_node; }
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
private:
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
/* Qt-only settings */
bool fHideTrayIcon;
bool fMinimizeToTray;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index a1da85bda7..8679ced685 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -74,7 +74,7 @@ static QSet<QString> savedPaymentRequests;
// Warning: ipcSendCommandLine() is called early in init,
// so don't use "Q_EMIT message()", but "QMessageBox::"!
//
-void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char* argv[])
+void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
@@ -97,11 +97,11 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::MAIN);
+ SelectParams(CBaseChainParams::MAIN);
} else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::TESTNET);
+ SelectParams(CBaseChainParams::TESTNET);
}
}
}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 154f4a7ea6..eaf2bafe59 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -61,7 +61,7 @@ class PaymentServer : public QObject
public:
// Parse URIs on command line
// Returns false on error
- static void ipcParseCommandLine(interfaces::Node& node, int argc, char *argv[]);
+ static void ipcParseCommandLine(int argc, char *argv[]);
// Returns true if there were URIs on the command line
// which were successfully sent to an already-running
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 5debded4ea..d374d610ee 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -242,22 +242,6 @@ void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
}
-void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
-{
- if (event->key() == Qt::Key_Return)
- {
- // press return -> submit form
- if (ui->reqLabel->hasFocus() || ui->reqAmount->hasFocus() || ui->reqMessage->hasFocus())
- {
- event->ignore();
- on_receiveButton_clicked();
- return;
- }
- }
-
- this->QDialog::keyPressEvent(event);
-}
-
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 2f48cd58f0..27455e2906 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -49,9 +49,6 @@ public Q_SLOTS:
void reject() override;
void accept() override;
-protected:
- virtual void keyPressEvent(QKeyEvent *event) override;
-
private:
Ui::ReceiveCoinsDialog *ui;
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 6e6b2b8466..8e381861a0 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -24,8 +24,8 @@
#include <QScreen>
-SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
- QWidget(nullptr, f), curAlignment(0), m_node(node)
+SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) :
+ QWidget(nullptr, f), curAlignment(0)
{
// set reference point, paddings
int paddingRight = 50;
@@ -124,7 +124,6 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
setFixedSize(r.size());
move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
- subscribeToCoreSignals();
installEventFilter(this);
GUIUtil::handleCloseWindowShortcut(this);
@@ -132,14 +131,28 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
SplashScreen::~SplashScreen()
{
- unsubscribeFromCoreSignals();
+ if (m_node) unsubscribeFromCoreSignals();
+}
+
+void SplashScreen::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ subscribeToCoreSignals();
+ if (m_shutdown) m_node->startShutdown();
+}
+
+void SplashScreen::shutdown()
+{
+ m_shutdown = true;
+ if (m_node) m_node->startShutdown();
}
bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
if (ev->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (keyEvent->key() == Qt::Key_Q) {
- m_node.startShutdown();
+ shutdown();
}
}
return QObject::eventFilter(obj, ev);
@@ -172,21 +185,21 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
: _("press q to shutdown").translated) +
strprintf("\n%d", nProgress) + "%");
}
-#ifdef ENABLE_WALLET
-void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
-{
- m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
- m_connected_wallets.emplace_back(std::move(wallet));
-}
-#endif
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
- m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_handler_init_message = m_node->handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
+ m_handler_show_progress = m_node->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+}
+
+void SplashScreen::handleLoadWallet()
+{
#ifdef ENABLE_WALLET
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
+ m_handler_load_wallet = m_node->walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
+ m_connected_wallets.emplace_back(std::move(wallet));
+ });
#endif
}
@@ -221,6 +234,6 @@ void SplashScreen::paintEvent(QPaintEvent *event)
void SplashScreen::closeEvent(QCloseEvent *event)
{
- m_node.startShutdown(); // allows an "emergency" shutdown during startup
+ shutdown(); // allows an "emergency" shutdown during startup
event->ignore();
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 3158524117..a0cd677d3d 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -28,8 +28,9 @@ class SplashScreen : public QWidget
Q_OBJECT
public:
- explicit SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle);
+ explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle);
~SplashScreen();
+ void setNode(interfaces::Node& node);
protected:
void paintEvent(QPaintEvent *event) override;
@@ -42,6 +43,9 @@ public Q_SLOTS:
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
+ /** Handle wallet load notifications. */
+ void handleLoadWallet();
+
protected:
bool eventFilter(QObject * obj, QEvent * ev) override;
@@ -50,15 +54,16 @@ private:
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
- /** Connect wallet signals to splash screen */
- void ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet);
+ /** Initiate shutdown */
+ void shutdown();
QPixmap pixmap;
QString curMessage;
QColor curColor;
int curAlignment;
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
+ bool m_shutdown = false;
std::unique_ptr<interfaces::Handler> m_handler_init_message;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 035c8196bc..84f981dff3 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -108,7 +108,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 031913bd02..86356b43c8 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -68,11 +68,11 @@ int main(int argc, char* argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
- BitcoinApplication app(*node);
+ BitcoinApplication app;
+ app.setNode(*node);
app.setApplicationName("Bitcoin-Qt-test");
- node->setupServerArgs(); // Make gArgs available in the NodeContext
- node->context()->args->ClearArgs(); // Clear added args again
+ app.node().context()->args = &gArgs; // Make gArgs available in the NodeContext
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
@@ -81,7 +81,7 @@ int main(int argc, char* argv[])
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
}
- RPCNestedTests test3(*node);
+ RPCNestedTests test3(app.node());
if (QTest::qExec(&test3) != 0) {
fInvalid = true;
}
@@ -90,11 +90,11 @@ int main(int argc, char* argv[])
fInvalid = true;
}
#ifdef ENABLE_WALLET
- WalletTests test5(*node);
+ WalletTests test5(app.node());
if (QTest::qExec(&test5) != 0) {
fInvalid = true;
}
- AddressBookTests test6(*node);
+ AddressBookTests test6(app.node());
if (QTest::qExec(&test6) != 0) {
fInvalid = true;
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 475fd589af..adcfe0d25c 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -163,7 +163,7 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 01922cf996..b7f85446f4 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -28,7 +28,7 @@
#include <QVBoxLayout>
/** "Help message" or "About" dialog box */
-HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about) :
+HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
QDialog(parent),
ui(new Ui::HelpMessageDialog)
{
diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h
index 425b468f40..d2a5d5f67f 100644
--- a/src/qt/utilitydialog.h
+++ b/src/qt/utilitydialog.h
@@ -12,10 +12,6 @@ QT_BEGIN_NAMESPACE
class QMainWindow;
QT_END_NAMESPACE
-namespace interfaces {
- class Node;
-}
-
namespace Ui {
class HelpMessageDialog;
}
@@ -26,7 +22,7 @@ class HelpMessageDialog : public QDialog
Q_OBJECT
public:
- explicit HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about);
+ explicit HelpMessageDialog(QWidget *parent, bool about);
~HelpMessageDialog();
void printToConsole();
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 3aed98e0e8..828f84ffcc 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -35,11 +35,11 @@ WalletController::WalletController(ClientModel& client_model, const PlatformStyl
, m_platform_style(platform_style)
, m_options_model(client_model.getOptionsModel())
{
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_handler_load_wallet = m_node.walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
getOrCreateWallet(std::move(wallet));
});
- for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.getWallets()) {
+ for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.walletClient().getWallets()) {
getOrCreateWallet(std::move(wallet));
}
@@ -66,7 +66,7 @@ std::map<std::string, bool> WalletController::listWalletDir() const
{
QMutexLocker locker(&m_mutex);
std::map<std::string, bool> wallets;
- for (const std::string& name : m_node.listWalletDir()) {
+ for (const std::string& name : m_node.walletClient().listWalletDir()) {
wallets[name] = false;
}
for (WalletModel* wallet_model : m_wallets) {
@@ -250,7 +250,7 @@ void CreateWalletActivity::createWallet()
QTimer::singleShot(500, worker(), [this, name, flags] {
WalletCreationStatus status;
- std::unique_ptr<interfaces::Wallet> wallet = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, status);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, status, m_error_message, m_warning_message);
if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
@@ -321,7 +321,7 @@ void OpenWalletActivity::open(const std::string& path)
showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
QTimer::singleShot(0, worker(), [this, path] {
- std::unique_ptr<interfaces::Wallet> wallet = node().loadWallet(path, m_error_message, m_warning_message);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e374dd191c..6a3f903206 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -413,7 +413,7 @@ void WalletModel::subscribeToCoreSignals()
m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1));
- m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(boost::bind(NotifyCanGetAddressesChanged, this));
+ m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(std::bind(NotifyCanGetAddressesChanged, this));
}
void WalletModel::unsubscribeFromCoreSignals()
@@ -581,7 +581,7 @@ QString WalletModel::getDisplayName() const
bool WalletModel::isMultiwallet()
{
- return m_node.getWallets().size() > 1;
+ return m_node.walletClient().getWallets().size() > 1;
}
void WalletModel::refresh(bool pk_hash_only)
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 76aa9dbfc1..b04e106b2d 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -81,9 +81,9 @@ static UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-static UniValue getnetworkhashps(const JSONRPCRequest& request)
+static RPCHelpMan getnetworkhashps()
{
- RPCHelpMan{"getnetworkhashps",
+ return RPCHelpMan{"getnetworkhashps",
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
@@ -97,10 +97,12 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
+},
+ };
}
static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
@@ -200,9 +202,9 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri
}
}
-static UniValue generatetodescriptor(const JSONRPCRequest& request)
+static RPCHelpMan generatetodescriptor()
{
- RPCHelpMan{
+ return RPCHelpMan{
"generatetodescriptor",
"\nMine blocks immediately to a specified descriptor (before the RPC call returns)\n",
{
@@ -218,9 +220,8 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
},
RPCExamples{
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
- }
- .Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -234,22 +235,25 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
ChainstateManager& chainman = EnsureChainman(request.context);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
}
-static UniValue generate(const JSONRPCRequest& request)
+static RPCHelpMan generate()
{
- const std::string help_str{"generate ( nblocks maxtries ) has been replaced by the -generate cli option. Refer to -help for more information."};
+ return RPCHelpMan{"generate", "has been replaced by the -generate cli option. Refer to -help for more information.", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
if (request.fHelp) {
- throw std::runtime_error(help_str);
+ throw std::runtime_error(self.ToString());
} else {
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, help_str);
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, self.ToString());
}
+ }};
}
-static UniValue generatetoaddress(const JSONRPCRequest& request)
+static RPCHelpMan generatetoaddress()
{
- RPCHelpMan{"generatetoaddress",
+ return RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
@@ -267,8 +271,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -283,11 +287,13 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
CScript coinbase_script = GetScriptForDestination(destination);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
}
-static UniValue generateblock(const JSONRPCRequest& request)
+static RPCHelpMan generateblock()
{
- RPCHelpMan{"generateblock",
+ return RPCHelpMan{"generateblock",
"\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n",
{
{"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
@@ -309,8 +315,8 @@ static UniValue generateblock(const JSONRPCRequest& request)
"\nGenerate a block to myaddress, with txs rawtx and mempool_txid\n"
+ HelpExampleCli("generateblock", R"("myaddress" '["rawtx", "mempool_txid"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const auto address_or_descriptor = request.params[0].get_str();
CScript coinbase_script;
std::string error;
@@ -390,11 +396,13 @@ static UniValue generateblock(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.pushKV("hash", block_hash.GetHex());
return obj;
+},
+ };
}
-static UniValue getmininginfo(const JSONRPCRequest& request)
+static RPCHelpMan getmininginfo()
{
- RPCHelpMan{"getmininginfo",
+ return RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
@@ -413,8 +421,8 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
const CTxMemPool& mempool = EnsureMemPool(request.context);
@@ -423,18 +431,20 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
- obj.pushKV("networkhashps", getnetworkhashps(request));
+ obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-static UniValue prioritisetransaction(const JSONRPCRequest& request)
+static RPCHelpMan prioritisetransaction()
{
- RPCHelpMan{"prioritisetransaction",
+ return RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
@@ -451,8 +461,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -464,6 +474,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true;
+},
+ };
}
@@ -495,9 +507,9 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-static UniValue getblocktemplate(const JSONRPCRequest& request)
+static RPCHelpMan getblocktemplate()
{
- RPCHelpMan{"getblocktemplate",
+ return RPCHelpMan{"getblocktemplate",
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
@@ -511,12 +523,13 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
{"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
},
},
{"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported softfork deployment"},
+ {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
},
},
},
@@ -528,7 +541,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "version", "The preferred block version"},
{RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
{
- {RPCResult::Type::STR, "", "rulename"},
+ {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
}},
{RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
{
@@ -536,7 +549,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
- {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block",
+ {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -552,15 +565,12 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
}},
}},
- {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
{
- {RPCResult::Type::ELISION, "", ""},
+ {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
}},
{RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
- {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
+ {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
{RPCResult::Type::STR, "target", "The hash target"},
{RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
@@ -574,13 +584,14 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
{RPCResult::Type::STR, "bits", "compressed target of next block"},
{RPCResult::Type::NUM, "height", "The height of the next block"},
+ {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}
}},
RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
std::string strMode = "template";
@@ -888,6 +899,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
class submitblock_StateCatcher final : public CValidationInterface
@@ -908,10 +921,10 @@ protected:
}
};
-static UniValue submitblock(const JSONRPCRequest& request)
+static RPCHelpMan submitblock()
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
- RPCHelpMan{"submitblock",
+ return RPCHelpMan{"submitblock",
"\nAttempts to submit new block to network.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
{
@@ -923,8 +936,8 @@ static UniValue submitblock(const JSONRPCRequest& request)
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
@@ -969,11 +982,13 @@ static UniValue submitblock(const JSONRPCRequest& request)
return "inconclusive";
}
return BIP22ValidationResult(sc->state);
+},
+ };
}
-static UniValue submitheader(const JSONRPCRequest& request)
+static RPCHelpMan submitheader()
{
- RPCHelpMan{"submitheader",
+ return RPCHelpMan{"submitheader",
"\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n",
{
@@ -985,8 +1000,8 @@ static UniValue submitheader(const JSONRPCRequest& request)
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CBlockHeader h;
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed");
@@ -1005,11 +1020,13 @@ static UniValue submitheader(const JSONRPCRequest& request)
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
}
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
+},
+ };
}
-static UniValue estimatesmartfee(const JSONRPCRequest& request)
+static RPCHelpMan estimatesmartfee()
{
- RPCHelpMan{"estimatesmartfee",
+ return RPCHelpMan{"estimatesmartfee",
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
"confirmation within conf_target blocks if possible and return the number of blocks\n"
"for which the estimate is valid. Uses virtual transaction size as defined\n"
@@ -1043,8 +1060,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1070,11 +1087,13 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
}
result.pushKV("blocks", feeCalc.returnedTarget);
return result;
+},
+ };
}
-static UniValue estimaterawfee(const JSONRPCRequest& request)
+static RPCHelpMan estimaterawfee()
{
- RPCHelpMan{"estimaterawfee",
+ return RPCHelpMan{"estimaterawfee",
"\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
" implementation of fee estimation. The parameters it can be called with\n"
@@ -1126,8 +1145,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimaterawfee", "6 0.9")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1186,6 +1205,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
}
return result;
+},
+ };
}
void RegisterMiningRPCCommands(CRPCTable &t)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e9343b3348..7e2052c7cb 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -197,7 +197,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
if (fStateStats) {
if (IsDeprecatedRPCEnabled("banscore")) {
// banscore is deprecated in v0.21 for removal in v0.22
- obj.pushKV("banscore", statestats.nMisbehavior);
+ obj.pushKV("banscore", statestats.m_misbehavior_score);
}
obj.pushKV("synced_headers", statestats.nSyncHeight);
obj.pushKV("synced_blocks", statestats.nCommonHeight);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index abc8168c55..d6988ee3ac 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1300,7 +1300,7 @@ UniValue combinepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
}
UniValue finalizepsbt(const JSONRPCRequest& request)
@@ -1435,7 +1435,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
}
UniValue converttopsbt(const JSONRPCRequest& request)
@@ -1502,7 +1502,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
}
UniValue utxoupdatepsbt(const JSONRPCRequest& request)
@@ -1590,7 +1590,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
}
UniValue joinpsbts(const JSONRPCRequest& request)
@@ -1683,7 +1683,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << shuffled_psbt;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
}
UniValue analyzepsbt(const JSONRPCRequest& request)
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 9c8e7fe04a..f32d9abac6 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -261,13 +261,11 @@ CRPCTable::CRPCTable()
}
}
-bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
+void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
{
- if (IsRPCRunning())
- return false;
+ CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
mapCommands[name].push_back(pcmd);
- return true;
}
bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 6da3e94ea2..b2358ac5b2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -160,7 +160,7 @@ public:
/**
* Appends a CRPCCommand to the dispatch table.
*
- * Returns false if RPC server is already running (dump concurrency protection).
+ * Precondition: RPC server is not running
*
* Commands with different method names but the same unique_id will
* be considered aliases, and only the first registered method name will
@@ -169,7 +169,7 @@ public:
* between calls based on method name, and aliased commands can also
* register different names, types, and numbers of parameters.
*/
- bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
+ void appendCommand(const std::string& name, const CRPCCommand* pcmd);
bool removeCommand(const std::string& name, const CRPCCommand* pcmd);
};
diff --git a/src/sync.cpp b/src/sync.cpp
index 4be13a3c48..322198a852 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -238,12 +238,15 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
if (!LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
+template void AssertLockNotHeldInternal(const char*, const char*, int, Mutex*);
+template void AssertLockNotHeldInternal(const char*, const char*, int, RecursiveMutex*);
void DeleteLock(void* cs)
{
diff --git a/src/sync.h b/src/sync.h
index 05ff2ee8a9..7b397a8003 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -53,8 +53,9 @@ void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
template <typename MutexType>
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs);
void DeleteLock(void* cs);
bool LockStackEmpty();
@@ -69,8 +70,9 @@ inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, v
inline void LeaveCritical() {}
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
template <typename MutexType>
-inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
-inline void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
+inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index c0a2fca9ca..93bfa88024 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -232,10 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
- }
+ Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
@@ -249,20 +246,14 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
- }
+ Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
- }
+ Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
@@ -292,10 +283,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
- }
+ Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
{
LOCK(dummyNode.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
index 40ca01bd9f..e3aefa18a3 100644
--- a/src/test/fuzz/asmap.cpp
+++ b/src/test/fuzz/asmap.cpp
@@ -33,7 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
if (buffer.size() < 1 + 3 + 4) return;
int asmap_size = 3 + (buffer[0] & 127);
bool ipv6 = buffer[0] & 128;
- int addr_size = ipv6 ? 16 : 4;
+ const size_t addr_size = ipv6 ? ADDR_IPV6_SIZE : ADDR_IPV4_SIZE;
if (buffer.size() < size_t(1 + asmap_size + addr_size)) return;
std::vector<bool> asmap = ipv6 ? IPV6_PREFIX_ASMAP : IPV4_PREFIX_ASMAP;
asmap.reserve(asmap.size() + 8 * asmap_size);
@@ -43,7 +43,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
}
if (!SanityCheckASMap(asmap)) return;
+
+ const uint8_t* addr_data = buffer.data() + 1 + asmap_size;
CNetAddr net_addr;
- net_addr.SetRaw(ipv6 ? NET_IPV6 : NET_IPV4, buffer.data() + 1 + asmap_size);
+ if (ipv6) {
+ assert(addr_size == ADDR_IPV6_SIZE);
+ net_addr.SetLegacyIPv6(Span<const uint8_t>(addr_data, addr_size));
+ } else {
+ assert(addr_size == ADDR_IPV4_SIZE);
+ in_addr ipv4;
+ memcpy(&ipv4, addr_data, addr_size);
+ net_addr.SetIP(CNetAddr{ipv4});
+ }
(void)net_addr.GetMappedAS(asmap);
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
new file mode 100644
index 0000000000..1ff9d6b286
--- /dev/null
+++ b/src/test/fuzz/net.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2020 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 <chainparams.h>
+#include <chainparamsbase.h>
+#include <net.h>
+#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
+#include <protocol.h>
+#include <random.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const BasicTestingSetup basic_testing_setup;
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const std::optional<CAddress> address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address) {
+ return;
+ }
+ const std::optional<CAddress> address_bind = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address_bind) {
+ return;
+ }
+
+ CNode node{fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
+ fuzzed_data_provider.ConsumeIntegral<int>(),
+ INVALID_SOCKET,
+ *address,
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ *address_bind,
+ fuzzed_data_provider.ConsumeRandomLengthString(32),
+ fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH})};
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 12)) {
+ case 0: {
+ node.CloseSocketDisconnect();
+ break;
+ }
+ case 1: {
+ node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32));
+ break;
+ }
+ case 2: {
+ node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ }
+ case 3: {
+ const std::vector<bool> asmap = ConsumeRandomLengthIntegralVector<bool>(fuzzed_data_provider, 128);
+ if (!SanityCheckASMap(asmap)) {
+ break;
+ }
+ CNodeStats stats;
+ node.copyStats(stats, asmap);
+ break;
+ }
+ case 4: {
+ node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ }
+ case 5: {
+ const CNode* add_ref_node = node.AddRef();
+ assert(add_ref_node == &node);
+ break;
+ }
+ case 6: {
+ if (node.GetRefCount() > 0) {
+ node.Release();
+ }
+ break;
+ }
+ case 7: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ node.AddAddressKnown(*addr_opt);
+ break;
+ }
+ case 8: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
+ node.PushAddress(*addr_opt, fast_random_context);
+ break;
+ }
+ case 9: {
+ const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+ if (!inv_opt) {
+ break;
+ }
+ node.AddKnownTx(inv_opt->hash);
+ break;
+ }
+ case 10: {
+ node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider));
+ break;
+ }
+ case 11: {
+ const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (!service_opt) {
+ break;
+ }
+ node.SetAddrLocal(*service_opt);
+ break;
+ }
+ case 12: {
+ const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ bool complete;
+ node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete);
+ break;
+ }
+ }
+ }
+
+ (void)node.GetAddrLocal();
+ (void)node.GetAddrName();
+ (void)node.GetId();
+ (void)node.GetLocalNonce();
+ (void)node.GetLocalServices();
+ (void)node.GetMyStartingHeight();
+ (void)node.GetRecvVersion();
+ const int ref_count = node.GetRefCount();
+ assert(ref_count >= 0);
+ (void)node.GetSendVersion();
+ (void)node.IsAddrRelayPeer();
+
+ const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ?
+ fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({NetPermissionFlags::PF_NONE, NetPermissionFlags::PF_BLOOMFILTER, NetPermissionFlags::PF_RELAY, NetPermissionFlags::PF_FORCERELAY, NetPermissionFlags::PF_NOBAN, NetPermissionFlags::PF_MEMPOOL, NetPermissionFlags::PF_ISIMPLICIT, NetPermissionFlags::PF_ALL}) :
+ static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ (void)node.HasPermission(net_permission_flags);
+}
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index 2901c704f6..8252f38726 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -17,9 +17,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider);
- for (int i = 0; i < 15; ++i) {
- (void)net_addr.GetByte(i);
- }
(void)net_addr.GetHash();
(void)net_addr.GetNetClass();
if (net_addr.GetNetwork() == Network::NET_IPV4) {
@@ -78,7 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)net_addr.ToString();
(void)net_addr.ToStringIP();
- const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
(void)sub_net.IsValid();
(void)sub_net.ToString();
diff --git a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
new file mode 100644
index 0000000000..d4f302a8d3
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 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 <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char* out32, const unsigned char* seckey, size_t seckeylen);
+int ec_seckey_export_der(const secp256k1_context* ctx, unsigned char* seckey, size_t* seckeylen, const unsigned char* key32, bool compressed);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ {
+ std::vector<uint8_t> out32(32);
+ (void)ec_seckey_import_der(secp256k1_context_sign, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE);
+ }
+ {
+ std::vector<uint8_t> seckey(CKey::SIZE);
+ const std::vector<uint8_t> key32 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
+ size_t seckeylen = CKey::SIZE;
+ const bool compressed = fuzzed_data_provider.ConsumeBool();
+ const bool exported = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, key32.data(), compressed);
+ if (exported) {
+ std::vector<uint8_t> out32(32);
+ const bool imported = ec_seckey_import_der(secp256k1_context_sign, out32.data(), seckey.data(), seckey.size()) == 1;
+ assert(imported && key32 == out32);
+ }
+ }
+ secp256k1_context_destroy(secp256k1_context_sign);
+}
diff --git a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
new file mode 100644
index 0000000000..ed8c7aba89
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 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 <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+bool SigHasLowR(const secp256k1_ecdsa_signature* sig);
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* input, size_t inputlen);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> signature_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (signature_bytes.data() == nullptr) {
+ return;
+ }
+ secp256k1_context* secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_ecdsa_signature sig_der_lax;
+ const bool parsed_der_lax = ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig_der_lax, signature_bytes.data(), signature_bytes.size()) == 1;
+ if (parsed_der_lax) {
+ ECC_Start();
+ (void)SigHasLowR(&sig_der_lax);
+ ECC_Stop();
+ }
+ secp256k1_context_destroy(secp256k1_context_verify);
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 9f9552edb9..ed6093a8a8 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -257,7 +257,7 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
- return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
}
void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 317000c771..917ae571f5 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -13,8 +13,10 @@
#include <streams.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
+#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <version.h>
#include <boost/test/unit_test.hpp>
@@ -188,6 +190,78 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode2->IsInboundConn() == true);
}
+BOOST_AUTO_TEST_CASE(cnetaddr_basic)
+{
+ CNetAddr addr;
+
+ // IPv4, INADDR_ANY
+ BOOST_REQUIRE(LookupHost("0.0.0.0", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0");
+
+ // IPv4, INADDR_NONE
+ BOOST_REQUIRE(LookupHost("255.255.255.255", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255");
+
+ // IPv4, casual
+ BOOST_REQUIRE(LookupHost("12.34.56.78", addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78");
+
+ // IPv6, in6addr_any
+ BOOST_REQUIRE(LookupHost("::", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+
+ BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "::");
+
+ // IPv6, casual
+ BOOST_REQUIRE(LookupHost("1122:3344:5566:7788:9900:aabb:ccdd:eeff", addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
+
+ // TORv2
+ addr.SetSpecial("6hzph5hv6337r6p2.onion");
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsTor());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+
+ // Internal
+ addr.SetInternal("esffpp");
+ BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid
+ BOOST_REQUIRE(addr.IsInternal());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");
+}
+
+BOOST_AUTO_TEST_CASE(cnetaddr_serialize)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+
+ addr.SetInternal("a");
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "fd6b88c08724ca978112ca1bbdcafac2");
+ s.clear();
+}
+
// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 49073ea657..6681c92bb5 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -185,6 +185,7 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(!ResolveSubNet("1.2.3.0/-1").IsValid());
BOOST_CHECK(ResolveSubNet("1.2.3.0/32").IsValid());
BOOST_CHECK(!ResolveSubNet("1.2.3.0/33").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1.2.3.0/300").IsValid());
BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/0").IsValid());
BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/33").IsValid());
BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/-1").IsValid());
@@ -216,6 +217,11 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:8")));
BOOST_CHECK(!CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:9")));
BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128");
+ // IPv4 address with IPv6 netmask or the other way around.
+ BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid());
+ BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid());
+ // Can't subnet TOR (or any other non-IPv4 and non-IPv6 network).
+ BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid());
subnet = ResolveSubNet("1.2.3.4/255.255.255.255");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
@@ -290,11 +296,13 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16");
subnet = ResolveSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000");
BOOST_CHECK_EQUAL(subnet.ToString(), "::/0");
+ // Invalid netmasks (with 1-bits after 0-bits)
subnet = ResolveSubNet("1.2.3.4/255.255.232.0");
- BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0");
+ BOOST_CHECK(!subnet.IsValid());
+ subnet = ResolveSubNet("1.2.3.4/255.0.255.255");
+ BOOST_CHECK(!subnet.IsValid());
subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
- BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
-
+ BOOST_CHECK(!subnet.IsValid());
}
BOOST_AUTO_TEST_CASE(netbase_getgroup)
@@ -428,7 +436,8 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret));
- BOOST_CHECK(LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
+ // We only do subnetting for IPv4 and IPv6
+ BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 9d63aad672..8a88e75892 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -97,8 +97,8 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
SelectParams(chainName);
SeedInsecureRand();
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
- InitLogging();
- AppInitParameterInteraction();
+ InitLogging(*m_node.args);
+ AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
SHA256AutoDetect();
ECC_Start();
@@ -141,8 +141,11 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ m_node.mempool = &::mempool;
+ m_node.mempool->setSanityCheck(1.0);
+
m_node.chainman = &::g_chainman;
- m_node.chainman->InitializeChainstate();
+ m_node.chainman->InitializeChainstate(*m_node.mempool);
::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk());
@@ -164,8 +167,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
g_parallel_script_checks = true;
- m_node.mempool = &::mempool;
- m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
m_node.peer_logic = MakeUnique<PeerLogicValidation>(*m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
@@ -196,49 +197,34 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup()
{
- // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
- // TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-segwitheight", "432");
- // Need to recreate chainparams
- SelectParams(CBaseChainParams::REGTEST);
-
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
- CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- for (int i = 0; i < COINBASE_MATURITY; i++)
- {
+ CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ for (int i = 0; i < COINBASE_MATURITY; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
-// Create a new block with just given transactions, coinbase paying to
-// scriptPubKey, and try to add it to the current chain.
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
- CBlock& block = pblocktemplate->block;
+ CTxMemPool empty_pool;
+ CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
- // Replace mempool-selected txns with just coinbase plus passed-in txns:
- block.vtx.resize(1);
- for (const CMutableTransaction& tx : txns)
+ Assert(block.vtx.size() == 1);
+ for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
- // IncrementExtraNonce creates a valid coinbase and merkleRoot
- {
- LOCK(cs_main);
- unsigned int extraNonce = 0;
- IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce);
}
+ RegenerateCommitments(block);
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
- CBlock result = block;
- return result;
+ return block;
}
TestChain100Setup::~TestChain100Setup()
@@ -246,8 +232,8 @@ TestChain100Setup::~TestChain100Setup()
gArgs.ForceSetArg("-segwitheight", "0");
}
-
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx)
+{
return FromTx(MakeTransactionRef(tx));
}
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 78b279e42a..22f5d6d936 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -102,15 +102,16 @@ class CBlock;
struct CMutableTransaction;
class CScript;
-//
-// Testing fixture that pre-creates a
-// 100-block REGTEST-mode block chain
-//
+/**
+ * Testing fixture that pre-creates a 100-block REGTEST-mode block chain
+ */
struct TestChain100Setup : public RegTestingSetup {
TestChain100Setup();
- // Create a new block with just given transactions, coinbase paying to
- // scriptPubKey, and try to add it to the current chain.
+ /**
+ * Create a new block with just given transactions, coinbase paying to
+ * scriptPubKey, and try to add it to the current chain.
+ */
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey);
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 2076a1096a..c8a375275f 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -20,6 +20,7 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
//! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
@@ -34,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 887a48124f..36badafc4e 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -23,12 +23,13 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(chainstatemanager)
{
ChainstateManager manager;
+ CTxMemPool mempool;
std::vector<CChainState*> chainstates;
const CChainParams& chainparams = Params();
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -54,7 +55,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -104,6 +105,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
size_t max_cache = 10000;
manager.m_total_coinsdb_cache = max_cache;
manager.m_total_coinstip_cache = max_cache;
@@ -112,7 +114,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -129,7 +131,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -147,7 +149,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
-
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 8bac914f05..a3b344d2c9 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -18,8 +18,9 @@ BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
//!
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
+ CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{blockman};
+ CChainState chainstate{mempool, blockman};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
CTxMemPool tx_pool{};
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 5f2c40bac6..52bf83b676 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -18,9 +18,7 @@
#define LOCKABLE __attribute__((lockable))
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
-#define GUARDED_VAR __attribute__((guarded_var))
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-#define PT_GUARDED_VAR __attribute__((pt_guarded_var))
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
@@ -33,14 +31,12 @@
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
-#define ASSERT_EXCLUSIVE_LOCK(...) __attribute((assert_exclusive_lock(__VA_ARGS__)))
+#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
#else
#define LOCKABLE
#define SCOPED_LOCKABLE
#define GUARDED_BY(x)
-#define GUARDED_VAR
#define PT_GUARDED_BY(x)
-#define PT_GUARDED_VAR
#define ACQUIRED_AFTER(...)
#define ACQUIRED_BEFORE(...)
#define EXCLUSIVE_LOCK_FUNCTION(...)
diff --git a/src/util/message.cpp b/src/util/message.cpp
index 1e7128d225..e1d5cff48c 100644
--- a/src/util/message.cpp
+++ b/src/util/message.cpp
@@ -64,7 +64,7 @@ bool MessageSign(
return false;
}
- signature = EncodeBase64(signature_bytes.data(), signature_bytes.size());
+ signature = EncodeBase64(signature_bytes);
return true;
}
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 4d51b9045e..079a4529a3 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -126,20 +126,20 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
hostOut = in;
}
-std::string EncodeBase64(const unsigned char* pch, size_t len)
+std::string EncodeBase64(Span<const unsigned char> input)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
- str.reserve(((len + 2) / 3) * 4);
- ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
+ str.reserve(((input.size() + 2) / 3) * 4);
+ ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
while (str.size() % 4) str += '=';
return str;
}
std::string EncodeBase64(const std::string& str)
{
- return EncodeBase64((const unsigned char*)str.data(), str.size());
+ return EncodeBase64(MakeUCharSpan(str));
}
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
@@ -201,20 +201,20 @@ std::string DecodeBase64(const std::string& str, bool* pf_invalid)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-std::string EncodeBase32(const unsigned char* pch, size_t len)
+std::string EncodeBase32(Span<const unsigned char> input)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
- str.reserve(((len + 4) / 5) * 8);
- ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len);
+ str.reserve(((input.size() + 4) / 5) * 8);
+ ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
while (str.size() % 8) str += '=';
return str;
}
std::string EncodeBase32(const std::string& str)
{
- return EncodeBase32((const unsigned char*)str.data(), str.size());
+ return EncodeBase32(MakeUCharSpan(str));
}
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
@@ -318,6 +318,18 @@ bool ParseInt64(const std::string& str, int64_t *out)
n <= std::numeric_limits<int64_t>::max();
}
+bool ParseUInt8(const std::string& str, uint8_t *out)
+{
+ uint32_t u32;
+ if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<uint8_t>(u32);
+ }
+ return true;
+}
+
bool ParseUInt32(const std::string& str, uint32_t *out)
{
if (!ParsePrechecks(str))
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index b4bbaeebf6..1519214140 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -48,11 +48,11 @@ bool IsHex(const std::string& str);
bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
-std::string EncodeBase64(const unsigned char* pch, size_t len);
+std::string EncodeBase64(Span<const unsigned char> input);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
-std::string EncodeBase32(const unsigned char* pch, size_t len);
+std::string EncodeBase32(Span<const unsigned char> input);
std::string EncodeBase32(const std::string& str);
void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
@@ -99,6 +99,13 @@ NODISCARD bool ParseInt32(const std::string& str, int32_t *out);
NODISCARD bool ParseInt64(const std::string& str, int64_t *out);
/**
+ * Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
+ * @returns true if the entire string could be parsed as valid integer,
+ * false if not the entire string could be parsed or when overflow or underflow occurred.
+ */
+NODISCARD bool ParseUInt8(const std::string& str, uint8_t *out);
+
+/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 00aa53df70..999937d906 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1019,7 +1019,7 @@ bool FileCommit(FILE *file)
return false;
}
#else
- #if defined(HAVE_FDATASYNC)
+ #if HAVE_FDATASYNC
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
diff --git a/src/validation.cpp b/src/validation.cpp
index cf2f9dde62..58af8744d9 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -370,9 +370,10 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
* and instead just erase from the mempool as needed.
*/
-static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
+static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(mempool.cs);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -1254,8 +1255,9 @@ void CoinsViews::InitCache()
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
+CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash)
: m_blockman(blockman),
+ m_mempool(mempool),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
@@ -2280,7 +2282,7 @@ bool CChainState::FlushStateToDisk(
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
- CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&::mempool);
+ CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@@ -2426,7 +2428,7 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
@@ -2472,7 +2474,6 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
-
}
/** Disconnect m_chain's tip.
@@ -2485,8 +2486,11 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
+bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
@@ -2517,14 +2521,14 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
- mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
+ m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
disconnectpool->removeEntry(it);
}
}
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(pindexDelete->pprev, chainparams);
+ UpdateTip(m_mempool, pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2585,6 +2589,9 @@ public:
*/
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
@@ -2625,11 +2632,11 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
+ m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(pindexNew, chainparams);
+ UpdateTip(m_mempool, pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2719,6 +2726,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
const CBlockIndex *pindexOldTip = m_chain.Tip();
const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2730,7 +2738,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2774,7 +2782,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
return false;
}
} else {
@@ -2791,9 +2799,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, true);
}
- mempool.check(&CoinsTip());
+ m_mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2867,7 +2875,8 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
LimitValidationInterfaceQueue();
{
- LOCK2(cs_main, ::mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
+ LOCK(cs_main);
+ LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@@ -3020,7 +3029,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
LimitValidationInterfaceQueue();
LOCK(cs_main);
- LOCK(::mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
+ LOCK(m_mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -3034,7 +3043,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -4517,7 +4526,8 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Loop until the tip is below nHeight, or we reach a pruned block.
while (!ShutdownRequested()) {
{
- LOCK2(cs_main, ::mempool.cs);
+ LOCK(cs_main);
+ LOCK(m_mempool.cs);
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
assert(tip == m_chain.Tip());
if (tip == nullptr || tip->nHeight < nHeight) break;
@@ -5246,7 +5256,7 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
+CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash)
{
bool is_snapshot = !snapshot_blockhash.IsNull();
std::unique_ptr<CChainState>& to_modify =
@@ -5255,8 +5265,7 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
-
- to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
+ to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
diff --git a/src/validation.h b/src/validation.h
index 534162d64a..cac9473c7a 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -245,7 +245,7 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
/**
* Closure representing one script verification
@@ -511,11 +511,14 @@ private:
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
+ //! mempool that is kept in sync with the chain
+ CTxMemPool& m_mempool;
+
//! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
std::unique_ptr<CoinsViews> m_coins_views;
public:
- explicit CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
+ explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -642,7 +645,7 @@ public:
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Apply the effects of a block disconnection on the UTXO set.
- bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
@@ -685,8 +688,8 @@ public:
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
- bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
- bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
+ bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -818,9 +821,11 @@ public:
//! Instantiate a new chainstate and assign it based upon whether it is
//! from a snapshot.
//!
+ //! @param[in] mempool The mempool to pass to the chainstate
+ // constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
- CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
+ CChainState& InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash = uint256())
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index bf05ef844a..3910599ca7 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,6 +5,7 @@
#include <init.h>
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <net.h>
#include <node/context.h>
#include <node/ui_interface.h>
@@ -66,13 +67,13 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
- " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+
+ argsman.AddHiddenArgs({"-zapwallettxes"});
}
bool WalletInit::ParameterInteraction() const
@@ -85,26 +86,12 @@ bool WalletInit::ParameterInteraction() const
return true;
}
- const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
-
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
- // -zapwallettxes implies dropping the mempool on startup
- if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
- }
-
- // -zapwallettxes implies a rescan
- if (zapwallettxes) {
- if (is_multiwallet) {
- return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-zapwallettxes"));
- }
- if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
- }
+ if (gArgs.IsArgSet("-zapwallettxes")) {
+ return InitError(Untranslated("-zapwallettxes has been removed. If you are attempting to remove a stuck transaction from your wallet, please use abandontransaction instead."));
}
if (gArgs.GetBoolArg("-sysperms", false))
@@ -129,5 +116,7 @@ void WalletInit::Construct(NodeContext& node) const
settings.rw_settings["wallet"] = wallets;
});
}
- node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet")));
+ auto wallet_client = interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet"));
+ node.wallet_client = wallet_client.get();
+ node.chain_clients.emplace_back(std::move(wallet_client));
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e0c3a1287a..9e36a09780 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -90,9 +90,9 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
}
}
-UniValue importprivkey(const JSONRPCRequest& request)
+RPCHelpMan importprivkey()
{
- RPCHelpMan{"importprivkey",
+ return RPCHelpMan{"importprivkey",
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -116,8 +116,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -189,11 +189,13 @@ UniValue importprivkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue abortrescan(const JSONRPCRequest& request)
+RPCHelpMan abortrescan()
{
- RPCHelpMan{"abortrescan",
+ return RPCHelpMan{"abortrescan",
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
@@ -206,8 +208,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("abortrescan", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -215,11 +217,13 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
+},
+ };
}
-UniValue importaddress(const JSONRPCRequest& request)
+RPCHelpMan importaddress()
{
- RPCHelpMan{"importaddress",
+ return RPCHelpMan{"importaddress",
"\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
@@ -243,8 +247,8 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -315,11 +319,13 @@ UniValue importaddress(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan importprunedfunds()
{
- RPCHelpMan{"importprunedfunds",
+ return RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
{"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
@@ -327,8 +333,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -371,11 +377,13 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
+},
+ };
}
-UniValue removeprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan removeprunedfunds()
{
- RPCHelpMan{"removeprunedfunds",
+ return RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
@@ -386,8 +394,8 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -408,11 +416,13 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importpubkey(const JSONRPCRequest& request)
+RPCHelpMan importpubkey()
{
- RPCHelpMan{"importpubkey",
+ return RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -432,8 +442,8 @@ UniValue importpubkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -492,12 +502,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importwallet(const JSONRPCRequest& request)
+RPCHelpMan importwallet()
{
- RPCHelpMan{"importwallet",
+ return RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
@@ -512,8 +524,8 @@ UniValue importwallet(const JSONRPCRequest& request)
"\nImport using the json rpc call\n"
+ HelpExampleRpc("importwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -649,11 +661,13 @@ UniValue importwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
return NullUniValue;
+},
+ };
}
-UniValue dumpprivkey(const JSONRPCRequest& request)
+RPCHelpMan dumpprivkey()
{
- RPCHelpMan{"dumpprivkey",
+ return RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
@@ -667,8 +681,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"")
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -693,12 +707,14 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
return EncodeSecret(vchSecret);
+},
+ };
}
-UniValue dumpwallet(const JSONRPCRequest& request)
+RPCHelpMan dumpwallet()
{
- RPCHelpMan{"dumpwallet",
+ return RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
"Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
@@ -716,8 +732,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
HelpExampleCli("dumpwallet", "\"test\"")
+ HelpExampleRpc("dumpwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
@@ -829,6 +845,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
reply.pushKV("filename", filepath.string());
return reply;
+},
+ };
}
struct ImportData
@@ -1239,9 +1257,9 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
}
-UniValue importmulti(const JSONRPCRequest& mainRequest)
+RPCHelpMan importmulti()
{
- RPCHelpMan{"importmulti",
+ return RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
@@ -1314,8 +1332,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
},
- }.Check(mainRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1423,6 +1441,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
return response;
+},
+ };
}
static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
@@ -1564,9 +1584,9 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request)
+RPCHelpMan importdescriptors()
{
- RPCHelpMan{"importdescriptors",
+ return RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
@@ -1615,8 +1635,8 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
"{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
- }.Check(main_request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1713,4 +1733,6 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
}
return response;
+},
+ };
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7a03c4f544..7ef0b2129b 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2061,6 +2061,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
+ "Manually selected coins are automatically unlocked.\n"
"Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops or fails.\n"
"Also see the listunspent call\n",
@@ -2494,7 +2495,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting bitcoind will be"
- "\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n",
+ "\napplied to the new wallet (eg -rescan, etc).\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
@@ -2836,6 +2837,15 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (!request.params[4].isNull()) {
const UniValue& options = request.params[4].get_obj();
+ RPCTypeCheckObj(options,
+ {
+ {"minimumAmount", UniValueType()},
+ {"maximumAmount", UniValueType()},
+ {"minimumSumAmount", UniValueType()},
+ {"maximumCount", UniValueType(UniValue::VNUM)},
+ },
+ true, true);
+
if (options.exists("minimumAmount"))
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
@@ -4161,17 +4171,17 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
return error.original;
}
-UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue importprivkey(const JSONRPCRequest& request);
-UniValue importaddress(const JSONRPCRequest& request);
-UniValue importpubkey(const JSONRPCRequest& request);
-UniValue dumpwallet(const JSONRPCRequest& request);
-UniValue importwallet(const JSONRPCRequest& request);
-UniValue importprunedfunds(const JSONRPCRequest& request);
-UniValue removeprunedfunds(const JSONRPCRequest& request);
-UniValue importmulti(const JSONRPCRequest& request);
-UniValue importdescriptors(const JSONRPCRequest& request);
+RPCHelpMan abortrescan();
+RPCHelpMan dumpprivkey();
+RPCHelpMan importprivkey();
+RPCHelpMan importaddress();
+RPCHelpMan importpubkey();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
+RPCHelpMan importprunedfunds();
+RPCHelpMan removeprunedfunds();
+RPCHelpMan importmulti();
+RPCHelpMan importdescriptors();
Span<const CRPCCommand> GetWalletRPCCommands()
{
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index a96d971734..14fb1fa89f 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -535,7 +535,7 @@ private:
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
- bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
+ bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 35bd965673..c23dea1338 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -10,7 +10,7 @@
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- m_chain_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
std::string sep;
sep += fs::path::preferred_separator;
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index c95b4f1f6e..f5bade77df 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -6,6 +6,7 @@
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <node/context.h>
#include <test/util/setup_common.h>
@@ -19,7 +20,7 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client;
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client;
};
#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index c228e06009..9b905569fc 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
{
SetWalletDir(m_walletdir_path_cases["default"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
{
SetWalletDir(m_walletdir_path_cases["custom"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
{
ASSERT_DEBUG_LOG("does not exist");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
SetWalletDir(m_walletdir_path_cases["file"]);
{
ASSERT_DEBUG_LOG("is not a directory");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
SetWalletDir(m_walletdir_path_cases["relative"]);
{
ASSERT_DEBUG_LOG("is a relative path");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
{
SetWalletDir(m_walletdir_path_cases["trailing"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{
SetWalletDir(m_walletdir_path_cases["trailing2"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 44f9eb5ddd..9241470745 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -11,5 +11,5 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
m_chain_notifications_handler = m_chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
- m_chain_client->registerRpcs();
+ m_wallet_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 99d7cfe921..12ad13b49b 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -21,7 +21,7 @@ struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7ef06663b5..c46e90d64b 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -24,9 +24,9 @@
#include <boost/test/unit_test.hpp>
#include <univalue.h>
-extern UniValue importmulti(const JSONRPCRequest& request);
-extern UniValue dumpwallet(const JSONRPCRequest& request);
-extern UniValue importwallet(const JSONRPCRequest& request);
+RPCHelpMan importmulti();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
// Ensure that fee levels defined in the wallet are at least as high
// as the default levels for node policy.
@@ -219,7 +219,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(keys);
- UniValue response = importmulti(request);
+ UniValue response = importmulti().HandleRequest(request);
BOOST_CHECK_EQUAL(response.write(),
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
"timestamp %d. There was an error reading a block from time %d, which is after or within %d "
@@ -274,7 +274,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
- ::dumpwallet(request);
+ ::dumpwallet().HandleRequest(request);
RemoveWallet(wallet);
}
@@ -291,7 +291,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
- ::importwallet(request);
+ ::importwallet().HandleRequest(request);
RemoveWallet(wallet);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index c132a4be42..839e74a2be 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -183,11 +183,6 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocati
return wallet;
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
-{
- return LoadWallet(chain, WalletLocation(name), error, warnings);
-}
-
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
{
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
@@ -276,7 +271,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
- LOCK(cs_wallet);
+ AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
return nullptr;
@@ -1210,15 +1205,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
isminetype CWallet::IsMine(const CTxIn &txin) const
{
+ AssertLockHeld(cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
{
- LOCK(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
- {
- const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.tx->vout.size())
- return IsMine(prev.tx->vout[txin.prevout.n]);
- }
+ const CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
return ISMINE_NO;
}
@@ -1243,16 +1236,19 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
isminetype CWallet::IsMine(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey);
}
isminetype CWallet::IsMine(const CTxDestination& dest) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest));
}
isminetype CWallet::IsMine(const CScript& script) const
{
+ AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script));
@@ -1264,6 +1260,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
+ LOCK(cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
@@ -1281,13 +1278,12 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
+ AssertLockHeld(cs_wallet);
if (IsMine(script))
{
CTxDestination address;
if (!ExtractDestination(script, address))
return true;
-
- LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) {
return true;
}
@@ -1297,6 +1293,7 @@ bool CWallet::IsChange(const CScript& script) const
CAmount CWallet::GetChange(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0);
@@ -1304,6 +1301,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const
{
+ AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout)
if (IsMine(txout))
return true;
@@ -1362,6 +1360,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const
{
+ LOCK(cs_wallet);
CAmount nChange = 0;
for (const CTxOut& txout : tx.vout)
{
@@ -1597,6 +1596,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut;
}
+ LOCK(pwallet->cs_wallet);
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
@@ -1965,36 +1965,38 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted() const
{
- std::set<uint256> s;
- return IsTrusted(s);
+ std::set<uint256> trusted_parents;
+ LOCK(pwallet->cs_wallet);
+ return pwallet->IsTrusted(*this, trusted_parents);
}
-bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
+bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
{
+ AssertLockHeld(cs_wallet);
// Quick answer in most cases
- if (!pwallet->chain().checkFinalTx(*tx)) return false;
- int nDepth = GetDepthInMainChain();
+ if (!chain().checkFinalTx(*wtx.tx)) return false;
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
// using wtx's cached debit
- if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) return false;
+ if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
- if (!InMempool()) return false;
+ if (!wtx.InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool:
- for (const CTxIn& txin : tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
// Transactions not sent by us: not trusted
- const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
+ const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
// Check that this specific input being spent is trusted
- if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
// If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted
- if (!parent->IsTrusted(trusted_parents)) return false;
+ if (!IsTrusted(*parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
@@ -2080,7 +2082,7 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(trusted_parents)};
+ const bool is_trusted{IsTrusted(wtx, trusted_parents)};
const int tx_depth{wtx.GetDepthInMainChain()};
const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
@@ -2148,7 +2150,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(trusted_parents);
+ bool safeTx = IsTrusted(wtx, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2580,10 +2582,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
- if (lockUnspents) {
- LockCoin(txin.prevout);
- }
}
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
+ }
+
}
return true;
@@ -3172,28 +3175,10 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
return DBErrors::LOAD_OK;
}
-DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx);
- if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
- {
- if (database->Rewrite("\x04pool"))
- {
- for (const auto& spk_man_pair : m_spk_managers) {
- spk_man_pair.second->RewriteDB();
- }
- }
- }
-
- if (nZapWalletTxRet != DBErrors::LOAD_OK)
- return nZapWalletTxRet;
-
- return DBErrors::LOAD_OK;
-}
-
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
+ bool is_mine;
{
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@@ -3201,8 +3186,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose;
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO,
+ NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
@@ -3217,17 +3203,16 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
- // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
- // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
- // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
- if (IsMine(address)) {
- WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
- return false;
- }
-
+ bool is_mine;
{
LOCK(cs_wallet);
-
+ // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
+ // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
+ // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ if (IsMine(address)) {
+ WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
+ return false;
+ }
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
@@ -3235,9 +3220,10 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
WalletBatch(*database).EraseDestData(strAddress, item.first);
}
m_address_book.erase(address);
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
WalletBatch(*database).ErasePurpose(EncodeDestination(address));
return WalletBatch(*database).EraseName(EncodeDestination(address));
@@ -3345,7 +3331,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(trusted_parents))
+ if (!IsTrusted(wtx, trusted_parents))
continue;
if (wtx.IsImmatureCoinBase())
@@ -3364,9 +3350,6 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
-
- if (!balances.count(addr))
- balances[addr] = 0;
balances[addr] += n;
}
}
@@ -3770,20 +3753,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
- // needed to restore wallet transaction meta data after -zapwallettxes
- std::list<CWalletTx> vWtx;
-
- if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- chain.initMessage(_("Zapping all transactions from wallet...").translated);
-
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, CreateWalletDatabase(location.GetPath()));
- DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
- if (nZapWalletRet != DBErrors::LOAD_OK) {
- error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
- return nullptr;
- }
- }
-
chain.initMessage(_("Loading wallet...").translated);
int64_t nStart = GetTimeMillis();
@@ -4060,30 +4029,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->database->IncrementUpdateCounter();
-
- // Restore wallet transaction metadata after -zapwallettxes=1
- if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")
- {
- WalletBatch batch(*walletInstance->database);
-
- for (const CWalletTx& wtxOld : vWtx)
- {
- uint256 hash = wtxOld.GetHash();
- std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);
- if (mi != walletInstance->mapWallet.end())
- {
- const CWalletTx* copyFrom = &wtxOld;
- CWalletTx* copyTo = &mi->second;
- copyTo->mapValue = copyFrom->mapValue;
- copyTo->vOrderForm = copyFrom->vOrderForm;
- copyTo->nTimeReceived = copyFrom->nTimeReceived;
- copyTo->nTimeSmart = copyFrom->nTimeSmart;
- copyTo->fFromMe = copyFrom->fFromMe;
- copyTo->nOrderPos = copyFrom->nOrderPos;
- batch.WriteTx(*copyTo);
- }
- }
- }
}
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 2f9d301000..42622c9312 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -275,7 +275,7 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet,
class CWalletTx
{
private:
- const CWallet* pwallet;
+ const CWallet* const pwallet;
/** Constant used in hashBlock to indicate tx has been abandoned, only used at
* serialization/deserialization to avoid ambiguity with conflicted.
@@ -502,7 +502,6 @@ public:
bool InMempool() const;
bool IsTrusted() const;
- bool IsTrusted(std::set<uint256>& trusted_parents) const;
int64_t GetTxTime() const;
@@ -805,7 +804,8 @@ public:
/** Interface for accessing chain state. */
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
- const CWalletTx* GetWalletTx(const uint256& hash) const;
+ const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
@@ -1051,20 +1051,20 @@ public:
bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
- isminetype IsMine(const CTxDestination& dest) const;
- isminetype IsMine(const CScript& script) const;
- isminetype IsMine(const CTxIn& txin) const;
+ isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Returns amount of debit if the input matches the
* filter, otherwise returns 0
*/
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
- isminetype IsMine(const CTxOut& txout) const;
+ isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
- bool IsChange(const CTxOut& txout) const;
- bool IsChange(const CScript& script) const;
- CAmount GetChange(const CTxOut& txout) const;
- bool IsMine(const CTransaction& tx) const;
+ bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
@@ -1075,7 +1075,6 @@ public:
void chainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(bool& fFirstRunRet);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
@@ -1179,7 +1178,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1285,7 +1284,7 @@ public:
void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
- void SetupDescriptorScriptPubKeyMans();
+ void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4635c19b3d..962ea66fa0 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -930,23 +930,6 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u
return DBErrors::LOAD_OK;
}
-DBErrors WalletBatch::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- // build list of wallet TXs
- std::vector<uint256> vTxHash;
- DBErrors err = FindWalletTx(vTxHash, vWtx);
- if (err != DBErrors::LOAD_OK)
- return err;
-
- // erase each wallet TX
- for (const uint256& hash : vTxHash) {
- if (!EraseTx(hash))
- return DBErrors::CORRUPT;
- }
-
- return DBErrors::LOAD_OK;
-}
-
void MaybeCompactWalletDB()
{
static std::atomic<bool> fOneThread(false);
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index eaeb2bb863..2548c17508 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -257,7 +257,6 @@ public:
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 04806903c2..e2431cbbb7 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -86,6 +86,14 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
+ const int so_keepalive_option {1};
+ rc = zmq_setsockopt(psocket, ZMQ_TCP_KEEPALIVE, &so_keepalive_option, sizeof(so_keepalive_option));
+ if (rc != 0) {
+ zmqError("Failed to set SO_KEEPALIVE");
+ zmq_close(psocket);
+ return false;
+ }
+
rc = zmq_bind(psocket, address.c_str());
if (rc != 0)
{
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index cce6210129..1dd751b493 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -13,9 +13,9 @@
namespace {
-UniValue getzmqnotifications(const JSONRPCRequest& request)
+static RPCHelpMan getzmqnotifications()
{
- RPCHelpMan{"getzmqnotifications",
+ return RPCHelpMan{"getzmqnotifications",
"\nReturns information about the active ZeroMQ notifications.\n",
{},
RPCResult{
@@ -33,8 +33,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
HelpExampleCli("getzmqnotifications", "")
+ HelpExampleRpc("getzmqnotifications", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue result(UniValue::VARR);
if (g_zmq_notification_interface != nullptr) {
for (const auto* n : g_zmq_notification_interface->GetActiveNotifiers()) {
@@ -47,6 +47,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
const CRPCCommand commands[] =