aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.leveldb.include1
-rw-r--r--src/Makefile.qt.include7
-rw-r--r--src/addrman.cpp98
-rw-r--r--src/addrman.h28
-rw-r--r--src/bench/verify_script.cpp2
-rw-r--r--src/bitcoin-cli.cpp113
-rw-r--r--src/chainparams.cpp38
-rw-r--r--src/consensus/tx_verify.cpp2
-rw-r--r--src/consensus/tx_verify.h4
-rw-r--r--src/hash.cpp2
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp2
-rw-r--r--src/interfaces/ipc.h7
-rw-r--r--src/ipc/capnp/context.h23
-rw-r--r--src/ipc/capnp/protocol.cpp7
-rw-r--r--src/ipc/context.h19
-rw-r--r--src/ipc/interfaces.cpp1
-rw-r--r--src/ipc/protocol.h5
-rw-r--r--src/net.cpp21
-rw-r--r--src/net.h1
-rw-r--r--src/net_permissions.h3
-rw-r--r--src/net_processing.cpp64
-rw-r--r--src/net_processing.h2
-rw-r--r--src/node/blockstorage.cpp2
-rw-r--r--src/node/transaction.cpp119
-rw-r--r--src/qt/bitcoin.cpp99
-rw-r--r--src/qt/bitcoin.h34
-rw-r--r--src/qt/initexecutor.cpp66
-rw-r--r--src/qt/initexecutor.h46
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/test/test_main.cpp1
-rw-r--r--src/rest.cpp1
-rw-r--r--src/rpc/blockchain.cpp27
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/net.cpp6
-rw-r--r--src/script/interpreter.cpp10
-rw-r--r--src/script/interpreter.h3
-rw-r--r--src/script/sign.cpp21
-rw-r--r--src/test/addrman_tests.cpp119
-rw-r--r--src/test/fuzz/addrman.cpp2
-rw-r--r--src/test/fuzz/coins_view.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp12
-rw-r--r--src/test/fuzz/util.cpp155
-rw-r--r--src/test/fuzz/util.h155
-rw-r--r--src/test/script_tests.cpp14
-rw-r--r--src/test/sigopcount_tests.cpp4
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/test/util/setup_common.cpp4
-rw-r--r--src/test/validation_chainstate_tests.cpp1
-rw-r--r--src/tinyformat.h10
-rw-r--r--src/torcontrol.cpp23
-rw-r--r--src/validation.cpp30
-rw-r--r--src/validation.h10
-rw-r--r--src/wallet/rpcdump.cpp13
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/wallet.cpp66
57 files changed, 815 insertions, 704 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 7de5fb36ed..a8d6591e98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,7 +3,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Pattern rule to print variables, e.g. make print-top_srcdir
-print-%:
+print-%: FORCE
@echo '$*'='$($*)'
DIST_SUBDIRS = secp256k1 univalue
@@ -842,9 +842,11 @@ EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
if BUILD_MULTIPROCESS
LIBBITCOIN_IPC=libbitcoin_ipc.a
libbitcoin_ipc_a_SOURCES = \
+ ipc/capnp/context.h \
ipc/capnp/init-types.h \
ipc/capnp/protocol.cpp \
ipc/capnp/protocol.h \
+ ipc/context.h \
ipc/exception.h \
ipc/interfaces.cpp \
ipc/process.cpp \
diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include
index 8a28f4f249..ce1f93f11f 100644
--- a/src/Makefile.leveldb.include
+++ b/src/Makefile.leveldb.include
@@ -22,6 +22,7 @@ LEVELDB_CPPFLAGS_INT += -DHAVE_SNAPPY=0 -DHAVE_CRC32C=1
LEVELDB_CPPFLAGS_INT += -DHAVE_FDATASYNC=@HAVE_FDATASYNC@
LEVELDB_CPPFLAGS_INT += -DHAVE_FULLFSYNC=@HAVE_FULLFSYNC@
LEVELDB_CPPFLAGS_INT += -DHAVE_O_CLOEXEC=@HAVE_O_CLOEXEC@
+LEVELDB_CPPFLAGS_INT += -DFALLTHROUGH_INTENDED=[[fallthrough]]
if WORDS_BIGENDIAN
LEVELDB_CPPFLAGS_INT += -DLEVELDB_IS_BIG_ENDIAN=1
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index a1821cafe3..6f450bbc74 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -40,9 +40,9 @@ QT_MOC_CPP = \
qt/moc_askpassphrasedialog.cpp \
qt/moc_createwalletdialog.cpp \
qt/moc_bantablemodel.cpp \
+ qt/moc_bitcoin.cpp \
qt/moc_bitcoinaddressvalidator.cpp \
qt/moc_bitcoinamountfield.cpp \
- qt/moc_bitcoin.cpp \
qt/moc_bitcoingui.cpp \
qt/moc_bitcoinunits.cpp \
qt/moc_clientmodel.cpp \
@@ -51,6 +51,7 @@ QT_MOC_CPP = \
qt/moc_csvmodelwriter.cpp \
qt/moc_editaddressdialog.cpp \
qt/moc_guiutil.cpp \
+ qt/moc_initexecutor.cpp \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
@@ -109,9 +110,9 @@ BITCOIN_QT_H = \
qt/addresstablemodel.h \
qt/askpassphrasedialog.h \
qt/bantablemodel.h \
+ qt/bitcoin.h \
qt/bitcoinaddressvalidator.h \
qt/bitcoinamountfield.h \
- qt/bitcoin.h \
qt/bitcoingui.h \
qt/bitcoinunits.h \
qt/clientmodel.h \
@@ -122,6 +123,7 @@ BITCOIN_QT_H = \
qt/editaddressdialog.h \
qt/guiconstants.h \
qt/guiutil.h \
+ qt/initexecutor.h \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
@@ -227,6 +229,7 @@ BITCOIN_QT_BASE_CPP = \
qt/clientmodel.cpp \
qt/csvmodelwriter.cpp \
qt/guiutil.cpp \
+ qt/initexecutor.cpp \
qt/intro.cpp \
qt/modaloverlay.cpp \
qt/networkstyle.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 389d106164..8192b4eba6 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -6,7 +6,6 @@
#include <addrman.h>
#include <hash.h>
-#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
#include <serialize.h>
@@ -732,100 +731,3 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
}
return bits;
}
-
-void CAddrMan::ResetI2PPorts()
-{
- for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- const auto id = vvNew[bucket][i];
- if (id == -1) {
- continue;
- }
- auto it = mapInfo.find(id);
- if (it == mapInfo.end()) {
- return;
- }
- auto& addr_info = it->second;
- if (!addr_info.IsI2P() || addr_info.GetPort() == I2P_SAM31_PORT) {
- continue;
- }
-
- auto addr_info_newport = addr_info;
- // The below changes addr_info_newport.GetKey(), which is used in finding a
- // bucket and a position within that bucket. So a re-bucketing may be necessary.
- addr_info_newport.port = I2P_SAM31_PORT;
-
- // Reposition entries of vvNew within the same bucket because we don't know the source
- // address which led to the decision to store the entry in vvNew[bucket] so we can't
- // re-evaluate that decision, but even if we could, CAddrInfo::GetNewBucket() does not
- // use CAddrInfo::GetKey() so it would end up in the same bucket as before the port
- // change.
- const auto i_target = addr_info_newport.GetBucketPosition(nKey, true, bucket);
-
- if (i_target == i) { // No need to re-position.
- addr_info = addr_info_newport;
- continue;
- }
-
- // Reposition from i to i_target, removing the entry from i_target (if any).
- ClearNew(bucket, i_target);
- vvNew[bucket][i_target] = id;
- vvNew[bucket][i] = -1;
- addr_info = addr_info_newport;
- }
- }
-
- for (int bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- const auto id = vvTried[bucket][i];
- if (id == -1) {
- continue;
- }
- auto it = mapInfo.find(id);
- if (it == mapInfo.end()) {
- return;
- }
- auto& addr_info = it->second;
- if (!addr_info.IsI2P() || addr_info.GetPort() == I2P_SAM31_PORT) {
- continue;
- }
-
- auto addr_info_newport = addr_info;
- // The below changes addr_info_newport.GetKey(), which is used in finding a
- // bucket and a position within that bucket. So a re-bucketing may be necessary.
- addr_info_newport.port = I2P_SAM31_PORT;
-
- const auto bucket_target = addr_info_newport.GetTriedBucket(nKey, m_asmap);
- const auto i_target = addr_info_newport.GetBucketPosition(nKey, false, bucket_target);
-
- if (bucket_target == bucket && i_target == i) { // No need to re-position.
- addr_info = addr_info_newport;
- continue;
- }
-
- // Reposition from (bucket, i) to (bucket_target, i_target). If the latter is
- // occupied, then move the entry from there to vvNew.
-
- const auto old_target_id = vvTried[bucket_target][i_target];
- if (old_target_id != -1) {
- CAddrInfo& old_target_info = mapInfo[old_target_id];
-
- old_target_info.fInTried = false;
- vvTried[bucket_target][i_target] = -1;
- --nTried;
-
- const auto new_bucket = old_target_info.GetNewBucket(nKey, m_asmap);
- const auto new_bucket_i = old_target_info.GetBucketPosition(nKey, true, new_bucket);
- ClearNew(new_bucket, new_bucket_i);
-
- old_target_info.nRefCount = 1;
- vvNew[new_bucket][new_bucket_i] = old_target_id;
- ++nNew;
- }
-
- vvTried[bucket_target][i_target] = id;
- vvTried[bucket][i] = -1;
- addr_info = addr_info_newport;
- }
- }
-}
diff --git a/src/addrman.h b/src/addrman.h
index 2a5c6c06b4..1fc64ac07f 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -334,12 +334,18 @@ public:
nUBuckets ^= (1 << 30);
}
- if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
- throw std::ios_base::failure("Corrupt CAddrMan serialization, nNew exceeds limit.");
+ if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0) {
+ throw std::ios_base::failure(
+ strprintf("Corrupt CAddrMan serialization: nNew=%d, should be in [0, %u]",
+ nNew,
+ ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
- if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) {
- throw std::ios_base::failure("Corrupt CAddrMan serialization, nTried exceeds limit.");
+ if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {
+ throw std::ios_base::failure(
+ strprintf("Corrupt CAddrMan serialization: nTried=%d, should be in [0, %u]",
+ nTried,
+ ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
// Deserialize entries from the new table.
@@ -452,8 +458,6 @@ public:
RemoveInvalid();
- ResetI2PPorts();
-
Check();
}
@@ -532,12 +536,12 @@ public:
}
//! Mark an entry as accessible.
- void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
- Good_(addr, test_before_evict, nTime);
+ Good_(addr, /* test_before_evict */ true, nTime);
Check();
}
@@ -769,14 +773,6 @@ private:
//! Remove invalid addresses.
void RemoveInvalid() EXCLUSIVE_LOCKS_REQUIRED(cs);
- /**
- * Reset the ports of I2P peers to 0.
- * This is needed as a temporary measure because now we enforce port 0 and
- * only connect to I2P hosts if the port is 0, but in the early days some
- * I2P addresses with port 8333 were rumoured and persisted into addrmans.
- */
- void ResetI2PPorts() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
friend class CAddrManTest;
};
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 39e74b9b2b..928aa7573c 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -21,7 +21,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
const ECCVerifyHandle verify_handle;
ECC_Start();
- const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH;
+ const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
const int witnessversion = 0;
// Key pair.
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 7a5f945511..1ec6411e32 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,6 +9,7 @@
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <policy/feerate.h>
#include <rpc/client.h>
#include <rpc/mining.h>
#include <rpc/protocol.h>
@@ -28,6 +29,10 @@
#include <string>
#include <tuple>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
#include <support/events.h>
@@ -48,6 +53,9 @@ static constexpr int8_t UNKNOWN_NETWORK{-1};
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std::string DEFAULT_NBLOCKS = "1";
+/** Default -color setting. */
+static const std::string DEFAULT_COLOR_SETTING{"auto"};
+
static void SetupCliArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
@@ -66,6 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions(argsman);
+ argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -338,7 +347,9 @@ public:
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO]["result"].isNull()) {
+ result.pushKV("has_wallet", true);
result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
+ result.pushKV("walletname", batch[ID_WALLETINFO]["result"]["walletname"]);
if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
}
@@ -874,6 +885,100 @@ static void GetWalletBalances(UniValue& result)
}
/**
+ * ParseGetInfoResult takes in -getinfo result in UniValue object and parses it
+ * into a user friendly UniValue string to be printed on the console.
+ * @param[out] result Reference to UniValue result containing the -getinfo output.
+ */
+static void ParseGetInfoResult(UniValue& result)
+{
+ if (!find_value(result, "error").isNull()) return;
+
+ std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
+ bool should_colorize = false;
+
+#ifndef WIN32
+ if (isatty(fileno(stdout))) {
+ // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
+ should_colorize = true;
+ }
+#endif
+
+ if (gArgs.IsArgSet("-color")) {
+ const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
+ if (color == "always") {
+ should_colorize = true;
+ } else if (color == "never") {
+ should_colorize = false;
+ } else if (color != "auto") {
+ throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
+ }
+ }
+
+ if (should_colorize) {
+ RESET = "\x1B[0m";
+ GREEN = "\x1B[32m";
+ BLUE = "\x1B[34m";
+ YELLOW = "\x1B[33m";
+ MAGENTA = "\x1B[35m";
+ CYAN = "\x1B[36m";
+ }
+
+ std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
+ result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
+ result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
+ result_string += strprintf("Verification progress: %.4f%%\n", result["verificationprogress"].get_real() * 100);
+ result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
+
+ result_string += strprintf(
+ "%sNetwork: in %s, out %s, total %s%s\n",
+ GREEN,
+ result["connections"]["in"].getValStr(),
+ result["connections"]["out"].getValStr(),
+ result["connections"]["total"].getValStr(),
+ RESET);
+ result_string += strprintf("Version: %s\n", result["version"].getValStr());
+ result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
+ const std::string proxy = result["proxy"].getValStr();
+ result_string += strprintf("Proxy: %s\n", proxy.empty() ? "N/A" : proxy);
+ result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
+
+ if (!result["has_wallet"].isNull()) {
+ const std::string walletname = result["walletname"].getValStr();
+ result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
+
+ result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
+ if (!result["unlocked_until"].isNull()) {
+ result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
+ }
+ result_string += strprintf("Transaction fee rate (-paytxfee) (%s/kvB): %s\n\n", CURRENCY_UNIT, result["paytxfee"].getValStr());
+ }
+ if (!result["balance"].isNull()) {
+ result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
+ }
+
+ if (!result["balances"].isNull()) {
+ result_string += strprintf("%sBalances%s\n", CYAN, RESET);
+
+ size_t max_balance_length{10};
+
+ for (const std::string& wallet : result["balances"].getKeys()) {
+ max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
+ }
+
+ for (const std::string& wallet : result["balances"].getKeys()) {
+ result_string += strprintf("%*s %s\n",
+ max_balance_length,
+ result["balances"][wallet].getValStr(),
+ wallet.empty() ? "\"\"" : wallet);
+ }
+ result_string += "\n";
+ }
+
+ result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, result["warnings"].getValStr());
+ result.setStr(result_string);
+}
+
+/**
* Call RPC getnewaddress.
* @returns getnewaddress response as a UniValue object.
*/
@@ -994,9 +1099,13 @@ static int CommandLineRPC(int argc, char *argv[])
UniValue result = find_value(reply, "result");
const UniValue& error = find_value(reply, "error");
if (error.isNull()) {
- if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
- GetWalletBalances(result); // fetch multiwallet balances and append to result
+ if (gArgs.GetBoolArg("-getinfo", false)) {
+ if (!gArgs.IsArgSet("-rpcwallet")) {
+ GetWalletBalances(result); // fetch multiwallet balances and append to result
+ }
+ ParseGetInfoResult(result);
}
+
ParseResult(result, strPrint);
} else {
ParseError(error, strPrint, nRet);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 58a27e053b..eb6c382f23 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -91,8 +91,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021
- consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000001533efd8d716a517fe2c5008");
- consensus.defaultAssumeValid = uint256S("0x0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"); // 654683
+ consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000001fa4663bbbe19f82de910280");
+ consensus.defaultAssumeValid = uint256S("0x00000000000000000008a89e854d57e5667df88f1cdef6fde2fbca1de5b639ad"); // 691719
/**
* The message start string is designed to be unlikely to occur in normal data.
@@ -105,7 +105,7 @@ public:
pchMessageStart[3] = 0xd9;
nDefaultPort = 8333;
nPruneAfterHeight = 100000;
- m_assumed_blockchain_size = 350;
+ m_assumed_blockchain_size = 420;
m_assumed_chain_state_size = 6;
genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN);
@@ -166,10 +166,10 @@ public:
};
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72
- /* nTime */ 1603995752,
- /* nTxCount */ 582083445,
- /* dTxRate */ 3.508976121410527,
+ // Data from RPC: getchaintxstats 4096 00000000000000000008a89e854d57e5667df88f1cdef6fde2fbca1de5b639ad
+ /* nTime */ 1626697539,
+ /* nTxCount */ 656509474,
+ /* dTxRate */ 2.424920418708139,
};
}
};
@@ -210,8 +210,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
- consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001db6ec4ac88cf2272c6");
- consensus.defaultAssumeValid = uint256S("0x000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0"); // 1864000
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000005180c3bd8290da33a1a");
+ consensus.defaultAssumeValid = uint256S("0x0000000000004ae2f3896ca8ecd41c460a35bf6184e145d91558cece1c688a76"); // 2010000
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
@@ -261,10 +261,10 @@ public:
};
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0
- /* nTime */ 1603359686,
- /* nTxCount */ 58090238,
- /* dTxRate */ 0.1232886622799463,
+ // Data from RPC: getchaintxstats 4096 0000000000004ae2f3896ca8ecd41c460a35bf6184e145d91558cece1c688a76
+ /* nTime */ 1625727096,
+ /* nTxCount */ 60408943,
+ /* dTxRate */ 0.08379062270367649,
};
}
};
@@ -284,15 +284,15 @@ public:
vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333");
- consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000019fd16269a");
- consensus.defaultAssumeValid = uint256S("0x0000002a1de0f46379358c1fd09906f7ac59adf3712323ed90eb59e4c183c020"); // 9434
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000008546553c03");
+ consensus.defaultAssumeValid = uint256S("0x000000187d4440e5bff91488b700a140441e089a8aaea707414982460edbfe54"); // 47200
m_assumed_blockchain_size = 1;
m_assumed_chain_state_size = 0;
chainTxData = ChainTxData{
- // Data from RPC: getchaintxstats 4096 0000002a1de0f46379358c1fd09906f7ac59adf3712323ed90eb59e4c183c020
- /* nTime */ 1603986000,
- /* nTxCount */ 9582,
- /* dTxRate */ 0.00159272030651341,
+ // Data from RPC: getchaintxstats 4096 000000187d4440e5bff91488b700a140441e089a8aaea707414982460edbfe54
+ /* nTime */ 1626696658,
+ /* nTxCount */ 387761,
+ /* dTxRate */ 0.04035946932424404,
};
} else {
const auto signet_challenge = args.GetArgs("-signetchallenge");
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 88d8da6ed5..0ab790ccdc 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -144,7 +144,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
return nSigOps;
}
-int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags)
+int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags)
{
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index d5fd43e131..264433c33d 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -49,10 +49,10 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma
* Compute total signature operation cost of a transaction.
* @param[in] tx Transaction for which we are computing the cost
* @param[in] inputs Map of previous transactions that have outputs we're spending
- * @param[out] flags Script verification flags
+ * @param[in] flags Script verification flags
* @return Total signature operation cost of tx
*/
-int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags);
+int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags);
/**
* Check if transaction is final and can be included in a block with the
diff --git a/src/hash.cpp b/src/hash.cpp
index cc46043c2b..3465caa3a9 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -47,8 +47,10 @@ unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vData
switch (vDataToHash.size() & 3) {
case 3:
k1 ^= tail[2] << 16;
+ [[fallthrough]];
case 2:
k1 ^= tail[1] << 8;
+ [[fallthrough]];
case 1:
k1 ^= tail[0];
k1 *= c1;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 782e557478..cde9821f3d 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -204,7 +204,7 @@ bool TxIndex::Init()
// Attempt to migrate txindex from the old database to the new one. Even if
// chain_tip is null, the node could be reindexing and we still want to
// delete txindex records in the old database.
- if (!m_db->MigrateData(*pblocktree, m_chainstate->m_chain.GetLocator())) {
+ if (!m_db->MigrateData(*m_chainstate->m_blockman.m_block_tree_db, m_chainstate->m_chain.GetLocator())) {
return false;
}
diff --git a/src/init.cpp b/src/init.cpp
index 9afd76d62d..3915fa8fcb 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -264,7 +264,6 @@ void Shutdown(NodeContext& node)
chainstate->ResetCoinsViews();
}
}
- pblocktree.reset();
}
for (const auto& client : node.chain_clients) {
client->stop();
@@ -1355,6 +1354,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
UnloadBlockIndex(node.mempool.get(), chainman);
+ auto& pblocktree{chainman.m_blockman.m_block_tree_db};
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
pblocktree.reset();
diff --git a/src/interfaces/ipc.h b/src/interfaces/ipc.h
index e9e6c78053..963649fc9a 100644
--- a/src/interfaces/ipc.h
+++ b/src/interfaces/ipc.h
@@ -9,6 +9,10 @@
#include <memory>
#include <typeindex>
+namespace ipc {
+struct Context;
+} // namespace ipc
+
namespace interfaces {
class Init;
@@ -58,6 +62,9 @@ public:
addCleanup(typeid(Interface), &iface, std::move(cleanup));
}
+ //! IPC context struct accessor (see struct definition for more description).
+ virtual ipc::Context& context() = 0;
+
protected:
//! Internal implementation of public addCleanup method (above) as a
//! type-erased virtual function, since template functions can't be virtual.
diff --git a/src/ipc/capnp/context.h b/src/ipc/capnp/context.h
new file mode 100644
index 0000000000..06e1614494
--- /dev/null
+++ b/src/ipc/capnp/context.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_IPC_CAPNP_CONTEXT_H
+#define BITCOIN_IPC_CAPNP_CONTEXT_H
+
+#include <ipc/context.h>
+
+namespace ipc {
+namespace capnp {
+//! Cap'n Proto context struct. Generally the parent ipc::Context struct should
+//! be used instead of this struct to give all IPC protocols access to
+//! application state, so there aren't unnecessary differences between IPC
+//! protocols. But this specialized struct can be used to pass capnp-specific
+//! function and object types to capnp hooks.
+struct Context : ipc::Context
+{
+};
+} // namespace capnp
+} // namespace ipc
+
+#endif // BITCOIN_IPC_CAPNP_CONTEXT_H
diff --git a/src/ipc/capnp/protocol.cpp b/src/ipc/capnp/protocol.cpp
index 74c66c899a..37b57a9525 100644
--- a/src/ipc/capnp/protocol.cpp
+++ b/src/ipc/capnp/protocol.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <interfaces/init.h>
+#include <ipc/capnp/context.h>
#include <ipc/capnp/init.capnp.h>
#include <ipc/capnp/init.capnp.proxy.h>
#include <ipc/capnp/protocol.h>
@@ -54,7 +55,7 @@ public:
{
assert(!m_loop);
mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
- m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ m_loop.emplace(exe_name, &IpcLogFn, &m_context);
mp::ServeStream<messages::Init>(*m_loop, fd, init);
m_loop->loop();
m_loop.reset();
@@ -63,13 +64,14 @@ public:
{
mp::ProxyTypeRegister::types().at(type)(iface).cleanup.emplace_back(std::move(cleanup));
}
+ Context& context() override { return m_context; }
void startLoop(const char* exe_name)
{
if (m_loop) return;
std::promise<void> promise;
m_loop_thread = std::thread([&] {
util::ThreadRename("capnp-loop");
- m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ m_loop.emplace(exe_name, &IpcLogFn, &m_context);
{
std::unique_lock<std::mutex> lock(m_loop->m_mutex);
m_loop->addClient(lock);
@@ -80,6 +82,7 @@ public:
});
promise.get_future().wait();
}
+ Context m_context;
std::thread m_loop_thread;
std::optional<mp::EventLoop> m_loop;
};
diff --git a/src/ipc/context.h b/src/ipc/context.h
new file mode 100644
index 0000000000..924d7d7450
--- /dev/null
+++ b/src/ipc/context.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_IPC_CONTEXT_H
+#define BITCOIN_IPC_CONTEXT_H
+
+namespace ipc {
+//! Context struct used to give IPC protocol implementations or implementation
+//! hooks access to application state, in case they need to run extra code that
+//! isn't needed within a single process, like code copying global state from an
+//! existing process to a new process when it's initialized, or code dealing
+//! with shared objects that are created or destroyed remotely.
+struct Context
+{
+};
+} // namespace ipc
+
+#endif // BITCOIN_IPC_CONTEXT_H
diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp
index ad4b78ed81..580590fde9 100644
--- a/src/ipc/interfaces.cpp
+++ b/src/ipc/interfaces.cpp
@@ -60,6 +60,7 @@ public:
{
m_protocol->addCleanup(type, iface, std::move(cleanup));
}
+ Context& context() override { return m_protocol->context(); }
const char* m_exe_name;
const char* m_process_argv0;
interfaces::Init& m_init;
diff --git a/src/ipc/protocol.h b/src/ipc/protocol.h
index af955b0007..4cd892e411 100644
--- a/src/ipc/protocol.h
+++ b/src/ipc/protocol.h
@@ -12,6 +12,8 @@
#include <typeindex>
namespace ipc {
+struct Context;
+
//! IPC protocol interface for calling IPC methods over sockets.
//!
//! There may be different implementations of this interface for different IPC
@@ -33,6 +35,9 @@ public:
//! Add cleanup callback to interface that will run when the interface is
//! deleted.
virtual void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) = 0;
+
+ //! Context accessor.
+ virtual Context& context() = 0;
};
} // namespace ipc
diff --git a/src/net.cpp b/src/net.cpp
index 70ba875c4b..3a1bb138ab 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1212,16 +1212,29 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
{
- if (conn_type != ConnectionType::OUTBOUND_FULL_RELAY && conn_type != ConnectionType::BLOCK_RELAY) return false;
-
- const int max_connections = conn_type == ConnectionType::OUTBOUND_FULL_RELAY ? m_max_outbound_full_relay : m_max_outbound_block_relay;
+ std::optional<int> max_connections;
+ switch (conn_type) {
+ case ConnectionType::INBOUND:
+ case ConnectionType::MANUAL:
+ case ConnectionType::FEELER:
+ return false;
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ max_connections = m_max_outbound_full_relay;
+ break;
+ case ConnectionType::BLOCK_RELAY:
+ max_connections = m_max_outbound_block_relay;
+ break;
+ // no limit for ADDR_FETCH because -seednode has no limit either
+ case ConnectionType::ADDR_FETCH:
+ break;
+ } // no default case, so the compiler can warn about missing cases
// Count existing connections
int existing_connections = WITH_LOCK(cs_vNodes,
return std::count_if(vNodes.begin(), vNodes.end(), [conn_type](CNode* node) { return node->m_conn_type == conn_type; }););
// Max connections of specified type already exist
- if (existing_connections >= max_connections) return false;
+ if (max_connections != std::nullopt && existing_connections >= max_connections) return false;
// Max total outbound connections already exist
CSemaphoreGrant grant(*semOutbound, true);
diff --git a/src/net.h b/src/net.h
index e1f109a2c4..a8836dfcb4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -893,6 +893,7 @@ public:
*
* @param[in] address Address of node to try connecting to
* @param[in] conn_type ConnectionType::OUTBOUND or ConnectionType::BLOCK_RELAY
+ * or ConnectionType::ADDR_FETCH
* @return bool Returns false if there are no available
* slots for this connection:
* - conn_type not a supported ConnectionType
diff --git a/src/net_permissions.h b/src/net_permissions.h
index c00689465e..bc979e3792 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -31,7 +31,8 @@ enum class NetPermissionFlags : uint32_t {
NoBan = (1U << 4) | Download,
// Can query the mempool
Mempool = (1U << 5),
- // Can request addrs without hitting a privacy-preserving cache
+ // Can request addrs without hitting a privacy-preserving cache, and send us
+ // unlimited amounts of addrs.
Addr = (1U << 7),
// True if the user did not specifically set fine grained permissions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 315d2ac5cd..44f9879a23 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -155,6 +155,13 @@ static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;
/** The maximum number of address records permitted in an ADDR message. */
static constexpr size_t MAX_ADDR_TO_SEND{1000};
+/** The maximum rate of address records we're willing to process on average. Can be bypassed using
+ * the NetPermissionFlags::Addr permission. */
+static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1};
+/** The soft limit of the address processing token bucket (the regular MAX_ADDR_RATE_PER_SECOND
+ * based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR
+ * is exempt from this limit. */
+static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND};
// Internal stuff
namespace {
@@ -233,6 +240,15 @@ struct Peer {
std::atomic_bool m_wants_addrv2{false};
/** Whether this peer has already sent us a getaddr message. */
bool m_getaddr_recvd{false};
+ /** Number of addr messages that can be processed from this peer. Start at 1 to
+ * permit self-announcement. */
+ double m_addr_token_bucket{1.0};
+ /** When m_addr_token_bucket was last updated */
+ std::chrono::microseconds m_addr_token_timestamp{GetTime<std::chrono::microseconds>()};
+ /** Total number of addresses that were dropped due to rate limiting. */
+ std::atomic<uint64_t> m_addr_rate_limited{0};
+ /** Total number of addresses that were processed (excludes rate limited ones). */
+ std::atomic<uint64_t> m_addr_processed{0};
/** Set of txids to reconsider once their parent transactions have been accepted **/
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
@@ -1239,6 +1255,8 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
}
stats.m_ping_wait = ping_wait;
+ stats.m_addr_processed = peer->m_addr_processed.load();
+ stats.m_addr_rate_limited = peer->m_addr_rate_limited.load();
return true;
}
@@ -2583,6 +2601,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Get recent addresses
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
peer->m_getaddr_sent = true;
+ // When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response
+ // (bypassing the MAX_ADDR_PROCESSING_TOKEN_BUCKET limit).
+ peer->m_addr_token_bucket += MAX_ADDR_TO_SEND;
}
if (!pfrom.IsInboundConn()) {
@@ -2777,11 +2798,34 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
std::vector<CAddress> vAddrOk;
int64_t nNow = GetAdjustedTime();
int64_t nSince = nNow - 10 * 60;
+
+ // Update/increment addr rate limiting bucket.
+ const auto current_time = GetTime<std::chrono::microseconds>();
+ if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
+ // Don't increment bucket if it's already full
+ const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us);
+ const double increment = CountSecondsDouble(time_diff) * MAX_ADDR_RATE_PER_SECOND;
+ peer->m_addr_token_bucket = std::min<double>(peer->m_addr_token_bucket + increment, MAX_ADDR_PROCESSING_TOKEN_BUCKET);
+ }
+ peer->m_addr_token_timestamp = current_time;
+
+ const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr);
+ uint64_t num_proc = 0;
+ uint64_t num_rate_limit = 0;
+ Shuffle(vAddr.begin(), vAddr.end(), FastRandomContext());
for (CAddress& addr : vAddr)
{
if (interruptMsgProc)
return;
+ // Apply rate limiting.
+ if (rate_limited) {
+ if (peer->m_addr_token_bucket < 1.0) {
+ ++num_rate_limit;
+ continue;
+ }
+ peer->m_addr_token_bucket -= 1.0;
+ }
// We only bother storing full nodes, though this may include
// things which we would not make an outbound connection to, in
// part because we may make feeler connections to them.
@@ -2795,6 +2839,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Do not process banned/discouraged addresses beyond remembering we received them
continue;
}
+ ++num_proc;
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) {
// Relay to a limited number of other nodes
@@ -2804,9 +2849,20 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (fReachable)
vAddrOk.push_back(addr);
}
+ peer->m_addr_processed += num_proc;
+ peer->m_addr_rate_limited += num_rate_limit;
+ LogPrint(BCLog::NET, "Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d%s\n",
+ vAddr.size(),
+ num_proc,
+ num_rate_limit,
+ pfrom.GetId(),
+ fLogIPs ? ", peeraddr=" + pfrom.addr.ToString() : "");
+
m_addrman.Add(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000) peer->m_getaddr_sent = false;
- if (pfrom.IsAddrFetchConn()) {
+
+ // AddrFetch: Require multiple addresses to avoid disconnecting on self-announcements
+ if (pfrom.IsAddrFetchConn() && vAddr.size() > 1) {
LogPrint(BCLog::NET, "addrfetch connection completed peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
@@ -4390,6 +4446,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
const auto current_time = GetTime<std::chrono::microseconds>();
+ if (pto->IsAddrFetchConn() && current_time - std::chrono::seconds(pto->nTimeConnected) > 10 * AVG_ADDRESS_BROADCAST_INTERVAL) {
+ LogPrint(BCLog::NET, "addrfetch connection timeout; disconnecting peer=%d\n", pto->GetId());
+ pto->fDisconnect = true;
+ return true;
+ }
+
MaybeSendPing(*pto, *peer, current_time);
// MaybeSendPing may have marked peer for disconnection
diff --git a/src/net_processing.h b/src/net_processing.h
index d5801aadd3..c537efb5db 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -29,6 +29,8 @@ struct CNodeStateStats {
int m_starting_height = -1;
std::chrono::microseconds m_ping_wait;
std::vector<int> vHeightInFlight;
+ uint64_t m_addr_processed = 0;
+ uint64_t m_addr_rate_limited = 0;
};
class PeerManager : public CValidationInterface, public NetEventsInterface
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 0083b74b33..90f7ba191d 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -518,7 +518,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
}
nFile++;
}
- pblocktree->WriteReindexing(false);
+ WITH_LOCK(::cs_main, chainman.m_blockman.m_block_tree_db->WriteReindexing(false));
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index f21b390915..d3bce069b0 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -28,65 +28,83 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
{
- // BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
- // node.peerman is assigned both before chain clients and before RPC server is accepting calls,
- // and reset after chain clients and RPC sever are stopped. node.peerman should never be null here.
- assert(node.peerman);
+ // BroadcastTransaction can be called by either sendrawtransaction RPC or the wallet.
+ // chainman, mempool and peerman are initialized before the RPC server and wallet are started
+ // and reset after the RPC sever and wallet are stopped.
+ assert(node.chainman);
assert(node.mempool);
+ assert(node.peerman);
+
std::promise<void> promise;
- uint256 hashTx = tx->GetHash();
+ uint256 txid = tx->GetHash();
+ uint256 wtxid = tx->GetWitnessHash();
bool callback_set = false;
- { // cs_main scope
- assert(node.chainman);
- LOCK(cs_main);
- // If the transaction is already confirmed in the chain, don't do anything
- // and return early.
- CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
- for (size_t o = 0; o < tx->vout.size(); o++) {
- const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
- // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
- // So if the output does exist, then this transaction exists in the chain.
- if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
- }
- if (!node.mempool->exists(hashTx)) {
- // Transaction is not already in the mempool.
- if (max_tx_fee > 0) {
- // First, call ATMP with test_accept and check the fee. If ATMP
- // fails here, return error immediately.
+ {
+ LOCK(cs_main);
+
+ // If the transaction is already confirmed in the chain, don't do anything
+ // and return early.
+ CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
+ for (size_t o = 0; o < tx->vout.size(); o++) {
+ const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
+ // IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
+ // So if the output does exist, then this transaction exists in the chain.
+ if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
+ }
+
+ if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
+ // There's already a transaction in the mempool with this txid. Don't
+ // try to submit this transaction to the mempool (since it'll be
+ // rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
+ // transaction if relay=true.
+ //
+ // The mempool transaction may have the same or different witness (and
+ // wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
+ wtxid = mempool_tx->GetWitnessHash();
+ } else {
+ // Transaction is not already in the mempool.
+ if (max_tx_fee > 0) {
+ // First, call ATMP with test_accept and check the fee. If ATMP
+ // fails here, return error immediately.
+ const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
+ true /* test_accept */);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ return HandleATMPError(result.m_state, err_string);
+ } else if (result.m_base_fees.value() > max_tx_fee) {
+ return TransactionError::MAX_FEE_EXCEEDED;
+ }
+ }
+ // Try to submit the transaction to the mempool.
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
- true /* test_accept */);
+ false /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
- } else if (result.m_base_fees.value() > max_tx_fee) {
- return TransactionError::MAX_FEE_EXCEEDED;
}
- }
- // Try to submit the transaction to the mempool.
- const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
- false /* test_accept */);
- if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
- return HandleATMPError(result.m_state, err_string);
- }
- // Transaction was accepted to the mempool.
+ // Transaction was accepted to the mempool.
- if (wait_callback) {
- // For transactions broadcast from outside the wallet, make sure
- // that the wallet has been notified of the transaction before
- // continuing.
- //
- // This prevents a race where a user might call sendrawtransaction
- // with a transaction to/from their wallet, immediately call some
- // wallet RPC, and get a stale result because callbacks have not
- // yet been processed.
- CallFunctionInValidationInterfaceQueue([&promise] {
- promise.set_value();
- });
- callback_set = true;
- }
- }
+ if (relay) {
+ // the mempool tracks locally submitted transactions to make a
+ // best-effort of initial broadcast
+ node.mempool->AddUnbroadcastTx(txid);
+ }
+ if (wait_callback) {
+ // For transactions broadcast from outside the wallet, make sure
+ // that the wallet has been notified of the transaction before
+ // continuing.
+ //
+ // This prevents a race where a user might call sendrawtransaction
+ // with a transaction to/from their wallet, immediately call some
+ // wallet RPC, and get a stale result because callbacks have not
+ // yet been processed.
+ CallFunctionInValidationInterfaceQueue([&promise] {
+ promise.set_value();
+ });
+ callback_set = true;
+ }
+ }
} // cs_main
if (callback_set) {
@@ -96,10 +114,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
if (relay) {
- // the mempool tracks locally submitted transactions to make a
- // best-effort of initial broadcast
- node.mempool->AddUnbroadcastTx(hashTx);
- node.peerman->RelayTransaction(hashTx, tx->GetWitnessHash());
+ node.peerman->RelayTransaction(txid, wtxid);
}
return TransactionError::OK;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 442c813a5a..4ab4e388d9 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -7,12 +7,19 @@
#endif
#include <qt/bitcoin.h>
-#include <qt/bitcoingui.h>
#include <chainparams.h>
+#include <init.h>
+#include <interfaces/handler.h>
+#include <interfaces/node.h>
+#include <node/context.h>
+#include <node/ui_interface.h>
+#include <noui.h>
+#include <qt/bitcoingui.h>
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
+#include <qt/initexecutor.h>
#include <qt/intro.h>
#include <qt/networkstyle.h>
#include <qt/optionsmodel.h>
@@ -20,6 +27,11 @@
#include <qt/splashscreen.h>
#include <qt/utilitydialog.h>
#include <qt/winshutdownmonitor.h>
+#include <uint256.h>
+#include <util/system.h>
+#include <util/threadnames.h>
+#include <util/translation.h>
+#include <validation.h>
#ifdef ENABLE_WALLET
#include <qt/paymentserver.h>
@@ -27,18 +39,6 @@
#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>
-#include <util/threadnames.h>
-#include <util/translation.h>
-#include <validation.h>
-
#include <boost/signals2/connection.hpp>
#include <memory>
@@ -155,54 +155,11 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
}
}
-BitcoinCore::BitcoinCore(interfaces::Node& node) :
- QObject(), m_node(node)
-{
-}
-
-void BitcoinCore::handleRunawayException(const std::exception *e)
-{
- PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
-}
-
-void BitcoinCore::initialize()
-{
- try
- {
- util::ThreadRename("qt-init");
- qDebug() << __func__ << ": Running initialization in thread";
- interfaces::BlockAndHeaderTipInfo tip_info;
- bool rv = m_node.appInitMain(&tip_info);
- Q_EMIT initializeResult(rv, tip_info);
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
-}
-
-void BitcoinCore::shutdown()
-{
- try
- {
- qDebug() << __func__ << ": Running Shutdown in thread";
- m_node.appShutdown();
- qDebug() << __func__ << ": Shutdown finished";
- Q_EMIT shutdownResult();
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
-}
-
static int qt_argc = 1;
static const char* qt_argv = "bitcoin-qt";
BitcoinApplication::BitcoinApplication():
QApplication(qt_argc, const_cast<char **>(&qt_argv)),
- coreThread(nullptr),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
@@ -230,13 +187,7 @@ void BitcoinApplication::setupPlatformStyle()
BitcoinApplication::~BitcoinApplication()
{
- if(coreThread)
- {
- qDebug() << __func__ << ": Stopping thread";
- coreThread->quit();
- coreThread->wait();
- qDebug() << __func__ << ": Stopped thread";
- }
+ m_executor.reset();
delete window;
window = nullptr;
@@ -291,22 +242,15 @@ bool BitcoinApplication::baseInitialize()
void BitcoinApplication::startThread()
{
- if(coreThread)
- return;
- coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore(node());
- executor->moveToThread(coreThread);
+ assert(!m_executor);
+ m_executor.emplace(node());
/* communication to and from thread */
- connect(executor, &BitcoinCore::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(executor, &BitcoinCore::shutdownResult, this, &BitcoinApplication::shutdownResult);
- connect(executor, &BitcoinCore::runawayException, this, &BitcoinApplication::handleRunawayException);
- connect(this, &BitcoinApplication::requestedInitialize, executor, &BitcoinCore::initialize);
- connect(this, &BitcoinApplication::requestedShutdown, executor, &BitcoinCore::shutdown);
- /* make sure executor object is deleted in its own thread */
- connect(coreThread, &QThread::finished, executor, &QObject::deleteLater);
-
- coreThread->start();
+ connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &BitcoinApplication::shutdownResult);
+ connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
+ connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
+ connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
}
void BitcoinApplication::parameterSetup()
@@ -339,7 +283,6 @@ void BitcoinApplication::requestShutdown()
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
- startThread();
window->hide();
// Must disconnect node signals otherwise current thread can deadlock since
// no event loop is running.
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index f9fab0534b..ed2f26b7f3 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -9,11 +9,14 @@
#include <config/bitcoin-config.h>
#endif
-#include <QApplication>
+#include <interfaces/node.h>
+#include <qt/initexecutor.h>
+
#include <assert.h>
#include <memory>
+#include <optional>
-#include <interfaces/node.h>
+#include <QApplication>
class BitcoinGUI;
class ClientModel;
@@ -26,31 +29,6 @@ class WalletController;
class WalletModel;
-/** Class encapsulating Bitcoin Core startup and shutdown.
- * Allows running startup and shutdown in a different thread from the UI thread.
- */
-class BitcoinCore: public QObject
-{
- Q_OBJECT
-public:
- explicit BitcoinCore(interfaces::Node& node);
-
-public Q_SLOTS:
- void initialize();
- void shutdown();
-
-Q_SIGNALS:
- void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
- void shutdownResult();
- void runawayException(const QString &message);
-
-private:
- /// Pass fatal exception message to UI thread
- void handleRunawayException(const std::exception *e);
-
- interfaces::Node& m_node;
-};
-
/** Main Bitcoin application object */
class BitcoinApplication: public QApplication
{
@@ -112,7 +90,7 @@ Q_SIGNALS:
void windowShown(BitcoinGUI* window);
private:
- QThread *coreThread;
+ std::optional<InitExecutor> m_executor;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp
new file mode 100644
index 0000000000..7060f74dab
--- /dev/null
+++ b/src/qt/initexecutor.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2014-2021 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 <qt/initexecutor.h>
+
+#include <interfaces/node.h>
+#include <util/system.h>
+#include <util/threadnames.h>
+
+#include <exception>
+
+#include <QDebug>
+#include <QObject>
+#include <QString>
+#include <QThread>
+
+InitExecutor::InitExecutor(interfaces::Node& node)
+ : QObject(), m_node(node)
+{
+ this->moveToThread(&m_thread);
+ m_thread.start();
+}
+
+InitExecutor::~InitExecutor()
+{
+ qDebug() << __func__ << ": Stopping thread";
+ m_thread.quit();
+ m_thread.wait();
+ qDebug() << __func__ << ": Stopped thread";
+}
+
+void InitExecutor::handleRunawayException(const std::exception* e)
+{
+ PrintExceptionContinue(e, "Runaway exception");
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
+}
+
+void InitExecutor::initialize()
+{
+ try {
+ util::ThreadRename("qt-init");
+ qDebug() << __func__ << ": Running initialization in thread";
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+}
+
+void InitExecutor::shutdown()
+{
+ try {
+ qDebug() << __func__ << ": Running Shutdown in thread";
+ m_node.appShutdown();
+ qDebug() << __func__ << ": Shutdown finished";
+ Q_EMIT shutdownResult();
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+}
diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h
new file mode 100644
index 0000000000..319ce40465
--- /dev/null
+++ b/src/qt/initexecutor.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2014-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_INITEXECUTOR_H
+#define BITCOIN_QT_INITEXECUTOR_H
+
+#include <interfaces/node.h>
+
+#include <exception>
+
+#include <QObject>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+/** Class encapsulating Bitcoin Core startup and shutdown.
+ * Allows running startup and shutdown in a different thread from the UI thread.
+ */
+class InitExecutor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit InitExecutor(interfaces::Node& node);
+ ~InitExecutor();
+
+public Q_SLOTS:
+ void initialize();
+ void shutdown();
+
+Q_SIGNALS:
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
+ void shutdownResult();
+ void runawayException(const QString& message);
+
+private:
+ /// Pass fatal exception message to UI thread
+ void handleRunawayException(const std::exception* e);
+
+ interfaces::Node& m_node;
+ QThread m_thread;
+};
+
+#endif // BITCOIN_QT_INITEXECUTOR_H
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 9c57816f91..56f55363b2 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -288,6 +288,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes
}
if (breakParsing)
break;
+ [[fallthrough]];
}
case STATE_ARGUMENT: // In or after argument
case STATE_EATING_SPACES_IN_ARG:
@@ -401,6 +402,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes
strResult = lastResult.get_str();
else
strResult = lastResult.write(2);
+ [[fallthrough]];
case STATE_ARGUMENT:
case STATE_EATING_SPACES:
return true;
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index eb86f027ef..7d66f67f8a 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -8,6 +8,7 @@
#include <interfaces/node.h>
#include <qt/bitcoin.h>
+#include <qt/initexecutor.h>
#include <qt/test/apptests.h>
#include <qt/test/rpcnestedtests.h>
#include <qt/test/uritests.h>
diff --git a/src/rest.cpp b/src/rest.cpp
index d599f381e3..e50ab33e54 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -524,6 +524,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
// convert hex to bin, continue then with bin part
std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
+ [[fallthrough]];
}
case RetFormat::BINARY: {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c4a89c9772..f8a4d4ccab 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1377,23 +1377,24 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
}
- if (ThresholdState::STARTED == thresholdState)
- {
+ const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
+ if (has_signal) {
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
bip9.pushKV("since", since_height);
- if (ThresholdState::STARTED == thresholdState)
- {
+ if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
- statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
- statsUV.pushKV("possible", statsStruct.possible);
+ if (ThresholdState::LOCKED_IN != thresholdState) {
+ statsUV.pushKV("threshold", statsStruct.threshold);
+ statsUV.pushKV("possible", statsStruct.possible);
+ }
bip9.pushKV("statistics", statsUV);
}
bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
@@ -1422,7 +1423,8 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
{RPCResult::Type::NUM, "difficulty", "the current difficulty"},
- {RPCResult::Type::NUM, "mediantime", "median time for the current best block"},
+ {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
{RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
@@ -1439,18 +1441,18 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)",
{
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)",
+ {RPCResult::Type::OBJ, "statistics", "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
{
- {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
- {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"},
+ {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
+ {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
{RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
{RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"},
+ {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
}},
}},
{RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
@@ -1478,6 +1480,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
+ obj.pushKV("time", (int64_t)tip->nTime);
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 2762d78493..692096367c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -850,7 +850,7 @@ static RPCHelpMan getblocktemplate()
case ThresholdState::LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= g_versionbitscache.Mask(consensusParams, pos);
- // FALL THROUGH to get vbavailable set...
+ [[fallthrough]];
case ThresholdState::STARTED:
{
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 3013c76825..dba0f971b2 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -242,6 +242,8 @@ static RPCHelpMan getpeerinfo()
heights.push_back(height);
}
obj.pushKV("inflight", heights);
+ obj.pushKV("addr_processed", statestats.m_addr_processed);
+ obj.pushKV("addr_rate_limited", statestats.m_addr_rate_limited);
}
UniValue permissions(UniValue::VARR);
for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
@@ -337,7 +339,7 @@ static RPCHelpMan addconnection()
"\nOpen an outbound connection to a specified node. This RPC is for testing only.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."},
- {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open, either \"outbound-full-relay\" or \"block-relay-only\"."},
+ {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\" or \"addr-fetch\")."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -363,6 +365,8 @@ static RPCHelpMan addconnection()
conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
} else if (conn_type_in == "block-relay-only") {
conn_type = ConnectionType::BLOCK_RELAY;
+ } else if (conn_type_in == "addr-fetch") {
+ conn_type = ConnectionType::ADDR_FETCH;
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString());
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index ef48f89965..dd7c0a4a05 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1807,16 +1807,16 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
-static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
+static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
{
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
if (sigversion == SigVersion::TAPSCRIPT) {
// OP_SUCCESSx processing overrides everything, including stack element size limits
- CScript::const_iterator pc = scriptPubKey.begin();
- while (pc < scriptPubKey.end()) {
+ CScript::const_iterator pc = exec_script.begin();
+ while (pc < exec_script.end()) {
opcodetype opcode;
- if (!scriptPubKey.GetOp(pc, opcode)) {
+ if (!exec_script.GetOp(pc, opcode)) {
// Note how this condition would not be reached if an unknown OP_SUCCESSx was found
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
@@ -1839,7 +1839,7 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
}
// Run the script interpreter.
- if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, execdata, serror)) return false;
+ if (!EvalScript(stack, exec_script, flags, checker, sigversion, execdata, serror)) return false;
// Scripts inside witness implicitly require cleanstack behaviour
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 034c937b99..37ae713996 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -39,8 +39,7 @@ enum
* All flags are intended to be soft forks: the set of acceptable scripts under
* flags (A | B) is a subset of the acceptable scripts under flag (A).
*/
-enum
-{
+enum : uint32_t {
SCRIPT_VERIFY_NONE = 0,
// Evaluate P2SH subscripts (BIP16).
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 65276f641f..7864e690d8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -612,15 +612,18 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
- std::vector<valtype> solutions;
- auto whichtype = Solver(script, solutions);
- if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
- if (whichtype == TxoutType::SCRIPTHASH) {
- auto h160 = uint160(solutions[0]);
- CScript subscript;
- if (provider.GetCScript(CScriptID{h160}, subscript)) {
- whichtype = Solver(subscript, solutions);
- if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
+ int version;
+ valtype program;
+ if (script.IsWitnessProgram(version, program)) return true;
+ if (script.IsPayToScriptHash()) {
+ std::vector<valtype> solutions;
+ auto whichtype = Solver(script, solutions);
+ if (whichtype == TxoutType::SCRIPTHASH) {
+ auto h160 = uint160(solutions[0]);
+ CScript subscript;
+ if (provider.GetCScript(CScriptID{h160}, subscript)) {
+ if (subscript.IsWitnessProgram(version, program)) return true;
+ }
}
}
return false;
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index f2eed956cd..5e5c5eba69 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
-#include <i2p.h>
#include <test/data/asmap.raw.h>
#include <test/util/setup_common.h>
#include <util/asmap.h>
@@ -77,7 +76,7 @@ public:
{
int64_t nLastSuccess = 1;
// Set last good connection in the deep past.
- Good(addr, true, nLastSuccess);
+ Good(addr, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
@@ -967,121 +966,5 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
}
-BOOST_AUTO_TEST_CASE(reset_i2p_ports)
-{
- CAddrManTest addrman1;
- CAddrManTest addrman2;
- const uint32_t good_time{static_cast<uint32_t>(GetAdjustedTime())};
- constexpr uint16_t port = 8333;
-
- // Has its port changed, will be re-positioned within the same bucket in vvNew.
- const CAddress i2p_new1{
- ResolveService("72l3ucjkuscrbiiepoehuwqgknyzgo7zuix5ty4puwrkyhtmnsga.b32.i2p", port),
- NODE_NONE,
- good_time};
-
- // Has its port changed, will not be re-positioned in vvNew because ports 0 and 10075 result in
- // the same bucket position.
- const CAddress i2p_new2{
- ResolveService("gehtac45oaghz54ypyopim64mql7oad2bqclla74l6tfeolzmodq.b32.i2p", 10075),
- NODE_NONE,
- good_time};
-
- // Remains unchanged, port is already as it should be.
- const CAddress i2p_new3{
- ResolveService("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p",
- I2P_SAM31_PORT),
- NODE_NONE,
- good_time};
-
- // Has its port changed, re-positioning in vvNew will cause i2p_new3 to be evicted.
- const CAddress i2p_new4{
- ResolveService("c4cbbkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p", port),
- NODE_NONE,
- good_time};
-
- // Remains unchanged.
- const CAddress ipv4_new{ResolveService("1.2.3.4", port), NODE_NONE, good_time};
-
- // Has its port changed, will be re-positioned in vvTried.
- const CAddress i2p_tried1{
- ResolveService("h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p", port),
- NODE_NONE,
- good_time};
-
- // Has its port changed, will not be re-positioned in vvTried because ports 0 and 10537
- // result in the same position (bucket, i).
- const CAddress i2p_tried2{
- ResolveService("pjs7or2ctvteeo5tu4bwyrtydeuhqhvdprtujn4daxr75jpebjxa.b32.i2p", 10537),
- NODE_NONE,
- good_time};
-
- // Remains unchanged, port is already as it should be.
- const CAddress i2p_tried3{
- ResolveService("hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p",
- I2P_SAM31_PORT),
- NODE_NONE,
- good_time};
-
- // Has its port changed, re-positioning in vvTried will cause i2p_tried3 to be moved to vvNew.
- const CAddress i2p_tried4{
- ResolveService("hna37nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p", port),
- NODE_NONE,
- good_time};
-
- // Remains unchanged.
- const CAddress ipv4_tried{ResolveService("2.3.4.5", port), NODE_NONE, good_time};
-
- const CNetAddr source;
-
- CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
-
- addrman1.Add(i2p_new1, source);
- addrman1.Add(i2p_new2, source);
- addrman1.Add(i2p_new3, source);
- addrman1.Add(i2p_new4, source);
- addrman1.Add(ipv4_new, source);
-
- addrman1.Add(i2p_tried1, source);
- addrman1.Good(i2p_tried1);
- addrman1.Add(i2p_tried2, source);
- addrman1.Good(i2p_tried2);
- addrman1.Add(i2p_tried3, source);
- addrman1.Good(i2p_tried3);
- addrman1.Add(i2p_tried4, source);
- addrman1.Good(i2p_tried4);
- addrman1.Add(ipv4_tried, source);
- addrman1.Good(ipv4_tried);
-
- stream << addrman1;
- stream >> addrman2;
-
- const size_t max_addresses{0};
- const size_t max_pct{0};
-
- auto addresses = addrman2.GetAddr(max_addresses, max_pct, NET_I2P);
- BOOST_REQUIRE_EQUAL(addresses.size(), 7UL);
- std::sort(addresses.begin(), addresses.end()); // Just some deterministic order.
- BOOST_CHECK_EQUAL(addresses[0].ToStringIP(), i2p_new4.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[0].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[1].ToStringIP(), i2p_new2.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[1].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[2].ToStringIP(), i2p_tried4.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[2].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[3].ToStringIP(), i2p_tried3.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[3].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[4].ToStringIP(), i2p_tried1.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[4].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[5].ToStringIP(), i2p_tried2.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[5].GetPort(), I2P_SAM31_PORT);
- BOOST_CHECK_EQUAL(addresses[6].ToStringIP(), i2p_new1.ToStringIP());
- BOOST_CHECK_EQUAL(addresses[6].GetPort(), I2P_SAM31_PORT);
-
- addresses = addrman2.GetAddr(max_addresses, max_pct, NET_IPV4);
- BOOST_REQUIRE_EQUAL(addresses.size(), 2UL);
- std::sort(addresses.begin(), addresses.end()); // Just some deterministic order.
- BOOST_CHECK_EQUAL(addresses[0].ToStringIPPort(), ipv4_new.ToStringIPPort());
- BOOST_CHECK_EQUAL(addresses[1].ToStringIPPort(), ipv4_tried.ToStringIPPort());
-}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index db0b461873..8513f1e6df 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -80,7 +80,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
[&] {
const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
if (opt_service) {
- addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+ addr_man.Good(*opt_service, ConsumeTime(fuzzed_data_provider));
}
},
[&] {
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index f452696689..bbdb2c6917 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -258,7 +258,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
// consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
return;
}
- const int flags = fuzzed_data_provider.ConsumeIntegral<int>();
+ const auto flags{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
// Avoid:
// script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index c4e4d4c785..7b99193ad0 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -58,19 +58,7 @@ void initialize_process_message()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
-
- // Temporary debug for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=35027
- {
- LOCK(::cs_main);
- assert(CheckDiskSpace(gArgs.GetDataDirNet()));
- assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
- }
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
- {
- LOCK(::cs_main);
- assert(CheckDiskSpace(gArgs.GetDataDirNet()));
- assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
- }
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index ece3214ed5..0d87f687d3 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -343,3 +343,158 @@ CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) no
Assert(call_size == std::variant_size_v<CTxDestination>);
return tx_destination;
}
+
+CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
+{
+ // Avoid:
+ // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
+ //
+ // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
+ const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
+ assert(MoneyRange(fee));
+ const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
+ const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
+ return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
+}
+
+bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
+{
+ for (const CTxIn& tx_in : tx.vin) {
+ const Coin& coin = inputs.AccessCoin(tx_in.prevout);
+ if (coin.IsSpent()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
+ CNetAddr net_addr;
+ if (network == Network::NET_IPV4) {
+ in_addr v4_addr = {};
+ v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ net_addr = CNetAddr{v4_addr};
+ } else if (network == Network::NET_IPV6) {
+ if (fuzzed_data_provider.remaining_bytes() >= 16) {
+ in6_addr v6_addr = {};
+ memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
+ net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ }
+ } else if (network == Network::NET_INTERNAL) {
+ net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
+ } else if (network == Network::NET_ONION) {
+ net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
+ }
+ return net_addr;
+}
+
+FILE* FuzzedFileProvider::open()
+{
+ SetFuzzedErrNo(m_fuzzed_data_provider);
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return nullptr;
+ }
+ std::string mode;
+ CallOneOf(
+ m_fuzzed_data_provider,
+ [&] {
+ mode = "r";
+ },
+ [&] {
+ mode = "r+";
+ },
+ [&] {
+ mode = "w";
+ },
+ [&] {
+ mode = "w+";
+ },
+ [&] {
+ mode = "a";
+ },
+ [&] {
+ mode = "a+";
+ });
+#if defined _GNU_SOURCE && !defined __ANDROID__
+ const cookie_io_functions_t io_hooks = {
+ FuzzedFileProvider::read,
+ FuzzedFileProvider::write,
+ FuzzedFileProvider::seek,
+ FuzzedFileProvider::close,
+ };
+ return fopencookie(this, mode.c_str(), io_hooks);
+#else
+ (void)mode;
+ return nullptr;
+#endif
+}
+
+ssize_t FuzzedFileProvider::read(void* cookie, char* buf, size_t size)
+{
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
+ if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
+ if (random_bytes.empty()) {
+ return 0;
+ }
+ std::memcpy(buf, random_bytes.data(), random_bytes.size());
+ if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ fuzzed_file->m_offset += random_bytes.size();
+ return random_bytes.size();
+}
+
+ssize_t FuzzedFileProvider::write(void* cookie, const char* buf, size_t size)
+{
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
+ const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
+ if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ }
+ fuzzed_file->m_offset += n;
+ return n;
+}
+
+int FuzzedFileProvider::seek(void* cookie, int64_t* offset, int whence)
+{
+ assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
+ int64_t new_offset = 0;
+ if (whence == SEEK_SET) {
+ new_offset = *offset;
+ } else if (whence == SEEK_CUR) {
+ if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
+ return -1;
+ }
+ new_offset = fuzzed_file->m_offset + *offset;
+ } else if (whence == SEEK_END) {
+ const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
+ if (AdditionOverflow(n, *offset)) {
+ return -1;
+ }
+ new_offset = n + *offset;
+ }
+ if (new_offset < 0) {
+ return -1;
+ }
+ fuzzed_file->m_offset = new_offset;
+ *offset = new_offset;
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
+}
+
+int FuzzedFileProvider::close(void* cookie)
+{
+ FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
+ return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 9f09395a9a..bb017b3497 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -164,20 +164,7 @@ template <typename WeakEnumType, size_t size>
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
}
-[[nodiscard]] inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
-{
- // Avoid:
- // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
- //
- // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
- const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
- assert(MoneyRange(fee));
- const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
- const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
- const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
- const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
- return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
-}
+[[nodiscard]] CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept;
[[nodiscard]] CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept;
@@ -215,16 +202,7 @@ template <class T>
return std::numeric_limits<T>::max() - i < j;
}
-[[nodiscard]] inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
-{
- for (const CTxIn& tx_in : tx.vin) {
- const Coin& coin = inputs.AccessCoin(tx_in.prevout);
- if (coin.IsSpent()) {
- return true;
- }
- }
- return false;
-}
+[[nodiscard]] bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept;
/**
* Sets errno to a value selected from the given std::array `errnos`.
@@ -259,27 +237,7 @@ inline void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider) noexcept
return result;
}
-inline CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
- CNetAddr net_addr;
- if (network == Network::NET_IPV4) {
- in_addr v4_addr = {};
- v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
- net_addr = CNetAddr{v4_addr};
- } else if (network == Network::NET_IPV6) {
- if (fuzzed_data_provider.remaining_bytes() >= 16) {
- in6_addr v6_addr = {};
- memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
- net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
- }
- } else if (network == Network::NET_INTERNAL) {
- net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
- } else if (network == Network::NET_ONION) {
- net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
- }
- return net_addr;
-}
+CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept;
inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
@@ -329,112 +287,15 @@ public:
{
}
- FILE* open()
- {
- SetFuzzedErrNo(m_fuzzed_data_provider);
- if (m_fuzzed_data_provider.ConsumeBool()) {
- return nullptr;
- }
- std::string mode;
- CallOneOf(
- m_fuzzed_data_provider,
- [&] {
- mode = "r";
- },
- [&] {
- mode = "r+";
- },
- [&] {
- mode = "w";
- },
- [&] {
- mode = "w+";
- },
- [&] {
- mode = "a";
- },
- [&] {
- mode = "a+";
- });
-#if defined _GNU_SOURCE && !defined __ANDROID__
- const cookie_io_functions_t io_hooks = {
- FuzzedFileProvider::read,
- FuzzedFileProvider::write,
- FuzzedFileProvider::seek,
- FuzzedFileProvider::close,
- };
- return fopencookie(this, mode.c_str(), io_hooks);
-#else
- (void)mode;
- return nullptr;
-#endif
- }
+ FILE* open();
- static ssize_t read(void* cookie, char* buf, size_t size)
- {
- FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
- SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
- if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
- return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
- }
- const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
- if (random_bytes.empty()) {
- return 0;
- }
- std::memcpy(buf, random_bytes.data(), random_bytes.size());
- if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
- return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
- }
- fuzzed_file->m_offset += random_bytes.size();
- return random_bytes.size();
- }
+ static ssize_t read(void* cookie, char* buf, size_t size);
- static ssize_t write(void* cookie, const char* buf, size_t size)
- {
- FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
- SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
- const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
- if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
- return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
- }
- fuzzed_file->m_offset += n;
- return n;
- }
+ static ssize_t write(void* cookie, const char* buf, size_t size);
- static int seek(void* cookie, int64_t* offset, int whence)
- {
- assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
- FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
- SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
- int64_t new_offset = 0;
- if (whence == SEEK_SET) {
- new_offset = *offset;
- } else if (whence == SEEK_CUR) {
- if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
- return -1;
- }
- new_offset = fuzzed_file->m_offset + *offset;
- } else if (whence == SEEK_END) {
- const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
- if (AdditionOverflow(n, *offset)) {
- return -1;
- }
- new_offset = n + *offset;
- }
- if (new_offset < 0) {
- return -1;
- }
- fuzzed_file->m_offset = new_offset;
- *offset = new_offset;
- return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
- }
+ static int seek(void* cookie, int64_t* offset, int whence);
- static int close(void* cookie)
- {
- FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
- SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
- return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
- }
+ static int close(void* cookie);
};
[[nodiscard]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 62fd81673d..56e2aa63b9 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -123,7 +123,7 @@ static ScriptError_t ParseScriptError(const std::string& name)
BOOST_FIXTURE_TEST_SUITE(script_tests, BasicTestingSetup)
-void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, int flags, const std::string& message, int scriptError, CAmount nValue = 0)
+void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, uint32_t flags, const std::string& message, int scriptError, CAmount nValue = 0)
{
bool expect = (scriptError == SCRIPT_ERR_OK);
if (flags & SCRIPT_VERIFY_CLEANSTACK) {
@@ -139,8 +139,8 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
for (int i = 0; i < 16; ++i) {
- int extra_flags = InsecureRandBits(16);
- int combined_flags = expect ? (flags & ~extra_flags) : (flags | extra_flags);
+ uint32_t extra_flags(InsecureRandBits(16));
+ uint32_t combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
// Weed out some invalid flag combinations.
if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
@@ -150,7 +150,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
#if defined(HAVE_CONSENSUS_LIB)
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << tx2;
- int libconsensus_flags = flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL;
+ uint32_t libconsensus_flags{flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL};
if (libconsensus_flags == flags) {
int expectedSuccessCode = expect ? 1 : 0;
if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
@@ -258,7 +258,7 @@ private:
bool havePush;
std::vector<unsigned char> push;
std::string comment;
- int flags;
+ uint32_t flags;
int scriptError;
CAmount nValue;
@@ -278,7 +278,7 @@ private:
}
public:
- TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK), nValue(nValue_)
+ TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK), nValue(nValue_)
{
CScript scriptPubKey = script;
if (wm == WitnessMode::PKH) {
@@ -1677,7 +1677,7 @@ static void AssetTest(const UniValue& test)
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
BOOST_CHECK(prevouts.size() == mtx.vin.size());
size_t idx = test["index"].get_int64();
- unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
+ uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())};
bool fin = test.exists("final") && test["final"].get_bool();
if (test.exists("success")) {
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 12fc575c1e..db96fd4940 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
* Verifies script execution of the zeroth scriptPubKey of tx output and
* zeroth scriptSig and witness of tx input.
*/
-static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags)
+static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, uint32_t flags)
{
ScriptError error;
CTransaction inputi(input);
@@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();
// Default flags
- int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH;
+ const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
// Multisig script (legacy counting)
{
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 40c53cb2ec..571f792a53 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -446,7 +446,7 @@ static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
}
-static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
+static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, uint32_t flags, bool success)
{
ScriptError error;
CTransaction inputi(input);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index d9d236be1d..5334c4623e 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -141,12 +141,11 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
- pblocktree.reset(new CBlockTreeDB(1 << 20, true));
-
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
m_node.chainman = std::make_unique<ChainstateManager>();
+ m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(1 << 20, true);
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
@@ -169,7 +168,6 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.scheduler.reset();
m_node.chainman->Reset();
m_node.chainman.reset();
- pblocktree.reset();
}
TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 2893b412fb..315ef22599 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;
+ WITH_LOCK(::cs_main, manager.m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(1 << 20, true));
CTxMemPool mempool;
//! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
diff --git a/src/tinyformat.h b/src/tinyformat.h
index bc893ccda5..bedaa14007 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -797,27 +797,27 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
break;
case 'X':
out.setf(std::ios::uppercase);
- // Falls through
+ [[fallthrough]];
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
- // Falls through
+ [[fallthrough]];
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
- // Falls through
+ [[fallthrough]];
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'A':
out.setf(std::ios::uppercase);
- // Falls through
+ [[fallthrough]];
case 'a':
# ifdef _MSC_VER
// Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html
@@ -829,7 +829,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
break;
case 'G':
out.setf(std::ios::uppercase);
- // Falls through
+ [[fallthrough]];
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index a0499fa51f..bb296456ba 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -132,28 +132,35 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
bool TorControlConnection::Connect(const std::string& tor_control_center, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
{
- if (b_conn)
+ if (b_conn) {
Disconnect();
- // Parse tor_control_center address:port
- struct sockaddr_storage connect_to_addr;
- int connect_to_addrlen = sizeof(connect_to_addr);
- if (evutil_parse_sockaddr_port(tor_control_center.c_str(),
- (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
+ }
+
+ CService control_service;
+ if (!Lookup(tor_control_center, control_service, 9051, fNameLookup)) {
+ LogPrintf("tor: Failed to look up control center %s\n", tor_control_center);
+ return false;
+ }
+
+ struct sockaddr_storage control_address;
+ socklen_t control_address_len = sizeof(control_address);
+ if (!control_service.GetSockAddr(reinterpret_cast<struct sockaddr*>(&control_address), &control_address_len)) {
LogPrintf("tor: Error parsing socket address %s\n", tor_control_center);
return false;
}
// Create a new socket, set up callbacks and enable notification bits
b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
- if (!b_conn)
+ if (!b_conn) {
return false;
+ }
bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr, TorControlConnection::eventcb, this);
bufferevent_enable(b_conn, EV_READ|EV_WRITE);
this->connected = _connected;
this->disconnected = _disconnected;
// Finally, connect to tor_control_center
- if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
+ if (bufferevent_socket_connect(b_conn, reinterpret_cast<struct sockaddr*>(&control_address), control_address_len) < 0) {
LogPrintf("tor: Error connecting to address %s\n", tor_control_center);
return false;
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 26333d7026..f81c27e8e3 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -170,8 +170,6 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
return chain.Genesis();
}
-std::unique_ptr<CBlockTreeDB> pblocktree;
-
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
@@ -2075,7 +2073,7 @@ bool CChainState::FlushStateToDisk(
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
if (!fHavePruned) {
- pblocktree->WriteFlag("prunedblockfiles", true);
+ m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);
fHavePruned = true;
}
}
@@ -2127,7 +2125,7 @@ bool CChainState::FlushStateToDisk(
vBlocks.push_back(*it);
setDirtyBlockIndex.erase(it++);
}
- if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
+ if (!m_blockman.m_block_tree_db->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
return AbortNode(state, "Failed to write to block index database");
}
}
@@ -3700,11 +3698,11 @@ CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
bool BlockManager::LoadBlockIndex(
const Consensus::Params& consensus_params,
- CBlockTreeDB& blocktree,
std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
{
- if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
+ if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
return false;
+ }
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
@@ -3764,25 +3762,25 @@ void BlockManager::Unload() {
m_block_index.clear();
}
-bool CChainState::LoadBlockIndexDB()
+bool BlockManager::LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkComparator>& setBlockIndexCandidates)
{
- if (!m_blockman.LoadBlockIndex(
- m_params.GetConsensus(), *pblocktree,
+ if (!LoadBlockIndex(
+ ::Params().GetConsensus(),
setBlockIndexCandidates)) {
return false;
}
// Load block file info
- pblocktree->ReadLastBlockFile(nLastBlockFile);
+ m_block_tree_db->ReadLastBlockFile(nLastBlockFile);
vinfoBlockFile.resize(nLastBlockFile + 1);
LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
- pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
+ m_block_tree_db->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
}
LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
for (int nFile = nLastBlockFile + 1; true; nFile++) {
CBlockFileInfo info;
- if (pblocktree->ReadBlockFileInfo(nFile, info)) {
+ if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
vinfoBlockFile.push_back(info);
} else {
break;
@@ -3792,7 +3790,7 @@ bool CChainState::LoadBlockIndexDB()
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : m_blockman.m_block_index) {
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
@@ -3807,13 +3805,13 @@ bool CChainState::LoadBlockIndexDB()
}
// Check whether we have ever pruned block & undo files
- pblocktree->ReadFlag("prunedblockfiles", fHavePruned);
+ m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
if (fHavePruned)
LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
// Check whether we need to continue reindexing
bool fReindexing = false;
- pblocktree->ReadReindexing(fReindexing);
+ m_block_tree_db->ReadReindexing(fReindexing);
if(fReindexing) fReindex = true;
return true;
@@ -4114,7 +4112,7 @@ bool ChainstateManager::LoadBlockIndex()
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = ActiveChainstate().LoadBlockIndexDB();
+ bool ret = m_blockman.LoadBlockIndexDB(ActiveChainstate().setBlockIndexCandidates);
if (!ret) return false;
needs_init = m_blockman.m_block_index.empty();
}
diff --git a/src/validation.h b/src/validation.h
index 9a2be3ad97..18a09d4aa3 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -446,6 +446,10 @@ public:
*/
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
+ std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
+
+ bool LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkComparator>& setBlockIndexCandidates) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
/**
* Load the blocktree off disk and into memory. Populate certain metadata
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
@@ -456,7 +460,6 @@ public:
*/
bool LoadBlockIndex(
const Consensus::Params& consensus_params,
- CBlockTreeDB& blocktree,
std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -798,8 +801,6 @@ private:
void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
//! Indirection necessary to make lock annotations work with an optional mempool.
RecursiveMutex* MempoolMutex() const LOCK_RETURNED(m_mempool->cs)
{
@@ -1047,9 +1048,6 @@ public:
}
};
-/** Global variable that points to the active block tree (protected by cs_main) */
-extern std::unique_ptr<CBlockTreeDB> pblocktree;
-
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
/** Dump the mempool to disk. */
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index ea97b339cf..ac60504419 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -740,7 +740,7 @@ RPCHelpMan dumpwallet()
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
- LOCK2(wallet.cs_wallet, spk_man.cs_KeyStore);
+ LOCK(wallet.cs_wallet);
EnsureWalletIsUnlocked(wallet);
@@ -762,9 +762,16 @@ RPCHelpMan dumpwallet()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
std::map<CKeyID, int64_t> mapKeyBirth;
- const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
wallet.GetKeyBirthTimes(mapKeyBirth);
+ int64_t block_time = 0;
+ CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
+
+ // Note: To avoid a lock order issue, access to cs_main must be locked before cs_KeyStore.
+ // So we do the two things in this function that lock cs_main first: GetKeyBirthTimes, and findBlock.
+ LOCK(spk_man.cs_KeyStore);
+
+ const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
std::set<CScriptID> scripts = spk_man.GetCScripts();
// sort time/key pairs
@@ -779,8 +786,6 @@ RPCHelpMan dumpwallet()
file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", wallet.GetLastBlockHeight(), wallet.GetLastBlockHash().ToString());
- int64_t block_time = 0;
- CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
file << strprintf("# mined on %s\n", FormatISO8601DateTime(block_time));
file << "\n";
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 572a695662..e329e0cf8f 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -207,7 +207,7 @@ public:
virtual bool CanGetAddresses(bool internal = false) const { return false; }
/** Upgrades the wallet to the specified version */
- virtual bool Upgrade(int prev_version, int new_version, bilingual_str& error) { return false; }
+ virtual bool Upgrade(int prev_version, int new_version, bilingual_str& error) { return true; }
virtual bool HavePrivateKeys() const { return false; }
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 27565aefc9..9a61ca698d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2298,44 +2298,48 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
- LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
- assert(spk_man != nullptr);
- LOCK(spk_man->cs_KeyStore);
-
- // get birth times for keys with metadata
- for (const auto& entry : spk_man->mapKeyMetadata) {
- if (entry.second.nCreateTime) {
- mapKeyBirth[entry.first] = entry.second.nCreateTime;
- }
- }
-
// map in which we'll infer heights of other keys
std::map<CKeyID, const CWalletTx::Confirmation*> mapKeyFirstBlock;
CWalletTx::Confirmation max_confirm;
max_confirm.block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.block_height, FoundBlock().hash(max_confirm.hashBlock)));
- for (const CKeyID &keyid : spk_man->GetKeys()) {
- if (mapKeyBirth.count(keyid) == 0)
- mapKeyFirstBlock[keyid] = &max_confirm;
- }
- // if there are no such keys, we're done
- if (mapKeyFirstBlock.empty())
- return;
+ {
+ LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
+ assert(spk_man != nullptr);
+ LOCK(spk_man->cs_KeyStore);
+
+ // get birth times for keys with metadata
+ for (const auto& entry : spk_man->mapKeyMetadata) {
+ if (entry.second.nCreateTime) {
+ mapKeyBirth[entry.first] = entry.second.nCreateTime;
+ }
+ }
+
+ // Prepare to infer birth heights for keys without metadata
+ for (const CKeyID &keyid : spk_man->GetKeys()) {
+ if (mapKeyBirth.count(keyid) == 0)
+ mapKeyFirstBlock[keyid] = &max_confirm;
+ }
- // find first block that affects those keys, if there are any left
- for (const auto& entry : mapWallet) {
- // iterate over all wallet transactions...
- const CWalletTx &wtx = entry.second;
- if (wtx.m_confirm.status == CWalletTx::CONFIRMED) {
- // ... which are already in a block
- for (const CTxOut &txout : wtx.tx->vout) {
- // iterate over all their outputs
- for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) {
- // ... and all their affected keys
- auto rit = mapKeyFirstBlock.find(keyid);
- if (rit != mapKeyFirstBlock.end() && wtx.m_confirm.block_height < rit->second->block_height) {
- rit->second = &wtx.m_confirm;
+ // if there are no such keys, we're done
+ if (mapKeyFirstBlock.empty())
+ return;
+
+ // find first block that affects those keys, if there are any left
+ for (const auto& entry : mapWallet) {
+ // iterate over all wallet transactions...
+ const CWalletTx &wtx = entry.second;
+ if (wtx.m_confirm.status == CWalletTx::CONFIRMED) {
+ // ... which are already in a block
+ for (const CTxOut &txout : wtx.tx->vout) {
+ // iterate over all their outputs
+ for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) {
+ // ... and all their affected keys
+ auto rit = mapKeyFirstBlock.find(keyid);
+ if (rit != mapKeyFirstBlock.end() && wtx.m_confirm.block_height < rit->second->block_height) {
+ rit->second = &wtx.m_confirm;
+ }
}
}
}