mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-18 13:17:55 +02:00
Merge 75135c673e into merged_master (Bitcoin PR bitcoin/bitcoin#27861)
This commit is contained in:
commit
a8a9ae2ae1
32 changed files with 407 additions and 205 deletions
|
|
@ -216,6 +216,7 @@ BITCOIN_CORE_H = \
|
|||
netbase.h \
|
||||
netgroup.h \
|
||||
netmessagemaker.h \
|
||||
node/abort.h \
|
||||
node/blockmanager_args.h \
|
||||
node/blockstorage.h \
|
||||
node/caches.h \
|
||||
|
|
@ -326,6 +327,7 @@ BITCOIN_CORE_H = \
|
|||
util/readwritefile.h \
|
||||
util/result.h \
|
||||
util/serfloat.h \
|
||||
util/signalinterrupt.h \
|
||||
util/sock.h \
|
||||
util/spanparsing.h \
|
||||
util/string.h \
|
||||
|
|
@ -419,6 +421,7 @@ libbitcoin_node_a_SOURCES = \
|
|||
net.cpp \
|
||||
net_processing.cpp \
|
||||
netgroup.cpp \
|
||||
node/abort.cpp \
|
||||
node/blockmanager_args.cpp \
|
||||
node/blockstorage.cpp \
|
||||
node/caches.cpp \
|
||||
|
|
@ -778,6 +781,7 @@ libbitcoin_util_a_SOURCES = \
|
|||
util/moneystr.cpp \
|
||||
util/rbf.cpp \
|
||||
util/readwritefile.cpp \
|
||||
util/signalinterrupt.cpp \
|
||||
util/thread.cpp \
|
||||
util/threadinterrupt.cpp \
|
||||
util/threadnames.cpp \
|
||||
|
|
@ -1016,7 +1020,6 @@ libbitcoinkernel_la_SOURCES = \
|
|||
logging.cpp \
|
||||
node/blockstorage.cpp \
|
||||
node/chainstate.cpp \
|
||||
node/interface_ui.cpp \
|
||||
node/utxo_snapshot.cpp \
|
||||
policy/feerate.cpp \
|
||||
policy/fees.cpp \
|
||||
|
|
@ -1055,6 +1058,7 @@ libbitcoinkernel_la_SOURCES = \
|
|||
util/moneystr.cpp \
|
||||
util/rbf.cpp \
|
||||
util/serfloat.cpp \
|
||||
util/signalinterrupt.cpp \
|
||||
util/strencodings.cpp \
|
||||
util/string.cpp \
|
||||
util/syserror.cpp \
|
||||
|
|
|
|||
|
|
@ -97,6 +97,15 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
std::cout << "Warning: " << warning.original << std::endl;
|
||||
}
|
||||
void flushError(const std::string& debug_message) override
|
||||
{
|
||||
std::cerr << "Error flushing block data to disk: " << debug_message << std::endl;
|
||||
}
|
||||
void fatalError(const std::string& debug_message, const bilingual_str& user_message) override
|
||||
{
|
||||
std::cerr << "Error: " << debug_message << std::endl;
|
||||
std::cerr << (user_message.empty() ? "A fatal internal error occurred." : user_message.original) << std::endl;
|
||||
}
|
||||
};
|
||||
auto notifications = std::make_unique<KernelNotifications>();
|
||||
|
||||
|
|
@ -112,8 +121,9 @@ int main(int argc, char* argv[])
|
|||
const node::BlockManager::Options blockman_opts{
|
||||
.chainparams = chainman_opts.chainparams,
|
||||
.blocks_dir = abs_datadir / "blocks",
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
ChainstateManager chainman{chainman_opts, blockman_opts};
|
||||
ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts};
|
||||
|
||||
node::CacheSizes cache_sizes;
|
||||
cache_sizes.block_tree_db = 2 << 20;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <interfaces/chain.h>
|
||||
#include <kernel/chain.h>
|
||||
#include <logging.h>
|
||||
#include <node/abort.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <node/context.h>
|
||||
#include <node/database_args.h>
|
||||
|
|
@ -30,9 +31,10 @@ constexpr auto SYNC_LOG_INTERVAL{30s};
|
|||
constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
|
||||
|
||||
template <typename... Args>
|
||||
static void FatalError(const char* fmt, const Args&... args)
|
||||
void BaseIndex::FatalErrorf(const char* fmt, const Args&... args)
|
||||
{
|
||||
AbortNode(tfm::format(fmt, args...));
|
||||
auto message = tfm::format(fmt, args...);
|
||||
node::AbortNode(m_chain->context()->exit_status, message);
|
||||
}
|
||||
|
||||
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
|
||||
|
|
@ -197,7 +199,7 @@ void BaseIndex::ThreadSync()
|
|||
break;
|
||||
}
|
||||
if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
|
||||
FatalError("%s: Failed to rewind index %s to a previous chain tip",
|
||||
FatalErrorf("%s: Failed to rewind index %s to a previous chain tip",
|
||||
__func__, GetName());
|
||||
return;
|
||||
}
|
||||
|
|
@ -221,14 +223,14 @@ void BaseIndex::ThreadSync()
|
|||
CBlock block;
|
||||
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex);
|
||||
if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) {
|
||||
FatalError("%s: Failed to read block %s from disk",
|
||||
FatalErrorf("%s: Failed to read block %s from disk",
|
||||
__func__, pindex->GetBlockHash().ToString());
|
||||
return;
|
||||
} else {
|
||||
block_info.data = █
|
||||
}
|
||||
if (!CustomAppend(block_info)) {
|
||||
FatalError("%s: Failed to write block %s to index database",
|
||||
FatalErrorf("%s: Failed to write block %s to index database",
|
||||
__func__, pindex->GetBlockHash().ToString());
|
||||
return;
|
||||
}
|
||||
|
|
@ -294,7 +296,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
|
|||
const CBlockIndex* best_block_index = m_best_block_index.load();
|
||||
if (!best_block_index) {
|
||||
if (pindex->nHeight != 0) {
|
||||
FatalError("%s: First block connected is not the genesis block (height=%d)",
|
||||
FatalErrorf("%s: First block connected is not the genesis block (height=%d)",
|
||||
__func__, pindex->nHeight);
|
||||
return;
|
||||
}
|
||||
|
|
@ -312,7 +314,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
|
|||
return;
|
||||
}
|
||||
if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
|
||||
FatalError("%s: Failed to rewind index %s to a previous chain tip",
|
||||
FatalErrorf("%s: Failed to rewind index %s to a previous chain tip",
|
||||
__func__, GetName());
|
||||
return;
|
||||
}
|
||||
|
|
@ -325,7 +327,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
|
|||
// processed, and the index object being safe to delete.
|
||||
SetBestBlockIndex(pindex);
|
||||
} else {
|
||||
FatalError("%s: Failed to write block %s to index",
|
||||
FatalErrorf("%s: Failed to write block %s to index",
|
||||
__func__, pindex->GetBlockHash().ToString());
|
||||
return;
|
||||
}
|
||||
|
|
@ -345,7 +347,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
|
|||
}
|
||||
|
||||
if (!locator_tip_index) {
|
||||
FatalError("%s: First block (hash=%s) in locator was not found",
|
||||
FatalErrorf("%s: First block (hash=%s) in locator was not found",
|
||||
__func__, locator_tip_hash.ToString());
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ private:
|
|||
|
||||
virtual bool AllowPrune() const = 0;
|
||||
|
||||
template <typename... Args>
|
||||
void FatalErrorf(const char* fmt, const Args&... args);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<interfaces::Chain> m_chain;
|
||||
Chainstate* m_chainstate{nullptr};
|
||||
|
|
|
|||
12
src/init.cpp
12
src/init.cpp
|
|
@ -868,10 +868,6 @@ bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
|
|||
// Enable heap terminate-on-corruption
|
||||
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
|
||||
#endif
|
||||
if (!InitShutdownState(exit_status)) {
|
||||
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
|
||||
}
|
||||
|
||||
if (!SetupNetworking()) {
|
||||
return InitError(Untranslated("Initializing networking failed."));
|
||||
}
|
||||
|
|
@ -1062,7 +1058,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|||
|
||||
// Also report errors from parsing before daemonization
|
||||
{
|
||||
KernelNotifications notifications{};
|
||||
kernel::Notifications notifications{};
|
||||
ChainstateManager::Options chainman_opts_dummy{
|
||||
.chainparams = chainparams,
|
||||
.datadir = args.GetDataDirNet(),
|
||||
|
|
@ -1077,6 +1073,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|||
BlockManager::Options blockman_opts_dummy{
|
||||
.chainparams = chainman_opts_dummy.chainparams,
|
||||
.blocks_dir = args.GetBlocksDirPath(),
|
||||
.notifications = chainman_opts_dummy.notifications,
|
||||
};
|
||||
auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)};
|
||||
if (!blockman_result) {
|
||||
|
|
@ -1551,7 +1548,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
|
||||
// ********************************************************* Step 7: load block chain
|
||||
|
||||
node.notifications = std::make_unique<KernelNotifications>();
|
||||
node.notifications = std::make_unique<KernelNotifications>(node.exit_status);
|
||||
fReindex = args.GetBoolArg("-reindex", false);
|
||||
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
|
||||
ChainstateManager::Options chainman_opts{
|
||||
|
|
@ -1567,6 +1564,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
BlockManager::Options blockman_opts{
|
||||
.chainparams = chainman_opts.chainparams,
|
||||
.blocks_dir = args.GetBlocksDirPath(),
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
|
||||
|
||||
|
|
@ -1606,7 +1604,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
|
||||
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
|
||||
|
||||
node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
|
||||
node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
ChainstateManager& chainman = *node.chainman;
|
||||
|
||||
node::ChainstateLoadOptions options;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H
|
||||
#define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H
|
||||
|
||||
#include <kernel/notifications_interface.h>
|
||||
#include <util/fs.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
|
@ -25,6 +26,7 @@ struct BlockManagerOpts {
|
|||
bool fast_prune{false};
|
||||
bool stop_after_block_import{DEFAULT_STOPAFTERBLOCKIMPORT};
|
||||
const fs::path blocks_dir;
|
||||
Notifications& notifications;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
|||
|
|
@ -14,9 +14,12 @@
|
|||
|
||||
|
||||
namespace kernel {
|
||||
Context* g_context;
|
||||
|
||||
Context::Context()
|
||||
{
|
||||
assert(!g_context);
|
||||
g_context = this;
|
||||
std::string sha256_algo = SHA256AutoDetect();
|
||||
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
|
||||
RandomInit();
|
||||
|
|
@ -26,6 +29,8 @@ Context::Context()
|
|||
Context::~Context()
|
||||
{
|
||||
ECC_Stop();
|
||||
assert(g_context);
|
||||
g_context = nullptr;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef BITCOIN_KERNEL_CONTEXT_H
|
||||
#define BITCOIN_KERNEL_CONTEXT_H
|
||||
|
||||
#include <util/signalinterrupt.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace kernel {
|
||||
|
|
@ -16,12 +18,24 @@ namespace kernel {
|
|||
//! State stored directly in this struct should be simple. More complex state
|
||||
//! should be stored to std::unique_ptr members pointing to opaque types.
|
||||
struct Context {
|
||||
//! Interrupt object that can be used to stop long-running kernel operations.
|
||||
util::SignalInterrupt interrupt;
|
||||
|
||||
//! Declare default constructor and destructor that are not inline, so code
|
||||
//! instantiating the kernel::Context struct doesn't need to #include class
|
||||
//! definitions for all the unique_ptr members.
|
||||
Context();
|
||||
~Context();
|
||||
};
|
||||
|
||||
//! Global pointer to kernel::Context for legacy code. New code should avoid
|
||||
//! using this, and require state it needs to be passed to it directly.
|
||||
//!
|
||||
//! Having this pointer is useful because it allows state be moved out of global
|
||||
//! variables into the kernel::Context struct before all global references to
|
||||
//! that state are removed. This allows the global references to be removed
|
||||
//! incrementally, instead of all at once.
|
||||
extern Context* g_context;
|
||||
} // namespace kernel
|
||||
|
||||
#endif // BITCOIN_KERNEL_CONTEXT_H
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
#include <logging.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <serialize.h>
|
||||
#include <shutdown.h>
|
||||
#include <streams.h>
|
||||
#include <sync.h>
|
||||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
|
|||
} else {
|
||||
++expired;
|
||||
}
|
||||
if (ShutdownRequested())
|
||||
if (active_chainstate.m_chainman.m_interrupt)
|
||||
return false;
|
||||
}
|
||||
std::map<uint256, CAmount> mapDeltas;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
#ifndef BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H
|
||||
#define BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H
|
||||
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CBlockIndex;
|
||||
enum class SynchronizationState;
|
||||
struct bilingual_str;
|
||||
|
||||
namespace kernel {
|
||||
|
||||
|
|
@ -27,6 +28,23 @@ public:
|
|||
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {}
|
||||
virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {}
|
||||
virtual void warning(const bilingual_str& warning) {}
|
||||
|
||||
//! The flush error notification is sent to notify the user that an error
|
||||
//! occurred while flushing block data to disk. Kernel code may ignore flush
|
||||
//! errors that don't affect the immediate operation it is trying to
|
||||
//! perform. Applications can choose to handle the flush error notification
|
||||
//! by logging the error, or notifying the user, or triggering an early
|
||||
//! shutdown as a precaution against causing more errors.
|
||||
virtual void flushError(const std::string& debug_message) {}
|
||||
|
||||
//! The fatal error notification is sent to notify the user when an error
|
||||
//! occurs in kernel code that can't be recovered from. After this
|
||||
//! notification is sent, whatever function triggered the error should also
|
||||
//! return an error code or raise an exception. Applications can choose to
|
||||
//! handle the fatal error notification by logging the error, or notifying
|
||||
//! the user, or triggering an early shutdown as a precaution against
|
||||
//! causing more errors.
|
||||
virtual void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) {}
|
||||
};
|
||||
} // namespace kernel
|
||||
|
||||
|
|
|
|||
27
src/node/abort.cpp
Normal file
27
src/node/abort.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2023 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <node/abort.h>
|
||||
|
||||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/translation.h>
|
||||
#include <warnings.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace node {
|
||||
|
||||
void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message, bool shutdown)
|
||||
{
|
||||
SetMiscWarning(Untranslated(debug_message));
|
||||
LogPrintf("*** %s\n", debug_message);
|
||||
InitError(user_message.empty() ? _("A fatal internal error occurred, see debug.log for details") : user_message);
|
||||
exit_status.store(EXIT_FAILURE);
|
||||
if (shutdown) StartShutdown();
|
||||
}
|
||||
} // namespace node
|
||||
17
src/node/abort.h
Normal file
17
src/node/abort.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2023 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_NODE_ABORT_H
|
||||
#define BITCOIN_NODE_ABORT_H
|
||||
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
namespace node {
|
||||
void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message = {}, bool shutdown = true);
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_ABORT_H
|
||||
|
|
@ -14,12 +14,12 @@
|
|||
#include <logging.h>
|
||||
#include <pow.h>
|
||||
#include <reverse_iterator.h>
|
||||
#include <shutdown.h>
|
||||
#include <signet.h>
|
||||
#include <streams.h>
|
||||
#include <undo.h>
|
||||
#include <util/batchpriority.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <map>
|
||||
|
|
@ -260,7 +260,7 @@ bool BlockManager::LoadBlockIndex(ChainstateManager& chainman)
|
|||
if (fTrimHeaders) {
|
||||
trim_below_height = std::numeric_limits<int>::max();
|
||||
}
|
||||
if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }, trim_below_height)) {
|
||||
if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }, m_interrupt, trim_below_height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ bool BlockManager::LoadBlockIndex(ChainstateManager& chainman)
|
|||
CBlockIndexHeightOnlyComparator());
|
||||
|
||||
for (CBlockIndex* pindex : vSortedByHeight) {
|
||||
if (ShutdownRequested()) return false;
|
||||
if (m_interrupt) return false;
|
||||
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
|
||||
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
|
||||
|
||||
|
|
@ -540,7 +540,7 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize)
|
|||
{
|
||||
FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize);
|
||||
if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
|
||||
AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
|
||||
m_opts.notifications.flushError("Flushing undo file to disk failed. This is likely the result of an I/O error.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,7 +559,7 @@ void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
|
|||
|
||||
FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize);
|
||||
if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
|
||||
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
|
||||
m_opts.notifications.flushError("Flushing block file to disk failed. This is likely the result of an I/O error.");
|
||||
}
|
||||
// we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
|
||||
// e.g. during IBD or a sync after a node going offline
|
||||
|
|
@ -671,7 +671,8 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne
|
|||
bool out_of_space;
|
||||
size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
|
||||
if (out_of_space) {
|
||||
return AbortNode("Disk space is too low!", _("Disk space is too low!"));
|
||||
m_opts.notifications.fatalError("Disk space is too low!", _("Disk space is too low!"));
|
||||
return false;
|
||||
}
|
||||
if (bytes_allocated != 0 && IsPruneMode()) {
|
||||
m_check_for_pruning = true;
|
||||
|
|
@ -695,7 +696,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
|
|||
bool out_of_space;
|
||||
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
|
||||
if (out_of_space) {
|
||||
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
return FatalError(m_opts.notifications, state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
}
|
||||
if (bytes_allocated != 0 && IsPruneMode()) {
|
||||
m_check_for_pruning = true;
|
||||
|
|
@ -735,7 +736,7 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid
|
|||
return error("ConnectBlock(): FindUndoPos failed");
|
||||
}
|
||||
if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash(), GetParams().MessageStart())) {
|
||||
return AbortNode(state, "Failed to write undo data");
|
||||
return FatalError(m_opts.notifications, state, "Failed to write undo data");
|
||||
}
|
||||
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
||||
// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
|
||||
|
|
@ -855,7 +856,7 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CCha
|
|||
}
|
||||
if (!position_known) {
|
||||
if (!WriteBlockToDisk(block, blockPos, GetParams().MessageStart())) {
|
||||
AbortNode("Failed to write block");
|
||||
m_opts.notifications.fatalError("Failed to write block");
|
||||
return FlatFilePos();
|
||||
}
|
||||
}
|
||||
|
|
@ -903,8 +904,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
|
|||
}
|
||||
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
|
||||
chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
|
||||
if (ShutdownRequested()) {
|
||||
LogPrintf("Shutdown requested. Exit %s\n", __func__);
|
||||
if (chainman.m_interrupt) {
|
||||
LogPrintf("Interrupt requested. Exit %s\n", __func__);
|
||||
return;
|
||||
}
|
||||
nFile++;
|
||||
|
|
@ -922,8 +923,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
|
|||
if (file) {
|
||||
LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
|
||||
chainman.ActiveChainstate().LoadExternalBlockFile(file);
|
||||
if (ShutdownRequested()) {
|
||||
LogPrintf("Shutdown requested. Exit %s\n", __func__);
|
||||
if (chainman.m_interrupt) {
|
||||
LogPrintf("Interrupt requested. Exit %s\n", __func__);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -939,7 +940,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)) {
|
||||
AbortNode(strprintf("Failed to connect best block (%s)", state.ToString()));
|
||||
chainman.GetNotifications().fatalError(strprintf("Failed to connect best block (%s)", state.ToString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ struct FlatFilePos;
|
|||
namespace Consensus {
|
||||
struct Params;
|
||||
}
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
namespace node {
|
||||
|
||||
|
|
@ -163,10 +166,12 @@ private:
|
|||
public:
|
||||
using Options = kernel::BlockManagerOpts;
|
||||
|
||||
explicit BlockManager(Options opts)
|
||||
explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts)
|
||||
: m_prune_mode{opts.prune_target > 0},
|
||||
m_opts{std::move(opts)} {};
|
||||
m_opts{std::move(opts)},
|
||||
m_interrupt{interrupt} {};
|
||||
|
||||
const util::SignalInterrupt& m_interrupt;
|
||||
std::atomic<bool> m_importing{false};
|
||||
|
||||
BlockMap m_block_index GUARDED_BY(cs_main);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@
|
|||
|
||||
#include <common/args.h>
|
||||
#include <common/system.h>
|
||||
#include <kernel/context.h>
|
||||
#include <logging.h>
|
||||
#include <node/abort.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/check.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
#include <util/translation.h>
|
||||
|
|
@ -72,4 +77,14 @@ void KernelNotifications::warning(const bilingual_str& warning)
|
|||
DoWarning(warning);
|
||||
}
|
||||
|
||||
void KernelNotifications::flushError(const std::string& debug_message)
|
||||
{
|
||||
AbortNode(m_exit_status, debug_message);
|
||||
}
|
||||
|
||||
void KernelNotifications::fatalError(const std::string& debug_message, const bilingual_str& user_message)
|
||||
{
|
||||
node::AbortNode(m_exit_status, debug_message, user_message, m_shutdown_on_fatal_error);
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <kernel/notifications_interface.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -18,6 +19,8 @@ namespace node {
|
|||
class KernelNotifications : public kernel::Notifications
|
||||
{
|
||||
public:
|
||||
KernelNotifications(std::atomic<int>& exit_status) : m_exit_status{exit_status} {}
|
||||
|
||||
void blockTip(SynchronizationState state, CBlockIndex& index) override;
|
||||
|
||||
void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) override;
|
||||
|
|
@ -25,6 +28,15 @@ public:
|
|||
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override;
|
||||
|
||||
void warning(const bilingual_str& warning) override;
|
||||
|
||||
void flushError(const std::string& debug_message) override;
|
||||
|
||||
void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) override;
|
||||
|
||||
//! Useful for tests, can be set to false to avoid shutdown on fatal error.
|
||||
bool m_shutdown_on_fatal_error{true};
|
||||
private:
|
||||
std::atomic<int>& m_exit_status;
|
||||
};
|
||||
} // namespace node
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ void AppTests::appTests()
|
|||
// Reset global state to avoid interfering with later tests.
|
||||
ClearGlobalAssetDir();
|
||||
LogInstance().DisconnectTestLogger();
|
||||
AbortShutdown();
|
||||
}
|
||||
|
||||
//! Entry point for BitcoinGUI tests.
|
||||
|
|
|
|||
|
|
@ -5,107 +5,40 @@
|
|||
|
||||
#include <shutdown.h>
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <kernel/context.h>
|
||||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <util/check.h>
|
||||
#include <util/tokenpipe.h>
|
||||
#include <warnings.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#ifdef WIN32
|
||||
#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));
|
||||
LogPrintf("*** %s\n", strMessage);
|
||||
if (user_message.empty()) {
|
||||
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;
|
||||
}
|
||||
|
||||
static std::atomic<bool> fRequestShutdown(false);
|
||||
#ifdef WIN32
|
||||
/** On windows it is possible to simply use a condition variable. */
|
||||
std::mutex g_shutdown_mutex;
|
||||
std::condition_variable g_shutdown_cv;
|
||||
#else
|
||||
/** On UNIX-like operating systems use the self-pipe trick.
|
||||
*/
|
||||
static TokenPipeEnd g_shutdown_r;
|
||||
static TokenPipeEnd g_shutdown_w;
|
||||
#endif
|
||||
|
||||
bool InitShutdownState(std::atomic<int>& exit_status)
|
||||
{
|
||||
g_exit_status = &exit_status;
|
||||
#ifndef WIN32
|
||||
std::optional<TokenPipe> pipe = TokenPipe::Make();
|
||||
if (!pipe) return false;
|
||||
g_shutdown_r = pipe->TakeReadEnd();
|
||||
g_shutdown_w = pipe->TakeWriteEnd();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#include <system_error>
|
||||
|
||||
void StartShutdown()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(g_shutdown_mutex);
|
||||
fRequestShutdown = true;
|
||||
g_shutdown_cv.notify_one();
|
||||
#else
|
||||
// This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe.
|
||||
// Make sure that the token is only written once even if multiple threads call this concurrently or in
|
||||
// case of a reentrant signal.
|
||||
if (!fRequestShutdown.exchange(true)) {
|
||||
// Write an arbitrary byte to the write end of the shutdown pipe.
|
||||
int res = g_shutdown_w.TokenWrite('x');
|
||||
if (res != 0) {
|
||||
LogPrintf("Sending shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
try {
|
||||
Assert(kernel::g_context)->interrupt();
|
||||
} catch (const std::system_error&) {
|
||||
LogPrintf("Sending shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AbortShutdown()
|
||||
{
|
||||
if (fRequestShutdown) {
|
||||
// Cancel existing shutdown by waiting for it, this will reset condition flags and remove
|
||||
// the shutdown token from the pipe.
|
||||
WaitForShutdown();
|
||||
}
|
||||
fRequestShutdown = false;
|
||||
Assert(kernel::g_context)->interrupt.reset();
|
||||
}
|
||||
|
||||
bool ShutdownRequested()
|
||||
{
|
||||
return fRequestShutdown;
|
||||
return bool{Assert(kernel::g_context)->interrupt};
|
||||
}
|
||||
|
||||
void WaitForShutdown()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(g_shutdown_mutex);
|
||||
g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); });
|
||||
#else
|
||||
int res = g_shutdown_r.TokenRead();
|
||||
if (res != 'x') {
|
||||
try {
|
||||
Assert(kernel::g_context)->interrupt.wait();
|
||||
} catch (const std::system_error&) {
|
||||
LogPrintf("Reading shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,6 @@
|
|||
#ifndef BITCOIN_SHUTDOWN_H
|
||||
#define BITCOIN_SHUTDOWN_H
|
||||
|
||||
#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(std::atomic<int>& exit_status);
|
||||
|
||||
/** Request shutdown of the application. */
|
||||
void StartShutdown();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,16 @@
|
|||
#include <chainparams.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <node/context.h>
|
||||
#include <node/kernel_notifications.h>
|
||||
#include <util/chaintype.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
using node::BlockManager;
|
||||
using node::BLOCK_SERIALIZATION_HEADER_SIZE;
|
||||
using node::BlockManager;
|
||||
using node::KernelNotifications;
|
||||
using node::MAX_BLOCKFILE_SIZE;
|
||||
|
||||
// use BasicTestingSetup here for the data directory configuration, setup, and cleanup
|
||||
|
|
@ -21,11 +23,13 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup)
|
|||
BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
|
||||
{
|
||||
const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)};
|
||||
KernelNotifications notifications{m_node.exit_status};
|
||||
const BlockManager::Options blockman_opts{
|
||||
.chainparams = *params,
|
||||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = notifications,
|
||||
};
|
||||
BlockManager blockman{blockman_opts};
|
||||
BlockManager blockman{m_node.kernel->interrupt, blockman_opts};
|
||||
CChain chain {};
|
||||
// simulate adding a genesis block normally
|
||||
BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
|||
|
||||
m_cache_sizes = CalculateCacheSizes(m_args);
|
||||
|
||||
m_node.notifications = std::make_unique<KernelNotifications>();
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status);
|
||||
|
||||
const ChainstateManager::Options chainman_opts{
|
||||
.chainparams = chainparams,
|
||||
|
|
@ -219,8 +219,9 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
|||
const BlockManager::Options blockman_opts{
|
||||
.chainparams = chainman_opts.chainparams,
|
||||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{
|
||||
.path = m_args.GetDataDirNet() / "blocks" / "index",
|
||||
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
|||
LOCK(::cs_main);
|
||||
chainman.ResetChainstates();
|
||||
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
|
||||
m_node.notifications = std::make_unique<KernelNotifications>();
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status);
|
||||
const ChainstateManager::Options chainman_opts{
|
||||
.chainparams = ::Params(),
|
||||
.datadir = chainman.m_options.datadir,
|
||||
|
|
@ -392,11 +392,12 @@ struct SnapshotTestSetup : TestChain100Setup {
|
|||
const BlockManager::Options blockman_opts{
|
||||
.chainparams = chainman_opts.chainparams,
|
||||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
// For robustness, ensure the old manager is destroyed before creating a
|
||||
// new one.
|
||||
m_node.chainman.reset();
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
}
|
||||
return *Assert(m_node.chainman);
|
||||
}
|
||||
|
|
@ -566,7 +567,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
|
|||
auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes;
|
||||
|
||||
SnapshotCompletionResult res;
|
||||
auto mock_shutdown = [](bilingual_str msg) {};
|
||||
m_node.notifications->m_shutdown_on_fatal_error = false;
|
||||
|
||||
fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(chainman.m_options.datadir);
|
||||
BOOST_CHECK(fs::exists(snapshot_chainstate_dir));
|
||||
|
|
@ -576,8 +577,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
|
|||
const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),
|
||||
return chainman.ActiveTip()->GetBlockHash());
|
||||
|
||||
res = WITH_LOCK(::cs_main,
|
||||
return chainman.MaybeCompleteSnapshotValidation(mock_shutdown));
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS);
|
||||
|
||||
WITH_LOCK(::cs_main, BOOST_CHECK(chainman.IsSnapshotValidated()));
|
||||
|
|
@ -593,8 +593,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
|
|||
BOOST_CHECK_EQUAL(all_chainstates[0], &active_cs);
|
||||
|
||||
// Trying completion again should return false.
|
||||
res = WITH_LOCK(::cs_main,
|
||||
return chainman.MaybeCompleteSnapshotValidation(mock_shutdown));
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED);
|
||||
|
||||
// The invalid snapshot path should not have been used.
|
||||
|
|
@ -647,7 +646,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
|
|||
Chainstate& validation_chainstate = *std::get<0>(chainstates);
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
SnapshotCompletionResult res;
|
||||
auto mock_shutdown = [](bilingual_str msg) {};
|
||||
m_node.notifications->m_shutdown_on_fatal_error = false;
|
||||
|
||||
// Test tampering with the IBD UTXO set with an extra coin to ensure it causes
|
||||
// snapshot completion to fail.
|
||||
|
|
@ -663,8 +662,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
|
|||
fs::path snapshot_chainstate_dir = gArgs.GetDataDirNet() / "chainstate_snapshot";
|
||||
BOOST_CHECK(fs::exists(snapshot_chainstate_dir));
|
||||
|
||||
res = WITH_LOCK(::cs_main,
|
||||
return chainman.MaybeCompleteSnapshotValidation(mock_shutdown));
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH);
|
||||
|
||||
auto all_chainstates = chainman.GetAll();
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
#include <logging.h>
|
||||
#include <pow.h>
|
||||
#include <random.h>
|
||||
#include <shutdown.h>
|
||||
#include <uint256.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/translation.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ const CBlockIndex *CBlockTreeDB::RegenerateFullIndex(const CBlockIndex *pindexTr
|
|||
return pindexNew;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, int trimBelowHeight)
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
std::unique_ptr<CDBIterator> pcursor(NewIterator());
|
||||
|
|
@ -383,7 +383,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
|
|||
|
||||
// Load m_block_index
|
||||
while (pcursor->Valid()) {
|
||||
if (ShutdownRequested()) return false;
|
||||
if (interrupt) return false;
|
||||
std::pair<uint8_t, uint256> key;
|
||||
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
|
||||
CDiskBlockIndex diskindex;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class uint256;
|
|||
namespace Consensus {
|
||||
struct Params;
|
||||
};
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
//! -dbcache default (MiB)
|
||||
static const int64_t nDefaultDbCache = 450;
|
||||
|
|
@ -100,7 +103,7 @@ public:
|
|||
void ReadReindexing(bool &fReindexing);
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, int trimBelowHeight)
|
||||
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
// ELEMENTS:
|
||||
const CBlockIndex *RegenerateFullIndex(const CBlockIndex *pindexTrimmed, CBlockIndex *pindexNew) const;
|
||||
|
|
|
|||
74
src/util/signalinterrupt.cpp
Normal file
74
src/util/signalinterrupt.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/signalinterrupt.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <util/tokenpipe.h>
|
||||
#endif
|
||||
|
||||
#include <ios>
|
||||
#include <optional>
|
||||
|
||||
namespace util {
|
||||
|
||||
SignalInterrupt::SignalInterrupt() : m_flag{false}
|
||||
{
|
||||
#ifndef WIN32
|
||||
std::optional<TokenPipe> pipe = TokenPipe::Make();
|
||||
if (!pipe) throw std::ios_base::failure("Could not create TokenPipe");
|
||||
m_pipe_r = pipe->TakeReadEnd();
|
||||
m_pipe_w = pipe->TakeWriteEnd();
|
||||
#endif
|
||||
}
|
||||
|
||||
SignalInterrupt::operator bool() const
|
||||
{
|
||||
return m_flag;
|
||||
}
|
||||
|
||||
void SignalInterrupt::reset()
|
||||
{
|
||||
// Cancel existing interrupt by waiting for it, this will reset condition flags and remove
|
||||
// the token from the pipe.
|
||||
if (*this) wait();
|
||||
m_flag = false;
|
||||
}
|
||||
|
||||
void SignalInterrupt::operator()()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_flag = true;
|
||||
m_cv.notify_one();
|
||||
#else
|
||||
// This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe.
|
||||
// Make sure that the token is only written once even if multiple threads call this concurrently or in
|
||||
// case of a reentrant signal.
|
||||
if (!m_flag.exchange(true)) {
|
||||
// Write an arbitrary byte to the write end of the pipe.
|
||||
int res = m_pipe_w.TokenWrite('x');
|
||||
if (res != 0) {
|
||||
throw std::ios_base::failure("Could not write interrupt token");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SignalInterrupt::wait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_cv.wait(lk, [this] { return m_flag.load(); });
|
||||
#else
|
||||
int res = m_pipe_r.TokenRead();
|
||||
if (res != 'x') {
|
||||
throw std::ios_base::failure("Did not read expected interrupt token");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
52
src/util/signalinterrupt.h
Normal file
52
src/util/signalinterrupt.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2023 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_UTIL_SIGNALINTERRUPT_H
|
||||
#define BITCOIN_UTIL_SIGNALINTERRUPT_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <util/tokenpipe.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace util {
|
||||
/**
|
||||
* Helper class that manages an interrupt flag, and allows a thread or
|
||||
* signal to interrupt another thread.
|
||||
*
|
||||
* This class is safe to be used in a signal handler. If sending an interrupt
|
||||
* from a signal handler is not necessary, the more lightweight \ref
|
||||
* CThreadInterrupt class can be used instead.
|
||||
*/
|
||||
|
||||
class SignalInterrupt
|
||||
{
|
||||
public:
|
||||
SignalInterrupt();
|
||||
explicit operator bool() const;
|
||||
void operator()();
|
||||
void reset();
|
||||
void wait();
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_flag;
|
||||
|
||||
#ifndef WIN32
|
||||
// On UNIX-like operating systems use the self-pipe trick.
|
||||
TokenPipeEnd m_pipe_r;
|
||||
TokenPipeEnd m_pipe_w;
|
||||
#else
|
||||
// On windows use a condition variable, since we don't have any signals there
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
#endif
|
||||
};
|
||||
} // namespace util
|
||||
|
||||
#endif // BITCOIN_UTIL_SIGNALINTERRUPT_H
|
||||
|
|
@ -12,11 +12,17 @@
|
|||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
/*
|
||||
A helper class for interruptible sleeps. Calling operator() will interrupt
|
||||
any current sleep, and after that point operator bool() will return true
|
||||
until reset.
|
||||
*/
|
||||
/**
|
||||
* A helper class for interruptible sleeps. Calling operator() will interrupt
|
||||
* any current sleep, and after that point operator bool() will return true
|
||||
* until reset.
|
||||
*
|
||||
* This class should not be used in a signal handler. It uses thread
|
||||
* synchronization primitives that are not safe to use with signals. If sending
|
||||
* an interrupt from a signal handler is necessary, the \ref SignalInterrupt
|
||||
* class can be used instead.
|
||||
*/
|
||||
|
||||
class CThreadInterrupt
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include <util/hasher.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/time.h>
|
||||
#include <util/trace.h>
|
||||
|
|
@ -1957,9 +1958,9 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage)
|
||||
bool FatalError(Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage)
|
||||
{
|
||||
AbortNode(strMessage, userMessage);
|
||||
notifications.fatalError(strMessage, userMessage);
|
||||
return state.Error(strMessage);
|
||||
}
|
||||
|
||||
|
|
@ -2277,7 +2278,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// We don't write down blocks to disk if they may have been
|
||||
// corrupted, so this should be impossible unless we're having hardware
|
||||
// problems.
|
||||
return AbortNode(state, "Corrupt block found indicating potential hardware failure; shutting down");
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Corrupt block found indicating potential hardware failure; shutting down");
|
||||
}
|
||||
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
||||
}
|
||||
|
|
@ -2736,7 +2737,7 @@ bool Chainstate::FlushStateToDisk(
|
|||
if (fDoFullFlush || fPeriodicWrite) {
|
||||
// Ensure we can write block index
|
||||
if (!CheckDiskSpace(m_blockman.m_opts.blocks_dir)) {
|
||||
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
}
|
||||
{
|
||||
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
|
||||
|
|
@ -2756,7 +2757,7 @@ bool Chainstate::FlushStateToDisk(
|
|||
}
|
||||
}
|
||||
if (!m_blockman.WriteBlockIndexDB()) {
|
||||
return AbortNode(state, "Failed to write to block index database");
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Failed to write to block index database");
|
||||
}
|
||||
|
||||
// This should be done inside WriteBatchSync, but CBlockIndex is const there
|
||||
|
|
@ -2814,11 +2815,11 @@ bool Chainstate::FlushStateToDisk(
|
|||
// an overestimation, as most will delete an existing entry or
|
||||
// overwrite one. Still, use a conservative safety factor of 2.
|
||||
if (!CheckDiskSpace(m_chainman.m_options.datadir, 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
|
||||
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!"));
|
||||
}
|
||||
// Flush the chainstate (which may refer to block index entries).
|
||||
if (!CoinsTip().Flush())
|
||||
return AbortNode(state, "Failed to write to coin database");
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Failed to write to coin database");
|
||||
m_last_flush = nNow;
|
||||
full_flush_completed = true;
|
||||
TRACE5(utxocache, flush,
|
||||
|
|
@ -2834,7 +2835,7 @@ bool Chainstate::FlushStateToDisk(
|
|||
GetMainSignals().ChainStateFlushed(m_chain.GetLocator());
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
return AbortNode(state, std::string("System error while flushing: ") + e.what());
|
||||
return FatalError(m_chainman.GetNotifications(), state, std::string("System error while flushing: ") + e.what());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3096,7 +3097,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
if (!pblock) {
|
||||
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
|
||||
if (!m_blockman.ReadBlockFromDisk(*pblockNew, *pindexNew)) {
|
||||
return AbortNode(state, "Failed to read block");
|
||||
return FatalError(m_chainman.GetNotifications(), state, "Failed to read block");
|
||||
}
|
||||
pthisBlock = pblockNew;
|
||||
} else {
|
||||
|
|
@ -3294,7 +3295,7 @@ bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex*
|
|||
// If we're unable to disconnect a block during normal operation,
|
||||
// then that is a failure of our local system -- we should abort
|
||||
// rather than stay on a less work chain.
|
||||
AbortNode(state, "Failed to disconnect block; see debug.log for details");
|
||||
FatalError(m_chainman.GetNotifications(), state, "Failed to disconnect block; see debug.log for details");
|
||||
return false;
|
||||
}
|
||||
fBlocksDisconnected = true;
|
||||
|
|
@ -3521,11 +3522,11 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
|
|||
break;
|
||||
}
|
||||
|
||||
// We check shutdown only after giving ActivateBestChainStep a chance to run once so that we
|
||||
// never shutdown before connecting the genesis block during LoadChainTip(). Previously this
|
||||
// caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks
|
||||
// We check interrupt only after giving ActivateBestChainStep a chance to run once so that we
|
||||
// never interrupt before connecting the genesis block during LoadChainTip(). Previously this
|
||||
// caused an assert() failure during interrupt in such cases as the UTXO DB flushing checks
|
||||
// that the best block hash is non-null.
|
||||
if (ShutdownRequested()) break;
|
||||
if (m_chainman.m_interrupt) break;
|
||||
} while (pindexNewTip != pindexMostWork);
|
||||
CheckBlockIndex();
|
||||
|
||||
|
|
@ -3615,7 +3616,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||
|
||||
// Disconnect (descendants of) pindex, and mark them invalid.
|
||||
while (true) {
|
||||
if (ShutdownRequested()) break;
|
||||
if (m_chainman.m_interrupt) break;
|
||||
|
||||
// Make sure the queue of validation callbacks doesn't grow unboundedly.
|
||||
LimitValidationInterfaceQueue();
|
||||
|
|
@ -4442,7 +4443,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
|
|||
}
|
||||
ReceivedBlockTransactions(block, pindex, blockPos);
|
||||
} catch (const std::runtime_error& e) {
|
||||
return AbortNode(state, std::string("System error: ") + e.what());
|
||||
return FatalError(m_chainman.GetNotifications(), state, std::string("System error: ") + e.what());
|
||||
}
|
||||
|
||||
FlushStateToDisk(state, FlushStateMode::NONE);
|
||||
|
|
@ -4552,7 +4553,7 @@ void Chainstate::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_f
|
|||
{
|
||||
if (!m_mempool) return;
|
||||
::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
|
||||
m_mempool->SetLoadTried(!ShutdownRequested());
|
||||
m_mempool->SetLoadTried(!m_chainman.m_interrupt);
|
||||
}
|
||||
|
||||
bool Chainstate::LoadChainTip()
|
||||
|
|
@ -4685,7 +4686,7 @@ VerifyDBResult CVerifyDB::VerifyDB(
|
|||
skipped_l3_checks = true;
|
||||
}
|
||||
}
|
||||
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
|
||||
if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;
|
||||
}
|
||||
if (pindexFailure) {
|
||||
LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
|
||||
|
|
@ -4718,7 +4719,7 @@ VerifyDBResult CVerifyDB::VerifyDB(
|
|||
LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
|
||||
return VerifyDBResult::CORRUPTED_BLOCK_DB;
|
||||
}
|
||||
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
|
||||
if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4886,7 +4887,7 @@ bool ChainstateManager::LoadBlockIndex()
|
|||
}
|
||||
|
||||
for (CBlockIndex* pindex : vSortedByHeight) {
|
||||
if (ShutdownRequested()) return false;
|
||||
if (m_interrupt) return false;
|
||||
if (pindex->IsAssumedValid() ||
|
||||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
||||
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
|
||||
|
|
@ -4992,7 +4993,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
// such as a block fails to deserialize.
|
||||
uint64_t nRewind = blkdat.GetPos();
|
||||
while (!blkdat.eof()) {
|
||||
if (ShutdownRequested()) return;
|
||||
if (m_chainman.m_interrupt) return;
|
||||
|
||||
blkdat.SetPos(nRewind);
|
||||
nRewind++; // start one byte further next time, in case of failure
|
||||
|
|
@ -5133,7 +5134,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
}
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
AbortNode(std::string("System error: ") + e.what());
|
||||
m_chainman.GetNotifications().fatalError(std::string("System error: ") + e.what());
|
||||
}
|
||||
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
|
||||
}
|
||||
|
|
@ -5585,7 +5586,7 @@ bool ChainstateManager::ActivateSnapshot(
|
|||
snapshot_chainstate.reset();
|
||||
bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true);
|
||||
if (!removed) {
|
||||
AbortNode(strprintf("Failed to remove snapshot chainstate dir (%s). "
|
||||
GetNotifications().fatalError(strprintf("Failed to remove snapshot chainstate dir (%s). "
|
||||
"Manually remove it before restarting.\n", fs::PathToString(*snapshot_datadir)));
|
||||
}
|
||||
}
|
||||
|
|
@ -5625,13 +5626,13 @@ struct StopHashingException : public std::exception
|
|||
{
|
||||
const char* what() const throw() override
|
||||
{
|
||||
return "ComputeUTXOStats interrupted by shutdown.";
|
||||
return "ComputeUTXOStats interrupted.";
|
||||
}
|
||||
};
|
||||
|
||||
static void SnapshotUTXOHashBreakpoint()
|
||||
static void SnapshotUTXOHashBreakpoint(const util::SignalInterrupt& interrupt)
|
||||
{
|
||||
if (ShutdownRequested()) throw StopHashingException();
|
||||
if (interrupt) throw StopHashingException();
|
||||
}
|
||||
|
||||
bool ChainstateManager::PopulateAndValidateSnapshot(
|
||||
|
|
@ -5708,7 +5709,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|||
// If our average Coin size is roughly 41 bytes, checking every 120,000 coins
|
||||
// means <5MB of memory imprecision.
|
||||
if (coins_processed % 120000 == 0) {
|
||||
if (ShutdownRequested()) {
|
||||
if (m_interrupt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -5765,7 +5766,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|||
|
||||
try {
|
||||
maybe_stats = ComputeUTXOStats(
|
||||
CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, SnapshotUTXOHashBreakpoint);
|
||||
CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
|
||||
} catch (StopHashingException const&) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -5850,8 +5851,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|||
// through IsUsable() checks, or
|
||||
//
|
||||
// (ii) giving each chainstate its own lock instead of using cs_main for everything.
|
||||
SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation(
|
||||
std::function<void(bilingual_str)> shutdown_fnc)
|
||||
SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
if (m_ibd_chainstate.get() == &this->ActiveChainstate() ||
|
||||
|
|
@ -5903,7 +5903,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation(
|
|||
user_error = strprintf(Untranslated("%s\n%s"), user_error, util::ErrorString(rename_result));
|
||||
}
|
||||
|
||||
shutdown_fnc(user_error);
|
||||
GetNotifications().fatalError(user_error.original, user_error);
|
||||
};
|
||||
|
||||
if (index_new.GetBlockHash() != snapshot_blockhash) {
|
||||
|
|
@ -5943,7 +5943,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation(
|
|||
CoinStatsHashType::HASH_SERIALIZED,
|
||||
&ibd_coins_db,
|
||||
m_blockman,
|
||||
SnapshotUTXOHashBreakpoint);
|
||||
[&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
|
||||
} catch (StopHashingException const&) {
|
||||
return SnapshotCompletionResult::STATS_FAILED;
|
||||
}
|
||||
|
|
@ -6052,9 +6052,10 @@ static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts)
|
|||
return std::move(opts);
|
||||
}
|
||||
|
||||
ChainstateManager::ChainstateManager(Options options, node::BlockManager::Options blockman_options)
|
||||
: m_options{Flatten(std::move(options))},
|
||||
m_blockman{std::move(blockman_options)} {}
|
||||
ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options)
|
||||
: m_interrupt{interrupt},
|
||||
m_options{Flatten(std::move(options))},
|
||||
m_blockman{interrupt, std::move(blockman_options)} {}
|
||||
|
||||
ChainstateManager::~ChainstateManager()
|
||||
{
|
||||
|
|
@ -6199,13 +6200,13 @@ bool ChainstateManager::ValidatedSnapshotCleanup()
|
|||
|
||||
fs::path tmp_old{ibd_chainstate_path + "_todelete"};
|
||||
|
||||
auto rename_failed_abort = [](
|
||||
auto rename_failed_abort = [this](
|
||||
fs::path p_old,
|
||||
fs::path p_new,
|
||||
const fs::filesystem_error& err) {
|
||||
LogPrintf("%s: error renaming file (%s): %s\n",
|
||||
__func__, fs::PathToString(p_old), err.what());
|
||||
AbortNode(strprintf(
|
||||
GetNotifications().fatalError(strprintf(
|
||||
"Rename of '%s' -> '%s' failed. "
|
||||
"Cannot clean up the background chainstate leveldb directory.",
|
||||
fs::PathToString(p_old), fs::PathToString(p_new)));
|
||||
|
|
@ -6230,7 +6231,7 @@ bool ChainstateManager::ValidatedSnapshotCleanup()
|
|||
}
|
||||
|
||||
if (!DeleteCoinsDBFromDisk(tmp_old, /*is_snapshot=*/false)) {
|
||||
// No need to AbortNode because once the unneeded bg chainstate data is
|
||||
// No need to FatalError because once the unneeded bg chainstate data is
|
||||
// moved, it will not interfere with subsequent initialization.
|
||||
LogPrintf("Deletion of %s failed. Please remove it manually, as the " /* Continued */
|
||||
"directory is now unnecessary.\n",
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ class SnapshotMetadata;
|
|||
namespace Consensus {
|
||||
struct Params;
|
||||
} // namespace Consensus
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
/** Maximum number of dedicated script-checking threads allowed */
|
||||
static const int MAX_SCRIPTCHECK_THREADS = 15;
|
||||
|
|
@ -113,7 +116,7 @@ void StopScriptCheckWorkerThreads();
|
|||
|
||||
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
|
||||
|
||||
bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{});
|
||||
bool FatalError(kernel::Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = {});
|
||||
|
||||
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
|
||||
double GuessVerificationProgress(const CBlockIndex* pindex, int64_t blockInterval);
|
||||
|
|
@ -969,7 +972,7 @@ private:
|
|||
public:
|
||||
using Options = kernel::ChainstateManagerOpts;
|
||||
|
||||
explicit ChainstateManager(Options options, node::BlockManager::Options blockman_options);
|
||||
explicit ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options);
|
||||
|
||||
const CChainParams& GetParams() const { return m_options.chainparams; }
|
||||
const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); }
|
||||
|
|
@ -992,6 +995,7 @@ public:
|
|||
*/
|
||||
RecursiveMutex& GetMutex() const LOCK_RETURNED(::cs_main) { return ::cs_main; }
|
||||
|
||||
const util::SignalInterrupt& m_interrupt;
|
||||
const Options m_options;
|
||||
std::thread m_load_block;
|
||||
//! A single BlockManager instance is shared across each constructed
|
||||
|
|
@ -1062,10 +1066,7 @@ public:
|
|||
//! If the coins match (expected), then mark the validation chainstate for
|
||||
//! deletion and continue using the snapshot chainstate as active.
|
||||
//! Otherwise, revert to using the ibd chainstate and shutdown.
|
||||
SnapshotCompletionResult MaybeCompleteSnapshotValidation(
|
||||
std::function<void(bilingual_str)> shutdown_fnc =
|
||||
[](bilingual_str msg) { AbortNode(msg.original, msg); })
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! The most-work chain.
|
||||
Chainstate& ActiveChainstate() const;
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@
|
|||
#include <issuance.h>
|
||||
#include <key_io.h>
|
||||
#include <mainchainrpc.h>
|
||||
#include <node/context.h>
|
||||
#include <node/kernel_notifications.h>
|
||||
#include <rpc/rawtransaction_util.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/server_util.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/generic.hpp>
|
||||
#include <script/pegins.h>
|
||||
#include <secp256k1.h>
|
||||
#include <timedata.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/rpc/util.h>
|
||||
|
|
@ -199,11 +202,8 @@ RPCHelpMan getpeginaddress()
|
|||
}
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, message);
|
||||
}
|
||||
CTxDestination mainchain_dest(WitnessV0ScriptHash(calculate_contract(fedpegscripts.front().second, dest_script)));
|
||||
// P2SH-wrapped is the only valid choice for non-dynafed chains but still an
|
||||
// option for dynafed-enabled ones as well
|
||||
node::KernelNotifications notifications{};
|
||||
ChainstateManager::Options opts{
|
||||
node::KernelNotifications notifications{pwallet->chain().context()->exit_status};
|
||||
ChainstateManager::Options chain_opts{
|
||||
.chainparams = chainparams,
|
||||
.datadir = gArgs.GetDataDirNet(),
|
||||
.adjusted_time_callback = GetAdjustedTime,
|
||||
|
|
@ -211,7 +211,15 @@ RPCHelpMan getpeginaddress()
|
|||
.assumed_valid_block = consensus.defaultAssumeValid,
|
||||
.notifications = notifications,
|
||||
};
|
||||
if (!DeploymentActiveAfter(pwallet->chain().getTip(), ChainstateManager(opts, {chainparams}), Consensus::DEPLOYMENT_DYNA_FED) ||
|
||||
node::BlockManager::Options block_opts{
|
||||
.chainparams = chain_opts.chainparams,
|
||||
.blocks_dir = gArgs.GetBlocksDirPath(),
|
||||
.notifications = chain_opts.notifications,
|
||||
};
|
||||
CTxDestination mainchain_dest(WitnessV0ScriptHash(calculate_contract(fedpegscripts.front().second, dest_script)));
|
||||
// P2SH-wrapped is the only valid choice for non-dynafed chains but still an
|
||||
// option for dynafed-enabled ones as well
|
||||
if (!DeploymentActiveAfter(pwallet->chain().getTip(), ChainstateManager(util::SignalInterrupt(), chain_opts, block_opts), Consensus::DEPLOYMENT_DYNA_FED) ||
|
||||
fedpegscripts.front().first.IsPayToScriptHash()) {
|
||||
mainchain_dest = ScriptHash(GetScriptForDestination(mainchain_dest));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import re
|
|||
import sys
|
||||
|
||||
FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS = [
|
||||
'FatalError,0',
|
||||
'FatalErrorf,0',
|
||||
'fprintf,1',
|
||||
'tfm::format,1', # Assuming tfm::::format(std::ostream&, ...
|
||||
'LogConnectFailure,1',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import sys
|
|||
|
||||
FALSE_POSITIVES = [
|
||||
("src/dbwrapper.cpp", "vsnprintf(p, limit - p, format, backup_ap)"),
|
||||
("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"),
|
||||
("src/index/base.cpp", "FatalErrorf(const char* fmt, const Args&... args)"),
|
||||
("src/index/base.h", "FatalErrorf(const char* fmt, const Args&... args)"),
|
||||
("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"),
|
||||
("src/clientversion.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
|
||||
("src/test/translation_tests.cpp", "strprintf(format, arg)"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue