aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml19
-rwxr-xr-xci/test/04_install.sh2
-rw-r--r--contrib/verify-binaries/README.md2
-rw-r--r--src/bitcoind.cpp68
-rw-r--r--src/index/base.cpp6
-rw-r--r--src/init.cpp4
-rw-r--r--src/init.h2
-rw-r--r--src/interfaces/node.h3
-rw-r--r--src/net_processing.cpp57
-rw-r--r--src/node/blockstorage.cpp3
-rw-r--r--src/node/context.h3
-rw-r--r--src/node/interfaces.cpp8
-rw-r--r--src/node/miner.h3
-rw-r--r--src/qt/bitcoin.cpp11
-rw-r--r--src/qt/bitcoin.h4
-rw-r--r--src/shutdown.cpp7
-rw-r--r--src/shutdown.h4
-rw-r--r--src/txmempool.h3
-rw-r--r--src/txrequest.cpp6
-rw-r--r--src/validation.cpp9
-rw-r--r--src/validation.h2
-rw-r--r--src/wallet/test/coinselector_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp5
-rw-r--r--src/wallet/test/walletdb_tests.cpp2
-rw-r--r--src/wallet/test/walletload_tests.cpp2
-rwxr-xr-xtest/functional/feature_abortnode.py2
-rwxr-xr-xtest/functional/p2p_leak_tx.py33
-rwxr-xr-xtest/functional/test_framework/test_node.py16
-rwxr-xr-xtest/lint/lint-includes.py7
29 files changed, 165 insertions, 130 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 6af5638f8f..3090deef78 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -248,6 +248,7 @@ task:
docker_arguments:
CI_IMAGE_NAME_TAG: ubuntu:lunar
FILE_ENV: "./ci/test/00_setup_env_native_tsan.sh"
+ << : *CREDITS_TEMPLATE
env:
<< : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
@@ -348,21 +349,3 @@ task:
CI_USE_APT_INSTALL: "no"
PACKAGE_MANAGER_INSTALL: "echo" # Nothing to do
FILE_ENV: "./ci/test/00_setup_env_mac_native_arm64.sh"
-
-task:
- name: 'ARM64 Android APK [jammy]'
- << : *CONTAINER_DEPENDS_TEMPLATE
- container:
- docker_arguments:
- CI_IMAGE_NAME_TAG: ubuntu:jammy
- FILE_ENV: "./ci/test/00_setup_env_android.sh"
- << : *CREDITS_TEMPLATE
- android_sdk_cache:
- folder: "depends/SDKs/android"
- fingerprint_key: "ANDROID_API_LEVEL=28 ANDROID_BUILD_TOOLS_VERSION=28.0.3 ANDROID_NDK_VERSION=23.2.8568313"
- depends_sources_cache:
- folder: "depends/sources"
- fingerprint_script: git rev-parse HEAD:depends/packages
- << : *MAIN_TEMPLATE
- env:
- << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index 2a5689e251..626461df03 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -42,7 +42,7 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
if [ -n "${RESTART_CI_DOCKER_BEFORE_RUN}" ] ; then
echo "Restart docker before run to stop and clear all containers started with --rm"
- podman container kill --all # Similar to "systemctl restart docker"
+ podman container stop --all # Similar to "systemctl restart docker"
echo "Prune all dangling images"
docker image prune --force
fi
diff --git a/contrib/verify-binaries/README.md b/contrib/verify-binaries/README.md
index c62d760e1a..04d683e69b 100644
--- a/contrib/verify-binaries/README.md
+++ b/contrib/verify-binaries/README.md
@@ -17,7 +17,7 @@ must obtain that key for your local GPG installation.
You can obtain these keys by
- through a browser using a key server (e.g. keyserver.ubuntu.com),
- manually using the `gpg --keyserver <url> --recv-keys <key>` command, or
- - you can run the packaged `verify.py ... --import-keys` script to
+ - you can run the packaged `verify.py --import-keys ...` script to
have it automatically retrieve unrecognized keys.
#### Usage
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index aefb130e9c..c561f9aa14 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -112,20 +112,30 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
#endif
-static bool AppInit(NodeContext& node, int argc, char* argv[])
+static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
{
- bool fRet = false;
-
- util::ThreadSetInternalName("init");
-
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
- ArgsManager& args = *Assert(node.args);
SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
}
+ if (auto error = common::InitConfig(args)) {
+ return InitError(error->message, error->details);
+ }
+
+ // Error out when loose non-argument tokens are encountered on command line
+ for (int i = 1; i < argc; i++) {
+ if (!IsSwitchChar(argv[i][0])) {
+ return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
+ }
+ }
+ return true;
+}
+
+static bool ProcessInitCommands(ArgsManager& args)
+{
// Process help and version before taking care about datadir
if (HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
@@ -142,6 +152,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
return true;
}
+ return false;
+}
+
+static bool AppInit(NodeContext& node)
+{
+ bool fRet = false;
+ ArgsManager& args = *Assert(node.args);
+
#if HAVE_DECL_FORK
// Communication with parent after daemonizing. This is used for signalling in the following ways:
// - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
@@ -153,23 +171,12 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
std::any context{&node};
try
{
- if (auto error = common::InitConfig(args)) {
- return InitError(error->message, error->details);
- }
-
- // Error out when loose non-argument tokens are encountered on command line
- for (int i = 1; i < argc; i++) {
- if (!IsSwitchChar(argv[i][0])) {
- return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
- }
- }
-
// -server defaults to true for bitcoind but not for the GUI so do this here
args.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
InitLogging(args);
InitParameterInteraction(args);
- if (!AppInitBasicSetup(args)) {
+ if (!AppInitBasicSetup(args, node.exit_status)) {
// InitError will have been called with detailed error, which ends up on console
return false;
}
@@ -236,12 +243,6 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
}
#endif
SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
- if (fRet) {
- WaitForShutdown();
- }
- Interrupt(node);
- Shutdown(node);
-
return fRet;
}
@@ -264,5 +265,22 @@ MAIN_FUNCTION
// Connect bitcoind signal handlers
noui_connect();
- return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
+ util::ThreadSetInternalName("init");
+
+ // Interpret command line arguments
+ ArgsManager& args = *Assert(node.args);
+ if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE;
+ // Process early info return commands such as -help or -version
+ if (ProcessInitCommands(args)) return EXIT_SUCCESS;
+
+ // Start application
+ if (AppInit(node)) {
+ WaitForShutdown();
+ } else {
+ node.exit_status = EXIT_FAILURE;
+ }
+ Interrupt(node);
+ Shutdown(node);
+
+ return node.exit_status;
}
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3f91910db2..a713be3480 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -33,11 +33,7 @@ constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
template <typename... Args>
static void FatalError(const char* fmt, const Args&... args)
{
- std::string strMessage = tfm::format(fmt, args...);
- SetMiscWarning(Untranslated(strMessage));
- LogPrintf("*** %s\n", strMessage);
- InitError(_("A fatal internal error occurred, see debug.log for details"));
- StartShutdown();
+ AbortNode(tfm::format(fmt, args...));
}
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
diff --git a/src/init.cpp b/src/init.cpp
index e8c804b9a7..38e1dbb4a2 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -800,7 +800,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
std::terminate();
};
-bool AppInitBasicSetup(const ArgsManager& args)
+bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
@@ -814,7 +814,7 @@ bool AppInitBasicSetup(const ArgsManager& args)
// Enable heap terminate-on-corruption
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
#endif
- if (!InitShutdownState()) {
+ if (!InitShutdownState(exit_status)) {
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
}
diff --git a/src/init.h b/src/init.h
index 8c5b2e77d3..fabb4f66d6 100644
--- a/src/init.h
+++ b/src/init.h
@@ -38,7 +38,7 @@ void InitParameterInteraction(ArgsManager& args);
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
-bool AppInitBasicSetup(const ArgsManager& args);
+bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status);
/**
* Initialization: parameter interaction.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 479c585b88..3f8df57124 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -80,6 +80,9 @@ public:
//! Get warnings.
virtual bilingual_str getWarnings() = 0;
+ //! Get exit status.
+ virtual int getExitStatus() = 0;
+
// Get log flags.
virtual uint32_t getLogCategories() = 0;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 51bcaf6d73..6597019797 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -51,9 +51,7 @@
#include <optional>
#include <typeinfo>
-/** How long to cache transactions in mapRelay for normal relay */
-static constexpr auto RELAY_TX_CACHE_TIME = 15min;
-/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
+/** How long a transaction has to be in the mempool before it can unconditionally be relayed. */
static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
/** Headers download timeout.
* Timeout = base + per_header * (expected number of headers) */
@@ -851,6 +849,7 @@ private:
std::shared_ptr<const CBlock> m_most_recent_block GUARDED_BY(m_most_recent_block_mutex);
std::shared_ptr<const CBlockHeaderAndShortTxIDs> m_most_recent_compact_block GUARDED_BY(m_most_recent_block_mutex);
uint256 m_most_recent_block_hash GUARDED_BY(m_most_recent_block_mutex);
+ std::unique_ptr<const std::map<uint256, CTransactionRef>> m_most_recent_block_txs GUARDED_BY(m_most_recent_block_mutex);
// Data about the low-work headers synchronization, aggregated from all peers' HeadersSyncStates.
/** Mutex guarding the other m_headers_presync_* variables. */
@@ -910,7 +909,7 @@ private:
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
CTransactionRef FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now)
- EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, NetEventsInterface::g_msgproc_mutex);
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, peer.m_getdata_requests_mutex, NetEventsInterface::g_msgproc_mutex)
@@ -919,12 +918,6 @@ private:
/** Process a new block. Perform any post-processing housekeeping */
void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked);
- /** Relay map (txid or wtxid -> CTransactionRef) */
- typedef std::map<uint256, CTransactionRef> MapRelay;
- MapRelay mapRelay GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
- /** Expiration-time ordered list of (expire time, relay map entry) pairs. */
- std::deque<std::pair<std::chrono::microseconds, MapRelay::iterator>> g_relay_expiration GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
-
/**
* When a peer sends us a valid block, instruct it to announce blocks to us
* using CMPCTBLOCK if possible by adding its nodeid to the end of
@@ -1927,10 +1920,17 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
std::async(std::launch::deferred, [&] { return msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock); })};
{
+ auto most_recent_block_txs = std::make_unique<std::map<uint256, CTransactionRef>>();
+ for (const auto& tx : pblock->vtx) {
+ most_recent_block_txs->emplace(tx->GetHash(), tx);
+ most_recent_block_txs->emplace(tx->GetWitnessHash(), tx);
+ }
+
LOCK(m_most_recent_block_mutex);
m_most_recent_block_hash = hashBlock;
m_most_recent_block = pblock;
m_most_recent_compact_block = pcmpctblock;
+ m_most_recent_block_txs = std::move(most_recent_block_txs);
}
m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
@@ -2301,13 +2301,17 @@ CTransactionRef PeerManagerImpl::FindTxForGetData(const Peer::TxRelay& tx_relay,
}
}
- // Otherwise, the transaction must have been announced recently.
- if (tx_relay.m_recently_announced_invs.contains(gtxid.GetHash())) {
- // If it was, it can be relayed from either the mempool...
- if (txinfo.tx) return std::move(txinfo.tx);
- // ... or the relay pool.
- auto mi = mapRelay.find(gtxid.GetHash());
- if (mi != mapRelay.end()) return mi->second;
+ // Otherwise, the transaction might have been announced recently.
+ bool recent = tx_relay.m_recently_announced_invs.contains(gtxid.GetHash());
+ if (recent && txinfo.tx) return std::move(txinfo.tx);
+
+ // Or it might be from the most recent block
+ {
+ LOCK(m_most_recent_block_mutex);
+ if (m_most_recent_block_txs != nullptr) {
+ auto it = m_most_recent_block_txs->find(gtxid.GetHash());
+ if (it != m_most_recent_block_txs->end()) return it->second;
+ }
}
return {};
@@ -5778,7 +5782,6 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
continue;
}
auto txid = txinfo.tx->GetHash();
- auto wtxid = txinfo.tx->GetWitnessHash();
// Peer told you to not send transactions at that feerate? Don't bother sending it.
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
continue;
@@ -5788,24 +5791,6 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
tx_relay->m_recently_announced_invs.insert(hash);
vInv.push_back(inv);
nRelayedTransactions++;
- {
- // Expire old relay messages
- while (!g_relay_expiration.empty() && g_relay_expiration.front().first < current_time)
- {
- mapRelay.erase(g_relay_expiration.front().second);
- g_relay_expiration.pop_front();
- }
-
- auto ret = mapRelay.emplace(txid, std::move(txinfo.tx));
- if (ret.second) {
- g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret.first);
- }
- // Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
- auto ret2 = mapRelay.emplace(wtxid, ret.first->second);
- if (ret2.second) {
- g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret2.first);
- }
- }
if (vInv.size() == MAX_INV_SZ) {
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index b7afa8a7c3..1368ae6f6d 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -928,8 +928,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) {
- LogPrintf("Failed to connect best block (%s)\n", state.ToString());
- StartShutdown();
+ AbortNode(strprintf("Failed to connect best block (%s)", state.ToString()));
return;
}
}
diff --git a/src/node/context.h b/src/node/context.h
index 9532153cdb..91b68fa5bb 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -7,7 +7,9 @@
#include <kernel/context.h>
+#include <atomic>
#include <cassert>
+#include <cstdlib>
#include <functional>
#include <memory>
#include <vector>
@@ -65,6 +67,7 @@ struct NodeContext {
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};
std::unique_ptr<KernelNotifications> notifications;
+ std::atomic<int> exit_status{EXIT_SUCCESS};
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the NodeContext struct doesn't need to #include class
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 57c0d302fd..94b607b1de 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -89,10 +89,11 @@ public:
void initLogging() override { InitLogging(args()); }
void initParameterInteraction() override { InitParameterInteraction(args()); }
bilingual_str getWarnings() override { return GetWarnings(true); }
+ int getExitStatus() override { return Assert(m_context)->exit_status.load(); }
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
- if (!AppInitBasicSetup(args())) return false;
+ if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false;
if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false;
m_context->kernel = std::make_unique<kernel::Context>();
@@ -105,7 +106,10 @@ public:
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
- return AppInitMain(*m_context, tip_info);
+ if (AppInitMain(*m_context, tip_info)) return true;
+ // Error during initialization, set exit status before continue
+ m_context->exit_status.store(EXIT_FAILURE);
+ return false;
}
void appShutdown() override
{
diff --git a/src/node/miner.h b/src/node/miner.h
index f1ccffff55..70de9e1db0 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -14,7 +14,10 @@
#include <optional>
#include <stdint.h>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
class ArgsManager;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index e33753f5e7..8f45af9485 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -9,6 +9,7 @@
#include <qt/bitcoin.h>
#include <chainparams.h>
+#include <node/context.h>
#include <common/args.h>
#include <common/init.h>
#include <common/system.h>
@@ -397,9 +398,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
{
qDebug() << __func__ << ": Initialization result: " << success;
- // Set exit result.
- returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
- if(success) {
+ if (success) {
delete m_splash;
m_splash = nullptr;
@@ -653,7 +652,6 @@ int GuiMain(int argc, char* argv[])
app.InitPruneSetting(prune_MiB);
}
- int rv = EXIT_SUCCESS;
try
{
app.createWindow(networkStyle.data());
@@ -666,10 +664,9 @@ int GuiMain(int argc, char* argv[])
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
- rv = app.getReturnValue();
} else {
// A dialog with detailed error will have been shown by InitError()
- rv = EXIT_FAILURE;
+ return EXIT_FAILURE;
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
@@ -678,5 +675,5 @@ int GuiMain(int argc, char* argv[])
PrintExceptionContinue(nullptr, "Runaway exception");
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
}
- return rv;
+ return app.node().getExitStatus();
}
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 9174e23de6..9622c9d57d 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -62,9 +62,6 @@ public:
/// Request core initialization
void requestInitialize();
- /// Get process return value
- int getReturnValue() const { return returnValue; }
-
/// Get window identifier of QMainWindow (BitcoinGUI)
WId getMainWinId() const;
@@ -104,7 +101,6 @@ private:
PaymentServer* paymentServer{nullptr};
WalletController* m_wallet_controller{nullptr};
#endif
- int returnValue{0};
const PlatformStyle* platformStyle{nullptr};
std::unique_ptr<QWidget> shutdownWindow;
SplashScreen* m_splash = nullptr;
diff --git a/src/shutdown.cpp b/src/shutdown.cpp
index 2fffc0663c..d70017d734 100644
--- a/src/shutdown.cpp
+++ b/src/shutdown.cpp
@@ -11,6 +11,7 @@
#include <logging.h>
#include <node/interface_ui.h>
+#include <util/check.h>
#include <util/tokenpipe.h>
#include <warnings.h>
@@ -20,6 +21,8 @@
#include <condition_variable>
#endif
+static std::atomic<int>* g_exit_status{nullptr};
+
bool AbortNode(const std::string& strMessage, bilingual_str user_message)
{
SetMiscWarning(Untranslated(strMessage));
@@ -28,6 +31,7 @@ bool AbortNode(const std::string& strMessage, bilingual_str user_message)
user_message = _("A fatal internal error occurred, see debug.log for details");
}
InitError(user_message);
+ Assert(g_exit_status)->store(EXIT_FAILURE);
StartShutdown();
return false;
}
@@ -44,8 +48,9 @@ static TokenPipeEnd g_shutdown_r;
static TokenPipeEnd g_shutdown_w;
#endif
-bool InitShutdownState()
+bool InitShutdownState(std::atomic<int>& exit_status)
{
+ g_exit_status = &exit_status;
#ifndef WIN32
std::optional<TokenPipe> pipe = TokenPipe::Make();
if (!pipe) return false;
diff --git a/src/shutdown.h b/src/shutdown.h
index 07a8315788..c119bee96f 100644
--- a/src/shutdown.h
+++ b/src/shutdown.h
@@ -8,13 +8,15 @@
#include <util/translation.h> // For bilingual_str
+#include <atomic>
+
/** Abort with a message */
bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{});
/** Initialize shutdown state. This must be called before using either StartShutdown(),
* AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe.
*/
-bool InitShutdownState();
+bool InitShutdownState(std::atomic<int>& exit_status);
/** Request shutdown of the application. */
void StartShutdown();
diff --git a/src/txmempool.h b/src/txmempool.h
index 000033086b..1ea029e949 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -33,8 +33,11 @@
#include <util/result.h>
#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
class CBlockIndex;
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index 40d36132de..dd042103bd 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -10,8 +10,12 @@
#include <random.h>
#include <uint256.h>
-#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/tag.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/tuple/tuple.hpp>
#include <chrono>
#include <unordered_map>
diff --git a/src/validation.cpp b/src/validation.cpp
index 99495ae2f7..d9a0fce34f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1993,8 +1993,6 @@ public:
}
};
-static std::array<ThresholdConditionCache, VERSIONBITS_NUM_BITS> warningcache GUARDED_BY(cs_main);
-
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
{
const Consensus::Params& consensusparams = chainman.GetConsensus();
@@ -2640,7 +2638,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(m_chainman, bit);
- ThresholdState state = checker.GetStateFor(pindex, params.GetConsensus(), warningcache.at(bit));
+ ThresholdState state = checker.GetStateFor(pindex, params.GetConsensus(), m_chainman.m_warningcache.at(bit));
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
@@ -5586,11 +5584,6 @@ ChainstateManager::~ChainstateManager()
LOCK(::cs_main);
m_versionbitscache.Clear();
-
- // TODO: The warning cache should probably become non-global
- for (auto& i : warningcache) {
- i.clear();
- }
}
bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool)
diff --git a/src/validation.h b/src/validation.h
index 48a1b075b8..cb5b291dab 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -938,6 +938,8 @@ private:
//! nullopt.
std::optional<int> GetSnapshotBaseHeight() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ std::array<ThresholdConditionCache, VERSIONBITS_NUM_BITS> m_warningcache GUARDED_BY(::cs_main);
+
//! Return true if a chainstate is considered usable.
//!
//! This is false when a background validation chainstate has completed its
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index a5394d8816..b1d67c1432 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -432,7 +432,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CAmount selection_target = 16 * CENT;
const auto& no_res = SelectCoinsBnB(GroupCoins(available_coins.All(), /*subtract_fee_outputs*/true),
selection_target, /*cost_of_change=*/0, MAX_STANDARD_TX_WEIGHT);
- BOOST_ASSERT(!no_res);
+ BOOST_REQUIRE(!no_res);
BOOST_CHECK(util::ErrorString(no_res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);
// Now add same coin value with a good size and check that it gets selected
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index cb1be9ea5b..65b02c267d 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -938,11 +938,10 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
}
// Add tx to wallet
- const auto& op_dest = wallet.GetNewDestination(OutputType::BECH32M, "");
- BOOST_ASSERT(op_dest);
+ const auto op_dest{*Assert(wallet.GetNewDestination(OutputType::BECH32M, ""))};
CMutableTransaction mtx;
- mtx.vout.push_back({COIN, GetScriptForDestination(*op_dest)});
+ mtx.vout.push_back({COIN, GetScriptForDestination(op_dest)});
mtx.vin.push_back(CTxIn(g_insecure_rand_ctx.rand256(), 0));
const auto& tx_id_to_spend = wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInMempool{})->GetHash();
diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp
index 00b2f47e14..17b6c4f7ed 100644
--- a/src/wallet/test/walletdb_tests.cpp
+++ b/src/wallet/test/walletdb_tests.cpp
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(walletdb_read_write_deadlock)
DatabaseStatus status;
bilingual_str error_string;
std::unique_ptr<WalletDatabase> db = MakeDatabase(m_path_root / strprintf("wallet_%d_.dat", db_format).c_str(), options, status, error_string);
- BOOST_ASSERT(status == DatabaseStatus::SUCCESS);
+ BOOST_CHECK_EQUAL(status, DatabaseStatus::SUCCESS);
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db)));
wallet->m_keypool_size = 4;
diff --git a/src/wallet/test/walletload_tests.cpp b/src/wallet/test/walletload_tests.cpp
index c1ff7baae1..73a4b77188 100644
--- a/src/wallet/test/walletload_tests.cpp
+++ b/src/wallet/test/walletload_tests.cpp
@@ -169,7 +169,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_ckey, TestingSetup)
// Fourth test case:
// Verify that loading up a 'ckey' with an invalid pubkey throws an error
CPubKey invalid_key;
- BOOST_ASSERT(!invalid_key.IsValid());
+ BOOST_CHECK(!invalid_key.IsValid());
SerializeData key = MakeSerializeData(DBKeys::CRYPTED_KEY, invalid_key);
records[key] = ckey_record_value;
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
index fa1bb6506a..586722aa65 100755
--- a/test/functional/feature_abortnode.py
+++ b/test/functional/feature_abortnode.py
@@ -40,7 +40,7 @@ class AbortNodeTest(BitcoinTestFramework):
# Check that node0 aborted
self.log.info("Waiting for crash")
- self.nodes[0].wait_until_stopped(timeout=5)
+ self.nodes[0].wait_until_stopped(timeout=5, expect_error=True)
self.log.info("Node crashed - now verifying restart fails")
self.nodes[0].assert_start_raises_init_error()
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
index ef327c7ce8..0a331eb96e 100755
--- a/test/functional/p2p_leak_tx.py
+++ b/test/functional/p2p_leak_tx.py
@@ -4,8 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction upload"""
-from test_framework.messages import msg_getdata, CInv, MSG_TX
-from test_framework.p2p import p2p_lock, P2PDataStore
+from test_framework.messages import msg_getdata, CInv, MSG_TX, MSG_WTX
+from test_framework.p2p import p2p_lock, P2PDataStore, P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -27,6 +27,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
self.miniwallet = MiniWallet(self.gen_node)
self.test_tx_in_block()
+ self.test_notfound_on_replaced_tx()
self.test_notfound_on_unannounced_tx()
def test_tx_in_block(self):
@@ -45,8 +46,36 @@ class P2PLeakTxTest(BitcoinTestFramework):
inbound_peer.send_and_ping(want_tx)
assert_equal(inbound_peer.last_message.get("tx").tx.getwtxid(), wtxid)
+ def test_notfound_on_replaced_tx(self):
+ self.gen_node.disconnect_p2ps()
+ inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore())
+
+ self.log.info("Transaction tx_a is broadcast")
+ tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node)
+ inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]])
+
+ tx_b = tx_a["tx"]
+ tx_b.vout[0].nValue -= 9000
+ self.gen_node.sendrawtransaction(tx_b.serialize().hex())
+
+ self.log.info("Re-request of tx_a after replacement is answered with notfound")
+ req_vec = [
+ CInv(t=MSG_TX, h=int(tx_a["txid"], 16)),
+ CInv(t=MSG_WTX, h=int(tx_a["wtxid"], 16)),
+ ]
+ want_tx = msg_getdata()
+ want_tx.inv = req_vec
+ with p2p_lock:
+ inbound_peer.last_message.pop("notfound", None)
+ inbound_peer.last_message.pop("tx", None)
+ inbound_peer.send_and_ping(want_tx)
+
+ assert_equal(inbound_peer.last_message.get("notfound").vec, req_vec)
+ assert "tx" not in inbound_peer.last_message
+
def test_notfound_on_unannounced_tx(self):
self.log.info("Check that we don't leak txs to inbound peers that we haven't yet announced to")
+ self.gen_node.disconnect_p2ps()
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer
MAX_REPEATS = 100
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 51bd697e81..4466bd544f 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -365,7 +365,7 @@ class TestNode():
if wait_until_stopped:
self.wait_until_stopped()
- def is_node_stopped(self):
+ def is_node_stopped(self, expected_ret_code=None):
"""Checks whether the node has stopped.
Returns True if the node has stopped. False otherwise.
@@ -377,8 +377,13 @@ class TestNode():
return False
# process has stopped. Assert that it didn't return an error code.
- assert return_code == 0, self._node_msg(
- "Node returned non-zero exit code (%d) when stopping" % return_code)
+ # unless 'expected_ret_code' is provided.
+ if expected_ret_code is not None:
+ assert return_code == expected_ret_code, self._node_msg(
+ "Node returned unexpected exit code (%d) vs (%d) when stopping" % (return_code, expected_ret_code))
+ else:
+ assert return_code == 0, self._node_msg(
+ "Node returned non-zero exit code (%d) when stopping" % return_code)
self.running = False
self.process = None
self.rpc_connected = False
@@ -386,8 +391,9 @@ class TestNode():
self.log.debug("Node stopped")
return True
- def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
- wait_until_helper(self.is_node_stopped, timeout=timeout, timeout_factor=self.timeout_factor)
+ def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False):
+ expected_ret_code = 1 if expect_error else None # Whether node shutdown return EXIT_FAILURE or EXIT_SUCCESS
+ wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code), timeout=timeout, timeout_factor=self.timeout_factor)
def replace_in_config(self, replacements):
"""
diff --git a/test/lint/lint-includes.py b/test/lint/lint-includes.py
index 459030bb0b..b14caa4855 100755
--- a/test/lint/lint-includes.py
+++ b/test/lint/lint-includes.py
@@ -23,15 +23,20 @@ EXCLUDED_DIRS = ["src/leveldb/",
EXPECTED_BOOST_INCLUDES = ["boost/date_time/posix_time/posix_time.hpp",
"boost/multi_index/hashed_index.hpp",
+ "boost/multi_index/identity.hpp",
+ "boost/multi_index/indexed_by.hpp",
"boost/multi_index/ordered_index.hpp",
"boost/multi_index/sequenced_index.hpp",
+ "boost/multi_index/tag.hpp",
"boost/multi_index_container.hpp",
"boost/process.hpp",
"boost/signals2/connection.hpp",
"boost/signals2/optional_last_value.hpp",
"boost/signals2/signal.hpp",
"boost/test/included/unit_test.hpp",
- "boost/test/unit_test.hpp"]
+ "boost/test/unit_test.hpp",
+ "boost/tuple/tuple.hpp",
+ ]
def get_toplevel():