aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.qt.include7
-rw-r--r--src/Makefile.test.include3
-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.cpp45
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/consensus/tx_verify.cpp2
-rw-r--r--src/consensus/tx_verify.h4
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp10
-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_processing.cpp24
-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/test/test_main.cpp1
-rw-r--r--src/rpc/blockchain.cpp27
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/script/interpreter.cpp10
-rw-r--r--src/script/interpreter.h7
-rw-r--r--src/test/addrman_tests.cpp119
-rw-r--r--src/test/fuzz/addrman.cpp13
-rw-r--r--src/test/fuzz/banman.cpp3
-rw-r--r--src/test/fuzz/coins_view.cpp2
-rw-r--r--src/test/fuzz/prevector.cpp6
-rw-r--r--src/test/fuzz/process_message.cpp12
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp17
-rw-r--r--src/test/fuzz/tx_pool.cpp12
-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/txvalidationcache_tests.cpp11
-rw-r--r--src/test/util/setup_common.cpp4
-rw-r--r--src/test/validation_chainstate_tests.cpp1
-rw-r--r--src/torcontrol.cpp23
-rw-r--r--src/util/epochguard.h10
-rw-r--r--src/validation.cpp98
-rw-r--r--src/validation.h21
-rw-r--r--src/wallet/rpcdump.cpp13
-rw-r--r--src/wallet/test/spend_tests.cpp61
-rw-r--r--src/wallet/test/util.cpp38
-rw-r--r--src/wallet/test/util.h19
-rw-r--r--src/wallet/test/wallet_tests.cpp16
-rw-r--r--src/wallet/wallet.cpp66
58 files changed, 800 insertions, 636 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 407fdf5a8f..a8d6591e98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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.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/Makefile.test.include b/src/Makefile.test.include
index fc2fd80166..a07a1bb002 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -152,6 +152,7 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/psbt_wallet_tests.cpp \
+ wallet/test/spend_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/walletdb_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
@@ -170,6 +171,8 @@ endif
BITCOIN_TEST_SUITE += \
+ wallet/test/util.cpp \
+ wallet/test/util.h \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
wallet/test/init_test_fixture.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..a8b45685d0 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");
@@ -490,11 +490,8 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
{
if (args.IsArgSet("-segwitheight")) {
int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight);
- if (height < -1 || height >= std::numeric_limits<int>::max()) {
- throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range. Use -1 to disable segwit.", height));
- } else if (height == -1) {
- LogPrintf("Segwit disabled for testing\n");
- height = std::numeric_limits<int>::max();
+ if (height < 0 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range.", height));
}
consensus.SegwitHeight = static_cast<int>(height);
}
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index e71b4bc859..79c1bc25bc 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
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/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..75394d96b1 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();
@@ -716,7 +715,7 @@ namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
-ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
+ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED | NODE_WITNESS);
int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types;
@@ -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();
@@ -1588,12 +1588,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
- if (DeploymentEnabled(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
- // Advertise witness capabilities.
- // The option to not set NODE_WITNESS is only used in the tests and should be removed.
- nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
- }
-
// ********************************************************* Step 11: import blocks
if (!CheckDiskSpace(gArgs.GetDataDirNet())) {
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_processing.cpp b/src/net_processing.cpp
index d23e546330..70eac468f3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -877,7 +877,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
}
m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
- uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
+ uint64_t nCMPCTBLOCKVersion = 2;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
@@ -1979,7 +1979,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
uint32_t nFetchFlags = 0;
- if ((pfrom.GetLocalServices() & NODE_WITNESS) && State(pfrom.GetId())->fHaveWitness) {
+ if (State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
@@ -2694,8 +2694,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom.GetLocalServices() & NODE_WITNESS)
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
@@ -2713,7 +2712,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ if (nCMPCTBLOCKVersion == 1 || nCMPCTBLOCKVersion == 2) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
@@ -2727,10 +2726,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.m_bip152_highbandwidth_from = fAnnounceUsingCMPCTBLOCK;
}
if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
- if (pfrom.GetLocalServices() & NODE_WITNESS)
- State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
- else
- State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
}
}
return;
@@ -2866,7 +2862,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
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;
}
@@ -4450,6 +4448,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/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/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/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/net.cpp b/src/rpc/net.cpp
index 083ba31b71..dba0f971b2 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -339,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, "", "",
@@ -365,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/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 617dfec98f..6dfccd9023 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -74,12 +74,10 @@ static RPCHelpMan getrawtransaction()
"getrawtransaction",
"\nReturn the raw transaction data.\n"
- "\nBy default this function only works for mempool transactions. When called with a blockhash\n"
- "argument, getrawtransaction will return the transaction if the specified block is available and\n"
- "the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n"
- "will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n"
- "is in a block in the blockchain.\n"
-
+ "\nBy default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n"
+ "and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block.\n"
+ "If -txindex is not enabled and a blockhash argument is passed, it will return the transaction if\n"
+ "the specified block is available and the transaction is found in that block.\n"
"\nHint: Use gettransaction for wallet transactions.\n"
"\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
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..93136a0b79 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).
@@ -140,6 +139,10 @@ enum
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
+
+ // Constants to point to the highest flag in use. Add new flags above this line.
+ //
+ SCRIPT_VERIFY_END_MARKER
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
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..92c34e74d9 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -44,6 +44,17 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
addr_man.m_asmap.clear();
}
}
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
+ const auto ser_version{fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ ds.SetVersion(ser_version);
+ try {
+ ds >> addr_man;
+ } catch (const std::ios_base::failure&) {
+ addr_man.Clear();
+ }
+ }
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
@@ -80,7 +91,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/banman.cpp b/src/test/fuzz/banman.cpp
index 182aabc79b..85a2ac990c 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -109,7 +109,8 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
BanMan ban_man_read{banlist_file, /* client_interface */ nullptr, /* default_ban_time */ 0};
banmap_t banmap_read;
ban_man_read.GetBanned(banmap_read);
- assert(banmap == banmap_read);
+ // Temporarily disabled to allow the remainder of the fuzz test to run while a fix is being worked on:
+ // assert(banmap == banmap_read);
}
}
fs::remove(banlist_file.string() + ".dat");
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/prevector.cpp b/src/test/fuzz/prevector.cpp
index 51956bbe9e..447f32ed16 100644
--- a/src/test/fuzz/prevector.cpp
+++ b/src/test/fuzz/prevector.cpp
@@ -206,10 +206,14 @@ public:
FUZZ_TARGET(prevector)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{3000};
+
FuzzedDataProvider prov(buffer.data(), buffer.size());
prevector_tester<8, int> test;
- while (prov.remaining_bytes()) {
+ while (--limit_max_ops >= 0 && prov.remaining_bytes()) {
switch (prov.ConsumeIntegralInRange<int>(0, 13 + 3 * (test.size() > 0))) {
case 0:
test.insert(prov.ConsumeIntegralInRange<size_t>(0, test.size()), prov.ConsumeIntegral<int>());
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/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 07059cce76..3b33115e72 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -16,12 +16,16 @@
FUZZ_TARGET(rolling_bloom_filter)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{3000};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
CRollingBloomFilter rolling_bloom_filter{
fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 1000),
0.999 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max())};
- while (fuzzed_data_provider.remaining_bytes() > 0) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.remaining_bytes() > 0) {
CallOneOf(
fuzzed_data_provider,
[&] {
@@ -32,13 +36,10 @@ FUZZ_TARGET(rolling_bloom_filter)
assert(present);
},
[&] {
- const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
- if (!u256) {
- return;
- }
- (void)rolling_bloom_filter.contains(*u256);
- rolling_bloom_filter.insert(*u256);
- const bool present = rolling_bloom_filter.contains(*u256);
+ const uint256 u256{ConsumeUInt256(fuzzed_data_provider)};
+ (void)rolling_bloom_filter.contains(u256);
+ rolling_bloom_filter.insert(u256);
+ const bool present = rolling_bloom_filter.contains(u256);
assert(present);
},
[&] {
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index bab34ea340..dadf772bc1 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -112,6 +112,10 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const CChainState& chain
FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{300};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
@@ -142,7 +146,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
return c.out.nValue;
};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
{
// Total supply is the mempool fee + all outpoints
CAmount supply_now{WITH_LOCK(tx_pool.cs, return tx_pool.GetTotalFee())};
@@ -285,6 +289,10 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{300};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
@@ -305,7 +313,7 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
CTxMemPool tx_pool_{/* estimator */ nullptr, /* check_ratio */ 1};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
if (fuzzed_data_provider.ConsumeBool()) {
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/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 23195c0a26..1924ea55b1 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -112,10 +112,15 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
- // If we add many more flags, this loop can get too expensive, but we can
- // rewrite in the future to randomly pick a set of flags to evaluate.
- for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) {
+
+ FastRandomContext insecure_rand(true);
+
+ for (int count = 0; count < 10000; ++count) {
TxValidationState state;
+
+ // Randomly selects flag combinations
+ uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1);
+
// Filter out incompatible flag choices
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
// CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in
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/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/util/epochguard.h b/src/util/epochguard.h
index 1570ec4eb4..3e63e093da 100644
--- a/src/util/epochguard.h
+++ b/src/util/epochguard.h
@@ -40,6 +40,9 @@ public:
Epoch() = default;
Epoch(const Epoch&) = delete;
Epoch& operator=(const Epoch&) = delete;
+ Epoch(Epoch&&) = delete;
+ Epoch& operator=(Epoch&&) = delete;
+ ~Epoch() = default;
bool guarded() const { return m_guarded; }
@@ -51,6 +54,13 @@ public:
// only allow modification via Epoch member functions
friend class Epoch;
Marker& operator=(const Marker&) = delete;
+
+ public:
+ Marker() = default;
+ Marker(const Marker&) = default;
+ Marker(Marker&&) = delete;
+ Marker& operator=(Marker&&) = delete;
+ ~Marker() = default;
};
class SCOPED_LOCKABLE Guard
diff --git a/src/validation.cpp b/src/validation.cpp
index 26333d7026..4e7bc635da 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,
@@ -1160,6 +1158,20 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
{
LOCK(cs_main);
+ if (mempool && !block_index) {
+ CTransactionRef ptx = mempool->get(hash);
+ if (ptx) return ptx;
+ }
+ if (g_txindex) {
+ CTransactionRef tx;
+ uint256 block_hash;
+ if (g_txindex->FindTx(hash, block_hash, tx)) {
+ if (!block_index || block_index->GetBlockHash() == block_hash) {
+ hashBlock = block_hash;
+ return tx;
+ }
+ }
+ }
if (block_index) {
CBlock block;
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
@@ -1170,15 +1182,6 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
}
}
}
- return nullptr;
- }
- if (mempool) {
- CTransactionRef ptx = mempool->get(hash);
- if (ptx) return ptx;
- }
- if (g_txindex) {
- CTransactionRef tx;
- if (g_txindex->FindTx(hash, hashBlock, tx)) return tx;
}
return nullptr;
}
@@ -1647,13 +1650,8 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity()
*pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception
{
- flags |= SCRIPT_VERIFY_P2SH;
- }
-
- // Enforce WITNESS rules whenever P2SH is in effect (and the segwit
- // deployment is defined).
- if (flags & SCRIPT_VERIFY_P2SH && DeploymentEnabled(consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
- flags |= SCRIPT_VERIFY_WITNESS;
+ // Enforce WITNESS rules whenever P2SH is in effect
+ flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS;
}
// Enforce the DERSIG (BIP66) rule
@@ -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");
}
}
@@ -3103,25 +3101,23 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
- if (DeploymentEnabled(consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
- if (commitpos == NO_WITNESS_COMMITMENT) {
- uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
- CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
- CTxOut out;
- out.nValue = 0;
- out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
- out.scriptPubKey[0] = OP_RETURN;
- out.scriptPubKey[1] = 0x24;
- out.scriptPubKey[2] = 0xaa;
- out.scriptPubKey[3] = 0x21;
- out.scriptPubKey[4] = 0xa9;
- out.scriptPubKey[5] = 0xed;
- memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
- commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
- CMutableTransaction tx(*block.vtx[0]);
- tx.vout.push_back(out);
- block.vtx[0] = MakeTransactionRef(std::move(tx));
- }
+ if (commitpos == NO_WITNESS_COMMITMENT) {
+ uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
+ CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
+ CTxOut out;
+ out.nValue = 0;
+ out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
+ out.scriptPubKey[0] = OP_RETURN;
+ out.scriptPubKey[1] = 0x24;
+ out.scriptPubKey[2] = 0xaa;
+ out.scriptPubKey[3] = 0x21;
+ out.scriptPubKey[4] = 0xa9;
+ out.scriptPubKey[5] = 0xed;
+ memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
+ commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
+ CMutableTransaction tx(*block.vtx[0]);
+ tx.vout.push_back(out);
+ block.vtx[0] = MakeTransactionRef(std::move(tx));
}
UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
return commitment;
@@ -3700,11 +3696,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 +3760,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 +3788,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 +3803,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 +4110,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..d3e4f3b983 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -141,15 +141,16 @@ void StartScriptCheckWorkerThreads(int threads_num);
/** Stop all of the script checking worker threads */
void StopScriptCheckWorkerThreads();
/**
- * Return transaction from the block at block_index.
- * If block_index is not provided, fall back to mempool.
- * If mempool is not provided or the tx couldn't be found in mempool, fall back to g_txindex.
+ * Return transaction with a given hash.
+ * If mempool is provided and block_index is not provided, check it first for the tx.
+ * If -txindex is available, check it next for the tx.
+ * Finally, if block_index is provided, check for tx by reading entire block from disk.
*
* @param[in] block_index The block to read from disk, or nullptr
- * @param[in] mempool If block_index is not provided, look in the mempool, if provided
+ * @param[in] mempool If provided, check mempool for tx
* @param[in] hash The txid
* @param[in] consensusParams The params
- * @param[out] hashBlock The hash of block_index, if the tx was found via block_index
+ * @param[out] hashBlock The block hash, if the tx was found via -txindex or block_index
* @returns The tx if found, otherwise nullptr
*/
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock);
@@ -446,6 +447,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 +461,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 +802,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 +1049,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/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
new file mode 100644
index 0000000000..66e7de4273
--- /dev/null
+++ b/src/wallet/test/spend_tests.cpp
@@ -0,0 +1,61 @@
+// 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.
+
+#include <policy/fees.h>
+#include <validation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/test/util.h>
+#include <wallet/test/wallet_test_fixture.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)
+
+BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
+{
+ CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+ auto wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
+
+ // Check that a subtract-from-recipient transaction slightly less than the
+ // coinbase input amount does not create a change output (because it would
+ // be uneconomical to add and spend the output), and make sure it pays the
+ // leftover input amount which would have been change to the recipient
+ // instead of the miner.
+ auto check_tx = [&wallet](CAmount leftover_input_amount) {
+ CRecipient recipient{GetScriptForRawPubKey({}), 50 * COIN - leftover_input_amount, true /* subtract fee */};
+ CTransactionRef tx;
+ CAmount fee;
+ int change_pos = -1;
+ bilingual_str error;
+ CCoinControl coin_control;
+ coin_control.m_feerate.emplace(10000);
+ coin_control.fOverrideFeeRate = true;
+ FeeCalculation fee_calc;
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
+ BOOST_CHECK_EQUAL(tx->vout.size(), 1);
+ BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
+ BOOST_CHECK_GT(fee, 0);
+ return fee;
+ };
+
+ // Send full input amount to recipient, check that only nonzero fee is
+ // subtracted (to_reduce == fee).
+ const CAmount fee{check_tx(0)};
+
+ // Send slightly less than full input amount to recipient, check leftover
+ // input amount is paid to recipient not the miner (to_reduce == fee - 123)
+ BOOST_CHECK_EQUAL(fee, check_tx(123));
+
+ // Send full input minus fee amount to recipient, check leftover input
+ // amount is paid to recipient not the miner (to_reduce == 0)
+ BOOST_CHECK_EQUAL(fee, check_tx(fee));
+
+ // Send full input minus more than the fee amount to recipient, check
+ // leftover input amount is paid to recipient not the miner (to_reduce ==
+ // -123). This overpays the recipient instead of overpaying the miner more
+ // than double the neccesary fee.
+ BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
new file mode 100644
index 0000000000..c3061b93c0
--- /dev/null
+++ b/src/wallet/test/util.cpp
@@ -0,0 +1,38 @@
+// 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.
+
+#include <wallet/test/util.h>
+
+#include <chain.h>
+#include <key.h>
+#include <test/util/setup_common.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <memory>
+
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
+{
+ auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockWalletDatabase());
+ {
+ LOCK2(wallet->cs_wallet, ::cs_main);
+ wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
+ }
+ wallet->LoadWallet();
+ {
+ auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
+ LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
+ spk_man->AddKeyPubKey(key, key.GetPubKey());
+ }
+ WalletRescanReserver reserver(*wallet);
+ reserver.reserve();
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
+ BOOST_CHECK_EQUAL(result.last_scanned_block, cchain.Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, cchain.Height());
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ return wallet;
+}
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
new file mode 100644
index 0000000000..288c111571
--- /dev/null
+++ b/src/wallet/test/util.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_WALLET_TEST_UTIL_H
+#define BITCOIN_WALLET_TEST_UTIL_H
+
+#include <memory>
+
+class CChain;
+class CKey;
+class CWallet;
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
+
+#endif // BITCOIN_WALLET_TEST_UTIL_H
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index a0070b8dd3..75a08b6f74 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -20,6 +20,7 @@
#include <util/translation.h>
#include <validation.h>
#include <wallet/coincontrol.h>
+#include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
@@ -480,20 +481,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- {
- LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- }
- wallet->LoadWallet();
- AddKey(*wallet, coinbaseKey);
- WalletRescanReserver reserver(*wallet);
- reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
- BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
- BOOST_CHECK(result.last_failed_block.IsNull());
+ wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
}
~ListCoinsTestingSetup()
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;
+ }
}
}
}