Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12944879
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
509 KB
Subscribers
None
View Options
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/checkqueue.h b/src/checkqueue.h
index f0f46fa45..b8ab4ba23 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -1,211 +1,252 @@
// Copyright (c) 2012-2018 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_CHECKQUEUE_H
#define BITCOIN_CHECKQUEUE_H
#include <sync.h>
+#include <tinyformat.h>
+#include <util/threadnames.h>
#include <algorithm>
#include <vector>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>
template <typename T> class CCheckQueueControl;
/**
* Queue for verifications that have to be performed.
* The verifications are represented by a type T, which must provide an
* operator(), returning a bool.
*
* One thread (the master) is assumed to push batches of verifications onto the
* queue, where they are processed by N-1 worker threads. When the master is
* done adding work, it temporarily joins the worker pool as an N'th worker,
* until all jobs are done.
*/
template <typename T> class CCheckQueue {
private:
//! Mutex to protect the inner state
boost::mutex mutex;
//! Worker threads block on this when out of work
boost::condition_variable condWorker;
//! Master thread blocks on this when out of work
boost::condition_variable condMaster;
//! The queue of elements to be processed.
//! As the order of booleans doesn't matter, it is used as a LIFO (stack)
std::vector<T> queue;
//! The number of workers (including the master) that are idle.
int nIdle{0};
//! The total number of workers (including the master).
int nTotal{0};
//! The temporary evaluation result.
bool fAllOk{true};
/**
* Number of verifications that haven't completed yet.
* This includes elements that are no longer queued, but still in the
* worker's own batches.
*/
unsigned int nTodo{0};
//! The maximum number of elements to be processed in one batch
const unsigned int nBatchSize;
+ std::vector<std::thread> m_worker_threads;
+ bool m_request_stop{false};
+
/** Internal function that does bulk of the verification work. */
- bool Loop(bool fMaster = false) {
+ bool Loop(bool fMaster) {
boost::condition_variable &cond = fMaster ? condMaster : condWorker;
std::vector<T> vChecks;
vChecks.reserve(nBatchSize);
unsigned int nNow = 0;
bool fOk = true;
do {
{
boost::unique_lock<boost::mutex> lock(mutex);
// first do the clean-up of the previous loop run (allowing us
// to do it in the same critsect)
if (nNow) {
fAllOk &= fOk;
nTodo -= nNow;
if (nTodo == 0 && !fMaster) {
// We processed the last element; inform the master it
// can exit and return the result
condMaster.notify_one();
}
} else {
// first iteration
nTotal++;
}
// logically, the do loop starts here
- while (queue.empty()) {
+ while (queue.empty() && !m_request_stop) {
if (fMaster && nTodo == 0) {
nTotal--;
bool fRet = fAllOk;
// reset the status for new work later
fAllOk = true;
// return the current status
return fRet;
}
nIdle++;
cond.wait(lock); // wait
nIdle--;
}
+ if (m_request_stop) {
+ return false;
+ }
+
// Decide how many work units to process now.
// * Do not try to do everything at once, but aim for
// increasingly smaller batches so all workers finish
// approximately simultaneously.
// * Try to account for idle jobs which will instantly start
// helping.
// * Don't do batches smaller than 1 (duh), or larger than
// nBatchSize.
nNow = std::max(
1U, std::min(nBatchSize, (unsigned int)queue.size() /
(nTotal + nIdle + 1)));
vChecks.resize(nNow);
for (unsigned int i = 0; i < nNow; i++) {
// We want the lock on the mutex to be as short as possible,
// so swap jobs from the global queue to the local batch
// vector instead of copying.
vChecks[i].swap(queue.back());
queue.pop_back();
}
// Check whether we need to do work at all
fOk = fAllOk;
}
// execute work
for (T &check : vChecks) {
if (fOk) {
fOk = check();
}
}
vChecks.clear();
} while (true);
}
public:
//! Mutex to ensure only one concurrent CCheckQueueControl
boost::mutex ControlMutex;
//! Create a new check queue
explicit CCheckQueue(unsigned int nBatchSizeIn)
: nBatchSize(nBatchSizeIn) {}
+ //! Create a pool of new worker threads.
+ void StartWorkerThreads(const int threads_num) {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ nIdle = 0;
+ nTotal = 0;
+ fAllOk = true;
+ }
+ assert(m_worker_threads.empty());
+ for (int n = 0; n < threads_num; ++n) {
+ m_worker_threads.emplace_back([this, n]() {
+ util::ThreadRename(strprintf("scriptch.%i", n));
+ Loop(false /* worker thread */);
+ });
+ }
+ }
+
//! Worker thread
- void Thread() { Loop(); }
+ void Thread() { Loop(false /* worker thread */); }
//! Wait until execution finishes, and return whether all evaluations were
//! successful.
- bool Wait() { return Loop(true); }
+ bool Wait() { return Loop(true /* master thread */); }
//! Add a batch of checks to the queue
void Add(std::vector<T> &vChecks) {
boost::unique_lock<boost::mutex> lock(mutex);
for (T &check : vChecks) {
queue.push_back(T());
check.swap(queue.back());
}
nTodo += vChecks.size();
if (vChecks.size() == 1) {
condWorker.notify_one();
} else if (vChecks.size() > 1) {
condWorker.notify_all();
}
}
- ~CCheckQueue() {}
+ //! Stop all of the worker threads.
+ void StopWorkerThreads() {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ m_request_stop = true;
+ }
+ condWorker.notify_all();
+ for (std::thread &t : m_worker_threads) {
+ t.join();
+ }
+ m_worker_threads.clear();
+ boost::unique_lock<boost::mutex> lock(mutex);
+ m_request_stop = false;
+ }
+
+ ~CCheckQueue() { assert(m_worker_threads.empty()); }
};
/**
* RAII-style controller object for a CCheckQueue that guarantees the passed
* queue is finished before continuing.
*/
template <typename T> class CCheckQueueControl {
private:
CCheckQueue<T> *const pqueue;
bool fDone;
public:
CCheckQueueControl() = delete;
CCheckQueueControl(const CCheckQueueControl &) = delete;
CCheckQueueControl &operator=(const CCheckQueueControl &) = delete;
explicit CCheckQueueControl(CCheckQueue<T> *const pqueueIn)
: pqueue(pqueueIn), fDone(false) {
// passed queue is supposed to be unused, or nullptr
if (pqueue != nullptr) {
ENTER_CRITICAL_SECTION(pqueue->ControlMutex);
}
}
bool Wait() {
if (pqueue == nullptr) {
return true;
}
bool fRet = pqueue->Wait();
fDone = true;
return fRet;
}
void Add(std::vector<T> &vChecks) {
if (pqueue != nullptr) {
pqueue->Add(vChecks);
}
}
~CCheckQueueControl() {
if (!fDone) {
Wait();
}
if (pqueue != nullptr) {
LEAVE_CRITICAL_SECTION(pqueue->ControlMutex);
}
}
};
#endif // BITCOIN_CHECKQUEUE_H
diff --git a/src/init.cpp b/src/init.cpp
index e4d26f742..1e13231e8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,3115 +1,3114 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <init.h>
#include <addrman.h>
#include <amount.h>
#include <avalanche/avalanche.h>
#include <avalanche/processor.h>
#include <avalanche/proof.h> // For AVALANCHE_LEGACY_PROOF_DEFAULT
#include <avalanche/validation.h>
#include <banman.h>
#include <blockdb.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <compat/sanity.h>
#include <config.h>
#include <consensus/validation.h>
#include <currencyunit.h>
#include <flatfile.h>
#include <fs.h>
#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <key.h>
#include <miner.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <network.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <policy/mempool.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <scheduler.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
#include <util/asmap.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <util/string.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <boost/algorithm/string/replace.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/thread/thread.hpp>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqrpc.h>
#endif
#ifndef WIN32
#include <attributes.h>
#include <cerrno>
#include <csignal>
#include <sys/stat.h>
#endif
#include <cstdint>
#include <cstdio>
#include <functional>
#include <set>
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
// block files don't count towards the fd_set size limit anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
static const char *DEFAULT_ASMAP_FILENAME = "ip_asn.map";
/**
* The PID file facilities.
*/
static const char *BITCOIN_PID_FILENAME = "bitcoind.pid";
static fs::path GetPidFile(const ArgsManager &args) {
return AbsPathForConfigVal(
fs::PathFromString(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
}
[[nodiscard]] static bool CreatePidFile(const ArgsManager &args) {
fsbridge::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
#else
tfm::format(file, "%d\n", getpid());
#endif
return true;
} else {
return InitError(strprintf(_("Unable to create the PID file '%s': %s"),
fs::PathToString(GetPidFile(args)),
std::strerror(errno)));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group created by
// AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
// fRequestShutdown, which makes main thread's WaitForShutdown() interrupts the
// thread group.
// And then, WaitForShutdown() makes all other on-going threads in the thread
// group join the main thread.
// Shutdown() is then called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing threads
// have exited.
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// ShutdownRequested() getting set, and then does the normal Qt shutdown thing.
//
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
static std::thread g_load_block;
static boost::thread_group threadGroup;
void Interrupt(NodeContext &node) {
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
InterruptMapPort();
if (g_avalanche) {
// Avalanche needs to be stopped before we interrupt the thread group as
// the scheduler will stop working then.
g_avalanche->stopEventLoop();
}
if (node.connman) {
node.connman->Interrupt();
}
if (g_txindex) {
g_txindex->Interrupt();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); });
}
void Shutdown(NodeContext &node) {
static Mutex g_shutdown_mutex;
TRY_LOCK(g_shutdown_mutex, lock_shutdown);
if (!lock_shutdown) {
return;
}
LogPrintf("%s: In progress...\n", __func__);
Assert(node.args);
/// Note: Shutdown() must be able to handle cases in which initialization
/// failed part of the way, for example if the data directory was found to
/// be locked. Be sure that anything that writes files or flushes caches
/// only does this if the respective module was initialized.
util::ThreadRename("shutoff");
if (node.mempool) {
node.mempool->AddTransactionsUpdated(1);
}
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
for (const auto &client : node.chain_clients) {
client->flush();
}
StopMapPort();
// Because avalanche and the network depend on each other, it is important
// to shut them down in this order:
// 1. Stop avalanche event loop.
// 2. Shutdown network processing.
// 3. Destroy avalanche::Processor.
// 4. Destroy CConnman
if (g_avalanche) {
g_avalanche->stopEventLoop();
}
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peerman) {
UnregisterValidationInterface(node.peerman.get());
}
// Follow the lock order requirements:
// * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling
// GetExtraFullOutboundCount which locks cs_vNodes.
// * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling
// ForEachNode which locks cs_vNodes.
// * CConnman::Stop calls DeleteNode, which calls FinalizeNode, which locks
// cs_main and calls EraseOrphansFor, which locks g_cs_orphans.
//
// Thus the implicit locking order requirement is:
// (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
if (node.connman) {
node.connman->StopThreads();
LOCK2(::cs_main, ::g_cs_orphans);
node.connman->StopNodes();
}
StopTorControl();
// After everything has been shut down, but before things get flushed, stop
// the CScheduler/checkqueue, threadGroup and load block thread.
if (node.scheduler) {
node.scheduler->stop();
}
if (g_load_block.joinable()) {
g_load_block.join();
}
threadGroup.interrupt_all();
threadGroup.join_all();
+ StopScriptCheckWorkerThreads();
// After the threads that potentially access these pointers have been
// stopped, destruct and reset all to nullptr.
node.peerman.reset();
// Destroy various global instances
g_avalanche.reset();
node.connman.reset();
node.banman.reset();
if (node.mempool && node.mempool->IsLoaded() &&
node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(*node.mempool);
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should
// avoid missing
if (node.chainman) {
LOCK(cs_main);
for (CChainState *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
}
}
}
// After there are no more peers/RPC left to give us new data which may
// generate CValidationInterface callbacks, flush them...
GetMainSignals().FlushBackgroundCallbacks();
// Stop and delete all indexes only after flushing background callbacks.
if (g_txindex) {
g_txindex->Stop();
g_txindex.reset();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); });
DestroyAllBlockFilterIndexes();
// Any future callbacks will be dropped. This should absolutely be safe - if
// missing a callback results in an unrecoverable situation, unclean
// shutdown would too. The only reason to do the above flushes is to let the
// wallet catch up with our current chain to avoid any strange pruning edge
// cases and make next startup faster by avoiding rescan.
if (node.chainman) {
LOCK(cs_main);
for (CChainState *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
}
}
pblocktree.reset();
}
for (const auto &client : node.chain_clients) {
client->stop();
}
#if ENABLE_ZMQ
if (g_zmq_notification_interface) {
UnregisterValidationInterface(g_zmq_notification_interface);
delete g_zmq_notification_interface;
g_zmq_notification_interface = nullptr;
}
#endif
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
globalVerifyHandle.reset();
ECC_Stop();
node.mempool.reset();
node.chainman = nullptr;
node.scheduler.reset();
try {
if (!fs::remove(GetPidFile(*node.args))) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n",
__func__);
}
} catch (const fs::filesystem_error &e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__,
fsbridge::get_filesystem_error_message(e));
}
node.args = nullptr;
LogPrintf("%s: done\n", __func__);
}
/**
* Signal handlers are very limited in what they are allowed to do.
* The execution context the handler is invoked in is not guaranteed,
* so we restrict handler operations to just touching variables:
*/
#ifndef WIN32
static void HandleSIGTERM(int) {
StartShutdown();
}
static void HandleSIGHUP(int) {
LogInstance().m_reopen_file = true;
}
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) {
StartShutdown();
Sleep(INFINITE);
return true;
}
#endif
#ifndef WIN32
static void registerSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(signal, &sa, NULL);
}
#endif
static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted() {
rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(
std::bind(RPCNotifyBlockChange, std::placeholders::_2));
}
static void OnRPCStopped() {
rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
void SetupServerArgs(NodeContext &node) {
assert(!node.args);
node.args = &gArgs;
ArgsManager &argsman = *node.args;
SetupHelpOptions(argsman);
SetupCurrencyUnitOptions(argsman);
// server-only for now
argsman.AddArg("-help-debug",
"Print help message with debugging options and exit", false,
OptionsCategory::DEBUG_TEST);
const auto defaultBaseParams =
CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams =
CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams =
CreateBaseChainParams(CBaseChainParams::REGTEST);
const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
const auto testnetChainParams =
CreateChainParams(CBaseChainParams::TESTNET);
const auto regtestChainParams =
CreateChainParams(CBaseChainParams::REGTEST);
// Hidden Options
std::vector<std::string> hidden_args = {
"-dbcrashratio", "-forcecompactdb", "-maxaddrtosend", "-parkdeepreorg",
"-automaticunparking", "-replayprotectionactivationtime",
"-enableminerfund",
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>",
"-min", "-resetguisettings", "-rootcertificates=<file>", "-splash",
"-uiplatform",
// TODO remove after the November 2020 upgrade
"-axionactivationtime"};
// Set all of the args and their help
// When adding new options to the categories, please keep and ensure
// alphabetical ordering. Do not translate _(...) -help-debug options, Many
// technical terms, and only a very small audience, so is unnecessary stress
// to translators.
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
#if defined(HAVE_SYSTEM)
argsman.AddArg(
"-alertnotify=<cmd>",
"Execute command when a relevant alert is received or we see "
"a really long fork (%s in cmd is replaced by message)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg(
"-assumevalid=<hex>",
strprintf(
"If this block is in the chain assume that it and its ancestors "
"are valid and potentially skip their script verification (0 to "
"verify all, default: %s, testnet: %s)",
defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>",
"Specify directory to hold blocks subdirectory for *.dat "
"files (default: <datadir>)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if defined(HAVE_SYSTEM)
argsman.AddArg("-blocknotify=<cmd>",
"Execute command when the best block changes (%s in cmd is "
"replaced by block hash)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg("-blockreconstructionextratxn=<n>",
strprintf("Extra transactions to keep in memory for compact "
"block reconstructions (default: %u)",
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-blocksonly",
strprintf("Whether to reject transactions from network peers. "
"Automatic broadcast and rebroadcast of any transactions "
"from inbound peers is disabled, unless the peer has the "
"'forcerelay' permission. RPC transactions are"
" not affected. (default: %u)",
DEFAULT_BLOCKSONLY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-conf=<file>",
strprintf("Specify path to read-only configuration file. Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbbatchsize",
strprintf("Maximum database write batch size in bytes (default: %u)",
DEFAULT_DB_BATCH_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbcache=<n>",
strprintf("Set database cache size in MiB (%d to %d, default: %d)",
MIN_DB_CACHE_MB, MAX_DB_CACHE_MB, DEFAULT_DB_CACHE_MB),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-debuglogfile=<file>",
strprintf("Specify location of debug log file. Relative paths "
"will be prefixed by a net-specific datadir "
"location. (0 to disable; default: %s)",
DEFAULT_DEBUGLOGFILE),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-feefilter",
strprintf("Tell other nodes to filter invs to us by our "
"mempool min fee (default: %d)",
DEFAULT_FEEFILTER),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-finalizationdelay=<n>",
strprintf("Set the minimum amount of time to wait between a "
"block header reception and the block finalization. "
"Unit is seconds (default: %d)",
DEFAULT_MIN_FINALIZATION_DELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-includeconf=<file>",
"Specify additional configuration file, relative to the -datadir path "
"(only useable from configuration file, not command line)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxreorgdepth=<n>",
strprintf("Configure at what depth blocks are considered "
"final (default: %d). Use -1 to disable.",
DEFAULT_MAX_REORG_DEPTH),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-loadblock=<file>",
"Imports blocks from external file on startup",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxmempool=<n>",
strprintf("Keep the transaction memory pool below <n> "
"megabytes (default: %u)",
DEFAULT_MAX_MEMPOOL_SIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxorphantx=<n>",
strprintf("Keep at most <n> unconnectable transactions in "
"memory (default: %u)",
DEFAULT_MAX_ORPHAN_TRANSACTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-mempoolexpiry=<n>",
strprintf("Do not keep transactions in the mempool longer "
"than <n> hours (default: %u)",
DEFAULT_MEMPOOL_EXPIRY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-minimumchainwork=<hex>",
strprintf(
"Minimum work assumed to exist on a valid chain in hex "
"(default: %s, testnet: %s)",
defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-par=<n>",
strprintf("Set the number of script verification threads (%u to %d, 0 "
"= auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS,
DEFAULT_SCRIPTCHECK_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool",
strprintf("Whether to save the mempool on shutdown and load "
"on restart (default: %u)",
DEFAULT_PERSIST_MEMPOOL),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-pid=<file>",
strprintf("Specify pid file. Relative paths will be prefixed "
"by a net-specific datadir location. (default: %s)",
BITCOIN_PID_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-prune=<n>",
strprintf("Reduce storage requirements by enabling pruning (deleting) "
"of old blocks. This allows the pruneblockchain RPC to be "
"called to delete specific blocks, and enables automatic "
"pruning of old blocks if a target size in MiB is provided. "
"This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the "
"entire blockchain. (default: 0 = disable pruning blocks, 1 "
"= allow manual pruning via RPC, >=%u = automatically prune "
"block files to stay under the specified target size in MiB)",
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex-chainstate",
"Rebuild chain state from the currently indexed blocks. When "
"in pruning mode or if blocks on disk might be corrupted, use "
"full -reindex instead.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex",
"Rebuild chain state and block index from the blk*.dat files on disk",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-settings=<file>",
strprintf(
"Specify path to dynamic settings data file. Can be disabled with "
"-nosettings. File is written at runtime and not meant to be "
"edited by users (use %s instead for custom settings). Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
#ifndef WIN32
argsman.AddArg(
"-sysperms",
"Create new files with system default permissions, instead of umask "
"077 (only effective with disabled wallet functionality)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-sysperms");
#endif
argsman.AddArg("-txindex",
strprintf("Maintain a full transaction index, used by the "
"getrawtransaction rpc call (default: %d)",
DEFAULT_TXINDEX),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block "
"(default: %s, values: %s).",
DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for "
"all known types are enabled.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-usecashaddr",
"Use Cash Address for destination encoding instead of base58 "
"(activate by default on Jan, 14)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-addnode=<ip>",
"Add a node to connect to and attempt to keep the connection "
"open (see the `addnode` RPC command help for more info)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-asmap=<file>",
strprintf("Specify asn mapping used for bucketing of the "
"peers (default: %s). Relative paths will be "
"prefixed by the net-specific datadir location.",
DEFAULT_ASMAP_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>",
strprintf("Default duration (in seconds) of manually "
"configured bans (default: %u)",
DEFAULT_MISBEHAVING_BANTIME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-bind=<addr>[:<port>][=onion]",
strprintf("Bind to given address and always listen on it (default: "
"0.0.0.0). Use [host]:port notation for IPv6. Append =onion "
"to tag any incoming connections to that address and port as "
"incoming Tor connections (default: 127.0.0.1:%u=onion, "
"testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)",
defaultBaseParams->OnionServiceTargetPort(),
testnetBaseParams->OnionServiceTargetPort(),
regtestBaseParams->OnionServiceTargetPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-connect=<ip>",
"Connect only to the specified node(s); -connect=0 disables automatic "
"connections (the rules for this peer are the same as for -addnode)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-discover",
"Discover own IP addresses (default: 1 when listening and no "
"-externalip or -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns",
strprintf("Allow DNS lookups for -addnode, -seednode and "
"-connect (default: %d)",
DEFAULT_NAME_LOOKUP),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-dnsseed",
strprintf(
"Query for peer addresses via DNS lookup, if low on addresses "
"(default: %u unless -connect used)",
DEFAULT_DNSSEED),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-fixedseeds",
strprintf(
"Allow fixed seeds if DNS seeds don't provide peers (default: %u)",
DEFAULT_FIXEDSEEDS),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg(
"-forcednsseed",
strprintf(
"Always query for peer addresses via DNS lookup (default: %d)",
DEFAULT_FORCEDNSSEED),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-overridednsseed",
"If set, only use the specified DNS seed when "
"querying for peer addresses via DNS lookup.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listen",
"Accept connections from outside (default: 1 if no -proxy or -connect)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listenonion",
strprintf("Automatically create Tor onion service (default: %d)",
DEFAULT_LISTEN_ONION),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxconnections=<n>",
strprintf("Maintain at most <n> connections to peers. The effective "
"limit depends on system limitations and might be lower than "
"the specified value (default: %u)",
DEFAULT_MAX_PEER_CONNECTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxreceivebuffer=<n>",
strprintf("Maximum per-connection receive buffer, <n>*1000 "
"bytes (default: %u)",
DEFAULT_MAXRECEIVEBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxsendbuffer=<n>",
strprintf(
"Maximum per-connection send buffer, <n>*1000 bytes (default: %u)",
DEFAULT_MAXSENDBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxtimeadjustment",
strprintf("Maximum allowed median peer time offset adjustment. Local "
"perspective of time may be influenced by peers forward or "
"backward by this amount. (default: %u seconds)",
DEFAULT_MAX_TIME_ADJUSTMENT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>",
strprintf("Use separate SOCKS5 proxy to reach peers via Tor "
"onion services (default: %s)",
"-proxy"),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-onlynet=<net>",
"Make outgoing connections only through network <net> (ipv4, ipv6 or "
"onion). Incoming connections are not affected by this option. This "
"option can be specified multiple times to allow multiple networks.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters",
strprintf("Support filtering of blocks and transaction with "
"bloom filters (default: %d)",
DEFAULT_PEERBLOOMFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peerblockfilters",
strprintf(
"Serve compact block filters to peers per BIP 157 (default: %u)",
DEFAULT_PEERBLOCKFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig",
strprintf("Relay non-P2SH multisig (default: %d)",
DEFAULT_PERMIT_BAREMULTISIG),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-port=<port>",
strprintf("Listen for connections on <port> (default: %u, "
"testnet: %u, regtest: %u)",
defaultChainParams->GetDefaultPort(),
testnetChainParams->GetDefaultPort(),
regtestChainParams->GetDefaultPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-proxyrandomize",
strprintf("Randomize credentials for every proxy connection. "
"This enables Tor stream isolation (default: %d)",
DEFAULT_PROXYRANDOMIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-seednode=<ip>",
"Connect to a node to retrieve peer addresses, and disconnect",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-networkactive",
"Enable all P2P network activity (default: 1). Can be changed "
"by the setnetworkactive RPC command",
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>",
strprintf("Specify connection timeout in milliseconds "
"(minimum: 1, default: %d)",
DEFAULT_CONNECT_TIMEOUT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peertimeout=<n>",
strprintf("Specify p2p connection timeout in seconds. This option "
"determines the amount of time a peer may be inactive before "
"the connection to it is dropped. (minimum: 1, default: %d)",
DEFAULT_PEER_CONNECT_TIMEOUT),
true, OptionsCategory::CONNECTION);
argsman.AddArg(
"-torcontrol=<ip>:<port>",
strprintf(
"Tor control port to use if onion listening enabled (default: %s)",
DEFAULT_TOR_CONTROL),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>",
"Tor control port password (default: empty)",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
argsman.AddArg("-upnp",
"Use UPnP to map the listening port (default: 1 when "
"listening and no -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
argsman.AddArg(
"-upnp",
strprintf("Use UPnP to map the listening port (default: %u)", 0),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#endif
#else
hidden_args.emplace_back("-upnp");
#endif
argsman.AddArg(
"-whitebind=<[permissions@]addr>",
"Bind to the given address and add permission flags to the peers "
"connecting to it."
"Use [host]:port notation for IPv6. Allowed permissions: " +
Join(NET_PERMISSIONS_DOC, ", ") +
". "
"Specify multiple permissions separated by commas (default: "
"download,noban,mempool,relay). Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-whitelist=<[permissions@]IP address or network>",
"Add permission flags to the peers connecting from the "
"given IP address (e.g. 1.2.3.4) or CIDR-notated network "
"(e.g. 1.2.3.0/24). "
"Uses the same permissions as -whitebind. Can be specified "
"multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxuploadtarget=<n>",
strprintf("Tries to keep outbound traffic under the given target (in "
"MiB per 24h). Limit does not apply to peers with 'download' "
"permission. 0 = no limit (default: %d)",
DEFAULT_MAX_UPLOAD_TARGET),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions(argsman);
#if ENABLE_ZMQ
argsman.AddArg("-zmqpubhashblock=<address>",
"Enable publish hash block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashtx=<address>",
"Enable publish hash transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblock=<address>",
"Enable publish raw block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtx=<address>",
"Enable publish raw transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequence=<address>",
"Enable publish hash block and tx sequence in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashblockhwm=<n>",
strprintf("Set publish hash block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashtxhwm=<n>",
strprintf("Set publish hash transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
false, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawblockhwm=<n>",
strprintf("Set publish raw block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawtxhwm=<n>",
strprintf("Set publish raw transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequencehwm=<n>",
strprintf("Set publish hash sequence message high water mark"
" (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
hidden_args.emplace_back("-zmqpubrawblock=<address>");
hidden_args.emplace_back("-zmqpubrawtx=<address>");
hidden_args.emplace_back("-zmqpubsequence=<n>");
hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
#endif
argsman.AddArg(
"-checkblocks=<n>",
strprintf("How many blocks to check at startup (default: %u, 0 = all)",
DEFAULT_CHECKBLOCKS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checklevel=<n>",
strprintf("How thorough the block verification of "
"-checkblocks is: %s (0-4, default: %u)",
Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex",
strprintf("Do a consistency check for the block tree, "
"chainstate, and other validation data structures "
"occasionally. (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-checkmempool=<n>",
strprintf(
"Run checks every <n> transactions (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkpoints",
strprintf("Only accept block chain matching built-in "
"checkpoints (default: %d)",
DEFAULT_CHECKPOINTS_ENABLED),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-deprecatedrpc=<method>",
"Allows deprecated RPC method(s) to be used",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-stopafterblockimport",
strprintf("Stop running after importing blocks from disk (default: %d)",
DEFAULT_STOPAFTERBLOCKIMPORT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-stopatheight",
strprintf("Stop running after reaching the given height in "
"the main chain (default: %u)",
DEFAULT_STOPATHEIGHT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-limitancestorcount=<n>",
strprintf("Do not accept transactions if number of in-mempool "
"ancestors is <n> or more (default: %u)",
DEFAULT_ANCESTOR_LIMIT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-limitancestorsize=<n>",
strprintf("Do not accept transactions whose size with all in-mempool "
"ancestors exceeds <n> kilobytes (default: %u)",
DEFAULT_ANCESTOR_SIZE_LIMIT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-limitdescendantcount=<n>",
strprintf("Do not accept transactions if any ancestor would have <n> "
"or more in-mempool descendants (default: %u)",
DEFAULT_DESCENDANT_LIMIT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-limitdescendantsize=<n>",
strprintf("Do not accept transactions if any ancestor would have more "
"than <n> kilobytes of in-mempool descendants (default: %u).",
DEFAULT_DESCENDANT_SIZE_LIMIT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-debug=<category>",
strprintf("Output debugging information (default: %u, "
"supplying <category> is optional)",
0) +
". " +
"If <category> is not supplied or if <category> = 1, "
"output all debugging information. <category> can be: " +
LogInstance().LogCategoriesString() + ".",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-debugexclude=<category>",
strprintf("Exclude debugging information for a category. Can be used "
"in conjunction with -debug=1 to output debug logs for all "
"categories except one or more specified categories."),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logips",
strprintf("Include IP addresses in debug output (default: %d)",
DEFAULT_LOGIPS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logtimestamps",
strprintf("Prepend debug output with timestamp (default: %d)",
DEFAULT_LOGTIMESTAMPS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#ifdef HAVE_THREAD_LOCAL
argsman.AddArg(
"-logthreadnames",
strprintf(
"Prepend debug output with name of the originating thread (only "
"available on platforms supporting thread_local) (default: %u)",
DEFAULT_LOGTHREADNAMES),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#else
hidden_args.emplace_back("-logthreadnames");
#endif
argsman.AddArg(
"-logsourcelocations",
strprintf(
"Prepend debug output with name of the originating source location "
"(source file, line number and function name) (default: %u)",
DEFAULT_LOGSOURCELOCATIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logtimemicros",
strprintf("Add microsecond precision to debug timestamps (default: %d)",
DEFAULT_LOGTIMEMICROS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>",
"Replace actual time with " + UNIX_EPOCH_TIME +
" (default: 0)",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxsigcachesize=<n>",
strprintf("Limit size of signature cache to <n> MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxscriptcachesize=<n>",
strprintf("Limit size of script cache to <n> MiB (default: %u)",
DEFAULT_MAX_SCRIPT_CACHE_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>",
strprintf("Maximum tip age in seconds to consider node in "
"initial block download (default: %u)",
DEFAULT_MAX_TIP_AGE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-printtoconsole",
"Send trace/debug info to console instead of debug.log file (default: "
"1 when no -daemon. To disable logging to file, set debuglogfile=0)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-printpriority",
strprintf("Log transaction priority and fee per kB when "
"mining blocks (default: %d)",
DEFAULT_PRINTPRIORITY),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-shrinkdebugfile",
"Shrink debug.log file on client startup (default: 1 when no -debug)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>",
"Append comment to the user agent string",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientname=<clientname>", "Set user agent client name",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientversion=<clientversion>",
"Set user agent client version", ArgsManager::ALLOW_ANY,
OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg(
"-acceptnonstdtxn",
strprintf(
"Relay and mine \"non-standard\" transactions (%sdefault: %u)",
"testnet/regtest only; ", defaultChainParams->RequireStandard()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-excessiveblocksize=<n>",
strprintf("Do not accept blocks larger than this limit, in "
"bytes (default: %d)",
DEFAULT_MAX_BLOCK_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
const auto &ticker = Currency::get().ticker;
argsman.AddArg(
"-dustrelayfee=<amt>",
strprintf("Fee rate (in %s/kB) used to define dust, the value of an "
"output such that it will cost about 1/3 of its value in "
"fees at this fee rate to spend it. (default: %s)",
ticker, FormatMoney(DUST_RELAY_TX_FEE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-bytespersigop",
strprintf("Equivalent bytes per sigop in transactions for "
"relay and mining (default: %u)",
DEFAULT_BYTES_PER_SIGOP),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarrier",
strprintf("Relay and mine data carrier transactions (default: %d)",
DEFAULT_ACCEPT_DATACARRIER),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarriersize",
strprintf("Maximum size of data in data carrier transactions "
"we relay and mine (default: %u)",
MAX_OP_RETURN_RELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-minrelaytxfee=<amt>",
strprintf("Fees (in %s/kB) smaller than this are rejected for "
"relaying, mining and transaction creation (default: %s)",
ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistrelay",
strprintf("Add 'relay' permission to whitelisted inbound peers "
"with default permissions. This will accept relayed "
"transactions even when not relaying transactions "
"(default: %d)",
DEFAULT_WHITELISTRELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistforcerelay",
strprintf("Add 'forcerelay' permission to whitelisted inbound peers"
" with default permissions. This will relay transactions "
"even if the transactions were already in the mempool "
"(default: %d)",
DEFAULT_WHITELISTFORCERELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
// Not sure this really belongs here, but it will do for now.
// FIXME: This doesn't work anyways.
argsman.AddArg("-excessutxocharge=<amt>",
strprintf("Fees (in %s/kB) to charge per utxo created for "
"relaying, and mining (default: %s)",
ticker, FormatMoney(DEFAULT_UTXO_FEE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-blockmaxsize=<n>",
strprintf("Set maximum block size in bytes (default: %d)",
DEFAULT_MAX_GENERATED_BLOCK_SIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg(
"-blockmintxfee=<amt>",
strprintf("Set lowest fee rate (in %s/kB) for transactions to "
"be included in block creation. (default: %s)",
ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-blockversion=<n>",
"Override block version to test forking scenarios",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rest",
strprintf("Accept public REST requests (default: %d)",
DEFAULT_REST_ENABLE),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcbind=<addr>[:port]",
"Bind to given address to listen for JSON-RPC connections. Do not "
"expose the RPC server to untrusted networks such as the public "
"internet! This option is ignored unless -rpcallowip is also passed. "
"Port is optional and overrides -rpcport. Use [host]:port notation "
"for IPv6. This option can be specified multiple times (default: "
"127.0.0.1 and ::1 i.e., localhost)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY |
ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpccookiefile=<loc>",
"Location of the auth cookie. Relative paths will be prefixed "
"by a net-specific datadir location. (default: data dir)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelist=<whitelist>",
"Set a whitelist to filter incoming RPC calls for a specific user. The "
"field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc "
"2>,...,<rpc n>. If multiple whitelists are set for a given user, they "
"are set-intersected. See -rpcwhitelistdefault documentation for "
"information on default whitelist behavior.",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelistdefault",
"Sets default behavior for rpc whitelisting. Unless "
"rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc "
"server acts as if all rpc users are subject to "
"empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault "
"is set to 1 and no -rpcwhitelist is set, rpc server acts as if all "
"rpc users are subject to empty whitelists.",
ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
argsman.AddArg(
"-rpcauth=<userpw>",
"Username and HMAC-SHA-256 hashed password for JSON-RPC connections. "
"The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A "
"canonical python script is included in share/rpcauth. The client then "
"connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> "
"pair of arguments. This option can be specified multiple times",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>",
strprintf("Listen for JSON-RPC connections on <port> "
"(default: %u, testnet: %u, regtest: %u)",
defaultBaseParams->RPCPort(),
testnetBaseParams->RPCPort(),
regtestBaseParams->RPCPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcallowip=<ip>",
"Allow JSON-RPC connections from specified source. Valid for "
"<ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. "
"1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). "
"This option can be specified multiple times",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcthreads=<n>",
strprintf(
"Set the number of threads to service RPC calls (default: %d)",
DEFAULT_HTTP_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpccorsdomain=value",
"Domain from which to accept cross origin requests (browser enforced)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcworkqueue=<n>",
strprintf("Set the depth of the work queue to service RPC "
"calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
#if HAVE_DECL_DAEMON
argsman.AddArg("-daemon",
"Run in the background as a daemon and accept commands",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
#endif
// Avalanche options.
argsman.AddArg(
"-enableavalanche",
strprintf("Enable avalanche (default: %u)", AVALANCHE_DEFAULT_ENABLED),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg("-enableavalanchepeerdiscovery",
strprintf("Enable avalanche peer discovery (default: %u)",
AVALANCHE_DEFAULT_PEER_DISCOVERY_ENABLED),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avacooldown",
strprintf("Mandatory cooldown between two avapoll (default: %u)",
AVALANCHE_DEFAULT_COOLDOWN),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avadelegation",
"Avalanche proof delegation to the master key used by this node "
"(default: none). Should be used in conjunction with -avaproof and "
"-avamasterkey",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg("-avaproof",
"Avalanche proof to be used by this node (default: none)",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-legacyavaproof",
strprintf("Use the legacy avalanche proof format (default: %u)",
AVALANCHE_DEFAULT_LEGACY_PROOF),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
argsman.AddArg("-avamasterkey",
"Master key associated with the proof. If a proof is "
"required, this is mandatory.",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg("-avasessionkey", "Avalanche session key (default: random)",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
// Add the hidden options
argsman.AddHiddenArgs(hidden_args);
}
std::string LicenseInfo() {
const std::string URL_SOURCE_CODE =
"<https://github.com/Bitcoin-ABC/bitcoin-abc>";
const std::string URL_WEBSITE = "<https://www.bitcoinabc.org>";
return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009,
COPYRIGHT_YEAR) +
" ") +
"\n" + "\n" +
strprintf(_("Please contribute if you find %s useful. "
"Visit %s for further information about the software.")
.translated,
PACKAGE_NAME, URL_WEBSITE) +
"\n" +
strprintf(_("The source code is available from %s.").translated,
URL_SOURCE_CODE) +
"\n" + "\n" + _("This is experimental software.").translated + "\n" +
strprintf(_("Distributed under the MIT software license, see the "
"accompanying file %s or %s")
.translated,
"COPYING", "<https://opensource.org/licenses/MIT>") +
"\n" + "\n" +
strprintf(_("This product includes software developed by the "
"OpenSSL Project for use in the OpenSSL Toolkit %s and "
"cryptographic software written by Eric Young and UPnP "
"software written by Thomas Bernard.")
.translated,
"<https://www.openssl.org>") +
"\n";
}
static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
static void BlockNotifyGenesisWait(const CBlockIndex *pBlockIndex) {
if (pBlockIndex != nullptr) {
{
LOCK(g_genesis_wait_mutex);
fHaveGenesis = true;
}
g_genesis_wait_cv.notify_all();
}
}
struct CImportingNow {
CImportingNow() {
assert(fImporting == false);
fImporting = true;
}
~CImportingNow() {
assert(fImporting == true);
fImporting = false;
}
};
// If we're using -prune with -reindex, then delete block files that will be
// ignored by the reindex. Since reindexing works by starting at block file 0
// and looping until a blockfile is missing, do the same here to delete any
// later block files after a gap. Also delete all rev files since they'll be
// rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync
// with what's actually on disk by the time we start downloading, so that
// pruning works correctly.
static void CleanupBlockRevFiles() {
std::map<std::string, fs::path> mapBlockFiles;
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
// Remove the rev files immediately and insert the blk file paths into an
// ordered map keyed by block file index.
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for "
"-reindex with -prune\n");
for (const auto &file : fs::directory_iterator{gArgs.GetBlocksDirPath()}) {
const std::string path = fs::PathToString(file.path().filename());
if (fs::is_regular_file(file) && path.length() == 12 &&
path.substr(8, 4) == ".dat") {
if (path.substr(0, 3) == "blk") {
mapBlockFiles[path.substr(3, 5)] = file.path();
} else if (path.substr(0, 3) == "rev") {
remove(file.path());
}
}
}
// Remove all block files that aren't part of a contiguous set starting at
// zero by walking the ordered map (keys are block file indices) by keeping
// a separate counter. Once we hit a gap (or if 0 doesn't exist) start
// removing block files.
int contiguousCounter = 0;
for (const auto &item : mapBlockFiles) {
if (atoi(item.first) == contiguousCounter) {
contiguousCounter++;
continue;
}
remove(item.second);
}
}
#if HAVE_SYSTEM
static void StartupNotify(const ArgsManager &args) {
std::string cmd = args.GetArg("-startupnotify", "");
if (!cmd.empty()) {
std::thread t(runCommand, cmd);
// thread runs free
t.detach();
}
}
#endif
static void ThreadImport(const Config &config, ChainstateManager &chainman,
std::vector<fs::path> vImportFiles,
const ArgsManager &args) {
ScheduleBatchPriority();
{
const CChainParams &chainParams = config.GetChainParams();
CImportingNow imp;
// -reindex
if (fReindex) {
int nFile = 0;
while (true) {
FlatFilePos pos(nFile, 0);
if (!fs::exists(GetBlockPosFilename(pos))) {
// No block files left to reindex
break;
}
FILE *file = OpenBlockFile(pos, true);
if (!file) {
// This error is logged in OpenBlockFile
break;
}
LogPrintf("Reindexing block file blk%05u.dat...\n",
(unsigned int)nFile);
LoadExternalBlockFile(config, file, &pos);
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exit %s\n", __func__);
return;
}
nFile++;
}
pblocktree->WriteReindexing(false);
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try
// initializing (no-op if reindexing worked):
LoadGenesisBlock(chainParams);
}
// -loadblock=
for (const fs::path &path : vImportFiles) {
FILE *file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n",
fs::PathToString(path));
LoadExternalBlockFile(config, file);
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exit %s\n", __func__);
return;
}
} else {
LogPrintf("Warning: Could not open blocks file %s\n",
fs::PathToString(path));
}
}
// Reconsider blocks we know are valid. They may have been marked
// invalid by, for instance, running an outdated version of the node
// software.
const MapCheckpoints &checkpoints =
chainParams.Checkpoints().mapCheckpoints;
for (const MapCheckpoints::value_type &i : checkpoints) {
const BlockHash &hash = i.second;
LOCK(cs_main);
CBlockIndex *pblockindex = LookupBlockIndex(hash);
if (pblockindex && !pblockindex->nStatus.isValid()) {
LogPrintf("Reconsidering checkpointed block %s ...\n",
hash.GetHex());
ResetBlockFailureFlags(pblockindex);
}
}
// scan for better chains in the block chain database, that are not yet
// connected in the active best chain
// We can't hold cs_main during ActivateBestChain even though we're
// accessing the chainman unique_ptrs since ABC requires us not to be
// holding cs_main, so retrieve the relevant pointers before the ABC
// call.
for (CChainState *chainstate :
WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(config, state, nullptr)) {
LogPrintf("Failed to connect best block (%s)\n",
state.ToString());
StartShutdown();
return;
}
}
if (args.GetBoolArg("-stopafterblockimport",
DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
return;
}
} // End scope of CImportingNow
chainman.ActiveChainstate().LoadMempool(config, args);
}
/** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
static bool InitSanityCheck() {
if (!ECC_InitSanityCheck()) {
return InitError(Untranslated(
"Elliptic curve cryptography sanity check failure. Aborting."));
}
if (!glibcxx_sanity_test()) {
return false;
}
if (!Random_SanityCheck()) {
return InitError(Untranslated(
"OS cryptographic RNG sanity check failure. Aborting."));
}
return true;
}
static bool AppInitServers(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node) {
const ArgsManager &args = *Assert(node.args);
RPCServerSignals::OnStarted(&OnRPCStarted);
RPCServerSignals::OnStopped(&OnRPCStopped);
if (!InitHTTPServer(config)) {
return false;
}
StartRPC();
node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(httpRPCRequestProcessor)) {
return false;
}
if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) {
StartREST(httpRPCRequestProcessor.context);
}
StartHTTPServer();
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction(ArgsManager &args) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (args.IsArgSet("-bind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
}
if (args.IsArgSet("-whitebind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
}
if (args.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (args.SoftSetBoolArg("-dnsseed", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
}
if (args.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -listen=0\n",
__func__);
}
// to protect privacy, do not use UPNP when a proxy is set. The user may
// still specify -listen=1 to listen locally, so don't rely on this
// happening through -listen below.
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
}
// to protect privacy, do not discover addresses by default
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
}
if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listenonion", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
}
if (args.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
}
// disable whitelistrelay in blocksonly mode
if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (args.SoftSetBoolArg("-whitelistrelay", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-whitelistrelay=0\n",
__func__);
}
}
// Forcing relay from whitelisted hosts implies we will accept relays from
// them in the first place.
if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (args.SoftSetBoolArg("-whitelistrelay", true)) {
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
}
/**
* Initialize global loggers.
*
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
void InitLogging(const ArgsManager &args) {
LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
LogInstance().m_file_path = AbsPathForConfigVal(
fs::PathFromString(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)));
LogInstance().m_print_to_console =
args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps =
args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros =
args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
#ifdef HAVE_THREAD_LOCAL
LogInstance().m_log_threadnames =
args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
#endif
LogInstance().m_log_sourcelocations =
args.GetBoolArg("-logsourcelocations", DEFAULT_LOGSOURCELOCATIONS);
fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
std::string version_string = FormatFullVersion();
#ifdef DEBUG
version_string += " (debug build)";
#else
version_string += " (release build)";
#endif
LogPrintf("%s version %s\n", CLIENT_NAME, version_string);
}
namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types;
} // namespace
[[noreturn]] static void new_handler_terminate() {
// Rather than throwing std::bad-alloc if allocation fails, terminate
// immediately to (try to) avoid chain corruption. Since LogPrintf may
// itself allocate memory, set the handler directly to terminate first.
std::set_new_handler(std::terminate);
LogPrintf("Error: Out of memory. Terminating.\n");
// The log was successful, terminate now.
std::terminate();
};
bool AppInitBasicSetup(ArgsManager &args) {
// Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, 0));
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
#endif
if (!SetupNetworking()) {
return InitError(Untranslated("Initializing networking failed"));
}
#ifndef WIN32
if (!args.GetBoolArg("-sysperms", false)) {
umask(077);
}
// Clean shutdown on SIGTERM
registerSignalHandler(SIGTERM, HandleSIGTERM);
registerSignalHandler(SIGINT, HandleSIGTERM);
// Reopen debug.log on SIGHUP
registerSignalHandler(SIGHUP, HandleSIGHUP);
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client
// closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#else
SetConsoleCtrlHandler(consoleCtrlHandler, true);
#endif
std::set_new_handler(new_handler_terminate);
return true;
}
bool AppInitParameterInteraction(Config &config, const ArgsManager &args) {
const CChainParams &chainparams = config.GetChainParams();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
std::string network = args.GetChainName();
bilingual_str errors;
for (const auto &arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s "
"network when in [%s] section.") +
Untranslated("\n"),
arg, network, network);
}
if (!errors.empty()) {
return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
for (const auto §ion : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") +
_("Section [%s] is not recognized.") +
Untranslated("\n"),
section.m_file, section.m_line, section.m_name);
}
if (!warnings.empty()) {
InitWarning(warnings);
}
if (!fs::is_directory(gArgs.GetBlocksDirPath())) {
return InitError(
strprintf(_("Specified blocks directory \"%s\" does not exist."),
args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
std::string blockfilterindex_value =
args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
const std::vector<std::string> names =
args.GetArgs("-blockfilterindex");
for (const auto &name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
return InitError(
strprintf(_("Unknown -blockfilterindex value %s."), name));
}
g_enabled_filter_types.insert(filter_type);
}
}
// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index
// are both enabled.
if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
return InitError(
_("Cannot set -peerblockfilters without -blockfilterindex."));
}
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
// if using block pruning, then disallow txindex
if (args.GetArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
return InitError(_("Prune mode is incompatible with -txindex."));
}
if (!g_enabled_filter_types.empty()) {
return InitError(
_("Prune mode is incompatible with -blockfilterindex."));
}
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind =
args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated(
"Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections =
args.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue
// described in #2695
nFD = RaiseFileDescriptorLimit(nMaxConnections + nBind +
MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS);
#ifdef USE_POLL
int fd_max = nFD;
#else
int fd_max = FD_SETSIZE;
#endif
nMaxConnections =
std::max(std::min<int>(nMaxConnections, fd_max - nBind -
MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS),
0);
if (nFD < MIN_CORE_FILEDESCRIPTORS) {
return InitError(_("Not enough file descriptors available."));
}
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections) {
// Not categorizing as "Warning" because this is the normal behavior for
// platforms using the select() interface for which FD_SETSIZE is
// usually 1024.
LogPrintf("Reducing -maxconnections from %d to %d, because of system "
"limitations.\n",
nUserMaxConnections, nMaxConnections);
}
// Step 3: parameter-to-internal-flags
if (args.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging
// messages
const std::vector<std::string> &categories = args.GetArgs("-debug");
if (std::none_of(
categories.begin(), categories.end(),
[](std::string cat) { return cat == "0" || cat == "none"; })) {
for (const auto &cat : categories) {
if (!LogInstance().EnableCategory(cat)) {
InitWarning(
strprintf(_("Unsupported logging category %s=%s."),
"-debug", cat));
}
}
}
}
// Now remove the logging categories which were explicitly excluded
for (const std::string &cat : args.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."),
"-debugexclude", cat));
}
}
fCheckBlockIndex = args.GetBoolArg("-checkblockindex",
chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled =
args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
if (fCheckpointsEnabled) {
LogPrintf("Checkpoints will be verified.\n");
} else {
LogPrintf("Skipping checkpoint verification.\n");
}
hashAssumeValid = BlockHash::fromHex(
args.GetArg("-assumevalid",
chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull()) {
LogPrintf("Assuming ancestors of block %s have valid signatures.\n",
hashAssumeValid.GetHex());
} else {
LogPrintf("Validating signatures for all blocks.\n");
}
if (args.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr =
args.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf(
Untranslated(
"Invalid non-hex (%s) minimum chain work value specified"),
minChainWorkStr));
}
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
nMinimumChainWork =
UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
}
LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
if (nMinimumChainWork <
UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n",
chainparams.GetConsensus().nMinimumChainWork.GetHex());
}
// mempool limits
int64_t nMempoolSizeMax =
args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin =
args.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) {
return InitError(strprintf(_("-maxmempool must be at least %d MB"),
std::ceil(nMempoolSizeMin / 1000000.0)));
}
// Configure excessive block size.
const int64_t nProposedExcessiveBlockSize =
args.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
if (nProposedExcessiveBlockSize <= 0 ||
!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
return InitError(
_("Excessive block size must be > 1,000,000 bytes (1MB)"));
}
// Check blockmaxsize does not exceed maximum accepted block size.
const int64_t nProposedMaxGeneratedBlockSize =
args.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
if (nProposedMaxGeneratedBlockSize <= 0) {
return InitError(_("Max generated block size must be greater than 0"));
}
if (uint64_t(nProposedMaxGeneratedBlockSize) > config.GetMaxBlockSize()) {
return InitError(_("Max generated block size (blockmaxsize) cannot "
"exceed the excessive block size "
"(excessiveblocksize)"));
}
// block pruning; get the amount of disk space (in MiB) to allot for block &
// undo files
int64_t nPruneArg = args.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(
_("Prune cannot be configured with a negative value."));
}
nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024;
if (nPruneArg == 1) {
// manual pruning: -prune=1
LogPrintf("Block pruning enabled. Use RPC call "
"pruneblockchain(height) to manually prune block and undo "
"files.\n");
nPruneTarget = std::numeric_limits<uint64_t>::max();
fPruneMode = true;
} else if (nPruneTarget) {
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
return InitError(
strprintf(_("Prune configured below the minimum of %d MiB. "
"Please use a higher number."),
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
}
LogPrintf("Prune configured to target %u MiB on disk for block and "
"undo files.\n",
nPruneTarget / 1024 / 1024);
fPruneMode = true;
}
nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
peer_connect_timeout =
args.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated(
"peertimeout cannot be configured with a negative value."));
}
// Obtain the amount to charge excess UTXO
if (args.IsArgSet("-excessutxocharge")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(args.GetArg("-excessutxocharge", ""), n);
if (!parsed || Amount::zero() > n) {
return InitError(AmountErrMsg(
"excessutxocharge", args.GetArg("-excessutxocharge", "")));
}
config.SetExcessUTXOCharge(n);
} else {
config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE);
}
if (args.IsArgSet("-minrelaytxfee")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(args.GetArg("-minrelaytxfee", ""), n);
if (!parsed || n == Amount::zero()) {
return InitError(AmountErrMsg("minrelaytxfee",
args.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in CWallet::Create()
::minRelayTxFee = CFeeRate(n);
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that
// happens.
if (args.IsArgSet("-blockmintxfee")) {
Amount n = Amount::zero();
if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n)) {
return InitError(AmountErrMsg("blockmintxfee",
args.GetArg("-blockmintxfee", "")));
}
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions.
if (args.IsArgSet("-dustrelayfee")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(args.GetArg("-dustrelayfee", ""), n);
if (!parsed || Amount::zero() == n) {
return InitError(
AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", "")));
}
dustRelayFee = CFeeRate(n);
}
fRequireStandard =
!args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf(
Untranslated(
"acceptnonstdtxn is not currently supported for %s chain"),
chainparams.NetworkIDString()));
}
nBytesPerSigOp = args.GetArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) {
return false;
}
fIsBareMultisigStd =
args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier =
args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(args.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
}
nMaxTipAge = args.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
return InitError(_(
"No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
}
return true;
}
static bool LockDataDirectory(bool probeOnly) {
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = GetDataDir();
if (!DirIsWritable(datadir)) {
return InitError(strprintf(
_("Cannot write to data directory '%s'; check permissions."),
fs::PathToString(datadir)));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
return InitError(strprintf(_("Cannot obtain a lock on data directory "
"%s. %s is probably already running."),
fs::PathToString(datadir), PACKAGE_NAME));
}
return true;
}
bool AppInitSanityChecks() {
// Step 4: sanity checks
// Initialize elliptic curve code
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
// Sanity check
if (!InitSanityCheck()) {
return InitError(strprintf(
_("Initialization sanity check failed. %s is shutting down."),
PACKAGE_NAME));
}
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon()
// hasn't yet happened, and a fork will cause weird behavior to it.
return LockDataDirectory(true);
}
bool AppInitLockDataDirectory() {
// After daemonization get the data directory lock again and hold on to it
// until exit. This creates a slight window for a race condition to happen,
// however this condition is harmless: it will at most make us exit without
// printing a message to console.
if (!LockDataDirectory(false)) {
// Detailed error printed inside LockDataDirectory
return false;
}
return true;
}
bool AppInitInterfaces(NodeContext &node) {
node.chain = interfaces::MakeChain(node, Params());
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
// when load() and start() interface methods are called below.
g_wallet_init_interface.Construct(node);
return true;
}
bool AppInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node,
interfaces::BlockAndHeaderTipInfo *tip_info) {
// Step 4a: application initialization
const ArgsManager &args = *Assert(node.args);
const CChainParams &chainparams = config.GetChainParams();
if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
}
BCLog::Logger &logger = LogInstance();
if (logger.m_print_to_file) {
if (args.GetBoolArg("-shrinkdebugfile",
logger.DefaultShrinkDebugFile())) {
// Do this first since it both loads a bunch of debug.log into
// memory, and because this needs to happen before any other
// debug.log printing.
logger.ShrinkDebugFile();
}
}
if (!logger.StartLogging()) {
return InitError(
strprintf(Untranslated("Could not open debug log file %s"),
fs::PathToString(logger.m_file_path)));
}
if (!logger.m_log_timestamps) {
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
}
LogPrintf("Default data directory %s\n",
fs::PathToString(GetDefaultDataDir()));
LogPrintf("Using data directory %s\n", fs::PathToString(GetDataDir()));
// Only log conf file usage message if conf file actually exists.
fs::path config_file_path =
GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME));
if (fs::exists(config_file_path)) {
LogPrintf("Config file: %s\n", fs::PathToString(config_file_path));
} else if (args.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
InitWarning(
strprintf(_("The specified config file %s does not exist\n"),
fs::PathToString(config_file_path)));
} else {
// Not categorizing as "Warning" because it's the default behavior
LogPrintf("Config file: %s (not found, skipping)\n",
fs::PathToString(config_file_path));
}
// Log the config arguments to debug.log
args.LogArgs();
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
// Warn about relative -datadir path.
if (args.IsArgSet("-datadir") &&
!fs::PathFromString(args.GetArg("-datadir", "")).is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will "
"be interpreted relative to the current working directory "
"'%s'. This is fragile, because if bitcoin is started in the "
"future from a different location, it will be unable to "
"locate the current data files. There could also be data "
"loss if bitcoin is started while in a temporary "
"directory.\n",
args.GetArg("-datadir", ""),
fs::PathToString(fs::current_path()));
}
InitSignatureCache();
InitScriptExecutionCache();
int script_threads = args.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script
// threads)
script_threads += GetNumCores();
}
// Subtract 1 because the main thread counts towards the par threads
script_threads = std::max(script_threads - 1, 0);
// Number of script-checking threads <= MAX_SCRIPTCHECK_THREADS
script_threads = std::min(script_threads, MAX_SCRIPTCHECK_THREADS);
LogPrintf("Script verification uses %d additional threads\n",
script_threads);
if (script_threads >= 1) {
- for (int i = 0; i < script_threads; ++i) {
- threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
- }
+ StartScriptCheckWorkerThreads(script_threads);
}
assert(!node.scheduler);
node.scheduler = std::make_unique<CScheduler>();
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop = [&node] {
node.scheduler->serviceQueue();
};
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>,
"scheduler", serviceLoop));
// Gather some entropy once per minute.
node.scheduler->scheduleEvery(
[] {
RandAddPeriodic();
return true;
},
std::chrono::minutes{1});
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
/**
* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllRPCCommands(config, rpcServer, tableRPC);
for (const auto &client : node.chain_clients) {
client->registerRpcs();
}
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
/**
* Start the RPC server. It will be started in "warmup" mode and not
* process calls yet (but it will verify that the server is there and will
* be ready later). Warmup mode will be completed when initialisation is
* finished.
*/
if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers(config, httpRPCRequestProcessor, node)) {
return InitError(
_("Unable to start HTTP server. See debug log for details."));
}
}
// Step 5: verify wallet database integrity
for (const auto &client : node.chain_clients) {
if (!client->verify()) {
return false;
}
}
// Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
// is not yet setup and may end up being set up twice if we
// need to reindex later.
assert(!node.banman);
node.banman = std::make_unique<BanMan>(
GetDataDir() / "banlist.dat", config.GetChainParams(), &uiInterface,
args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(
config, GetRand(std::numeric_limits<uint64_t>::max()),
GetRand(std::numeric_limits<uint64_t>::max()),
gArgs.GetBoolArg("-networkactive", true));
assert(!node.mempool);
int check_ratio = std::min<int>(
std::max<int>(
args.GetArg("-checkmempool",
chainparams.DefaultConsistencyChecks() ? 1 : 0),
0),
1000000);
node.mempool = std::make_unique<CTxMemPool>(check_ratio);
assert(!node.chainman);
node.chainman = &g_chainman;
ChainstateManager &chainman = *Assert(node.chainman);
assert(!node.peerman);
node.peerman =
PeerManager::make(chainparams, *node.connman, node.banman.get(),
*node.scheduler, chainman, *node.mempool,
args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY));
RegisterValidationInterface(node.peerman.get());
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
for (const std::string &cmt : args.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("User Agent comment (%s) contains unsafe characters."), cmt));
}
uacomments.push_back(cmt);
}
const std::string client_name = args.GetArg("-uaclientname", CLIENT_NAME);
const std::string client_version =
args.GetArg("-uaclientversion", FormatVersion(CLIENT_VERSION));
if (client_name != SanitizeString(client_name, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("-uaclientname (%s) contains invalid characters."), client_name));
}
if (client_version !=
SanitizeString(client_version, SAFE_CHARS_UA_COMMENT)) {
return InitError(
strprintf(_("-uaclientversion (%s) contains invalid characters."),
client_version));
}
const std::string strSubVersion =
FormatUserAgent(client_name, client_version, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
return InitError(strprintf(
_("Total length of network version string (%i) exceeds maximum "
"length (%i). Reduce the number or size of uacomments."),
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
if (args.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
for (const std::string &snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE) {
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
}
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) {
SetReachable(net, false);
}
}
}
// Check for host lookup allowed before parsing any network related
// parameters
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
bool proxyRandomize =
args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set
// a proxy, this is the default
std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid()) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
SetNameProxy(addrProxy);
// by default, -proxy sets onion as reachable, unless -noonion later
SetReachable(NET_ONION, true);
}
// -onion can be used to set only a proxy for .onion, or override normal
// proxy for .onion addresses.
// -noonion (or -onion=0) disables connecting to .onion entirely. An empty
// string is used to not override the onion proxy (in which case it defaults
// to -proxy set above, or none)
std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") {
// Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
} else {
CService onionProxy;
if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid()) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
}
}
fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = args.GetBoolArg("-discover", true);
for (const std::string &strAddr : args.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) &&
addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
// Read asmap file if configured
if (args.IsArgSet("-asmap")) {
fs::path asmap_path = fs::PathFromString(args.GetArg("-asmap", ""));
if (asmap_path.empty()) {
asmap_path = fs::PathFromString(DEFAULT_ASMAP_FILENAME);
}
if (!asmap_path.is_absolute()) {
asmap_path = GetDataDir() / asmap_path;
}
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
std::vector<bool> asmap = DecodeAsmap(asmap_path);
if (asmap.size() == 0) {
InitError(strprintf(_("Could not parse asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
const uint256 asmap_version = SerializeHash(asmap);
node.connman->SetAsmap(std::move(asmap));
LogPrintf("Using asmap version %s for IP bucketing\n",
asmap_version.ToString());
} else {
LogPrintf("Using /16 prefix for IP bucketing\n");
}
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create();
if (g_zmq_notification_interface) {
RegisterValidationInterface(g_zmq_notification_interface);
}
#endif
// Step 6.5 (I guess ?): Initialize Avalanche.
bilingual_str avalancheError;
g_avalanche = avalanche::Processor::MakeProcessor(
args, *node.chain, node.connman.get(), avalancheError);
if (!g_avalanche) {
InitError(avalancheError);
return false;
}
if (isAvalancheEnabled(args) &&
g_avalanche->isAvalancheServiceAvailable()) {
nLocalServices = ServiceFlags(nLocalServices | NODE_AVALANCHE);
}
// Step 7: load block chain
fReindex = args.GetBoolArg("-reindex", false);
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
int64_t nTotalCache = (args.GetArg("-dbcache", DEFAULT_DB_CACHE_MB) << 20);
// total cache cannot be less than MIN_DB_CACHE_MB
nTotalCache = std::max(nTotalCache, MIN_DB_CACHE_MB << 20);
// total cache cannot be greater than MAX_DB_CACHE_MB
nTotalCache = std::min(nTotalCache, MAX_DB_CACHE_MB << 20);
int64_t nBlockTreeDBCache =
std::min(nTotalCache / 8, MAX_BLOCK_DB_CACHE_MB << 20);
nTotalCache -= nBlockTreeDBCache;
int64_t nTxIndexCache =
std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX)
? MAX_TX_INDEX_CACHE_MB << 20
: 0);
nTotalCache -= nTxIndexCache;
int64_t filter_index_cache = 0;
if (!g_enabled_filter_types.empty()) {
size_t n_indexes = g_enabled_filter_types.size();
int64_t max_cache =
std::min(nTotalCache / 8, MAX_FILTER_INDEX_CACHE_MB << 20);
filter_index_cache = max_cache / n_indexes;
nTotalCache -= filter_index_cache * n_indexes;
}
// use 25%-50% of the remainder for disk cache
int64_t nCoinDBCache =
std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23));
// cap total coins db cache
nCoinDBCache = std::min(nCoinDBCache, MAX_COINS_DB_CACHE_MB << 20);
nTotalCache -= nCoinDBCache;
// the rest goes to in-memory cache
int64_t nCoinCacheUsage = nTotalCache;
int64_t nMempoolSizeMax =
args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n",
nBlockTreeDBCache * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n",
nTxIndexCache * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
filter_index_cache * (1.0 / 1024 / 1024),
BlockFilterTypeName(filter_type));
}
LogPrintf("* Using %.1f MiB for chain state database\n",
nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of "
"unused mempool space)\n",
nCoinCacheUsage * (1.0 / 1024 / 1024),
nMempoolSizeMax * (1.0 / 1024 / 1024));
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
const bool fReset = fReindex;
auto is_coinsview_empty =
[&](CChainState *chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return fReset || fReindexChainState ||
chainstate->CoinsTip().GetBestBlock().IsNull();
};
bilingual_str strLoadError;
uiInterface.InitMessage(_("Loading block index...").translated);
do {
bool failed_verification = false;
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
chainman.InitializeChainstate(*Assert(node.mempool));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
UnloadBlockIndex(node.mempool.get(), chainman);
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it
// first:
pblocktree.reset();
pblocktree.reset(
new CBlockTreeDB(nBlockTreeDBCache, false, fReset));
if (fReset) {
pblocktree->WriteReindexing(true);
// If we're reindexing in prune mode, wipe away unusable
// block files and all undo data files
if (fPruneMode) {
CleanupBlockRevFiles();
}
}
const Consensus::Params ¶ms = chainparams.GetConsensus();
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the block tree db with -reindex
// or -reindex-chainstate
if (!pblocktree->Upgrade(params)) {
strLoadError = _("Error upgrading block index database");
break;
}
if (ShutdownRequested()) {
break;
}
// LoadBlockIndex will load fHavePruned if we've ever removed a
// block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something
// different!
if (!chainman.LoadBlockIndex(params)) {
if (ShutdownRequested()) {
break;
}
strLoadError = _("Error loading block database");
break;
}
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way
// around).
if (!chainman.BlockIndex().empty() &&
!LookupBlockIndex(params.hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. "
"Wrong datadir for network?"));
}
// Check for changed -prune state. What we are concerned about
// is a user who has pruned blocks in the past, but is now
// trying to run unpruned.
if (fHavePruned && !fPruneMode) {
strLoadError =
_("You need to rebuild the database using -reindex to "
"go back to unpruned mode. This will redownload the "
"entire blockchain");
break;
}
// At this point blocktree args are consistent with what's on
// disk. If we're not mid-reindex (based on disk + args), add a
// genesis block on disk (otherwise we use the one already on
// disk).
// This is called again in ThreadImport after the reindex
// completes.
if (!fReindex && !LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
// At this point we're either in reindex or we've loaded a
// useful block tree into BlockIndex()!
bool failed_chainstate_init = false;
for (CChainState *chainstate : chainman.GetAll()) {
chainstate->InitCoinsDB(
/* cache_size_bytes */ nCoinDBCache,
/* in_memory */ false,
/* should_wipe */ fReset || fReindexChainState);
chainstate->CoinsErrorCatcher().AddReadErrCallback([]() {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."),
"", CClientUIInterface::MSG_ERROR);
});
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with
// -reindex or -reindex-chainstate
if (!chainstate->CoinsDB().Upgrade()) {
strLoadError = _("Error upgrading chainstate database");
failed_chainstate_init = true;
break;
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb
// with -reindex or -reindex-chainstate
if (!chainstate->ReplayBlocks(params)) {
strLoadError = _(
"Unable to replay blocks. You will need to rebuild "
"the database using -reindex-chainstate.");
failed_chainstate_init = true;
break;
}
// The on-disk coinsdb is now in a good state, create the
// cache
chainstate->InitCoinsCache(nCoinCacheUsage);
assert(chainstate->CanFlushToDisk());
if (!is_coinsview_empty(chainstate)) {
// LoadChainTip initializes the chain based on
// CoinsTip()'s best block
if (!chainstate->LoadChainTip(chainparams)) {
strLoadError =
_("Error initializing block database");
failed_chainstate_init = true;
// out of the per-chainstate loop
break;
}
assert(chainstate->m_chain.Tip() != nullptr);
}
}
if (failed_chainstate_init) {
// out of the chainstate activation do-while
break;
}
for (CChainState *chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(
_("Verifying blocks...").translated);
if (fHavePruned &&
args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) >
MIN_BLOCKS_TO_KEEP) {
LogPrintf(
"Prune: pruned datadir may not have more than "
"%d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
const CBlockIndex *tip = chainstate->m_chain.Tip();
RPCNotifyBlockChange(tip);
if (tip &&
tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError =
_("The block database contains a block which "
"appears to be from the future. "
"This may be due to your computer's date and "
"time being set incorrectly. "
"Only rebuild the block database if you are "
"sure that your computer's date and time are "
"correct");
failed_verification = true;
break;
}
// Only verify the DB of the active chainstate. This is
// fixed in later work when we allow VerifyDB to be
// parameterized by chainstate.
if (&::ChainstateActive() == chainstate &&
!CVerifyDB().VerifyDB(
config, &chainstate->CoinsDB(),
args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
args.GetArg("-checkblocks",
DEFAULT_CHECKBLOCKS))) {
strLoadError =
_("Corrupted block database detected");
failed_verification = true;
break;
}
}
}
} catch (const std::exception &e) {
LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");
failed_verification = true;
break;
}
if (!failed_verification) {
fLoaded = true;
LogPrintf(" block index %15dms\n",
GetTimeMillis() - load_block_index_start_time);
}
} while (false);
if (!fLoaded && !ShutdownRequested()) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
strLoadError + Untranslated(".\n\n") +
_("Do you want to rebuild the block database now?"),
strLoadError.original +
".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"",
CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
AbortShutdown();
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(strLoadError);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
// Encoded addresses using cashaddr instead of base58.
// We do this by default to avoid confusion with BTC addresses.
config.SetCashAddrEncoding(args.GetBoolArg("-usecashaddr", true));
// Step 8: load indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
g_txindex->Start();
}
for (const auto &filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
GetBlockFilterIndex(filter_type)->Start();
}
// Step 9: load wallet
for (const auto &client : node.chain_clients) {
if (!client->load()) {
return false;
}
}
// Step 10: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore
// prune after any wallet rescanning has taken place.
if (fPruneMode) {
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
LOCK(cs_main);
for (CChainState *chainstate : chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore...").translated);
chainstate->PruneAndFlush();
}
}
}
// Step 11: import blocks
if (!CheckDiskSpace(GetDataDir())) {
InitError(strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(GetDataDir()))));
return false;
}
if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
InitError(
strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(gArgs.GetBlocksDirPath()))));
return false;
}
// Either install a handler to notify us when genesis activates, or set
// fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
if (::ChainActive().Tip() == nullptr) {
block_notify_genesis_wait_connection =
uiInterface.NotifyBlockTip_connect(
std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
}
#if defined(HAVE_SYSTEM)
const std::string block_notify = args.GetArg("-blocknotify", "");
if (!block_notify.empty()) {
uiInterface.NotifyBlockTip_connect([block_notify](
SynchronizationState sync_state,
const CBlockIndex *pBlockIndex) {
if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) {
return;
}
std::string command = block_notify;
boost::replace_all(command, "%s",
pBlockIndex->GetBlockHash().GetHex());
std::thread t(runCommand, command);
// thread runs free
t.detach();
});
}
#endif
std::vector<fs::path> vImportFiles;
for (const std::string &strFile : args.GetArgs("-loadblock")) {
vImportFiles.push_back(fs::PathFromString(strFile));
}
g_load_block =
std::thread(&TraceThread<std::function<void()>>, "loadblk",
[=, &config, &chainman, &args] {
ThreadImport(config, chainman, vImportFiles, args);
});
// Wait for genesis block to be processed
{
WAIT_LOCK(g_genesis_wait_mutex, lock);
// We previously could hang here if StartShutdown() is called prior to
// ThreadImport getting started, so instead we just wait on a timer to
// check ShutdownRequested() regularly.
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
return false;
}
// Step 12: start node
int chain_active_height;
//// debug print
{
LOCK(cs_main);
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
chain_active_height = chainman.ActiveChain().Height();
if (tip_info) {
tip_info->block_height = chain_active_height;
tip_info->block_time =
chainman.ActiveChain().Tip()
? chainman.ActiveChain().Tip()->GetBlockTime()
: Params().GenesisBlock().GetBlockTime();
tip_info->verification_progress = GuessVerificationProgress(
Params().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && ::pindexBestHeader) {
tip_info->header_height = ::pindexBestHeader->nHeight;
tip_info->header_time = ::pindexBestHeader->GetBlockTime();
}
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
if (node.peerman) {
node.peerman->SetBestHeight(chain_active_height);
}
Discover();
// Map ports with UPnP
if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) {
StartMapPort();
}
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.m_max_outbound_full_relay = std::min(
MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
connOptions.m_max_outbound_block_relay = std::min(
MAX_BLOCK_RELAY_ONLY_CONNECTIONS,
connOptions.nMaxConnections - connOptions.m_max_outbound_full_relay);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc = node.peerman.get();
connOptions.nSendBufferMaxSize =
1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundLimit =
1024 * 1024 *
args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
connOptions.m_peer_connect_timeout = peer_connect_timeout;
for (const std::string &bind_arg : args.GetArgs("-bind")) {
CService bind_addr;
const size_t index = bind_arg.rfind('=');
if (index == std::string::npos) {
if (Lookup(bind_arg, bind_addr, GetListenPort(), false)) {
connOptions.vBinds.push_back(bind_addr);
continue;
}
} else {
const std::string network_type = bind_arg.substr(index + 1);
if (network_type == "onion") {
const std::string truncated_bind_arg =
bind_arg.substr(0, index);
if (Lookup(truncated_bind_arg, bind_addr,
BaseParams().OnionServiceTargetPort(), false)) {
connOptions.onion_binds.push_back(bind_addr);
continue;
}
}
}
return InitError(ResolveErrMsg("bind", bind_arg));
}
if (connOptions.onion_binds.empty()) {
connOptions.onion_binds.push_back(DefaultOnionServiceTarget());
}
if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
const auto bind_addr = connOptions.onion_binds.front();
if (connOptions.onion_binds.size() > 1) {
InitWarning(strprintf(
_("More than one onion bind address is provided. Using %s for "
"the automatically created Tor onion service."),
bind_addr.ToStringIPPort()));
}
StartTorControl(bind_addr);
}
for (const std::string &strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) {
return InitError(error);
}
connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto &net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) {
return InitError(error);
}
connOptions.vWhitelistedRange.push_back(subnet);
}
connOptions.vSeedNodes = args.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
const auto connect = args.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
}
if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
// Step 13: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading").translated);
for (const auto &client : node.chain_clients) {
client->start(*node.scheduler);
}
BanMan *banman = node.banman.get();
node.scheduler->scheduleEvery(
[banman] {
banman->DumpBanlist();
return true;
},
DUMP_BANS_INTERVAL);
// Start Avalanche's event loop.
g_avalanche->startEventLoop(*node.scheduler);
#if HAVE_SYSTEM
StartupNotify(args);
#endif
return true;
}
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 0db0b5fa1..8b8605033 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -1,422 +1,397 @@
// Copyright (c) 2012-2019 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 <checkqueue.h>
#include <sync.h>
#include <util/system.h>
#include <util/time.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <test/util/setup_common.h>
#include <thread>
#include <vector>
#include <boost/test/unit_test.hpp>
#include <memory>
#include <unordered_set>
#include <utility>
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
static const unsigned int QUEUE_BATCH_SIZE = 128;
static const int SCRIPT_CHECK_THREADS = 3;
struct FakeCheck {
bool operator()() { return true; }
void swap(FakeCheck &x){};
};
struct FakeCheckCheckCompletion {
static std::atomic<size_t> n_calls;
bool operator()() {
n_calls.fetch_add(1, std::memory_order_relaxed);
return true;
}
void swap(FakeCheckCheckCompletion &x){};
};
struct FailingCheck {
bool fails;
FailingCheck(bool _fails) : fails(_fails){};
FailingCheck() : fails(true){};
bool operator()() { return !fails; }
void swap(FailingCheck &x) { std::swap(fails, x.fails); };
};
struct UniqueCheck {
static Mutex m;
static std::unordered_multiset<size_t> results GUARDED_BY(m);
size_t check_id;
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
UniqueCheck() : check_id(0){};
bool operator()() {
LOCK(m);
results.insert(check_id);
return true;
}
void swap(UniqueCheck &x) { std::swap(x.check_id, check_id); };
};
struct MemoryCheck {
static std::atomic<size_t> fake_allocated_memory;
bool b{false};
bool operator()() { return true; }
MemoryCheck(){};
MemoryCheck(const MemoryCheck &x) {
// We have to do this to make sure that destructor calls are paired
//
// Really, copy constructor should be deletable, but CCheckQueue breaks
// if it is deleted because of internal push_back.
fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
};
MemoryCheck(bool b_) : b(b_) {
fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
};
~MemoryCheck() {
fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed);
};
void swap(MemoryCheck &x) { std::swap(b, x.b); };
};
struct FrozenCleanupCheck {
static std::atomic<uint64_t> nFrozen;
static std::condition_variable cv;
static std::mutex m;
// Freezing can't be the default initialized behavior given how the queue
// swaps in default initialized Checks.
bool should_freeze{false};
bool operator()() { return true; }
FrozenCleanupCheck() {}
~FrozenCleanupCheck() {
if (should_freeze) {
std::unique_lock<std::mutex> l(m);
nFrozen.store(1, std::memory_order_relaxed);
cv.notify_one();
cv.wait(
l, [] { return nFrozen.load(std::memory_order_relaxed) == 0; });
}
}
void swap(FrozenCleanupCheck &x) {
std::swap(should_freeze, x.should_freeze);
};
};
// Static Allocations
std::mutex FrozenCleanupCheck::m{};
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
std::condition_variable FrozenCleanupCheck::cv{};
Mutex UniqueCheck::m;
std::unordered_multiset<size_t> UniqueCheck::results;
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
// Queue Typedefs
typedef CCheckQueue<FakeCheckCheckCompletion> Correct_Queue;
typedef CCheckQueue<FakeCheck> Standard_Queue;
typedef CCheckQueue<FailingCheck> Failing_Queue;
typedef CCheckQueue<UniqueCheck> Unique_Queue;
typedef CCheckQueue<MemoryCheck> Memory_Queue;
typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;
/** This test case checks that the CCheckQueue works properly
* with each specified size_t Checks pushed.
*/
static void Correct_Queue_range(std::vector<size_t> range) {
auto small_queue = std::make_unique<Correct_Queue>(QUEUE_BATCH_SIZE);
- boost::thread_group tg;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { small_queue->Thread(); });
- }
+ small_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
// Make vChecks here to save on malloc (this test can be slow...)
std::vector<FakeCheckCheckCompletion> vChecks;
for (const size_t i : range) {
size_t total = i;
FakeCheckCheckCompletion::n_calls = 0;
CCheckQueueControl<FakeCheckCheckCompletion> control(small_queue.get());
while (total) {
vChecks.resize(std::min(total, (size_t)InsecureRandRange(10)));
total -= vChecks.size();
control.Add(vChecks);
}
BOOST_REQUIRE(control.Wait());
if (FakeCheckCheckCompletion::n_calls != i) {
BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i);
}
}
- tg.interrupt_all();
- tg.join_all();
+ small_queue->StopWorkerThreads();
}
/** Test that 0 checks is correct
*/
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Zero) {
std::vector<size_t> range;
range.push_back((size_t)0);
Correct_Queue_range(range);
}
/** Test that 1 check is correct
*/
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_One) {
std::vector<size_t> range;
range.push_back((size_t)1);
Correct_Queue_range(range);
}
/** Test that MAX check is correct
*/
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Max) {
std::vector<size_t> range;
range.push_back(100000);
Correct_Queue_range(range);
}
/** Test that random numbers of checks are correct
*/
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) {
std::vector<size_t> range;
range.reserve(100000 / 1000);
for (size_t i = 2; i < 100000;
i += std::max((size_t)1, (size_t)InsecureRandRange(std::min(
(size_t)1000, ((size_t)100000) - i)))) {
range.push_back(i);
}
Correct_Queue_range(range);
}
/** Test that failing checks are caught */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) {
auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE);
-
- boost::thread_group tg;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { fail_queue->Thread(); });
- }
+ fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (size_t i = 0; i < 1001; ++i) {
CCheckQueueControl<FailingCheck> control(fail_queue.get());
size_t remaining = i;
while (remaining) {
size_t r = InsecureRandRange(10);
std::vector<FailingCheck> vChecks;
vChecks.reserve(r);
for (size_t k = 0; k < r && remaining; k++, remaining--) {
vChecks.emplace_back(remaining == 1);
}
control.Add(vChecks);
}
bool success = control.Wait();
if (i > 0) {
BOOST_REQUIRE(!success);
} else if (i == 0) {
BOOST_REQUIRE(success);
}
}
- tg.interrupt_all();
- tg.join_all();
+ fail_queue->StopWorkerThreads();
}
// Test that a block validation which fails does not interfere with
// future blocks, ie, the bad state is cleared.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) {
auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE);
- boost::thread_group tg;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { fail_queue->Thread(); });
- }
+ fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (auto times = 0; times < 10; ++times) {
for (const bool end_fails : {true, false}) {
CCheckQueueControl<FailingCheck> control(fail_queue.get());
{
std::vector<FailingCheck> vChecks;
vChecks.resize(100, false);
vChecks[99] = end_fails;
control.Add(vChecks);
}
bool r = control.Wait();
BOOST_REQUIRE(r != end_fails);
}
}
- tg.interrupt_all();
- tg.join_all();
+ fail_queue->StopWorkerThreads();
}
// Test that unique checks are actually all called individually, rather than
// just one check being called repeatedly. Test that checks are not called
// more than once as well
BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) {
auto queue = std::make_unique<Unique_Queue>(QUEUE_BATCH_SIZE);
- boost::thread_group tg;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { queue->Thread(); });
- }
+ queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
size_t COUNT = 100000;
size_t total = COUNT;
{
CCheckQueueControl<UniqueCheck> control(queue.get());
while (total) {
size_t r = InsecureRandRange(10);
std::vector<UniqueCheck> vChecks;
for (size_t k = 0; k < r && total; k++) {
vChecks.emplace_back(--total);
}
control.Add(vChecks);
}
}
{
LOCK(UniqueCheck::m);
bool r = true;
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
for (size_t i = 0; i < COUNT; ++i) {
r = r && UniqueCheck::results.count(i) == 1;
}
BOOST_REQUIRE(r);
}
- tg.interrupt_all();
- tg.join_all();
+ queue->StopWorkerThreads();
}
// Test that blocks which might allocate lots of memory free their memory
// aggressively.
//
// This test attempts to catch a pathological case where by lazily freeing
// checks might mean leaving a check un-swapped out, and decreasing by 1 each
// time could leave the data hanging across a sequence of blocks.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) {
auto queue = std::make_unique<Memory_Queue>(QUEUE_BATCH_SIZE);
- boost::thread_group tg;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { queue->Thread(); });
- }
+ queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (size_t i = 0; i < 1000; ++i) {
size_t total = i;
{
CCheckQueueControl<MemoryCheck> control(queue.get());
while (total) {
size_t r = InsecureRandRange(10);
std::vector<MemoryCheck> vChecks;
for (size_t k = 0; k < r && total; k++) {
total--;
// Each iteration leaves data at the front, back, and middle
// to catch any sort of deallocation failure
vChecks.emplace_back(total == 0 || total == i ||
total == i / 2);
}
control.Add(vChecks);
}
}
BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0U);
}
- tg.interrupt_all();
- tg.join_all();
+ queue->StopWorkerThreads();
}
// Test that a new verification cannot occur until all checks
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) {
auto queue = std::make_unique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE);
- boost::thread_group tg;
bool fails = false;
- for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
- tg.create_thread([&] { queue->Thread(); });
- }
+ queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
std::thread t0([&]() {
CCheckQueueControl<FrozenCleanupCheck> control(queue.get());
std::vector<FrozenCleanupCheck> vChecks(1);
// Freezing can't be the default initialized behavior given how the
// queue
// swaps in default initialized Checks (otherwise freezing destructor
// would get called twice).
vChecks[0].should_freeze = true;
control.Add(vChecks);
// Hangs here
bool waitResult = control.Wait();
assert(waitResult);
});
{
std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
// Wait until the queue has finished all jobs and frozen
FrozenCleanupCheck::cv.wait(
l, []() { return FrozenCleanupCheck::nFrozen == 1; });
}
// Try to get control of the queue a bunch of times
for (auto x = 0; x < 100 && !fails; ++x) {
fails = queue->ControlMutex.try_lock();
}
{
// Unfreeze (we need lock n case of spurious wakeup)
std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
FrozenCleanupCheck::nFrozen = 0;
}
// Awaken frozen destructor
FrozenCleanupCheck::cv.notify_one();
// Wait for control to finish
t0.join();
- tg.interrupt_all();
- tg.join_all();
BOOST_REQUIRE(!fails);
+ queue->StopWorkerThreads();
}
/** Test that CCheckQueueControl is threadsafe */
BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) {
auto queue = std::make_unique<Standard_Queue>(QUEUE_BATCH_SIZE);
{
boost::thread_group tg;
std::atomic<int> nThreads{0};
std::atomic<int> fails{0};
for (size_t i = 0; i < 3; ++i) {
tg.create_thread([&] {
CCheckQueueControl<FakeCheck> control(queue.get());
// While sleeping, no other thread should execute to this point
auto observed = ++nThreads;
UninterruptibleSleep(std::chrono::milliseconds{10});
fails += observed != nThreads;
});
}
tg.join_all();
BOOST_REQUIRE_EQUAL(fails, 0);
}
{
boost::thread_group tg;
std::mutex m;
std::condition_variable cv;
bool has_lock{false};
bool has_tried{false};
bool done{false};
bool done_ack{false};
{
std::unique_lock<std::mutex> l(m);
tg.create_thread([&] {
CCheckQueueControl<FakeCheck> control(queue.get());
std::unique_lock<std::mutex> ll(m);
has_lock = true;
cv.notify_one();
cv.wait(ll, [&] { return has_tried; });
done = true;
cv.notify_one();
// Wait until the done is acknowledged
//
cv.wait(ll, [&] { return done_ack; });
});
// Wait for thread to get the lock
cv.wait(l, [&]() { return has_lock; });
bool fails = false;
for (auto x = 0; x < 100 && !fails; ++x) {
fails = queue->ControlMutex.try_lock();
}
has_tried = true;
cv.notify_one();
cv.wait(l, [&]() { return done; });
// Acknowledge the done
done_ack = true;
cv.notify_one();
BOOST_REQUIRE(!fails);
}
tg.join_all();
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index fb8abe5f7..b1693b8f4 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,939 +1,933 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Copyright (c) 2017-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h> // For CChainParams
#include <checkqueue.h>
#include <clientversion.h>
#include <config.h>
#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
#include <validation.h>
#include <test/data/tx_invalid.json.h>
#include <test/data/tx_valid.json.h>
#include <test/jsonutil.h>
#include <test/scriptflags.h>
#include <test/util/setup_common.h>
#include <test/util/transaction_utils.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
#include <map>
#include <string>
typedef std::vector<uint8_t> valtype;
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
static COutPoint buildOutPoint(const UniValue &vinput) {
TxId txid;
txid.SetHex(vinput[0].get_str());
return COutPoint(txid, vinput[1].get_int());
}
BOOST_AUTO_TEST_CASE(tx_valid) {
// Read tests from test/data/tx_valid.json
// Format is an array of arrays
// Inner arrays are either [ "comment" ]
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2],
// ...],"], serializedTransaction, verifyFlags
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to
// apply, or "NONE"
UniValue tests = read_json(
std::string(json_tests::tx_valid,
json_tests::tx_valid + sizeof(json_tests::tx_valid)));
ScriptError err;
for (size_t idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
if (test[0].isArray()) {
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
std::map<COutPoint, Amount> mapprevOutValues;
UniValue inputs = test[0].get_array();
bool fValid = true;
for (size_t inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
const UniValue &input = inputs[inpIdx];
if (!input.isArray()) {
fValid = false;
break;
}
UniValue vinput = input.get_array();
if (vinput.size() < 3 || vinput.size() > 4) {
fValid = false;
break;
}
COutPoint outpoint = buildOutPoint(vinput);
mapprevOutScriptPubKeys[outpoint] =
ParseScript(vinput[2].get_str());
if (vinput.size() >= 4) {
mapprevOutValues[outpoint] =
vinput[3].get_int64() * SATOSHI;
}
}
if (!fValid) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK,
PROTOCOL_VERSION);
CTransaction tx(deserialize, stream);
TxValidationState state;
BOOST_CHECK_MESSAGE(tx.IsCoinBase()
? CheckCoinbase(tx, state)
: CheckRegularTransaction(tx, state),
strTest);
BOOST_CHECK(state.IsValid());
// Check that CheckCoinbase reject non-coinbase transactions and
// vice versa.
BOOST_CHECK_MESSAGE(!(tx.IsCoinBase()
? CheckRegularTransaction(tx, state)
: CheckCoinbase(tx, state)),
strTest);
BOOST_CHECK(state.IsInvalid());
PrecomputedTransactionData txdata(tx);
for (size_t i = 0; i < tx.vin.size(); i++) {
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) {
BOOST_ERROR("Bad test: " << strTest);
break;
}
Amount amount = Amount::zero();
if (mapprevOutValues.count(tx.vin[i].prevout)) {
amount = mapprevOutValues[tx.vin[i].prevout];
}
uint32_t verify_flags = ParseScriptFlags(test[2].get_str());
BOOST_CHECK_MESSAGE(
VerifyScript(
tx.vin[i].scriptSig,
mapprevOutScriptPubKeys[tx.vin[i].prevout],
verify_flags,
TransactionSignatureChecker(&tx, i, amount, txdata),
&err),
strTest);
BOOST_CHECK_MESSAGE(err == ScriptError::OK,
ScriptErrorString(err));
}
}
}
}
BOOST_AUTO_TEST_CASE(tx_invalid) {
// Read tests from test/data/tx_invalid.json
// Format is an array of arrays
// Inner arrays are either [ "comment" ]
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2],
// ...],"], serializedTransaction, verifyFlags
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to
// apply, or "NONE"
UniValue tests = read_json(
std::string(json_tests::tx_invalid,
json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
// Initialize to ScriptError::OK. The tests expect err to be changed to a
// value other than ScriptError::OK.
ScriptError err = ScriptError::OK;
for (size_t idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
if (test[0].isArray()) {
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
std::map<COutPoint, Amount> mapprevOutValues;
UniValue inputs = test[0].get_array();
bool fValid = true;
for (size_t inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
const UniValue &input = inputs[inpIdx];
if (!input.isArray()) {
fValid = false;
break;
}
UniValue vinput = input.get_array();
if (vinput.size() < 3 || vinput.size() > 4) {
fValid = false;
break;
}
COutPoint outpoint = buildOutPoint(vinput);
mapprevOutScriptPubKeys[outpoint] =
ParseScript(vinput[2].get_str());
if (vinput.size() >= 4) {
mapprevOutValues[outpoint] =
vinput[3].get_int64() * SATOSHI;
}
}
if (!fValid) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK,
PROTOCOL_VERSION);
CTransaction tx(deserialize, stream);
TxValidationState state;
fValid = CheckRegularTransaction(tx, state) && state.IsValid();
PrecomputedTransactionData txdata(tx);
for (size_t i = 0; i < tx.vin.size() && fValid; i++) {
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) {
BOOST_ERROR("Bad test: " << strTest);
break;
}
Amount amount = Amount::zero();
if (0 != mapprevOutValues.count(tx.vin[i].prevout)) {
amount = mapprevOutValues[tx.vin[i].prevout];
}
uint32_t verify_flags = ParseScriptFlags(test[2].get_str());
fValid = VerifyScript(
tx.vin[i].scriptSig,
mapprevOutScriptPubKeys[tx.vin[i].prevout], verify_flags,
TransactionSignatureChecker(&tx, i, amount, txdata), &err);
}
BOOST_CHECK_MESSAGE(!fValid, strTest);
BOOST_CHECK_MESSAGE(err != ScriptError::OK, ScriptErrorString(err));
}
}
}
BOOST_AUTO_TEST_CASE(basic_transaction_tests) {
// Random real transaction
// (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
uint8_t ch[] = {
0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65,
0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8,
0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74,
0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00,
0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77,
0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f,
0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2,
0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e,
0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4,
0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2,
0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a,
0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34,
0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97,
0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b,
0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f,
0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef,
0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39,
0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00,
0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93,
0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43,
0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> vch(ch, ch + sizeof(ch) - 1);
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
CMutableTransaction tx;
stream >> tx;
TxValidationState state;
BOOST_CHECK_MESSAGE(CheckRegularTransaction(CTransaction(tx), state) &&
state.IsValid(),
"Simple deserialized transaction should be valid.");
// Check that duplicate txins fail
tx.vin.push_back(tx.vin[0]);
BOOST_CHECK_MESSAGE(!CheckRegularTransaction(CTransaction(tx), state) ||
!state.IsValid(),
"Transaction with duplicate txins should be invalid.");
}
BOOST_AUTO_TEST_CASE(test_Get) {
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(
keystore, coins, {{11 * CENT, 50 * CENT, 21 * CENT, 22 * CENT}});
CMutableTransaction t1;
t1.vin.resize(3);
t1.vin[0].prevout = COutPoint(dummyTransactions[0].GetId(), 1);
t1.vin[0].scriptSig << std::vector<uint8_t>(65, 0);
t1.vin[1].prevout = COutPoint(dummyTransactions[1].GetId(), 0);
t1.vin[1].scriptSig << std::vector<uint8_t>(65, 0)
<< std::vector<uint8_t>(33, 4);
t1.vin[2].prevout = COutPoint(dummyTransactions[1].GetId(), 1);
t1.vin[2].scriptSig << std::vector<uint8_t>(65, 0)
<< std::vector<uint8_t>(33, 4);
t1.vout.resize(2);
t1.vout[0].nValue = 90 * CENT;
t1.vout[0].scriptPubKey << OP_1;
BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins,
STANDARD_SCRIPT_VERIFY_FLAGS));
}
static void CreateCreditAndSpend(const FillableSigningProvider &keystore,
const CScript &outscript,
CTransactionRef &output,
CMutableTransaction &input,
bool success = true) {
CMutableTransaction outputm;
outputm.nVersion = 1;
outputm.vin.resize(1);
outputm.vin[0].prevout = COutPoint();
outputm.vin[0].scriptSig = CScript();
outputm.vout.resize(1);
outputm.vout[0].nValue = SATOSHI;
outputm.vout[0].scriptPubKey = outscript;
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
ssout << outputm;
ssout >> output;
BOOST_CHECK_EQUAL(output->vin.size(), 1UL);
BOOST_CHECK(output->vin[0] == outputm.vin[0]);
BOOST_CHECK_EQUAL(output->vout.size(), 1UL);
BOOST_CHECK(output->vout[0] == outputm.vout[0]);
CMutableTransaction inputm;
inputm.nVersion = 1;
inputm.vin.resize(1);
inputm.vin[0].prevout = COutPoint(output->GetId(), 0);
inputm.vout.resize(1);
inputm.vout[0].nValue = SATOSHI;
inputm.vout[0].scriptPubKey = CScript();
bool ret =
SignSignature(keystore, *output, inputm, 0, SigHashType().withForkId());
BOOST_CHECK_EQUAL(ret, success);
CDataStream ssin(SER_NETWORK, PROTOCOL_VERSION);
ssin << inputm;
ssin >> input;
BOOST_CHECK_EQUAL(input.vin.size(), 1UL);
BOOST_CHECK(input.vin[0] == inputm.vin[0]);
BOOST_CHECK_EQUAL(input.vout.size(), 1UL);
BOOST_CHECK(input.vout[0] == inputm.vout[0]);
}
static void CheckWithFlag(const CTransactionRef &output,
const CMutableTransaction &input, int flags,
bool success) {
ScriptError error;
CTransaction inputi(input);
bool ret = VerifyScript(
inputi.vin[0].scriptSig, output->vout[0].scriptPubKey,
flags | SCRIPT_ENABLE_SIGHASH_FORKID,
TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue),
&error);
BOOST_CHECK_EQUAL(ret, success);
}
static CScript PushAll(const std::vector<valtype> &values) {
CScript result;
for (const valtype &v : values) {
if (v.size() == 0) {
result << OP_0;
} else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) {
result << CScript::EncodeOP_N(v[0]);
} else {
result << v;
}
}
return result;
}
static void ReplaceRedeemScript(CScript &script, const CScript &redeemScript) {
std::vector<valtype> stack;
EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker());
BOOST_CHECK(stack.size() > 0);
stack.back() =
std::vector<uint8_t>(redeemScript.begin(), redeemScript.end());
script = PushAll(stack);
}
BOOST_AUTO_TEST_CASE(test_big_transaction) {
CKey key;
key.MakeNewKey(false);
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKeyPubKey(key, key.GetPubKey()));
CScript scriptPubKey = CScript()
<< ToByteVector(key.GetPubKey()) << OP_CHECKSIG;
std::vector<SigHashType> sigHashes;
sigHashes.emplace_back(SIGHASH_NONE | SIGHASH_FORKID);
sigHashes.emplace_back(SIGHASH_SINGLE | SIGHASH_FORKID);
sigHashes.emplace_back(SIGHASH_ALL | SIGHASH_FORKID);
sigHashes.emplace_back(SIGHASH_NONE | SIGHASH_FORKID |
SIGHASH_ANYONECANPAY);
sigHashes.emplace_back(SIGHASH_SINGLE | SIGHASH_FORKID |
SIGHASH_ANYONECANPAY);
sigHashes.emplace_back(SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY);
CMutableTransaction mtx;
mtx.nVersion = 1;
// create a big transaction of 4500 inputs signed by the same key.
const static size_t OUTPUT_COUNT = 4500;
mtx.vout.reserve(OUTPUT_COUNT);
for (size_t ij = 0; ij < OUTPUT_COUNT; ij++) {
size_t i = mtx.vin.size();
TxId prevId(uint256S("0000000000000000000000000000000000000000000000000"
"000000000000100"));
COutPoint outpoint(prevId, i);
mtx.vin.resize(mtx.vin.size() + 1);
mtx.vin[i].prevout = outpoint;
mtx.vin[i].scriptSig = CScript();
mtx.vout.emplace_back(1000 * SATOSHI, CScript() << OP_1);
}
// sign all inputs
for (size_t i = 0; i < mtx.vin.size(); i++) {
bool hashSigned =
SignSignature(keystore, scriptPubKey, mtx, i, 1000 * SATOSHI,
sigHashes.at(i % sigHashes.size()));
BOOST_CHECK_MESSAGE(hashSigned, "Failed to sign test transaction");
}
CTransaction tx(mtx);
// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
- boost::thread_group threadGroup;
CCheckQueue<CScriptCheck> scriptcheckqueue(128);
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
- for (int i = 0; i < 20; i++) {
- threadGroup.create_thread(std::bind(&CCheckQueue<CScriptCheck>::Thread,
- std::ref(scriptcheckqueue)));
- }
+ scriptcheckqueue.StartWorkerThreads(20);
std::vector<Coin> coins;
for (size_t i = 0; i < mtx.vin.size(); i++) {
CTxOut out;
out.nValue = 1000 * SATOSHI;
out.scriptPubKey = scriptPubKey;
coins.emplace_back(std::move(out), 1, false);
}
for (size_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
CScriptCheck check(coins[tx.vin[i].prevout.GetN()].GetTxOut(), tx, i,
STANDARD_SCRIPT_VERIFY_FLAGS, false, txdata);
vChecks.push_back(CScriptCheck());
check.swap(vChecks.back());
control.Add(vChecks);
}
bool controlCheck = control.Wait();
BOOST_CHECK(controlCheck);
-
- threadGroup.interrupt_all();
- threadGroup.join_all();
+ scriptcheckqueue.StopWorkerThreads();
}
SignatureData CombineSignatures(const CMutableTransaction &input1,
const CMutableTransaction &input2,
const CTransactionRef tx) {
SignatureData sigdata;
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
ProduceSignature(
DUMMY_SIGNING_PROVIDER,
MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue),
tx->vout[0].scriptPubKey, sigdata);
return sigdata;
}
BOOST_AUTO_TEST_CASE(test_witness) {
FillableSigningProvider keystore, keystore2;
CKey key1, key2, key3, key1L, key2L;
CPubKey pubkey1, pubkey2, pubkey3, pubkey1L, pubkey2L;
key1.MakeNewKey(true);
key2.MakeNewKey(true);
key3.MakeNewKey(true);
key1L.MakeNewKey(false);
key2L.MakeNewKey(false);
pubkey1 = key1.GetPubKey();
pubkey2 = key2.GetPubKey();
pubkey3 = key3.GetPubKey();
pubkey1L = key1L.GetPubKey();
pubkey2L = key2L.GetPubKey();
BOOST_CHECK(keystore.AddKeyPubKey(key1, pubkey1));
BOOST_CHECK(keystore.AddKeyPubKey(key2, pubkey2));
BOOST_CHECK(keystore.AddKeyPubKey(key1L, pubkey1L));
BOOST_CHECK(keystore.AddKeyPubKey(key2L, pubkey2L));
CScript scriptPubkey1, scriptPubkey2, scriptPubkey1L, scriptPubkey2L,
scriptMulti;
scriptPubkey1 << ToByteVector(pubkey1) << OP_CHECKSIG;
scriptPubkey2 << ToByteVector(pubkey2) << OP_CHECKSIG;
scriptPubkey1L << ToByteVector(pubkey1L) << OP_CHECKSIG;
scriptPubkey2L << ToByteVector(pubkey2L) << OP_CHECKSIG;
std::vector<CPubKey> oneandthree;
oneandthree.push_back(pubkey1);
oneandthree.push_back(pubkey3);
scriptMulti = GetScriptForMultisig(2, oneandthree);
BOOST_CHECK(keystore.AddCScript(scriptPubkey1));
BOOST_CHECK(keystore.AddCScript(scriptPubkey2));
BOOST_CHECK(keystore.AddCScript(scriptPubkey1L));
BOOST_CHECK(keystore.AddCScript(scriptPubkey2L));
BOOST_CHECK(keystore.AddCScript(scriptMulti));
BOOST_CHECK(keystore2.AddCScript(scriptMulti));
BOOST_CHECK(keystore2.AddKeyPubKey(key3, pubkey3));
CTransactionRef output1, output2;
CMutableTransaction input1, input2;
// Normal pay-to-compressed-pubkey.
CreateCreditAndSpend(keystore, scriptPubkey1, output1, input1);
CreateCreditAndSpend(keystore, scriptPubkey2, output2, input2);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
CheckWithFlag(output1, input2, 0, false);
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-compressed-pubkey.
CreateCreditAndSpend(keystore,
GetScriptForDestination(ScriptHash(scriptPubkey1)),
output1, input1);
CreateCreditAndSpend(keystore,
GetScriptForDestination(ScriptHash(scriptPubkey2)),
output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
CheckWithFlag(output1, input2, 0, true);
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// Normal pay-to-uncompressed-pubkey.
CreateCreditAndSpend(keystore, scriptPubkey1L, output1, input1);
CreateCreditAndSpend(keystore, scriptPubkey2L, output2, input2);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
CheckWithFlag(output1, input2, 0, false);
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH pay-to-uncompressed-pubkey.
CreateCreditAndSpend(keystore,
GetScriptForDestination(ScriptHash(scriptPubkey1L)),
output1, input1);
CreateCreditAndSpend(keystore,
GetScriptForDestination(ScriptHash(scriptPubkey2L)),
output2, input2);
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
CheckWithFlag(output1, input2, 0, true);
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// Normal 2-of-2 multisig
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
CheckWithFlag(output1, input1, 0, false);
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
CheckWithFlag(output2, input2, 0, false);
BOOST_CHECK(*output1 == *output2);
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
CreateCreditAndSpend(keystore,
GetScriptForDestination(ScriptHash(scriptMulti)),
output1, input1, false);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false);
CreateCreditAndSpend(keystore2,
GetScriptForDestination(ScriptHash(scriptMulti)),
output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
BOOST_CHECK(*output1 == *output2);
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
}
BOOST_AUTO_TEST_CASE(test_IsStandard) {
LOCK(cs_main);
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(
keystore, coins, {{11 * CENT, 50 * CENT, 21 * CENT, 22 * CENT}});
CMutableTransaction t;
t.vin.resize(1);
t.vin[0].prevout = COutPoint(dummyTransactions[0].GetId(), 1);
t.vin[0].scriptSig << std::vector<uint8_t>(65, 0);
t.vout.resize(1);
t.vout[0].nValue = 90 * CENT;
CKey key;
key.MakeNewKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Check dust with default relay fee:
Amount nDustThreshold = 3 * 182 * dustRelayFee.GetFeePerK() / 1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546 * SATOSHI);
// dust:
t.vout[0].nValue = nDustThreshold - SATOSHI;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "dust");
// not dust:
t.vout[0].nValue = nDustThreshold;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Disallowed nVersion
t.nVersion = -1;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "version");
t.nVersion = 0;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "version");
t.nVersion = 3;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "version");
// Allowed nVersion
t.nVersion = 1;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.nVersion = 2;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 1234 / 1000 * 3
dustRelayFee = CFeeRate(1234 * SATOSHI);
// dust:
t.vout[0].nValue = (672 - 1) * SATOSHI;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "dust");
// not dust:
t.vout[0].nValue = 672 * SATOSHI;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
t.vout[0].scriptPubKey = CScript() << OP_1;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("646578784062697477617463682e636f2092c558ed52c56d"
"8dd14ca76226bc936a84820d898443873eb03d8854b21fa3"
"952b99a2981873e74509281730d78a21786d34a38bd1ebab"
"822fad42278f7f4420db6ab1fd2b6826148d4f73bb41ec2d"
"40a6d5793d66e17074a0c56a8a7df21062308f483dd6e38d"
"53609d350038df0a1b2a9ac8332016e0b904f66880dd0108"
"81c4e8074cce8e4ad6c77cb3460e01bf0e7e811b5f945f83"
"732ba6677520a893d75d9a966cb8f85dc301656b1635c631"
"f5d00d4adf73f2dd112ca75cf19754651909becfbe65aed1"
"3afb2ab8");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("646578784062697477617463682e636f2092c558ed52c56d"
"8dd14ca76226bc936a84820d898443873eb03d8854b21fa3"
"952b99a2981873e74509281730d78a21786d34a38bd1ebab"
"822fad42278f7f4420db6ab1fd2b6826148d4f73bb41ec2d"
"40a6d5793d66e17074a0c56a8a7df21062308f483dd6e38d"
"53609d350038df0a1b2a9ac8332016e0b904f66880dd0108"
"81c4e8074cce8e4ad6c77cb3460e01bf0e7e811b5f945f83"
"732ba6677520a893d75d9a966cb8f85dc301656b1635c631"
"f5d00d4adf73f2dd112ca75cf19754651909becfbe65aed1"
"3afb2ab800");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
/**
* Check when a custom value is used for -datacarriersize .
*/
unsigned newMaxSize = 90;
gArgs.ForceSetArg("-datacarriersize", ToString(newMaxSize));
// Max user provided payload size is standard
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548"
"271967f1a67130b7105cd6a828e03909a67962e0ea1f61de"
"b649f6bc3f4cef3877696e64657878");
BOOST_CHECK_EQUAL(t.vout[0].scriptPubKey.size(), newMaxSize);
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Max user provided payload size + 1 is non-standard
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548"
"271967f1a67130b7105cd6a828e03909a67962e0ea1f61de"
"b649f6bc3f4cef3877696e6465787800");
BOOST_CHECK_EQUAL(t.vout[0].scriptPubKey.size(), newMaxSize + 1);
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
// Clear custom confirguration.
gArgs.ClearForcedArg("-datacarriersize");
// Data payload can be encoded in any way...
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript()
<< OP_RETURN << ParseHex("00") << ParseHex("01");
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()!
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0
<< ParseHex("01") << 2 << 3 << 4 << 5
<< 6 << 7 << 8 << 9 << 10 << 11 << 12
<< 13 << 14 << 15 << 16;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.vout[0].scriptPubKey = CScript()
<< OP_RETURN << 0 << ParseHex("01") << 2
<< ParseHex("fffffffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffffff");
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// ...so long as it only contains PUSHDATA's
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
// TxoutType::NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
// Only one TxoutType::NULL_DATA permitted in all cases
t.vout.resize(2);
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38");
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "multi-op-return");
t.vout[0].scriptPubKey =
CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "multi-op-return");
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "multi-op-return");
// Check large scriptSig (non-standard if size is >1650 bytes)
t.vout.resize(1);
t.vout[0].nValue = MAX_MONEY;
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
// OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes
t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(1647, 0); // 1650
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(1648, 0); // 1651
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptsig-size");
// Check scriptSig format (non-standard if there are any other ops than just
// PUSHs)
t.vin[0].scriptSig = CScript()
// OP_n (single byte pushes: n = 1, 0, -1, 16)
<< OP_TRUE << OP_0 << OP_1NEGATE
<< OP_16
// OP_PUSHx [...x bytes...]
<< std::vector<uint8_t>(75, 0)
// OP_PUSHDATA1 x [...x bytes...]
<< std::vector<uint8_t>(235, 0)
// OP_PUSHDATA2 x [...x bytes...]
<< std::vector<uint8_t>(1234, 0) << OP_9;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
const std::vector<uint8_t> non_push_ops = {
// arbitrary set of non-push operations
OP_NOP,
OP_VERIFY,
OP_IF,
OP_ROT,
OP_3DUP,
OP_SIZE,
OP_EQUAL,
OP_ADD,
OP_SUB,
OP_HASH256,
OP_CODESEPARATOR,
OP_CHECKSIG,
OP_CHECKLOCKTIMEVERIFY};
CScript::const_iterator pc = t.vin[0].scriptSig.begin();
while (pc < t.vin[0].scriptSig.end()) {
opcodetype opcode;
CScript::const_iterator prev_pc = pc;
// advance to next op
t.vin[0].scriptSig.GetOp(pc, opcode);
// for the sake of simplicity, we only replace single-byte push
// operations
if (opcode >= 1 && opcode <= OP_PUSHDATA4) {
continue;
}
int index = prev_pc - t.vin[0].scriptSig.begin();
// save op
uint8_t orig_op = *prev_pc;
// replace current push-op with each non-push-op
for (auto op : non_push_ops) {
t.vin[0].scriptSig[index] = op;
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptsig-not-pushonly");
}
// restore op
t.vin[0].scriptSig[index] = orig_op;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
}
// Check bare multisig (standard if policy flag fIsBareMultisigStd is set)
fIsBareMultisigStd = true;
// simple 1-of-1
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()});
t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(65, 0);
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
fIsBareMultisigStd = false;
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "bare-multisig");
fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
}
BOOST_AUTO_TEST_CASE(txsize_activation_test) {
const Config &config = GetConfig();
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus();
const int32_t magneticAnomalyActivationHeight =
params.magneticAnomalyHeight;
// A minimaly sized transction.
CTransaction minTx;
TxValidationState state;
BOOST_CHECK(ContextualCheckTransaction(
params, minTx, state, magneticAnomalyActivationHeight - 1, 5678, 1234));
BOOST_CHECK(!ContextualCheckTransaction(
params, minTx, state, magneticAnomalyActivationHeight, 5678, 1234));
BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-undersize");
}
BOOST_AUTO_TEST_CASE(tx_getvalueout) {
CMutableTransaction mtx;
// Negative output value
mtx.vout.resize(1);
mtx.vout[0].nValue = -1 * SATOSHI;
CTransaction negative_tx{mtx};
BOOST_CHECK_THROW(negative_tx.GetValueOut(), std::runtime_error);
// Good output
mtx.vout[0].nValue = 10000 * SATOSHI;
CTransaction valid_one_output_tx{mtx};
BOOST_CHECK_EQUAL(valid_one_output_tx.GetValueOut(), 10000 * SATOSHI);
// Maximum output
mtx.vout[0].nValue = MAX_MONEY;
CTransaction max_one_output_tx{mtx};
BOOST_CHECK_EQUAL(max_one_output_tx.GetValueOut(), MAX_MONEY);
// Too high output
mtx.vout[0].nValue = MAX_MONEY + 1 * SATOSHI;
CTransaction too_high_tx{mtx};
BOOST_CHECK_THROW(too_high_tx.GetValueOut(), std::runtime_error);
// Valid sum
mtx.vout.resize(2);
mtx.vout[0].nValue = 42 * SATOSHI;
mtx.vout[1].nValue = 1337 * SATOSHI;
CTransaction valid_tx{mtx};
BOOST_CHECK_EQUAL(valid_tx.GetValueOut(), 1379 * SATOSHI);
// Maximum sum
mtx.vout[0].nValue = MAX_MONEY - 1 * SATOSHI;
mtx.vout[1].nValue = 1 * SATOSHI;
CTransaction max_two_outputs_tx{mtx};
BOOST_CHECK_EQUAL(max_two_outputs_tx.GetValueOut(), MAX_MONEY);
// Too high sum
mtx.vout[0].nValue = MAX_MONEY - 1 * SATOSHI;
mtx.vout[1].nValue = 2 * SATOSHI;
CTransaction too_high_sum_tx{mtx};
BOOST_CHECK_THROW(too_high_sum_tx.GetValueOut(), std::runtime_error);
// First output valid, but the second output would cause an int64 overflow.
// This issue was encountered while fuzzing:
// https://github.com/bitcoin/bitcoin/issues/18046
mtx.vout[0].nValue = 2 * SATOSHI;
mtx.vout[1].nValue = (std::numeric_limits<int64_t>::max() - 1) * SATOSHI;
CTransaction overflow_sum_tx{mtx};
BOOST_CHECK_THROW(overflow_sum_tx.GetValueOut(), std::runtime_error);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 10fdd5af8..2e451fae6 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -1,410 +1,409 @@
// Copyright (c) 2011-2019 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 <test/util/setup_common.h>
#include <banman.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <init.h>
#include <interfaces/chain.h>
#include <logging.h>
#include <miner.h>
#include <net.h>
#include <net_processing.h>
#include <noui.h>
#include <pow/pow.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <scheduler.h>
#include <script/script_error.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <streams.h>
#include <txdb.h>
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/time.h>
#include <util/translation.h>
#include <util/vector.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <functional>
#include <memory>
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
FastRandomContext g_insecure_rand_ctx;
/**
* Random context to get unique temp data dirs. Separate from
* g_insecure_rand_ctx, which can be seeded from a const env var
*/
static FastRandomContext g_insecure_rand_ctx_temp_path;
/**
* Return the unsigned from the environment var if available,
* otherwise 0
*/
static uint256 GetUintFromEnv(const std::string &env_name) {
const char *num = std::getenv(env_name.c_str());
if (!num) {
return {};
}
return uint256S(num);
}
void Seed(FastRandomContext &ctx) {
// Should be enough to get the seed once for the process
static uint256 seed{};
static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"};
if (seed.IsNull()) {
seed = GetUintFromEnv(RANDOM_CTX_SEED);
}
if (seed.IsNull()) {
seed = GetRandHash();
}
LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__,
RANDOM_CTX_SEED, seed.GetHex());
ctx = FastRandomContext(seed);
}
std::ostream &operator<<(std::ostream &os, const uint256 &num) {
os << num.ToString();
return os;
}
std::ostream &operator<<(std::ostream &os, const ScriptError &err) {
os << ScriptErrorString(err);
return os;
}
std::vector<const char *> fixture_extra_args{};
BasicTestingSetup::BasicTestingSetup(
const std::string &chainName, const std::vector<const char *> &extra_args)
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME /
g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{} {
// clang-format off
std::vector<const char *> arguments = Cat(
{
"dummy",
"-printtoconsole=0",
"-logsourcelocations",
"-logtimemicros",
"-debug",
"-debugexclude=libevent",
"-debugexclude=leveldb",
},
extra_args);
// clang-format on
arguments = Cat(arguments, fixture_extra_args);
auto &config = const_cast<Config &>(GetConfig());
SetMockTime(0);
fs::create_directories(m_path_root);
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ClearPathCache();
{
SetupServerArgs(m_node);
std::string error;
const bool success{m_node.args->ParseParameters(
arguments.size(), arguments.data(), error)};
assert(success);
assert(error.empty());
}
SelectParams(chainName);
SeedInsecureRand();
InitLogging(*m_node.args);
AppInitParameterInteraction(config, *m_node.args);
LogInstance().StartLogging();
SHA256AutoDetect();
ECC_Start();
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
InitScriptExecutionCache();
m_node.chain = interfaces::MakeChain(m_node, config.GetChainParams());
g_wallet_init_interface.Construct(m_node);
fCheckBlockIndex = true;
static bool noui_connected = false;
if (!noui_connected) {
noui_connect();
noui_connected = true;
}
}
BasicTestingSetup::~BasicTestingSetup() {
LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
gArgs.ClearArgs();
ECC_Stop();
}
TestingSetup::TestingSetup(const std::string &chainName,
const std::vector<const char *> &extra_args)
: BasicTestingSetup(chainName, extra_args) {
const Config &config = GetConfig();
const CChainParams &chainparams = config.GetChainParams();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RPCServer rpcServer;
RegisterAllRPCCommands(config, rpcServer, tableRPC);
/**
* RPC does not come out of the warmup state on its own. Normally, this is
* handled in bitcoind's init path, but unit tests do not trigger this
* codepath, so we call it explicitly as part of setup.
*/
std::string rpcWarmupStatus;
if (RPCIsInWarmup(&rpcWarmupStatus)) {
SetRPCWarmupFinished();
}
m_node.scheduler = std::make_unique<CScheduler>();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
threadGroup.create_thread([&] { m_node.scheduler->serviceQueue(); });
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
m_node.mempool = std::make_unique<CTxMemPool>(1);
m_node.chainman = &::g_chainman;
m_node.chainman->InitializeChainstate(*m_node.mempool);
::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true,
/* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk());
::ChainstateActive().InitCoinsCache(1 << 23);
assert(::ChainstateActive().CanFlushToDisk());
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
{
BlockValidationState state;
if (!ActivateBestChain(config, state)) {
throw std::runtime_error(
strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
}
constexpr int script_check_threads = 2;
- for (int i = 0; i < script_check_threads; ++i) {
- threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
- }
+ StartScriptCheckWorkerThreads(script_check_threads);
m_node.banman = std::make_unique<BanMan>(
m_args.GetDataDirPath() / "banlist.dat", chainparams, nullptr,
DEFAULT_MISBEHAVING_BANTIME);
// Deterministic randomness for tests.
m_node.connman = std::make_unique<CConnman>(config, 0x1337, 0x1337);
m_node.peerman = PeerManager::make(
chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler,
*m_node.chainman, *m_node.mempool, false);
{
CConnman::Options options;
options.m_msgproc = m_node.peerman.get();
m_node.connman->Init(options);
}
}
TestingSetup::~TestingSetup() {
if (m_node.scheduler) {
m_node.scheduler->stop();
}
threadGroup.interrupt_all();
threadGroup.join_all();
+ StopScriptCheckWorkerThreads();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
m_node.connman.reset();
m_node.banman.reset();
m_node.args = nullptr;
UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman);
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
m_node.chainman = nullptr;
pblocktree.reset();
}
TestChain100Setup::TestChain100Setup() {
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey())
<< OP_CHECKSIG;
for (int i = 0; i < COINBASE_MATURITY; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey) {
const Config &config = GetConfig();
CTxMemPool empty_pool;
CBlock block =
BlockAssembler(config, empty_pool).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction &tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
// Order transactions by canonical order
std::sort(std::begin(block.vtx) + 1, std::end(block.vtx),
[](const std::shared_ptr<const CTransaction> &txa,
const std::shared_ptr<const CTransaction> &txb) -> bool {
return txa->GetId() < txb->GetId();
});
// IncrementExtraNonce creates a valid coinbase and merkleRoot
{
LOCK(cs_main);
unsigned int extraNonce = 0;
IncrementExtraNonce(&block, ::ChainActive().Tip(),
config.GetMaxBlockSize(), extraNonce);
}
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus();
while (!CheckProofOfWork(block.GetHash(), block.nBits, params)) {
++block.nNonce;
}
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
Assert(m_node.chainman)
->ProcessNewBlock(config, shared_pblock, true, nullptr);
return block;
}
TestChain100Setup::~TestChain100Setup() {}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
return FromTx(MakeTransactionRef(tx));
}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef &tx) {
return CTxMemPoolEntry(tx, nFee, nTime, nHeight, spendsCoinbase,
nSigOpCount, LockPoints());
}
/**
* @returns a real block
* (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) with 9
* txs.
*/
CBlock getBlock13b8a() {
CBlock block;
CDataStream stream(
ParseHex(
"0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb680000000"
"0000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558"
"da2fdb261b4d4c86041b1ab1bf9309010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0146ffff"
"ffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf2"
"54bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c"
"4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada9027"
"80da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100da"
"b24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd0221"
"00fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb4"
"01ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369e"
"d2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98e"
"c706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad"
"2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21ad"
"c6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e86"
"25a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d8982"
"35e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff02"
"80969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388"
"ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd3"
"88ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c522"
"92d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3"
"889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2f"
"afd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d3"
"1b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83aba"
"f975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cb"
"cba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044"
"022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae7406056"
"58022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e0100"
"3614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c342"
"3e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc"
"2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13"
"fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021"
"e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91"
"c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b4830"
"4502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9"
"236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddc"
"ce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942"
"fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad5652937"
"1864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd9"
"0111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37"
"f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b"
"25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d0014104"
"43bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf"
"7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffff"
"ffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a1232"
"2d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70a"
"e67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176"
"f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf"
"062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b956"
"00db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd40"
"67b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c6"
"9b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e"
"58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b217901000000"
"8b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3c"
"a2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b49"
"1d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33"
"d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312e"
"f1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144a"
"f553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd"
"48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b"
"659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0"
"aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc3"
"1895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9"
"944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830"
"450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1a"
"f03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d9"
"8a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a03"
"8fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261"
"b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f6351285"
"0811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa4207"
"0082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0"
"c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c0000000000"
"1976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000"
"000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed"
"8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc"
"87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded"
"4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e"
"3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29"
"934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695"
"a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93"
"376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e9"
"1349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49"
"304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654"
"d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66"
"ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e88"
"60c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8"
"fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb604203"
"4aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75e"
"ef7942fc9288edd37c32f5c388ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
return block;
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 3a8ede489..4c38ba31f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1,6121 +1,6124 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Copyright (c) 2017-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <validation.h>
#include <arith_uint256.h>
#include <avalanche/avalanche.h>
#include <avalanche/processor.h>
#include <blockdb.h>
#include <blockvalidity.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <checkqueue.h>
#include <config.h>
#include <consensus/activation.h>
#include <consensus/merkle.h>
#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <hash.h>
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
#include <minerfund.h>
#include <node/ui_interface.h>
#include <policy/fees.h>
#include <policy/mempool.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <pow/aserti32d.h> // For ResetASERTAnchorBlockCache
#include <pow/pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <reverse_iterator.h>
#include <script/script.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <shutdown.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
#include <undo.h>
#include <util/check.h> // For NDEBUG compile time check
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
#include <validationinterface.h>
#include <warnings.h>
#include <boost/algorithm/string/replace.hpp>
#include <optional>
#include <string>
#include <thread>
#define MICRO 0.000001
#define MILLI 0.001
/** Time to wait between writing blocks/block index to disk. */
static constexpr std::chrono::hours DATABASE_WRITE_INTERVAL{1};
/** Time to wait between flushing chainstate to disk. */
static constexpr std::chrono::hours DATABASE_FLUSH_INTERVAL{24};
const std::vector<std::string> CHECKLEVEL_DOC{
"level 0 reads the blocks from disk",
"level 1 verifies block validity",
"level 2 verifies undo data",
"level 3 checks disconnection of tip blocks",
"level 4 tries to reconnect the blocks",
"each level includes the checks of the previous levels",
};
ChainstateManager g_chainman;
CChainState &ChainstateActive() {
LOCK(::cs_main);
assert(g_chainman.m_active_chainstate);
return *g_chainman.m_active_chainstate;
}
CChain &ChainActive() {
LOCK(::cs_main);
return ::ChainstateActive().m_chain;
}
/**
* Global state
*
* Mutex to guard access to validation specific variables, such as reading
* or changing the chainstate.
*
* This may also need to be locked when updating the transaction pool, e.g. on
* AcceptToMemoryPool. See CTxMemPool::cs comment for details.
*
* The transaction pool has a separate lock to allow reading from it and the
* chainstate at the same time.
*/
RecursiveMutex cs_main;
CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
bool fPruneMode = false;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
uint64_t nPruneTarget = 0;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
BlockHash hashAssumeValid;
arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB);
// Internal stuff
namespace {
CBlockIndex *pindexBestInvalid = nullptr;
CBlockIndex *pindexBestParked = nullptr;
RecursiveMutex cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
int nLastBlockFile = 0;
/**
* Global flag to indicate we should check to see if there are block/undo files
* that should be deleted. Set on startup or if we allocate more file space when
* we're in prune mode.
*/
bool fCheckForPruning = false;
/** Dirty block index entries. */
std::set<const CBlockIndex *> setDirtyBlockIndex;
/** Dirty block file entries. */
std::set<int> setDirtyFileInfo;
} // namespace
BlockValidationOptions::BlockValidationOptions(const Config &config)
: excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true),
checkMerkleRoot(true) {}
CBlockIndex *LookupBlockIndex(const BlockHash &hash) {
AssertLockHeld(cs_main);
BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash);
return it == g_chainman.BlockIndex().end() ? nullptr : it->second;
}
CBlockIndex *FindForkInGlobalIndex(const CChain &chain,
const CBlockLocator &locator) {
AssertLockHeld(cs_main);
// Find the latest block common to locator and chain - we expect that
// locator.vHave is sorted descending by height.
for (const BlockHash &hash : locator.vHave) {
CBlockIndex *pindex = LookupBlockIndex(hash);
if (pindex) {
if (chain.Contains(pindex)) {
return pindex;
}
if (pindex->GetAncestor(chain.Height()) == chain.Tip()) {
return chain.Tip();
}
}
}
return chain.Genesis();
}
std::unique_ptr<CBlockTreeDB> pblocktree;
static uint32_t GetNextBlockScriptFlags(const Consensus::Params ¶ms,
const CBlockIndex *pindex);
bool TestLockPointValidity(const LockPoints *lp) {
AssertLockHeld(cs_main);
assert(lp);
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the
// chain
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which
// the LockPoints calculation was valid. If not LockPoints are no longer
// valid.
if (!::ChainActive().Contains(lp->maxInputBlock)) {
return false;
}
}
// LockPoints still valid
return true;
}
bool CheckSequenceLocks(const CTxMemPool &pool, const CTransaction &tx,
int flags, LockPoints *lp, bool useExistingLockPoints) {
AssertLockHeld(cs_main);
AssertLockHeld(pool.cs);
CBlockIndex *tip = ::ChainActive().Tip();
assert(tip != nullptr);
CBlockIndex index;
index.pprev = tip;
// CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate height
// based locks because when SequenceLocks() is called within ConnectBlock(),
// the height of the block *being* evaluated is what is used. Thus if we
// want to know if a transaction can be part of the *next* block, we need to
// use one more than ::ChainActive().Height()
index.nHeight = tip->nHeight + 1;
std::pair<int, int64_t> lockPair;
if (useExistingLockPoints) {
assert(lp);
lockPair.first = lp->height;
lockPair.second = lp->time;
} else {
// CoinsTip() contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
const CTxIn &txin = tx.vin[txinIndex];
Coin coin;
if (!viewMemPool.GetCoin(txin.prevout, coin)) {
return error("%s: Missing input", __func__);
}
if (coin.GetHeight() == MEMPOOL_HEIGHT) {
// Assume all mempool transaction confirm in the next block
prevheights[txinIndex] = tip->nHeight + 1;
} else {
prevheights[txinIndex] = coin.GetHeight();
}
}
lockPair = CalculateSequenceLocks(tx, flags, prevheights, index);
if (lp) {
lp->height = lockPair.first;
lp->time = lockPair.second;
// Also store the hash of the block with the highest height of all
// the blocks which have sequence locked prevouts. This hash needs
// to still be on the chain for these LockPoint calculations to be
// valid.
// Note: It is impossible to correctly calculate a maxInputBlock if
// any of the sequence locked inputs depend on unconfirmed txs,
// except in the special case where the relative lock time/height is
// 0, which is equivalent to no sequence lock. Since we assume input
// height of tip+1 for mempool txs and test the resulting lockPair
// from CalculateSequenceLocks against tip+1. We know
// EvaluateSequenceLocks will fail if there was a non-zero sequence
// lock on a mempool input, so we can use the return value of
// CheckSequenceLocks to indicate the LockPoints validity.
int maxInputHeight = 0;
for (const int height : prevheights) {
// Can ignore mempool inputs since we'll fail if they had
// non-zero locks.
if (height != tip->nHeight + 1) {
maxInputHeight = std::max(maxInputHeight, height);
}
}
lp->maxInputBlock = tip->GetAncestor(maxInputHeight);
}
}
return EvaluateSequenceLocks(index, lockPair);
}
// Command-line argument "-replayprotectionactivationtime=<timestamp>" will
// cause the node to switch to replay protected SigHash ForkID value when the
// median timestamp of the previous 11 blocks is greater than or equal to
// <timestamp>. Defaults to the pre-defined timestamp when not set.
static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms,
int64_t nMedianTimePast) {
return nMedianTimePast >= gArgs.GetArg("-replayprotectionactivationtime",
params.gluonActivationTime);
}
static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms,
const CBlockIndex *pindexPrev) {
if (pindexPrev == nullptr) {
return false;
}
return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast());
}
// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool
// were somehow broken and returning the wrong scriptPubKeys
static bool CheckInputsFromMempoolAndCache(
const CTransaction &tx, TxValidationState &state,
const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags,
PrecomputedTransactionData &txdata, int &nSigChecksOut)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
// pool.cs should be locked already, but go ahead and re-take the lock here
// to enforce that mempool doesn't change between when we check the view and
// when we actually call through to CheckInputScripts
LOCK(pool.cs);
assert(!tx.IsCoinBase());
for (const CTxIn &txin : tx.vin) {
const Coin &coin = view.AccessCoin(txin.prevout);
// AcceptToMemoryPoolWorker has already checked that the coins are
// available, so this shouldn't fail. If the inputs are not available
// here then return false.
if (coin.IsSpent()) {
return false;
}
// Check equivalence for available inputs.
const CTransactionRef &txFrom = pool.get(txin.prevout.GetTxId());
if (txFrom) {
assert(txFrom->GetId() == txin.prevout.GetTxId());
assert(txFrom->vout.size() > txin.prevout.GetN());
assert(txFrom->vout[txin.prevout.GetN()] == coin.GetTxOut());
} else {
const Coin &coinFromDisk =
::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
assert(!coinFromDisk.IsSpent());
assert(coinFromDisk.GetTxOut() == coin.GetTxOut());
}
}
// Call CheckInputScripts() to cache signature and script validity against
// current tip consensus rules.
return CheckInputScripts(tx, state, view, flags, /* cacheSigStore = */ true,
/* cacheFullScriptStore = */ true, txdata,
nSigChecksOut);
}
namespace {
class MemPoolAccept {
public:
MemPoolAccept(CTxMemPool &mempool)
: m_pool(mempool), m_view(&m_dummy),
m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool),
m_limit_ancestors(
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
m_limit_ancestor_size(
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) *
1000),
m_limit_descendants(
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize",
DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000) {}
// We put the arguments we're handed into a struct, so we can pass them
// around easier.
struct ATMPArgs {
const Config &m_config;
TxValidationState &m_state;
const int64_t m_accept_time;
const bool m_bypass_limits;
/*
* Return any outpoints which were not previously present in the coins
* cache, but were added as a result of validating the tx for mempool
* acceptance. This allows the caller to optionally remove the cache
* additions if the associated transaction ends up being rejected by
* the mempool.
*/
std::vector<COutPoint> &m_coins_to_uncache;
const bool m_test_accept;
Amount *m_fee_out;
};
// Single transaction acceptance
bool AcceptSingleTransaction(const CTransactionRef &ptx, ATMPArgs &args)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
private:
// All the intermediate state that gets passed between the various levels
// of checking a given transaction.
struct Workspace {
Workspace(const CTransactionRef &ptx,
const uint32_t next_block_script_verify_flags)
: m_ptx(ptx),
m_next_block_script_verify_flags(next_block_script_verify_flags) {
}
CTxMemPool::setEntries m_ancestors;
std::unique_ptr<CTxMemPoolEntry> m_entry;
Amount m_modified_fees;
const CTransactionRef &m_ptx;
// ABC specific flags that are used in both PreChecks and
// ConsensusScriptChecks
const uint32_t m_next_block_script_verify_flags;
int m_sig_checks_standard;
};
// Run the policy checks on a given transaction, excluding any script
// checks. Looks up inputs, calculates feerate, considers replacement,
// evaluates package limits, etc. As this function can be invoked for "free"
// by a peer, only tests that are fast should be done here (to avoid CPU
// DoS).
bool PreChecks(ATMPArgs &args, Workspace &ws)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Re-run the script checks, using consensus flags, and try to cache the
// result in the scriptcache. This should be done after
// PolicyScriptChecks(). This requires that all inputs either be in our
// utxo set or in the mempool.
bool ConsensusScriptChecks(ATMPArgs &args, Workspace &ws,
PrecomputedTransactionData &txdata)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Try to add the transaction to the mempool, removing any conflicts first.
// Returns true if the transaction is in the mempool after any size
// limiting is performed, false otherwise.
bool Finalize(ATMPArgs &args, Workspace &ws)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
private:
CTxMemPool &m_pool;
CCoinsViewCache m_view;
CCoinsViewMemPool m_viewmempool;
CCoinsView m_dummy;
// The package limits in effect at the time of invocation.
const size_t m_limit_ancestors;
const size_t m_limit_ancestor_size;
// These may be modified while evaluating a transaction (eg to account for
// in-mempool conflicts; see below).
size_t m_limit_descendants;
size_t m_limit_descendant_size;
};
bool MemPoolAccept::PreChecks(ATMPArgs &args, Workspace &ws) {
const CTransactionRef &ptx = ws.m_ptx;
const CTransaction &tx = *ws.m_ptx;
const TxId &txid = ws.m_ptx->GetId();
// Copy/alias what we need out of args
TxValidationState &state = args.m_state;
const int64_t nAcceptTime = args.m_accept_time;
const bool bypass_limits = args.m_bypass_limits;
std::vector<COutPoint> &coins_to_uncache = args.m_coins_to_uncache;
// Alias what we need out of ws
CTxMemPool::setEntries &setAncestors = ws.m_ancestors;
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry;
Amount &nModifiedFees = ws.m_modified_fees;
// Coinbase is only valid in a block, not as a loose transaction.
if (!CheckRegularTransaction(tx, state)) {
// state filled in by CheckRegularTransaction.
return false;
}
// Rather not work on nonstandard transactions (unless -testnet)
std::string reason;
if (fRequireStandard && !IsStandardTx(tx, reason)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
}
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
TxValidationState ctxState;
if (!ContextualCheckTransactionForCurrentBlock(
args.m_config.GetChainParams().GetConsensus(), tx, ctxState,
STANDARD_LOCKTIME_VERIFY_FLAGS)) {
// We copy the state from a dummy to ensure we don't increase the
// ban score of peer for transaction that could be valid in the future.
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND,
ctxState.GetRejectReason(),
ctxState.GetDebugMessage());
}
// Is it already in the memory pool?
if (m_pool.exists(txid)) {
return state.Invalid(TxValidationResult::TX_CONFLICT,
"txn-already-in-mempool");
}
// Check for conflicts with in-memory transactions
for (const CTxIn &txin : tx.vin) {
auto itConflicting = m_pool.mapNextTx.find(txin.prevout);
if (itConflicting != m_pool.mapNextTx.end()) {
// Disable replacement feature for good
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"txn-mempool-conflict");
}
}
LockPoints lp;
m_view.SetBackend(m_viewmempool);
CCoinsViewCache &coins_cache = ::ChainstateActive().CoinsTip();
// Do all inputs exist?
for (const CTxIn &txin : tx.vin) {
if (!coins_cache.HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
// Note: this call may add txin.prevout to the coins cache
// (coins_cache.cacheCoins) by way of FetchCoin(). It should be
// removed later (via coins_to_uncache) if this tx turns out to be
// invalid.
if (!m_view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for
// outputs.
if (coins_cache.HaveCoinInCache(COutPoint(txid, out))) {
return state.Invalid(TxValidationResult::TX_CONFLICT,
"txn-already-known");
}
}
// Otherwise assume this might be an orphan tx for which we just
// haven't seen parents yet.
return state.Invalid(TxValidationResult::TX_MISSING_INPUTS,
"bad-txns-inputs-missingorspent");
}
}
// Are the actual inputs available?
if (!m_view.HaveInputs(tx)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"bad-txns-inputs-spent");
}
// Bring the best block into scope.
m_view.GetBestBlock();
// we have all inputs cached now, so switch back to dummy (to protect
// against bugs where we pull more inputs from disk that miss being
// added to coins_to_uncache)
m_view.SetBackend(m_dummy);
// Only accept BIP68 sequence locked transactions that can be mined in
// the next block; we don't want our mempool filled up with transactions
// that can't be mined yet. Must keep pool.cs for this unless we change
// CheckSequenceLocks to take a CoinsViewCache instead of create its
// own.
if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) {
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND,
"non-BIP68-final");
}
Amount nFees = Amount::zero();
if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view),
nFees)) {
// state filled in by CheckTxInputs
return false;
}
// If fee_out is passed, return the fee to the caller
if (args.m_fee_out) {
*args.m_fee_out = nFees;
}
// Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard &&
!AreInputsStandard(tx, m_view, ws.m_next_block_script_verify_flags)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD,
"bad-txns-nonstandard-inputs");
}
// nModifiedFees includes any fee deltas from PrioritiseTransaction
nModifiedFees = nFees;
m_pool.ApplyDelta(txid, nModifiedFees);
// Keep track of transactions that spend a coinbase, which we re-scan
// during reorgs to ensure COINBASE_MATURITY is still met.
bool fSpendsCoinbase = false;
for (const CTxIn &txin : tx.vin) {
const Coin &coin = m_view.AccessCoin(txin.prevout);
if (coin.IsCoinBase()) {
fSpendsCoinbase = true;
break;
}
}
unsigned int nSize = tx.GetTotalSize();
// No transactions are allowed below minRelayTxFee except from disconnected
// blocks.
// Do not change this to use virtualsize without coordinating a network
// policy upgrade.
if (!bypass_limits && nModifiedFees < minRelayTxFee.GetFee(nSize)) {
return state.Invalid(
TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
}
// Validate input scripts against standard script flags.
const uint32_t scriptVerifyFlags =
ws.m_next_block_script_verify_flags | STANDARD_SCRIPT_VERIFY_FLAGS;
PrecomputedTransactionData txdata(tx);
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false,
txdata, ws.m_sig_checks_standard)) {
// State filled in by CheckInputScripts
return false;
}
entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime,
::ChainActive().Height(), fSpendsCoinbase,
ws.m_sig_checks_standard, lp));
unsigned int nVirtualSize = entry->GetTxVirtualSize();
Amount mempoolRejectFee =
m_pool
.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) *
1000000)
.GetFee(nVirtualSize);
if (!bypass_limits && mempoolRejectFee > Amount::zero() &&
nModifiedFees < mempoolRejectFee) {
return state.Invalid(
TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met",
strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
}
// Calculate in-mempool ancestors, up to a limit.
std::string errString;
if (!m_pool.CalculateMemPoolAncestors(
*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size,
m_limit_descendants, m_limit_descendant_size, errString)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"too-long-mempool-chain", errString);
}
return true;
}
bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs &args, Workspace &ws,
PrecomputedTransactionData &txdata) {
const CTransaction &tx = *ws.m_ptx;
const TxId &txid = tx.GetId();
TxValidationState &state = args.m_state;
// Check again against the next block's script verification flags
// to cache our script execution flags.
//
// This is also useful in case of bugs in the standard flags that cause
// transactions to pass as valid when they're actually invalid. For
// instance the STRICTENC flag was incorrectly allowing certain CHECKSIG
// NOT scripts to pass, even though they were invalid.
//
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
int nSigChecksConsensus;
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool,
ws.m_next_block_script_verify_flags,
txdata, nSigChecksConsensus)) {
// This can occur under some circumstances, if the node receives an
// unrequested tx which is invalid due to new consensus rules not
// being activated yet (during IBD).
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed "
"against next-block but not STANDARD flags %s, %s",
__func__, txid.ToString(), state.ToString());
}
if (ws.m_sig_checks_standard != nSigChecksConsensus) {
// We can't accept this transaction as we've used the standard count
// for the mempool/mining, but the consensus count will be enforced
// in validation (we don't want to produce bad block templates).
return error(
"%s: BUG! PLEASE REPORT THIS! SigChecks count differed between "
"standard and consensus flags in %s",
__func__, txid.ToString());
}
return true;
}
bool MemPoolAccept::Finalize(ATMPArgs &args, Workspace &ws) {
const TxId &txid = ws.m_ptx->GetId();
TxValidationState &state = args.m_state;
const bool bypass_limits = args.m_bypass_limits;
CTxMemPool::setEntries &setAncestors = ws.m_ancestors;
std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry;
// Store transaction in memory.
m_pool.addUnchecked(*entry, setAncestors);
// Trim mempool and check if tx was trimmed.
if (!bypass_limits) {
m_pool.LimitSize(
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
std::chrono::hours{
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(txid)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"mempool full");
}
}
return true;
}
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx,
ATMPArgs &args) {
AssertLockHeld(cs_main);
// mempool "read lock" (held through
// GetMainSignals().TransactionAddedToMempool())
LOCK(m_pool.cs);
Workspace workspace(ptx, GetNextBlockScriptFlags(
args.m_config.GetChainParams().GetConsensus(),
::ChainActive().Tip()));
if (!PreChecks(args, workspace)) {
return false;
}
// Only compute the precomputed transaction data if we need to verify
// scripts (ie, other policy checks pass). We perform the inexpensive
// checks first and avoid hashing and signature verification unless those
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata(*ptx);
if (!ConsensusScriptChecks(args, workspace, txdata)) {
return false;
}
// Tx was accepted, but not added
if (args.m_test_accept) {
return true;
}
if (!Finalize(args, workspace)) {
return false;
}
GetMainSignals().TransactionAddedToMempool(
ptx, m_pool.GetAndIncrementSequence());
return true;
}
} // namespace
/**
* (try to) add transaction to memory pool with a specified acceptance time.
*/
static bool
AcceptToMemoryPoolWithTime(const Config &config, CTxMemPool &pool,
TxValidationState &state, const CTransactionRef &tx,
int64_t nAcceptTime, bool bypass_limits,
bool test_accept, Amount *fee_out = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
std::vector<COutPoint> coins_to_uncache;
MemPoolAccept::ATMPArgs args{
config, state, nAcceptTime, bypass_limits,
coins_to_uncache, test_accept, fee_out};
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
if (!res) {
// Remove coins that were not present in the coins cache before calling
// ATMPW; this is to prevent memory DoS in case we receive a large
// number of invalid transactions that attempt to overrun the in-memory
// coins cache
// (`CCoinsViewCache::cacheCoins`).
for (const COutPoint &outpoint : coins_to_uncache) {
::ChainstateActive().CoinsTip().Uncache(outpoint);
}
}
// After we've (potentially) uncached entries, ensure our coins cache is
// still within its size limits
BlockValidationState stateDummy;
::ChainstateActive().FlushStateToDisk(config.GetChainParams(), stateDummy,
FlushStateMode::PERIODIC);
return res;
}
bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool,
TxValidationState &state, const CTransactionRef &tx,
bool bypass_limits, bool test_accept, Amount *fee_out) {
return AcceptToMemoryPoolWithTime(config, pool, state, tx, GetTime(),
bypass_limits, test_accept, fee_out);
}
CTransactionRef GetTransaction(const CBlockIndex *const block_index,
const CTxMemPool *const mempool,
const TxId &txid,
const Consensus::Params &consensusParams,
BlockHash &hashBlock) {
LOCK(cs_main);
if (block_index) {
CBlock block;
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
for (const auto &tx : block.vtx) {
if (tx->GetId() == txid) {
hashBlock = block_index->GetBlockHash();
return tx;
}
}
}
return nullptr;
}
if (mempool) {
CTransactionRef ptx = mempool->get(txid);
if (ptx) {
return ptx;
}
}
if (g_txindex) {
CTransactionRef tx;
if (g_txindex->FindTx(txid, hashBlock, tx)) {
return tx;
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////////
//
// CBlock and CBlockIndex
//
static bool WriteBlockToDisk(const CBlock &block, FlatFilePos &pos,
const CMessageHeader::MessageMagic &messageStart) {
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
if (fileout.IsNull()) {
return error("WriteBlockToDisk: OpenBlockFile failed");
}
// Write index header
unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
fileout << messageStart << nSize;
// Write block
long fileOutPos = ftell(fileout.Get());
if (fileOutPos < 0) {
return error("WriteBlockToDisk: ftell failed");
}
pos.nPos = (unsigned int)fileOutPos;
fileout << block;
return true;
}
Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams) {
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
// Force block reward to zero when right shift is undefined.
if (halvings >= 64) {
return Amount::zero();
}
Amount nSubsidy = 50 * COIN;
// Subsidy is cut in half every 210,000 blocks which will occur
// approximately every 4 years.
return ((nSubsidy / SATOSHI) >> halvings) * SATOSHI;
}
CoinsViews::CoinsViews(std::string ldb_name, size_t cache_size_bytes,
bool in_memory, bool should_wipe)
: m_dbview(GetDataDir() / ldb_name, cache_size_bytes, in_memory,
should_wipe),
m_catcherview(&m_dbview) {}
void CoinsViews::InitCache() {
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
CChainState::CChainState(CTxMemPool &mempool, BlockManager &blockman,
BlockHash from_snapshot_blockhash)
: m_blockman(blockman), m_mempool(mempool),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(size_t cache_size_bytes, bool in_memory,
bool should_wipe, std::string leveldb_name) {
if (!m_from_snapshot_blockhash.IsNull()) {
leveldb_name += "_" + m_from_snapshot_blockhash.ToString();
}
m_coins_views = std::make_unique<CoinsViews>(leveldb_name, cache_size_bytes,
in_memory, should_wipe);
}
void CChainState::InitCoinsCache(size_t cache_size_bytes) {
assert(m_coins_views != nullptr);
m_coinstip_cache_size_bytes = cache_size_bytes;
m_coins_views->InitCache();
}
// Note that though this is marked const, we may end up modifying
// `m_cached_finished_ibd`, which is a performance-related implementation
// detail. This function must be marked `const` so that `CValidationInterface`
// clients (which are given a `const CChainState*`) can call it.
//
bool CChainState::IsInitialBlockDownload() const {
// Optimization: pre-test latch before taking the lock.
if (m_cached_finished_ibd.load(std::memory_order_relaxed)) {
return false;
}
LOCK(cs_main);
if (m_cached_finished_ibd.load(std::memory_order_relaxed)) {
return false;
}
if (fImporting || fReindex) {
return true;
}
if (m_chain.Tip() == nullptr) {
return true;
}
if (m_chain.Tip()->nChainWork < nMinimumChainWork) {
return true;
}
if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) {
return true;
}
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
m_cached_finished_ibd.store(true, std::memory_order_relaxed);
return false;
}
static CBlockIndex const *pindexBestForkTip = nullptr;
static CBlockIndex const *pindexBestForkBase = nullptr;
static void AlertNotify(const std::string &strMessage) {
uiInterface.NotifyAlertChanged();
#if defined(HAVE_SYSTEM)
std::string strCmd = gArgs.GetArg("-alertnotify", "");
if (strCmd.empty()) {
return;
}
// Alert text should be plain ascii coming from a trusted source, but to be
// safe we first strip anything not in safeChars, then add single quotes
// around the whole string before passing it to the shell:
std::string singleQuote("'");
std::string safeStatus = SanitizeString(strMessage);
safeStatus = singleQuote + safeStatus + singleQuote;
boost::replace_all(strCmd, "%s", safeStatus);
std::thread t(runCommand, strCmd);
// thread runs free
t.detach();
#endif
}
static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial
// sync)
if (::ChainstateActive().IsInitialBlockDownload()) {
return;
}
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one
// mines it) of our head, drop it
if (pindexBestForkTip &&
::ChainActive().Height() - pindexBestForkTip->nHeight >= 72) {
pindexBestForkTip = nullptr;
}
if (pindexBestForkTip ||
(pindexBestInvalid &&
pindexBestInvalid->nChainWork >
::ChainActive().Tip()->nChainWork +
(GetBlockProof(*::ChainActive().Tip()) * 6))) {
if (!GetfLargeWorkForkFound() && pindexBestForkBase) {
std::string warning =
std::string("'Warning: Large-work fork detected, forking after "
"block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
AlertNotify(warning);
}
if (pindexBestForkTip && pindexBestForkBase) {
LogPrintf("%s: Warning: Large fork found\n forking the "
"chain at height %d (%s)\n lasting to height %d "
"(%s).\nChain state database corruption likely.\n",
__func__, pindexBestForkBase->nHeight,
pindexBestForkBase->phashBlock->ToString(),
pindexBestForkTip->nHeight,
pindexBestForkTip->phashBlock->ToString());
SetfLargeWorkForkFound(true);
} else {
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks "
"longer than our best chain.\nChain state database "
"corruption likely.\n",
__func__);
SetfLargeWorkInvalidChainFound(true);
}
} else {
SetfLargeWorkForkFound(false);
SetfLargeWorkInvalidChainFound(false);
}
}
static void CheckForkWarningConditionsOnNewFork(CBlockIndex *pindexNewForkTip)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag.
const CBlockIndex *pfork = ::ChainActive().FindFork(pindexNewForkTip);
// We define a condition where we should warn the user about as a fork of at
// least 7 blocks with a tip within 72 blocks (+/- 12 hours if no one mines
// it) of ours. We use 7 blocks rather arbitrarily as it represents just
// under 10% of sustained network hash rate operating on the fork, or a
// chain that is entirely longer than ours and invalid (note that this
// should be detected by both). We define it this way because it allows us
// to only store the highest fork tip (+ base) which meets the 7-block
// condition and from this always have the most-likely-to-cause-warning fork
if (pfork &&
(!pindexBestForkTip ||
pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) &&
pindexNewForkTip->nChainWork - pfork->nChainWork >
(GetBlockProof(*pfork) * 7) &&
::ChainActive().Height() - pindexNewForkTip->nHeight < 72) {
pindexBestForkTip = pindexNewForkTip;
pindexBestForkBase = pfork;
}
CheckForkWarningConditions();
}
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex *pindexNew) {
AssertLockHeld(cs_main);
if (!pindexBestInvalid ||
pindexNew->nChainWork > pindexBestInvalid->nChainWork) {
pindexBestInvalid = pindexNew;
}
if (pindexBestHeader != nullptr &&
pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
pindexBestHeader = ::ChainActive().Tip();
}
// If the invalid chain found is supposed to be finalized, we need to move
// back the finalization point.
if (IsBlockFinalized(pindexNew)) {
m_finalizedBlockIndex = pindexNew->pprev;
}
LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n",
__func__, pindexNew->GetBlockHash().ToString(),
pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble()) / log(2.0),
FormatISO8601DateTime(pindexNew->GetBlockTime()));
CBlockIndex *tip = ::ChainActive().Tip();
assert(tip);
LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n",
__func__, tip->GetBlockHash().ToString(),
::ChainActive().Height(),
log(tip->nChainWork.getdouble()) / log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
}
// Same as InvalidChainFound, above, except not called directly from
// InvalidateBlock, which does its own setBlockIndexCandidates management.
void CChainState::InvalidBlockFound(CBlockIndex *pindex,
const BlockValidationState &state) {
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus = pindex->nStatus.withFailed();
m_blockman.m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
InvalidChainFound(pindex);
}
}
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo,
int nHeight) {
// Mark inputs spent.
if (tx.IsCoinBase()) {
return;
}
txundo.vprevout.reserve(tx.vin.size());
for (const CTxIn &txin : tx.vin) {
txundo.vprevout.emplace_back();
bool is_spent = view.SpendCoin(txin.prevout, &txundo.vprevout.back());
assert(is_spent);
}
}
void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo,
int nHeight) {
SpendCoins(view, tx, txundo, nHeight);
AddCoins(view, tx, nHeight);
}
void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, int nHeight) {
// Mark inputs spent.
if (!tx.IsCoinBase()) {
for (const CTxIn &txin : tx.vin) {
bool is_spent = view.SpendCoin(txin.prevout);
assert(is_spent);
}
}
// Add outputs.
AddCoins(view, tx, nHeight);
}
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
if (!VerifyScript(scriptSig, m_tx_out.scriptPubKey, nFlags,
CachingTransactionSignatureChecker(
ptxTo, nIn, m_tx_out.nValue, cacheStore, txdata),
metrics, &error)) {
return false;
}
if ((pTxLimitSigChecks &&
!pTxLimitSigChecks->consume_and_check(metrics.nSigChecks)) ||
(pBlockLimitSigChecks &&
!pBlockLimitSigChecks->consume_and_check(metrics.nSigChecks))) {
// we can't assign a meaningful script error (since the script
// succeeded), but remove the ScriptError::OK which could be
// misinterpreted.
error = ScriptError::SIGCHECKS_LIMIT_EXCEEDED;
return false;
}
return true;
}
int GetSpendHeight(const CCoinsViewCache &inputs) {
LOCK(cs_main);
CBlockIndex *pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
return pindexPrev->nHeight + 1;
}
bool CheckInputScripts(const CTransaction &tx, TxValidationState &state,
const CCoinsViewCache &inputs, const uint32_t flags,
bool sigCacheStore, bool scriptCacheStore,
const PrecomputedTransactionData &txdata,
int &nSigChecksOut, TxSigCheckLimiter &txLimitSigChecks,
CheckInputsLimiter *pBlockLimitSigChecks,
std::vector<CScriptCheck> *pvChecks) {
AssertLockHeld(cs_main);
assert(!tx.IsCoinBase());
if (pvChecks) {
pvChecks->reserve(tx.vin.size());
}
// First check if script executions have been cached with the same flags.
// Note that this assumes that the inputs provided are correct (ie that the
// transaction hash which is in tx's prevouts properly commits to the
// scriptPubKey in the inputs view of that transaction).
ScriptCacheKey hashCacheEntry(tx, flags);
if (IsKeyInScriptCache(hashCacheEntry, !scriptCacheStore, nSigChecksOut)) {
if (!txLimitSigChecks.consume_and_check(nSigChecksOut) ||
(pBlockLimitSigChecks &&
!pBlockLimitSigChecks->consume_and_check(nSigChecksOut))) {
return state.Invalid(TxValidationResult::TX_CONSENSUS,
"too-many-sigchecks");
}
return true;
}
int nSigChecksTotal = 0;
for (size_t i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin &coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());
// We very carefully only pass in things to CScriptCheck which are
// clearly committed to by tx's hash. This provides a sanity
// check that our caching is not introducing consensus failures through
// additional data in, eg, the coins being spent being checked as a part
// of CScriptCheck.
// Verify signature
CScriptCheck check(coin.GetTxOut(), tx, i, flags, sigCacheStore, txdata,
&txLimitSigChecks, pBlockLimitSigChecks);
// If pvChecks is not null, defer the check execution to the caller.
if (pvChecks) {
pvChecks->push_back(std::move(check));
continue;
}
if (!check()) {
ScriptError scriptError = check.GetScriptError();
// Compute flags without the optional standardness flags.
// This differs from MANDATORY_SCRIPT_VERIFY_FLAGS as it contains
// additional upgrade flags (see AcceptToMemoryPoolWorker variable
// extraFlags).
uint32_t mandatoryFlags =
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS;
if (flags != mandatoryFlags) {
// Check whether the failure was caused by a non-mandatory
// script verification check. If so, ensure we return
// NOT_STANDARD instead of CONSENSUS to avoid downstream users
// splitting the network between upgraded and non-upgraded nodes
// by banning CONSENSUS-failing data providers.
CScriptCheck check2(coin.GetTxOut(), tx, i, mandatoryFlags,
sigCacheStore, txdata);
if (check2()) {
return state.Invalid(
TxValidationResult::TX_NOT_STANDARD,
strprintf("non-mandatory-script-verify-flag (%s)",
ScriptErrorString(scriptError)));
}
// update the error message to reflect the mandatory violation.
scriptError = check2.GetScriptError();
}
// MANDATORY flag failures correspond to
// TxValidationResult::TX_CONSENSUS. Because CONSENSUS failures are
// the most serious case of validation failures, we may need to
// consider using RECENT_CONSENSUS_CHANGE for any script failure
// that could be due to non-upgraded nodes which we may want to
// support, to avoid splitting the network (but this depends on the
// details of how net_processing handles such errors).
return state.Invalid(
TxValidationResult::TX_CONSENSUS,
strprintf("mandatory-script-verify-flag-failed (%s)",
ScriptErrorString(scriptError)));
}
nSigChecksTotal += check.GetScriptExecutionMetrics().nSigChecks;
}
nSigChecksOut = nSigChecksTotal;
if (scriptCacheStore && !pvChecks) {
// We executed all of the provided scripts, and were told to cache the
// result. Do so now.
AddKeyInScriptCache(hashCacheEntry, nSigChecksTotal);
}
return true;
}
static bool UndoWriteToDisk(const CBlockUndo &blockundo, FlatFilePos &pos,
const BlockHash &hashBlock,
const CMessageHeader::MessageMagic &messageStart) {
// Open history file to append
CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
if (fileout.IsNull()) {
return error("%s: OpenUndoFile failed", __func__);
}
// Write index header
unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion());
fileout << messageStart << nSize;
// Write undo data
long fileOutPos = ftell(fileout.Get());
if (fileOutPos < 0) {
return error("%s: ftell failed", __func__);
}
pos.nPos = (unsigned int)fileOutPos;
fileout << blockundo;
// calculate & write checksum
CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
hasher << hashBlock;
hasher << blockundo;
fileout << hasher.GetHash();
return true;
}
bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex) {
FlatFilePos pos = pindex->GetUndoPos();
if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
// Open history file to read
CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull()) {
return error("%s: OpenUndoFile failed", __func__);
}
// Read block
uint256 hashChecksum;
// We need a CHashVerifier as reserializing may lose data
CHashVerifier<CAutoFile> verifier(&filein);
try {
verifier << pindex->pprev->GetBlockHash();
verifier >> blockundo;
filein >> hashChecksum;
} catch (const std::exception &e) {
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
}
// Verify checksum
if (hashChecksum != verifier.GetHash()) {
return error("%s: Checksum mismatch", __func__);
}
return true;
}
/** Abort with a message */
static bool AbortNode(const std::string &strMessage,
bilingual_str user_message = bilingual_str()) {
SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
if (!user_message.empty()) {
user_message =
_("A fatal internal error occurred, see debug.log for details");
}
AbortError(user_message);
StartShutdown();
return false;
}
static bool AbortNode(BlockValidationState &state,
const std::string &strMessage,
const bilingual_str &userMessage = bilingual_str()) {
AbortNode(strMessage, userMessage);
return state.Error(strMessage);
}
/** Restore the UTXO in a Coin at a given COutPoint. */
DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view,
const COutPoint &out) {
bool fClean = true;
if (view.HaveCoin(out)) {
// Overwriting transaction output.
fClean = false;
}
if (undo.GetHeight() == 0) {
// Missing undo metadata (height and coinbase). Older versions included
// this information only in undo records for the last spend of a
// transactions' outputs. This implies that it must be present for some
// other output of the same tx.
const Coin &alternate = AccessByTxid(view, out.GetTxId());
if (alternate.IsSpent()) {
// Adding output for transaction without known metadata
return DisconnectResult::FAILED;
}
// This is somewhat ugly, but hopefully utility is limited. This is only
// useful when working from legacy on disck data. In any case, putting
// the correct information in there doesn't hurt.
const_cast<Coin &>(undo) = Coin(undo.GetTxOut(), alternate.GetHeight(),
alternate.IsCoinBase());
}
// If the coin already exists as an unspent coin in the cache, then the
// possible_overwrite parameter to AddCoin must be set to true. We have
// already checked whether an unspent coin exists above using HaveCoin, so
// we don't need to guess. When fClean is false, an unspent coin already
// existed and it is an overwrite.
view.AddCoin(out, std::move(undo), !fClean);
return fClean ? DisconnectResult::OK : DisconnectResult::UNCLEAN;
}
/**
* Undo the effects of this block (with given index) on the UTXO set represented
* by coins. When FAILED is returned, view is left in an indeterminate state.
*/
DisconnectResult CChainState::DisconnectBlock(const CBlock &block,
const CBlockIndex *pindex,
CCoinsViewCache &view) {
CBlockUndo blockUndo;
if (!UndoReadFromDisk(blockUndo, pindex)) {
error("DisconnectBlock(): failure reading undo data");
return DisconnectResult::FAILED;
}
return ApplyBlockUndo(blockUndo, block, pindex, view);
}
DisconnectResult ApplyBlockUndo(const CBlockUndo &blockUndo,
const CBlock &block, const CBlockIndex *pindex,
CCoinsViewCache &view) {
bool fClean = true;
if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) {
error("DisconnectBlock(): block and undo data inconsistent");
return DisconnectResult::FAILED;
}
// First, restore inputs.
for (size_t i = 1; i < block.vtx.size(); i++) {
const CTransaction &tx = *(block.vtx[i]);
const CTxUndo &txundo = blockUndo.vtxundo[i - 1];
if (txundo.vprevout.size() != tx.vin.size()) {
error("DisconnectBlock(): transaction and undo data inconsistent");
return DisconnectResult::FAILED;
}
for (size_t j = 0; j < tx.vin.size(); j++) {
const COutPoint &out = tx.vin[j].prevout;
const Coin &undo = txundo.vprevout[j];
DisconnectResult res = UndoCoinSpend(undo, view, out);
if (res == DisconnectResult::FAILED) {
return DisconnectResult::FAILED;
}
fClean = fClean && res != DisconnectResult::UNCLEAN;
}
}
// Second, revert created outputs.
for (const auto &ptx : block.vtx) {
const CTransaction &tx = *ptx;
const TxId &txid = tx.GetId();
const bool is_coinbase = tx.IsCoinBase();
// Check that all outputs are available and match the outputs in the
// block itself exactly.
for (size_t o = 0; o < tx.vout.size(); o++) {
if (tx.vout[o].scriptPubKey.IsUnspendable()) {
continue;
}
COutPoint out(txid, o);
Coin coin;
bool is_spent = view.SpendCoin(out, &coin);
if (!is_spent || tx.vout[o] != coin.GetTxOut() ||
uint32_t(pindex->nHeight) != coin.GetHeight() ||
is_coinbase != coin.IsCoinBase()) {
// transaction output mismatch
fClean = false;
}
}
}
// Move best block pointer to previous block.
view.SetBestBlock(block.hashPrevBlock);
return fClean ? DisconnectResult::OK : DisconnectResult::UNCLEAN;
}
static void FlushUndoFile(int block_file, bool finalize = false) {
FlatFilePos undo_pos_old(block_file, vinfoBlockFile[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.");
}
}
static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false) {
LOCK(cs_LastBlockFile);
FlatFilePos block_pos_old(nLastBlockFile,
vinfoBlockFile[nLastBlockFile].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.");
}
// 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
if (!fFinalize || finalize_undo) {
FlushUndoFile(nLastBlockFile, finalize_undo);
}
}
static bool FindUndoPos(BlockValidationState &state, int nFile,
FlatFilePos &pos, unsigned int nAddSize);
static bool WriteUndoDataForBlock(const CBlockUndo &blockundo,
BlockValidationState &state,
CBlockIndex *pindex,
const CChainParams &chainparams) {
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
FlatFilePos _pos;
if (!FindUndoPos(state, pindex->nFile, _pos,
::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) {
return error("ConnectBlock(): FindUndoPos failed");
}
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(),
chainparams.DiskMagic())) {
return AbortNode(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 in the block file info as below; note that this
// does not catch the case where the undo writes are keeping up with the
// block writes (usually when a synced up node is getting newly mined
// blocks) -- this case is caught in the FindBlockPos function
if (_pos.nFile < nLastBlockFile &&
static_cast<uint32_t>(pindex->nHeight) ==
vinfoBlockFile[_pos.nFile].nHeightLast) {
FlushUndoFile(_pos.nFile, true);
}
// update nUndoPos in block index
pindex->nUndoPos = _pos.nPos;
pindex->nStatus = pindex->nStatus.withUndo();
setDirtyBlockIndex.insert(pindex);
}
return true;
}
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
-void ThreadScriptCheck(int worker_num) {
- util::ThreadRename(strprintf("scriptch.%i", worker_num));
- scriptcheckqueue.Thread();
+void StartScriptCheckWorkerThreads(int threads_num) {
+ scriptcheckqueue.StartWorkerThreads(threads_num);
+}
+
+void StopScriptCheckWorkerThreads() {
+ scriptcheckqueue.StopWorkerThreads();
}
VersionBitsCache versionbitscache GUARDED_BY(cs_main);
int32_t ComputeBlockVersion(const CBlockIndex *pindexPrev,
const Consensus::Params ¶ms) {
LOCK(cs_main);
int32_t nVersion = VERSIONBITS_TOP_BITS;
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
ThresholdState state = VersionBitsState(
pindexPrev, params, static_cast<Consensus::DeploymentPos>(i),
versionbitscache);
if (state == ThresholdState::LOCKED_IN ||
state == ThresholdState::STARTED) {
nVersion |= VersionBitsMask(
params, static_cast<Consensus::DeploymentPos>(i));
}
}
// Clear the last 4 bits (miner fund activation).
return nVersion & ~uint32_t(0x0f);
}
// Returns the script flags which should be checked for the block after
// the given block.
static uint32_t GetNextBlockScriptFlags(const Consensus::Params ¶ms,
const CBlockIndex *pindex) {
uint32_t flags = SCRIPT_VERIFY_NONE;
// Start enforcing P2SH (BIP16)
if ((pindex->nHeight + 1) >= params.BIP16Height) {
flags |= SCRIPT_VERIFY_P2SH;
}
// Start enforcing the DERSIG (BIP66) rule.
if ((pindex->nHeight + 1) >= params.BIP66Height) {
flags |= SCRIPT_VERIFY_DERSIG;
}
// Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule.
if ((pindex->nHeight + 1) >= params.BIP65Height) {
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
// Start enforcing CSV (BIP68, BIP112 and BIP113) rule.
if ((pindex->nHeight + 1) >= params.CSVHeight) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
// If the UAHF is enabled, we start accepting replay protected txns
if (IsUAHFenabled(params, pindex)) {
flags |= SCRIPT_VERIFY_STRICTENC;
flags |= SCRIPT_ENABLE_SIGHASH_FORKID;
}
// If the DAA HF is enabled, we start rejecting transaction that use a high
// s in their signature. We also make sure that signature that are supposed
// to fail (for instance in multisig or other forms of smart contracts) are
// null.
if (IsDAAEnabled(params, pindex)) {
flags |= SCRIPT_VERIFY_LOW_S;
flags |= SCRIPT_VERIFY_NULLFAIL;
}
// When the magnetic anomaly fork is enabled, we start accepting
// transactions using the OP_CHECKDATASIG opcode and it's verify
// alternative. We also start enforcing push only signatures and
// clean stack.
if (IsMagneticAnomalyEnabled(params, pindex)) {
flags |= SCRIPT_VERIFY_CHECKDATASIG_SIGOPS;
flags |= SCRIPT_VERIFY_SIGPUSHONLY;
flags |= SCRIPT_VERIFY_CLEANSTACK;
}
if (IsGravitonEnabled(params, pindex)) {
flags |= SCRIPT_ENABLE_SCHNORR_MULTISIG;
flags |= SCRIPT_VERIFY_MINIMALDATA;
}
if (IsPhononEnabled(params, pindex)) {
flags |= SCRIPT_ENFORCE_SIGCHECKS;
}
// We make sure this node will have replay protection during the next hard
// fork.
if (IsReplayProtectionEnabled(params, pindex)) {
flags |= SCRIPT_ENABLE_REPLAY_PROTECTION;
}
return flags;
}
static int64_t nTimeCheck = 0;
static int64_t nTimeForks = 0;
static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
static int64_t nBlocksTotal = 0;
/**
* Apply the effects of this block (with given index) on the UTXO set
* represented by coins. Validity checks that depend on the UTXO set are also
* done; ConnectBlock() can fail if those validity checks fail (among other
* reasons).
*/
bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state,
CBlockIndex *pindex, CCoinsViewCache &view,
const CChainParams ¶ms,
BlockValidationOptions options,
bool fJustCheck) {
AssertLockHeld(cs_main);
assert(pindex);
assert(*pindex->phashBlock == block.GetHash());
int64_t nTimeStart = GetTimeMicros();
const Consensus::Params &consensusParams = params.GetConsensus();
// Check it again in case a previous version let a bad block in
// NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
// ContextualCheckBlockHeader() here. This means that if we add a new
// consensus rule that is enforced in one of those two functions, then we
// may have let in a block that violates the rule prior to updating the
// software, and we would NOT be enforcing the rule here. Fully solving
// upgrade from one software version to the next after a consensus rule
// change is potentially tricky and issue-specific.
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
// GetAdjustedTime() to go backward).
if (!CheckBlock(block, state, consensusParams,
options.withCheckPoW(!fJustCheck)
.withCheckMerkleRoot(!fJustCheck))) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// 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 error("%s: Consensus::CheckBlock: %s", __func__,
state.ToString());
}
// Verify that the view's current state corresponds to the previous block
BlockHash hashPrevBlock =
pindex->pprev == nullptr ? BlockHash() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock());
nBlocksTotal++;
// Special case for the genesis block, skipping connection of its
// transactions (its coinbase is unspendable)
if (block.GetHash() == consensusParams.hashGenesisBlock) {
if (!fJustCheck) {
view.SetBestBlock(pindex->GetBlockHash());
}
return true;
}
bool fScriptChecks = true;
if (!hashAssumeValid.IsNull()) {
// We've been configured with the hash of a block which has been
// externally verified to have a valid history. A suitable default value
// is included with the software and updated from time to time. Because
// validity relative to a piece of software is an objective fact these
// defaults can be easily reviewed. This setting doesn't force the
// selection of any particular chain but makes validating some faster by
// effectively caching the result of part of the verification.
BlockMap::const_iterator it =
m_blockman.m_block_index.find(hashAssumeValid);
if (it != m_blockman.m_block_index.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an
// ancestor of the best header.
// Script verification is skipped when connecting blocks under
// the assumevalid block. Assuming the assumevalid block is
// valid this is safe because block merkle hashes are still
// computed and checked, Of course, if an assumed valid block is
// invalid due to false scriptSigs this optimization would allow
// an invalid chain to be accepted.
// The equivalent time check discourages hash power from
// extorting the network via DOS attack into accepting an
// invalid block through telling users they must manually set
// assumevalid. Requiring a software change or burying the
// invalid block, regardless of the setting, makes it hard to
// hide the implication of the demand. This also avoids having
// release candidates that are hardly doing any signature
// verification at all in testing without having to artificially
// set the default assumed verified block further back. The test
// against nMinimumChainWork prevents the skipping when denied
// access to any chain at least as good as the expected chain.
fScriptChecks =
(GetBlockProofEquivalentTime(
*pindexBestHeader, *pindex, *pindexBestHeader,
consensusParams) <= 60 * 60 * 24 * 7 * 2);
}
}
}
int64_t nTime1 = GetTimeMicros();
nTimeCheck += nTime1 - nTimeStart;
LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n",
MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO,
nTimeCheck * MILLI / nBlocksTotal);
// Do not allow blocks that contain transactions which 'overwrite' older
// transactions, unless those are already completely spent. If such
// overwrites are allowed, coinbases and transactions depending upon those
// can be duplicated to remove the ability to spend the first instance --
// even after being sent to another address.
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html
// for more information. This logic is not necessary for memory pool
// transactions, as AcceptToMemoryPool already refuses previously-known
// transaction ids entirely. This rule was originally applied to all blocks
// with a timestamp after March 15, 2012, 0:00 UTC. Now that the whole
// chain is irreversibly beyond that time it is applied to all blocks
// except the two in the chain that violate it. This prevents exploiting
// the issue against nodes during their initial block download.
bool fEnforceBIP30 = !((pindex->nHeight == 91842 &&
pindex->GetBlockHash() ==
uint256S("0x00000000000a4d0a398161ffc163c503763"
"b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight == 91880 &&
pindex->GetBlockHash() ==
uint256S("0x00000000000743f190a18c5577a3c2d2a1f"
"610ae9601ac046a38084ccb7cd721")));
// Once BIP34 activated it was not possible to create new duplicate
// coinbases and thus other than starting with the 2 existing duplicate
// coinbase pairs, not possible to create overwriting txs. But by the time
// BIP34 activated, in each of the existing pairs the duplicate coinbase had
// overwritten the first before the first had been spent. Since those
// coinbases are sufficiently buried it's no longer possible to create
// further duplicate transactions descending from the known pairs either. If
// we're on the known chain at height greater than where BIP34 activated, we
// can save the db accesses needed for the BIP30 check.
// BIP34 requires that a block at height X (block X) has its coinbase
// scriptSig start with a CScriptNum of X (indicated height X). The above
// logic of no longer requiring BIP30 once BIP34 activates is flawed in the
// case that there is a block X before the BIP34 height of 227,931 which has
// an indicated height Y where Y is greater than X. The coinbase for block
// X would also be a valid coinbase for block Y, which could be a BIP30
// violation. An exhaustive search of all mainnet coinbases before the
// BIP34 height which have an indicated height greater than the block height
// reveals many occurrences. The 3 lowest indicated heights found are
// 209,921, 490,897, and 1,983,702 and thus coinbases for blocks at these 3
// heights would be the first opportunity for BIP30 to be violated.
// The search reveals a great many blocks which have an indicated height
// greater than 1,983,702, so we simply remove the optimization to skip
// BIP30 checking for blocks at height 1,983,702 or higher. Before we reach
// that block in another 25 years or so, we should take advantage of a
// future consensus change to do a new and improved version of BIP34 that
// will actually prevent ever creating any duplicate coinbases in the
// future.
static constexpr int BIP34_IMPLIES_BIP30_LIMIT = 1983702;
// There is no potential to create a duplicate coinbase at block 209,921
// because this is still before the BIP34 height and so explicit BIP30
// checking is still active.
// The final case is block 176,684 which has an indicated height of
// 490,897. Unfortunately, this issue was not discovered until about 2 weeks
// before block 490,897 so there was not much opportunity to address this
// case other than to carefully analyze it and determine it would not be a
// problem. Block 490,897 was, in fact, mined with a different coinbase than
// block 176,684, but it is important to note that even if it hadn't been or
// is remined on an alternate fork with a duplicate coinbase, we would still
// not run into a BIP30 violation. This is because the coinbase for 176,684
// is spent in block 185,956 in transaction
// d4f7fbbf92f4a3014a230b2dc70b8058d02eb36ac06b4a0736d9d60eaa9e8781. This
// spending transaction can't be duplicated because it also spends coinbase
// 0328dd85c331237f18e781d692c92de57649529bd5edf1d01036daea32ffde29. This
// coinbase has an indicated height of over 4.2 billion, and wouldn't be
// duplicatable until that height, and it's currently impossible to create a
// chain that long. Nevertheless we may wish to consider a future soft fork
// which retroactively prevents block 490,897 from creating a duplicate
// coinbase. The two historical BIP30 violations often provide a confusing
// edge case when manipulating the UTXO and it would be simpler not to have
// another edge case to deal with.
// testnet3 has no blocks before the BIP34 height with indicated heights
// post BIP34 before approximately height 486,000,000 and presumably will
// be reset before it reaches block 1,983,702 and starts doing unnecessary
// BIP30 checking again.
assert(pindex->pprev);
CBlockIndex *pindexBIP34height =
pindex->pprev->GetAncestor(consensusParams.BIP34Height);
// Only continue to enforce if we're below BIP34 activation height or the
// block hash at that height doesn't correspond.
fEnforceBIP30 =
fEnforceBIP30 &&
(!pindexBIP34height ||
!(pindexBIP34height->GetBlockHash() == consensusParams.BIP34Hash));
// TODO: Remove BIP30 checking from block height 1,983,702 on, once we have
// a consensus change that ensures coinbases at those heights can not
// duplicate earlier coinbases.
if (fEnforceBIP30 || pindex->nHeight >= BIP34_IMPLIES_BIP30_LIMIT) {
for (const auto &tx : block.vtx) {
for (size_t o = 0; o < tx->vout.size(); o++) {
if (view.HaveCoin(COutPoint(tx->GetId(), o))) {
LogPrintf("ERROR: ConnectBlock(): tried to overwrite "
"transaction\n");
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-txns-BIP30");
}
}
}
}
// Start enforcing BIP68 (sequence locks).
int nLockTimeFlags = 0;
if (pindex->nHeight >= consensusParams.CSVHeight) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
const uint32_t flags =
GetNextBlockScriptFlags(consensusParams, pindex->pprev);
int64_t nTime2 = GetTimeMicros();
nTimeForks += nTime2 - nTime1;
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n",
MILLI * (nTime2 - nTime1), nTimeForks * MICRO,
nTimeForks * MILLI / nBlocksTotal);
std::vector<int> prevheights;
Amount nFees = Amount::zero();
int nInputs = 0;
// Limit the total executed signature operations in the block, a consensus
// rule. Tracking during the CPU-consuming part (validation of uncached
// inputs) is per-input atomic and validation in each thread stops very
// quickly after the limit is exceeded, so an adversary cannot cause us to
// exceed the limit by much at all.
CheckInputsLimiter nSigChecksBlockLimiter(
GetMaxBlockSigChecksCount(options.getExcessiveBlockSize()));
std::vector<TxSigCheckLimiter> nSigChecksTxLimiters;
nSigChecksTxLimiters.resize(block.vtx.size() - 1);
CBlockUndo blockundo;
blockundo.vtxundo.resize(block.vtx.size() - 1);
CCheckQueueControl<CScriptCheck> control(fScriptChecks ? &scriptcheckqueue
: nullptr);
// Add all outputs
try {
for (const auto &ptx : block.vtx) {
AddCoins(view, *ptx, pindex->nHeight);
}
} catch (const std::logic_error &e) {
// This error will be thrown from AddCoin if we try to connect a block
// containing duplicate transactions. Such a thing should normally be
// caught early nowadays (due to ContextualCheckBlock's CTOR
// enforcement) however some edge cases can escape that:
// - ContextualCheckBlock does not get re-run after saving the block to
// disk, and older versions may have saved a weird block.
// - its checks are not applied to pre-CTOR chains, which we might visit
// with checkpointing off.
LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n");
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"tx-duplicate");
}
size_t txIndex = 0;
for (const auto &ptx : block.vtx) {
const CTransaction &tx = *ptx;
const bool isCoinBase = tx.IsCoinBase();
nInputs += tx.vin.size();
{
Amount txfee = Amount::zero();
TxValidationState tx_state;
if (!isCoinBase &&
!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight,
txfee)) {
// Any transaction validation failure in ConnectBlock is a block
// consensus failure.
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
tx_state.GetDebugMessage());
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__,
tx.GetId().ToString(), state.ToString());
}
nFees += txfee;
}
if (!MoneyRange(nFees)) {
LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n",
__func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-txns-accumulated-fee-outofrange");
}
// The following checks do not apply to the coinbase.
if (isCoinBase) {
continue;
}
// Check that transaction is BIP68 final BIP68 lock checks (as
// opposed to nLockTime checks) must be in ConnectBlock because they
// require the UTXO set.
prevheights.resize(tx.vin.size());
for (size_t j = 0; j < tx.vin.size(); j++) {
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).GetHeight();
}
if (!SequenceLocks(tx, nLockTimeFlags, prevheights, *pindex)) {
LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n",
__func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-txns-nonfinal");
}
// Don't cache results if we're actually connecting blocks (still
// consult the cache, though).
bool fCacheResults = fJustCheck;
const bool fEnforceSigCheck = flags & SCRIPT_ENFORCE_SIGCHECKS;
if (!fEnforceSigCheck) {
// Historically, there has been transactions with a very high
// sigcheck count, so we need to disable this check for such
// transactions.
nSigChecksTxLimiters[txIndex] = TxSigCheckLimiter::getDisabled();
}
std::vector<CScriptCheck> vChecks;
// nSigChecksRet may be accurate (found in cache) or 0 (checks were
// deferred into vChecks).
int nSigChecksRet;
TxValidationState tx_state;
if (fScriptChecks &&
!CheckInputScripts(tx, tx_state, view, flags, fCacheResults,
fCacheResults, PrecomputedTransactionData(tx),
nSigChecksRet, nSigChecksTxLimiters[txIndex],
&nSigChecksBlockLimiter, &vChecks)) {
// Any transaction validation failure in ConnectBlock is a block
// consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
tx_state.GetDebugMessage());
return error(
"ConnectBlock(): CheckInputScripts on %s failed with %s",
tx.GetId().ToString(), state.ToString());
}
control.Add(vChecks);
// Note: this must execute in the same iteration as CheckTxInputs (not
// in a separate loop) in order to detect double spends. However,
// this does not prevent double-spending by duplicated transaction
// inputs in the same transaction (cf. CVE-2018-17144) -- that check is
// done in CheckBlock (CheckRegularTransaction).
SpendCoins(view, tx, blockundo.vtxundo.at(txIndex), pindex->nHeight);
txIndex++;
}
int64_t nTime3 = GetTimeMicros();
nTimeConnect += nTime3 - nTime2;
LogPrint(BCLog::BENCH,
" - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) "
"[%.2fs (%.2fms/blk)]\n",
(unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2),
MILLI * (nTime3 - nTime2) / block.vtx.size(),
nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs - 1),
nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
Amount blockReward =
nFees + GetBlockSubsidy(pindex->nHeight, consensusParams);
if (block.vtx[0]->GetValueOut() > blockReward) {
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs "
"limit=%d)\n",
block.vtx[0]->GetValueOut(), blockReward);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-cb-amount");
}
const std::vector<CTxDestination> whitelist =
GetMinerFundWhitelist(consensusParams, pindex->pprev);
if (!whitelist.empty()) {
const Amount required = GetMinerFundAmount(blockReward);
for (auto &o : block.vtx[0]->vout) {
if (o.nValue < required) {
// This output doesn't qualify because its amount is too low.
continue;
}
CTxDestination address;
if (!ExtractDestination(o.scriptPubKey, address)) {
// Cannot decode address.
continue;
}
if (std::find(whitelist.begin(), whitelist.end(), address) !=
whitelist.end()) {
goto MinerFundSuccess;
}
}
// We did not find an output that match the miner fund requirements.
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-cb-minerfund");
}
MinerFundSuccess:
if (!control.Wait()) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"blk-bad-inputs", "parallel script check failed");
}
int64_t nTime4 = GetTimeMicros();
nTimeVerify += nTime4 - nTime2;
LogPrint(
BCLog::BENCH,
" - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n",
nInputs - 1, MILLI * (nTime4 - nTime2),
nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs - 1),
nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
if (fJustCheck) {
return true;
}
if (!WriteUndoDataForBlock(blockundo, state, pindex, params)) {
return false;
}
if (!pindex->IsValid(BlockValidity::SCRIPTS)) {
pindex->RaiseValidity(BlockValidity::SCRIPTS);
setDirtyBlockIndex.insert(pindex);
}
assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
int64_t nTime5 = GetTimeMicros();
nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n",
MILLI * (nTime5 - nTime4), nTimeIndex * MICRO,
nTimeIndex * MILLI / nBlocksTotal);
int64_t nTime6 = GetTimeMicros();
nTimeCallbacks += nTime6 - nTime5;
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n",
MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO,
nTimeCallbacks * MILLI / nBlocksTotal);
return true;
}
CoinsCacheSizeState
CChainState::GetCoinsCacheSizeState(const CTxMemPool *tx_pool) {
return this->GetCoinsCacheSizeState(
tx_pool, m_coinstip_cache_size_bytes,
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState
CChainState::GetCoinsCacheSizeState(const CTxMemPool *tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes) {
int64_t nMempoolUsage = tx_pool->DynamicMemoryUsage();
int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace =
max_coins_cache_size_bytes +
std::max<int64_t>(max_mempool_size_bytes - nMempoolUsage, 0);
//! No need to periodic flush if at least this much space still available.
static constexpr int64_t MAX_BLOCK_COINSDB_USAGE_BYTES =
10 * 1024 * 1024; // 10MB
int64_t large_threshold = std::max(
(9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE_BYTES);
if (cacheSize > nTotalSpace) {
LogPrintf("Cache size (%s) exceeds total space (%s)\n", cacheSize,
nTotalSpace);
return CoinsCacheSizeState::CRITICAL;
} else if (cacheSize > large_threshold) {
return CoinsCacheSizeState::LARGE;
}
return CoinsCacheSizeState::OK;
}
bool CChainState::FlushStateToDisk(const CChainParams &chainparams,
BlockValidationState &state,
FlushStateMode mode,
int nManualPruneHeight) {
LOCK(cs_main);
assert(this->CanFlushToDisk());
static std::chrono::microseconds nLastWrite{0};
static std::chrono::microseconds nLastFlush{0};
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
const size_t coins_count = CoinsTip().GetCacheSize();
const size_t coins_mem_usage = CoinsTip().DynamicMemoryUsage();
try {
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
CoinsCacheSizeState cache_state =
GetCoinsCacheSizeState(&m_mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) &&
!fReindex) {
if (nManualPruneHeight > 0) {
LOG_TIME_MILLIS_WITH_CATEGORY(
"find files to prune (manual)", BCLog::BENCH);
m_blockman.FindFilesToPruneManual(
setFilesToPrune, nManualPruneHeight, m_chain.Height());
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune",
BCLog::BENCH);
m_blockman.FindFilesToPrune(
setFilesToPrune, chainparams.PruneAfterHeight(),
m_chain.Height(), IsInitialBlockDownload());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
if (!fHavePruned) {
pblocktree->WriteFlag("prunedblockfiles", true);
fHavePruned = true;
}
}
}
const auto nNow = GetTime<std::chrono::microseconds>();
// Avoid writing/flushing immediately after startup.
if (nLastWrite.count() == 0) {
nLastWrite = nNow;
}
if (nLastFlush.count() == 0) {
nLastFlush = nNow;
}
// The cache is large and we're within 10% and 10 MiB of the limit,
// but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FlushStateMode::PERIODIC &&
cache_state >= CoinsCacheSizeState::LARGE;
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FlushStateMode::IF_NEEDED &&
cache_state >= CoinsCacheSizeState::CRITICAL;
// It's been a while since we wrote the block index to disk. Do this
// frequently, so we don't need to redownload after a crash.
bool fPeriodicWrite = mode == FlushStateMode::PERIODIC &&
nNow > nLastWrite + DATABASE_WRITE_INTERVAL;
// It's been very long since we flushed the cache. Do this
// infrequently, to optimize cache usage.
bool fPeriodicFlush = mode == FlushStateMode::PERIODIC &&
nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge ||
fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
return AbortNode(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);
// First make sure all block and undo data is flushed to
// disk.
FlushBlockFile();
}
// Then update all block file information (which may refer to
// block and undo files).
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk",
BCLog::BENCH);
std::vector<std::pair<int, const CBlockFileInfo *>> vFiles;
vFiles.reserve(setDirtyFileInfo.size());
for (int i : setDirtyFileInfo) {
vFiles.push_back(std::make_pair(i, &vinfoBlockFile[i]));
}
setDirtyFileInfo.clear();
std::vector<const CBlockIndex *> vBlocks;
vBlocks.reserve(setDirtyBlockIndex.size());
for (const CBlockIndex *cbi : setDirtyBlockIndex) {
vBlocks.push_back(cbi);
}
setDirtyBlockIndex.clear();
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile,
vBlocks)) {
return AbortNode(
state, "Failed to write to block index database");
}
}
// Finally remove any pruned files
if (fFlushForPrune) {
LOG_TIME_MILLIS_WITH_CATEGORY("unlink pruned files",
BCLog::BENCH);
UnlinkPrunedFiles(setFilesToPrune);
}
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the
// blocks / block index write was also done.
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
LOG_TIME_SECONDS(
strprintf("write coins cache to disk (%d coins, %.2fkB)",
coins_count, coins_mem_usage / 1000));
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is
// already an overestimation, as most will delete an existing
// entry or overwrite one. Still, use a conservative safety
// factor of 2.
if (!CheckDiskSpace(GetDataDir(),
48 * 2 * 2 * CoinsTip().GetCacheSize())) {
return AbortNode(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");
}
nLastFlush = nNow;
full_flush_completed = true;
}
}
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
GetMainSignals().ChainStateFlushed(m_chain.GetLocator());
}
} catch (const std::runtime_error &e) {
return AbortNode(state, std::string("System error while flushing: ") +
e.what());
}
return true;
}
void CChainState::ForceFlushStateToDisk() {
BlockValidationState state;
const CChainParams &chainparams = Params();
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__,
state.ToString());
}
}
void CChainState::PruneAndFlush() {
BlockValidationState state;
fCheckForPruning = true;
const CChainParams &chainparams = Params();
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__,
state.ToString());
}
}
/** Check warning conditions and do some notifications on new chain tip set. */
static void UpdateTip(CTxMemPool &mempool, const CChainParams ¶ms,
CBlockIndex *pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
// New best block
mempool.AddTransactionsUpdated(1);
{
LOCK(g_best_block_mutex);
g_best_block = pindexNew->GetBlockHash();
g_best_block_cv.notify_all();
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%ld "
"date='%s' progress=%f cache=%.1fMiB(%utxo)\n",
__func__, pindexNew->GetBlockHash().ToString(),
pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble()) / log(2.0),
pindexNew->GetChainTxCount(),
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(params.TxData(), pindexNew),
::ChainstateActive().CoinsTip().DynamicMemoryUsage() *
(1.0 / (1 << 20)),
::ChainstateActive().CoinsTip().GetCacheSize());
}
/**
* Disconnect m_chain's tip.
* After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
* should make the mempool consistent again by calling updateMempoolForReorg.
* with cs_main held.
*
* If disconnectpool is nullptr, then no disconnected transactions are added to
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
bool CChainState::DisconnectTip(const CChainParams ¶ms,
BlockValidationState &state,
DisconnectedBlockTransactions *disconnectpool) {
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
CBlockIndex *pindexDelete = m_chain.Tip();
const Consensus::Params &consensusParams = params.GetConsensus();
assert(pindexDelete);
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock &block = *pblock;
if (!ReadBlockFromDisk(block, pindexDelete, consensusParams)) {
return error("DisconnectTip(): Failed to read block");
}
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) !=
DisconnectResult::OK) {
return error("DisconnectTip(): DisconnectBlock %s failed",
pindexDelete->GetBlockHash().ToString());
}
bool flushed = view.Flush();
assert(flushed);
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n",
(GetTimeMicros() - nStart) * MILLI);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(params, state, FlushStateMode::IF_NEEDED)) {
return false;
}
// If this block is deactivating a fork, we move all mempool transactions
// in front of disconnectpool for reprocessing in a future
// updateMempoolForReorg call
if (pindexDelete->pprev != nullptr &&
GetNextBlockScriptFlags(consensusParams, pindexDelete) !=
GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)) {
LogPrint(BCLog::MEMPOOL,
"Disconnecting mempool due to rewind of upgrade block\n");
if (disconnectpool) {
disconnectpool->importMempool(m_mempool);
}
m_mempool.clear();
}
if (disconnectpool) {
disconnectpool->addForBlock(block.vtx, m_mempool);
}
// If the tip is finalized, then undo it.
if (m_finalizedBlockIndex == pindexDelete) {
m_finalizedBlockIndex = pindexDelete->pprev;
}
m_chain.SetTip(pindexDelete->pprev);
// Update ::ChainActive() and related variables.
UpdateTip(m_mempool, params, pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
return true;
}
static int64_t nTimeReadFromDisk = 0;
static int64_t nTimeConnectTotal = 0;
static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
struct PerBlockConnectTrace {
CBlockIndex *pindex = nullptr;
std::shared_ptr<const CBlock> pblock;
PerBlockConnectTrace() {}
};
/**
* Used to track blocks whose transactions were applied to the UTXO state as a
* part of a single ActivateBestChainStep call.
*
* This class is single-use, once you call GetBlocksConnected() you have to
* throw it away and make a new one.
*/
class ConnectTrace {
private:
std::vector<PerBlockConnectTrace> blocksConnected;
public:
explicit ConnectTrace() : blocksConnected(1) {}
void BlockConnected(CBlockIndex *pindex,
std::shared_ptr<const CBlock> pblock) {
assert(!blocksConnected.back().pindex);
assert(pindex);
assert(pblock);
blocksConnected.back().pindex = pindex;
blocksConnected.back().pblock = std::move(pblock);
blocksConnected.emplace_back();
}
std::vector<PerBlockConnectTrace> &GetBlocksConnected() {
// We always keep one extra block at the end of our list because blocks
// are added after all the conflicted transactions have been filled in.
// Thus, the last entry should always be an empty one waiting for the
// transactions from the next block. We pop the last entry here to make
// sure the list we return is sane.
assert(!blocksConnected.back().pindex);
blocksConnected.pop_back();
return blocksConnected;
}
};
bool CChainState::MarkBlockAsFinal(BlockValidationState &state,
const CBlockIndex *pindex) {
AssertLockHeld(cs_main);
if (pindex->nStatus.isInvalid()) {
// We try to finalize an invalid block.
LogPrintf("ERROR: %s: Trying to finalize invalid block %s\n", __func__,
pindex->GetBlockHash().ToString());
return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID,
"finalize-invalid-block");
}
// Check that the request is consistent with current finalization.
if (m_finalizedBlockIndex &&
!AreOnTheSameFork(pindex, m_finalizedBlockIndex)) {
LogPrintf("ERROR: %s: Trying to finalize block %s which conflicts with "
"already finalized block\n",
__func__, pindex->GetBlockHash().ToString());
return state.Invalid(BlockValidationResult::BLOCK_FINALIZATION,
"bad-fork-prior-finalized");
}
if (IsBlockFinalized(pindex)) {
// The block is already finalized.
return true;
}
// We have a new block to finalize.
m_finalizedBlockIndex = pindex;
return true;
}
static const CBlockIndex *FindBlockToFinalize(CBlockIndex *pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
const int32_t maxreorgdepth =
gArgs.GetArg("-maxreorgdepth", DEFAULT_MAX_REORG_DEPTH);
const int64_t finalizationdelay =
gArgs.GetArg("-finalizationdelay", DEFAULT_MIN_FINALIZATION_DELAY);
// Find our candidate.
// If maxreorgdepth is < 0 pindex will be null and auto finalization
// disabled
const CBlockIndex *pindex =
pindexNew->GetAncestor(pindexNew->nHeight - maxreorgdepth);
int64_t now = GetTime();
// If the finalization delay is not expired since the startup time,
// finalization should be avoided. Header receive time is not saved to disk
// and so cannot be anterior to startup time.
if (now < (GetStartupTime() + finalizationdelay)) {
return nullptr;
}
// While our candidate is not eligible (finalization delay not expired), try
// the previous one.
while (pindex && (pindex != ::ChainstateActive().GetFinalizedBlock())) {
// Check that the block to finalize is known for a long enough time.
// This test will ensure that an attacker could not cause a block to
// finalize by forking the chain with a depth > maxreorgdepth.
// If the block is loaded from disk, header receive time is 0 and the
// block will be finalized. This is safe because the delay since the
// node startup is already expired.
auto headerReceivedTime = pindex->GetHeaderReceivedTime();
// If finalization delay is <= 0, finalization always occurs immediately
if (now >= (headerReceivedTime + finalizationdelay)) {
return pindex;
}
pindex = pindex->pprev;
}
return nullptr;
}
/**
* Connect a new block to m_chain. pblock is either nullptr or a pointer to
* a CBlock corresponding to pindexNew, to bypass loading it again from disk.
*
* The block is always added to connectTrace (either after loading from disk or
* by copying pblock) - if that is not intended, care must be taken to remove
* the last entry in blocksConnected in case of failure.
*/
bool CChainState::ConnectTip(const Config &config, BlockValidationState &state,
CBlockIndex *pindexNew,
const std::shared_ptr<const CBlock> &pblock,
ConnectTrace &connectTrace,
DisconnectedBlockTransactions &disconnectpool) {
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
const CChainParams ¶ms = config.GetChainParams();
const Consensus::Params &consensusParams = params.GetConsensus();
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
if (!ReadBlockFromDisk(*pblockNew, pindexNew, consensusParams)) {
return AbortNode(state, "Failed to read block");
}
pthisBlock = pblockNew;
} else {
pthisBlock = pblock;
}
const CBlock &blockConnecting = *pthisBlock;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros();
nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n",
(nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{
CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, params,
BlockValidationOptions(config));
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
if (state.IsInvalid()) {
InvalidBlockFound(pindexNew, state);
}
return error("%s: ConnectBlock %s failed, %s", __func__,
pindexNew->GetBlockHash().ToString(),
state.ToString());
}
// Update the finalized block.
const CBlockIndex *pindexToFinalize = FindBlockToFinalize(pindexNew);
if (pindexToFinalize && !MarkBlockAsFinal(state, pindexToFinalize)) {
return error("ConnectTip(): MarkBlockAsFinal %s failed (%s)",
pindexNew->GetBlockHash().ToString(),
state.ToString());
}
nTime3 = GetTimeMicros();
nTimeConnectTotal += nTime3 - nTime2;
assert(nBlocksTotal > 0);
LogPrint(BCLog::BENCH,
" - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",
(nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO,
nTimeConnectTotal * MILLI / nBlocksTotal);
bool flushed = view.Flush();
assert(flushed);
}
int64_t nTime4 = GetTimeMicros();
nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",
(nTime4 - nTime3) * MILLI, nTimeFlush * MICRO,
nTimeFlush * MILLI / nBlocksTotal);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(config.GetChainParams(), state,
FlushStateMode::IF_NEEDED)) {
return false;
}
int64_t nTime5 = GetTimeMicros();
nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH,
" - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",
(nTime5 - nTime4) * MILLI, nTimeChainState * MICRO,
nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// If this block is activating a fork, we move all mempool transactions
// in front of disconnectpool for reprocessing in a future
// updateMempoolForReorg call
if (pindexNew->pprev != nullptr &&
GetNextBlockScriptFlags(consensusParams, pindexNew) !=
GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) {
LogPrint(BCLog::MEMPOOL,
"Disconnecting mempool due to acceptance of upgrade block\n");
disconnectpool.importMempool(m_mempool);
}
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
UpdateTip(m_mempool, params, pindexNew);
int64_t nTime6 = GetTimeMicros();
nTimePostConnect += nTime6 - nTime5;
nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH,
" - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",
(nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO,
nTimePostConnect * MILLI / nBlocksTotal);
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",
(nTime6 - nTime1) * MILLI, nTimeTotal * MICRO,
nTimeTotal * MILLI / nBlocksTotal);
connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
return true;
}
/**
* Return the tip of the chain with the most work in it, that isn't known to be
* invalid (it's however far from certain to be valid).
*/
CBlockIndex *CChainState::FindMostWorkChain() {
AssertLockHeld(cs_main);
do {
CBlockIndex *pindexNew = nullptr;
// Find the best candidate header.
{
std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator
it = setBlockIndexCandidates.rbegin();
if (it == setBlockIndexCandidates.rend()) {
return nullptr;
}
pindexNew = *it;
}
// If this block will cause a finalized block to be reorged, then we
// mark it as invalid.
if (m_finalizedBlockIndex &&
!AreOnTheSameFork(pindexNew, m_finalizedBlockIndex)) {
LogPrintf("Mark block %s invalid because it forks prior to the "
"finalization point %d.\n",
pindexNew->GetBlockHash().ToString(),
m_finalizedBlockIndex->nHeight);
pindexNew->nStatus = pindexNew->nStatus.withFailed();
InvalidChainFound(pindexNew);
}
const bool fAvalancheEnabled = isAvalancheEnabled(gArgs);
const bool fAutoUnpark =
gArgs.GetBoolArg("-automaticunparking", !fAvalancheEnabled);
const CBlockIndex *pindexFork = m_chain.FindFork(pindexNew);
// Check whether all blocks on the path between the currently active
// chain and the candidate are valid. Just going until the active chain
// is an optimization, as we know all blocks in it are valid already.
CBlockIndex *pindexTest = pindexNew;
bool hasValidAncestor = true;
while (hasValidAncestor && pindexTest && pindexTest != pindexFork) {
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// If this is a parked chain, but it has enough PoW, clear the park
// state.
bool fParkedChain = pindexTest->nStatus.isOnParkedChain();
if (fAutoUnpark && fParkedChain) {
const CBlockIndex *pindexTip = m_chain.Tip();
// During initialization, pindexTip and/or pindexFork may be
// null. In this case, we just ignore the fact that the chain is
// parked.
if (!pindexTip || !pindexFork) {
UnparkBlock(pindexTest);
continue;
}
// A parked chain can be unparked if it has twice as much PoW
// accumulated as the main chain has since the fork block.
CBlockIndex const *pindexExtraPow = pindexTip;
arith_uint256 requiredWork = pindexTip->nChainWork;
switch (pindexTip->nHeight - pindexFork->nHeight) {
// Limit the penality for depth 1, 2 and 3 to half a block
// worth of work to ensure we don't fork accidentally.
case 3:
case 2:
pindexExtraPow = pindexExtraPow->pprev;
// FALLTHROUGH
case 1: {
const arith_uint256 deltaWork =
pindexExtraPow->nChainWork - pindexFork->nChainWork;
requiredWork += (deltaWork >> 1);
break;
}
default:
requiredWork +=
pindexExtraPow->nChainWork - pindexFork->nChainWork;
break;
}
if (pindexNew->nChainWork > requiredWork) {
// We have enough, clear the parked state.
LogPrintf("Unpark chain up to block %s as it has "
"accumulated enough PoW.\n",
pindexNew->GetBlockHash().ToString());
fParkedChain = false;
UnparkBlock(pindexTest);
}
}
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
// for the most work chain if we come across them; we can't switch
// to a chain unless we have all the non-active-chain parent blocks.
bool fInvalidChain = pindexTest->nStatus.isInvalid();
bool fMissingData = !pindexTest->nStatus.hasData();
if (!(fInvalidChain || fParkedChain || fMissingData)) {
// The current block is acceptable, move to the parent, up to
// the fork point.
pindexTest = pindexTest->pprev;
continue;
}
// Candidate chain is not usable (either invalid or parked or
// missing data)
hasValidAncestor = false;
setBlockIndexCandidates.erase(pindexTest);
if (fInvalidChain &&
(pindexBestInvalid == nullptr ||
pindexNew->nChainWork > pindexBestInvalid->nChainWork)) {
pindexBestInvalid = pindexNew;
}
if (fParkedChain &&
(pindexBestParked == nullptr ||
pindexNew->nChainWork > pindexBestParked->nChainWork)) {
pindexBestParked = pindexNew;
}
LogPrintf("Considered switching to better tip %s but that chain "
"contains a%s%s%s block.\n",
pindexNew->GetBlockHash().ToString(),
fInvalidChain ? "n invalid" : "",
fParkedChain ? " parked" : "",
fMissingData ? " missing-data" : "");
CBlockIndex *pindexFailed = pindexNew;
// Remove the entire chain from the set.
while (pindexTest != pindexFailed) {
if (fInvalidChain || fParkedChain) {
pindexFailed->nStatus =
pindexFailed->nStatus.withFailedParent(fInvalidChain)
.withParkedParent(fParkedChain);
} else if (fMissingData) {
// If we're missing data, then add back to
// m_blocks_unlinked, so that if the block arrives in the
// future we can try adding to setBlockIndexCandidates
// again.
m_blockman.m_blocks_unlinked.insert(
std::make_pair(pindexFailed->pprev, pindexFailed));
}
setBlockIndexCandidates.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
if (fInvalidChain || fParkedChain) {
// We discovered a new chain tip that is either parked or
// invalid, we may want to warn.
CheckForkWarningConditionsOnNewFork(pindexNew);
}
}
if (fAvalancheEnabled && g_avalanche) {
g_avalanche->addBlockToReconcile(pindexNew);
}
// We found a candidate that has valid ancestors. This is our guy.
if (hasValidAncestor) {
return pindexNew;
}
} while (true);
}
/**
* Delete all entries in setBlockIndexCandidates that are worse than the current
* tip.
*/
void CChainState::PruneBlockIndexCandidates() {
// Note that we can't delete the current block itself, as we may need to
// return to it later in case a reorganization to a better block fails.
auto it = setBlockIndexCandidates.begin();
while (it != setBlockIndexCandidates.end() &&
setBlockIndexCandidates.value_comp()(*it, m_chain.Tip())) {
setBlockIndexCandidates.erase(it++);
}
// Either the current tip or a successor of it we're working towards is left
// in setBlockIndexCandidates.
assert(!setBlockIndexCandidates.empty());
}
/**
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either nullptr or a pointer to a CBlock corresponding to
* pindexMostWork.
*
* @returns true unless a system error occurred
*/
bool CChainState::ActivateBestChainStep(
const Config &config, BlockValidationState &state,
CBlockIndex *pindexMostWork, const std::shared_ptr<const CBlock> &pblock,
bool &fInvalidFound, ConnectTrace &connectTrace) {
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
const CBlockIndex *pindexOldTip = m_chain.Tip();
const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false;
DisconnectedBlockTransactions disconnectpool;
while (m_chain.Tip() && m_chain.Tip() != pindexFork) {
if (!DisconnectTip(config.GetChainParams(), state, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
disconnectpool.updateMempoolForReorg(config, false, m_mempool);
// 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");
return false;
}
fBlocksDisconnected = true;
}
// Build list of new blocks to connect.
std::vector<CBlockIndex *> vpindexToConnect;
bool fContinue = true;
int nHeight = pindexFork ? pindexFork->nHeight : -1;
while (fContinue && nHeight != pindexMostWork->nHeight) {
// Don't iterate the entire list of potential improvements toward the
// best tip, as we likely only need a few blocks along the way.
int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight);
vpindexToConnect.clear();
vpindexToConnect.reserve(nTargetHeight - nHeight);
CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
while (pindexIter && pindexIter->nHeight != nHeight) {
vpindexToConnect.push_back(pindexIter);
pindexIter = pindexIter->pprev;
}
nHeight = nTargetHeight;
// Connect new blocks.
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
if (!ConnectTip(config, state, pindexConnect,
pindexConnect == pindexMostWork
? pblock
: std::shared_ptr<const CBlock>(),
connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (state.GetResult() !=
BlockValidationResult::BLOCK_MUTATED) {
InvalidChainFound(vpindexToConnect.back());
}
state = BlockValidationState();
fInvalidFound = true;
fContinue = false;
break;
}
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in
// case any observers try to use it before shutdown.
disconnectpool.updateMempoolForReorg(config, false, m_mempool);
return false;
} else {
PruneBlockIndexCandidates();
if (!pindexOldTip ||
m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return
// temporarily to release the lock.
fContinue = false;
break;
}
}
}
}
if (fBlocksDisconnected || !disconnectpool.isEmpty()) {
// If any blocks were disconnected, we need to update the mempool even
// if disconnectpool is empty. The disconnectpool may also be non-empty
// if the mempool was imported due to new validation rules being in
// effect.
LogPrint(BCLog::MEMPOOL, "Updating mempool due to reorganization or "
"rules upgrade/downgrade\n");
disconnectpool.updateMempoolForReorg(config, true, m_mempool);
}
m_mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound) {
CheckForkWarningConditionsOnNewFork(pindexMostWork);
} else {
CheckForkWarningConditions();
}
return true;
}
static SynchronizationState GetSynchronizationState(bool init) {
if (!init) {
return SynchronizationState::POST_INIT;
}
if (::fReindex) {
return SynchronizationState::INIT_REINDEX;
}
return SynchronizationState::INIT_DOWNLOAD;
}
static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
static CBlockIndex *pindexHeaderOld = nullptr;
CBlockIndex *pindexHeader = nullptr;
{
LOCK(cs_main);
pindexHeader = pindexBestHeader;
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
fInitialBlockDownload =
::ChainstateActive().IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
}
// Send block tip changed notifications without cs_main
if (fNotify) {
uiInterface.NotifyHeaderTip(
GetSynchronizationState(fInitialBlockDownload), pindexHeader);
}
return fNotify;
}
static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
AssertLockNotHeld(cs_main);
if (GetMainSignals().CallbacksPending() > 10) {
SyncWithValidationInterfaceQueue();
}
}
bool CChainState::ActivateBestChain(const Config &config,
BlockValidationState &state,
std::shared_ptr<const CBlock> pblock) {
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
// us in the middle of ProcessNewBlock - do not assume pblock is set
// sanely for performance or correctness!
AssertLockNotHeld(cs_main);
const CChainParams ¶ms = config.GetChainParams();
// ABC maintains a fair degree of expensive-to-calculate internal state
// because this function periodically releases cs_main so that it does not
// lock up other threads for too long during large connects - and to allow
// for e.g. the callback queue to drain we use m_cs_chainstate to enforce
// mutual exclusion so that only one caller may execute this function at a
// time
LOCK(m_cs_chainstate);
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead.
// Note that if a validationinterface callback ends up calling
// ActivateBestChain this may lead to a deadlock! We should
// probably have a DEBUG_LOCKORDER test for this in the future.
LimitValidationInterfaceQueue();
{
LOCK(cs_main);
// Lock transaction pool for at least as long as it takes for
// connectTrace to be consumed
LOCK(m_mempool.cs);
CBlockIndex *starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
// We absolutely may not unlock cs_main until we've made forward
// progress (with the exception of shutdown due to hardware
// issues, low disk space, etc).
// Destructed before cs_main is unlocked
ConnectTrace connectTrace;
if (pindexMostWork == nullptr) {
pindexMostWork = FindMostWorkChain();
}
// Whether we have anything to do at all.
if (pindexMostWork == nullptr ||
pindexMostWork == m_chain.Tip()) {
break;
}
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
if (!ActivateBestChainStep(
config, state, pindexMostWork,
pblock && pblock->GetHash() ==
pindexMostWork->GetBlockHash()
? pblock
: nullBlockPtr,
fInvalidFound, connectTrace)) {
// A system error occurred
return false;
}
blocks_connected = true;
if (fInvalidFound) {
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
pindexNewTip = m_chain.Tip();
for (const PerBlockConnectTrace &trace :
connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex);
}
} while (!m_chain.Tip() ||
(starting_tip && CBlockIndexWorkComparator()(
m_chain.Tip(), starting_tip)));
// Check the index once we're done with the above loop, since
// we're going to release cs_main soon. If the index is in a bad
// state now, then it's better to know immediately rather than
// randomly have it cause a problem in a race.
CheckBlockIndex(params.GetConsensus());
if (!blocks_connected) {
return true;
}
const CBlockIndex *pindexFork = m_chain.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is
// called in the order in which blocks are connected
if (pindexFork != pindexNewTip) {
// Notify ValidationInterface subscribers
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork,
fInitialDownload);
// Always notify the UI if a new block tip was connected
uiInterface.NotifyBlockTip(
GetSynchronizationState(fInitialDownload), pindexNewTip);
}
}
// When we reach this point, we switched to a new tip (stored in
// pindexNewTip).
if (nStopAtHeight && pindexNewTip &&
pindexNewTip->nHeight >= nStopAtHeight) {
StartShutdown();
}
// 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
// that the best block hash is non-null.
if (ShutdownRequested()) {
break;
}
} while (pindexNewTip != pindexMostWork);
// Write changes periodically to disk, after relay.
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
return false;
}
return true;
}
bool ActivateBestChain(const Config &config, BlockValidationState &state,
std::shared_ptr<const CBlock> pblock) {
return ::ChainstateActive().ActivateBestChain(config, state,
std::move(pblock));
}
bool CChainState::PreciousBlock(const Config &config,
BlockValidationState &state,
CBlockIndex *pindex) {
{
LOCK(cs_main);
if (pindex->nChainWork < m_chain.Tip()->nChainWork) {
// Nothing to do, this block is not at the tip.
return true;
}
if (m_chain.Tip()->nChainWork > nLastPreciousChainwork) {
// The chain has been extended since the last call, reset the
// counter.
nBlockReverseSequenceId = -1;
}
nLastPreciousChainwork = m_chain.Tip()->nChainWork;
setBlockIndexCandidates.erase(pindex);
pindex->nSequenceId = nBlockReverseSequenceId;
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
// We can't keep reducing the counter if somebody really wants to
// call preciousblock 2**31-1 times on the same set of tips...
nBlockReverseSequenceId--;
}
// In case this was parked, unpark it.
UnparkBlock(pindex);
// Make sure it is added to the candidate list if appropriate.
if (pindex->IsValid(BlockValidity::TRANSACTIONS) &&
pindex->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
}
}
return ActivateBestChain(config, state);
}
bool PreciousBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) {
return ::ChainstateActive().PreciousBlock(config, state, pindex);
}
bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex, bool invalidate) {
CBlockIndex *to_mark_failed_or_parked = pindex;
bool pindex_was_in_chain = false;
int disconnected = 0;
const CChainParams &chainparams = config.GetChainParams();
// We do not allow ActivateBestChain() to run while UnwindBlock() is
// running, as that could cause the tip to change while we disconnect
// blocks. (Note for backport of Core PR16849: we acquire
// LOCK(m_cs_chainstate) in the Park, Invalidate and FinalizeBlock functions
// due to differences in our code)
AssertLockHeld(m_cs_chainstate);
// We'll be acquiring and releasing cs_main below, to allow the validation
// callbacks to run. However, we should keep the block index in a
// consistent state as we disconnect blocks -- in particular we need to
// add equal-work blocks to setBlockIndexCandidates as we disconnect.
// To avoid walking the block index repeatedly in search of candidates,
// build a map once so that we can look up candidate blocks by chain
// work as we go.
std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work;
{
LOCK(cs_main);
for (const auto &entry : m_blockman.m_block_index) {
CBlockIndex *candidate = entry.second;
// We don't need to put anything in our active chain into the
// multimap, because those candidates will be found and considered
// as we disconnect.
// Instead, consider only non-active-chain blocks that have at
// least as much work as where we expect the new tip to end up.
if (!m_chain.Contains(candidate) &&
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
candidate->IsValid(BlockValidity::TRANSACTIONS) &&
candidate->HaveTxsDownloaded()) {
candidate_blocks_by_work.insert(
std::make_pair(candidate->nChainWork, candidate));
}
}
}
// Disconnect (descendants of) pindex, and mark them invalid.
while (true) {
if (ShutdownRequested()) {
break;
}
// Make sure the queue of validation callbacks doesn't grow unboundedly.
LimitValidationInterfaceQueue();
LOCK(cs_main);
// Lock for as long as disconnectpool is in scope to make sure
// UpdateMempoolForReorg is called after DisconnectTip without unlocking
// in between
LOCK(m_mempool.cs);
if (!m_chain.Contains(pindex)) {
break;
}
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
// ActivateBestChain considers blocks already in m_chain
// unconditionally valid already, so force disconnect away from it.
DisconnectedBlockTransactions disconnectpool;
bool ret = DisconnectTip(chainparams, state, &disconnectpool);
// DisconnectTip will add transactions to disconnectpool.
// Adjust the mempool to be consistent with the new tip, adding
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
disconnectpool.updateMempoolForReorg(
config, /* fAddToMempool = */ (++disconnected <= 10) && ret,
m_mempool);
if (!ret) {
return false;
}
assert(invalid_walk_tip->pprev == m_chain.Tip());
// We immediately mark the disconnected blocks as invalid.
// This prevents a case where pruned nodes may fail to invalidateblock
// and be left unable to start as they have no tip candidates (as there
// are no blocks that meet the "have data and are not invalid per
// nStatus" criteria for inclusion in setBlockIndexCandidates).
invalid_walk_tip->nStatus =
invalidate ? invalid_walk_tip->nStatus.withFailed()
: invalid_walk_tip->nStatus.withParked();
setDirtyBlockIndex.insert(invalid_walk_tip);
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
if (invalid_walk_tip == to_mark_failed_or_parked->pprev &&
(invalidate ? to_mark_failed_or_parked->nStatus.hasFailed()
: to_mark_failed_or_parked->nStatus.isParked())) {
// We only want to mark the last disconnected block as
// Failed (or Parked); its children need to be FailedParent (or
// ParkedParent) instead.
to_mark_failed_or_parked->nStatus =
(invalidate
? to_mark_failed_or_parked->nStatus.withFailed(false)
.withFailedParent()
: to_mark_failed_or_parked->nStatus.withParked(false)
.withParkedParent());
setDirtyBlockIndex.insert(to_mark_failed_or_parked);
}
// Add any equal or more work headers to setBlockIndexCandidates
auto candidate_it = candidate_blocks_by_work.lower_bound(
invalid_walk_tip->pprev->nChainWork);
while (candidate_it != candidate_blocks_by_work.end()) {
if (!CBlockIndexWorkComparator()(candidate_it->second,
invalid_walk_tip->pprev)) {
setBlockIndexCandidates.insert(candidate_it->second);
candidate_it = candidate_blocks_by_work.erase(candidate_it);
} else {
++candidate_it;
}
}
// Track the last disconnected block, so we can correct its
// FailedParent (or ParkedParent) status in future iterations, or, if
// it's the last one, call InvalidChainFound on it.
to_mark_failed_or_parked = invalid_walk_tip;
}
CheckBlockIndex(chainparams.GetConsensus());
{
LOCK(cs_main);
if (m_chain.Contains(to_mark_failed_or_parked)) {
// If the to-be-marked invalid block is in the active chain,
// something is interfering and we can't proceed.
return false;
}
// Mark pindex (or the last disconnected block) as invalid (or parked),
// even when it never was in the main chain.
to_mark_failed_or_parked->nStatus =
invalidate ? to_mark_failed_or_parked->nStatus.withFailed()
: to_mark_failed_or_parked->nStatus.withParked();
setDirtyBlockIndex.insert(to_mark_failed_or_parked);
if (invalidate) {
m_blockman.m_failed_blocks.insert(to_mark_failed_or_parked);
}
// If any new blocks somehow arrived while we were disconnecting
// (above), then the pre-calculation of what should go into
// setBlockIndexCandidates may have missed entries. This would
// technically be an inconsistency in the block index, but if we clean
// it up here, this should be an essentially unobservable error.
// Loop back over all block index entries and add any missing entries
// to setBlockIndexCandidates.
for (const std::pair<const BlockHash, CBlockIndex *> &it :
m_blockman.m_block_index) {
CBlockIndex *i = it.second;
if (i->IsValid(BlockValidity::TRANSACTIONS) &&
i->HaveTxsDownloaded() &&
!setBlockIndexCandidates.value_comp()(i, m_chain.Tip())) {
setBlockIndexCandidates.insert(i);
}
}
if (invalidate) {
InvalidChainFound(to_mark_failed_or_parked);
}
}
// Only notify about a new block tip if the active chain was modified.
if (pindex_was_in_chain) {
uiInterface.NotifyBlockTip(
GetSynchronizationState(IsInitialBlockDownload()),
to_mark_failed_or_parked->pprev);
}
return true;
}
bool CChainState::InvalidateBlock(const Config &config,
BlockValidationState &state,
CBlockIndex *pindex) {
AssertLockNotHeld(m_cs_chainstate);
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock
LOCK(m_cs_chainstate);
return UnwindBlock(config, state, pindex, true);
}
bool CChainState::ParkBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) {
AssertLockNotHeld(m_cs_chainstate);
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock
LOCK(m_cs_chainstate);
return UnwindBlock(config, state, pindex, false);
}
bool CChainState::FinalizeBlock(const Config &config,
BlockValidationState &state,
CBlockIndex *pindex) {
AssertLockNotHeld(m_cs_chainstate);
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock
LOCK(m_cs_chainstate);
AssertLockNotHeld(cs_main);
CBlockIndex *pindexToInvalidate = nullptr;
{
LOCK(cs_main);
if (!MarkBlockAsFinal(state, pindex)) {
// state is set by MarkBlockAsFinal.
return false;
}
// We have a valid candidate, make sure it is not parked.
if (pindex->nStatus.isOnParkedChain()) {
UnparkBlock(pindex);
}
// If the finalized block is on the active chain, there is no need to
// rewind.
if (::ChainActive().Contains(pindex)) {
return true;
}
// If the finalized block is not on the active chain, that chain is
// invalid
// ...
const CBlockIndex *pindexFork = ::ChainActive().FindFork(pindex);
pindexToInvalidate = ::ChainActive().Next(pindexFork);
if (!pindexToInvalidate) {
return false;
}
} // end of locked cs_main scope
// ... therefore, we invalidate the block on the active chain that comes
// immediately after it
return UnwindBlock(config, state, pindexToInvalidate,
true /* invalidating */);
}
template <typename F>
bool CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase,
CBlockIndex *pindex, F f) {
BlockStatus newStatus = f(pindex->nStatus);
if (pindex->nStatus != newStatus &&
(!pindexBase ||
pindex->GetAncestor(pindexBase->nHeight) == pindexBase)) {
pindex->nStatus = newStatus;
setDirtyBlockIndex.insert(pindex);
if (newStatus.isValid()) {
m_blockman.m_failed_blocks.erase(pindex);
}
if (pindex->IsValid(BlockValidity::TRANSACTIONS) &&
pindex->HaveTxsDownloaded() &&
setBlockIndexCandidates.value_comp()(::ChainActive().Tip(),
pindex)) {
setBlockIndexCandidates.insert(pindex);
}
return true;
}
return false;
}
template <typename F, typename C, typename AC>
void CChainState::UpdateFlags(CBlockIndex *pindex, CBlockIndex *&pindexReset,
F f, C fChild, AC fAncestorWasChanged) {
AssertLockHeld(cs_main);
// Update the current block and ancestors; while we're doing this, identify
// which was the deepest ancestor we changed.
CBlockIndex *pindexDeepestChanged = pindex;
for (auto pindexAncestor = pindex; pindexAncestor != nullptr;
pindexAncestor = pindexAncestor->pprev) {
if (UpdateFlagsForBlock(nullptr, pindexAncestor, f)) {
pindexDeepestChanged = pindexAncestor;
}
}
if (pindexReset &&
pindexReset->GetAncestor(pindexDeepestChanged->nHeight) ==
pindexDeepestChanged) {
// reset pindexReset if it had a modified ancestor.
pindexReset = nullptr;
}
// Update all blocks under modified blocks.
BlockMap::iterator it = m_blockman.m_block_index.begin();
while (it != m_blockman.m_block_index.end()) {
UpdateFlagsForBlock(pindex, it->second, fChild);
UpdateFlagsForBlock(pindexDeepestChanged, it->second,
fAncestorWasChanged);
it++;
}
}
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
// In case we are reconsidering something before the finalization point,
// move the finalization point to the last common ancestor.
if (m_finalizedBlockIndex) {
m_finalizedBlockIndex =
LastCommonAncestor(pindex, m_finalizedBlockIndex);
}
UpdateFlags(
pindex, pindexBestInvalid,
[](const BlockStatus status) {
return status.withClearedFailureFlags();
},
[](const BlockStatus status) {
return status.withClearedFailureFlags();
},
[](const BlockStatus status) {
return status.withFailedParent(false);
});
}
void ResetBlockFailureFlags(CBlockIndex *pindex) {
return ::ChainstateActive().ResetBlockFailureFlags(pindex);
}
void CChainState::UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren) {
AssertLockHeld(cs_main);
UpdateFlags(
pindex, pindexBestParked,
[](const BlockStatus status) {
return status.withClearedParkedFlags();
},
[fClearChildren](const BlockStatus status) {
return fClearChildren ? status.withClearedParkedFlags()
: status.withParkedParent(false);
},
[](const BlockStatus status) {
return status.withParkedParent(false);
});
}
void UnparkBlockAndChildren(CBlockIndex *pindex) {
return ::ChainstateActive().UnparkBlockImpl(pindex, true);
}
void UnparkBlock(CBlockIndex *pindex) {
return ::ChainstateActive().UnparkBlockImpl(pindex, false);
}
bool CChainState::IsBlockFinalized(const CBlockIndex *pindex) const {
AssertLockHeld(cs_main);
return m_finalizedBlockIndex &&
m_finalizedBlockIndex->GetAncestor(pindex->nHeight) == pindex;
}
/** Return the currently finalized block index. */
const CBlockIndex *CChainState::GetFinalizedBlock() const {
AssertLockHeld(cs_main);
return m_finalizedBlockIndex;
}
CBlockIndex *BlockManager::AddToBlockIndex(const CBlockHeader &block) {
AssertLockHeld(cs_main);
// Check for duplicate
BlockHash hash = block.GetHash();
BlockMap::iterator it = m_block_index.find(hash);
if (it != m_block_index.end()) {
return it->second;
}
// Construct new block index object
CBlockIndex *pindexNew = new CBlockIndex(block);
// We assign the sequence id to blocks only when the full data is available,
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
pindexNew->nSequenceId = 0;
BlockMap::iterator mi =
m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
if (miPrev != m_block_index.end()) {
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
}
pindexNew->nTimeReceived = GetTime();
pindexNew->nTimeMax =
(pindexNew->pprev
? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime)
: pindexNew->nTime);
pindexNew->nChainWork =
(pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) +
GetBlockProof(*pindexNew);
pindexNew->RaiseValidity(BlockValidity::TREE);
if (pindexBestHeader == nullptr ||
pindexBestHeader->nChainWork < pindexNew->nChainWork) {
pindexBestHeader = pindexNew;
}
setDirtyBlockIndex.insert(pindexNew);
return pindexNew;
}
/**
* Mark a block as having its data received and checked (up to
* BLOCK_VALID_TRANSACTIONS).
*/
void CChainState::ReceivedBlockTransactions(const CBlock &block,
CBlockIndex *pindexNew,
const FlatFilePos &pos) {
pindexNew->nTx = block.vtx.size();
pindexNew->nSize = ::GetSerializeSize(block, PROTOCOL_VERSION);
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus = pindexNew->nStatus.withData();
pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS);
setDirtyBlockIndex.insert(pindexNew);
if (pindexNew->UpdateChainStats()) {
// If pindexNew is the genesis block or all parents are
// BLOCK_VALID_TRANSACTIONS.
std::deque<CBlockIndex *> queue;
queue.push_back(pindexNew);
// Recursively process any descendant blocks that now may be eligible to
// be connected.
while (!queue.empty()) {
CBlockIndex *pindex = queue.front();
queue.pop_front();
pindex->UpdateChainStats();
if (pindex->nSequenceId == 0) {
// We assign a sequence is when transaction are received to
// prevent a miner from being able to broadcast a block but not
// its content. However, a sequence id may have been set
// manually, for instance via PreciousBlock, in which case, we
// don't need to assign one.
pindex->nSequenceId = nBlockSequenceId++;
}
if (m_chain.Tip() == nullptr ||
!setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator,
std::multimap<CBlockIndex *, CBlockIndex *>::iterator>
range = m_blockman.m_blocks_unlinked.equal_range(pindex);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator it =
range.first;
queue.push_back(it->second);
range.first++;
m_blockman.m_blocks_unlinked.erase(it);
}
}
} else if (pindexNew->pprev &&
pindexNew->pprev->IsValid(BlockValidity::TREE)) {
m_blockman.m_blocks_unlinked.insert(
std::make_pair(pindexNew->pprev, pindexNew));
}
}
static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize,
unsigned int nHeight, uint64_t nTime,
bool fKnown = false) {
LOCK(cs_LastBlockFile);
unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
}
bool finalize_undo = false;
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
// when the undo file is keeping up with the block file, we want to
// flush it explicitly when it is lagging behind (more blocks arrive
// than are being connected), we let the undo block write case
// handle it
finalize_undo = (vinfoBlockFile[nFile].nHeightLast ==
(unsigned int)ChainActive().Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
}
}
pos.nFile = nFile;
pos.nPos = vinfoBlockFile[nFile].nSize;
}
if ((int)nFile != nLastBlockFile) {
if (!fKnown) {
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile,
vinfoBlockFile[nLastBlockFile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
}
vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
if (fKnown) {
vinfoBlockFile[nFile].nSize =
std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize);
} else {
vinfoBlockFile[nFile].nSize += nAddSize;
}
if (!fKnown) {
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!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
}
}
setDirtyFileInfo.insert(nFile);
return true;
}
static bool FindUndoPos(BlockValidationState &state, int nFile,
FlatFilePos &pos, unsigned int nAddSize) {
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
pos.nPos = vinfoBlockFile[nFile].nUndoSize;
vinfoBlockFile[nFile].nUndoSize += nAddSize;
setDirtyFileInfo.insert(nFile);
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!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
}
return true;
}
/**
* Return true if the provided block header is valid.
* Only verify PoW if blockValidationOptions is configured to do so.
* This allows validation of headers on which the PoW hasn't been done.
* For example: to validate template handed to mining software.
* Do not call this for any check that depends on the context.
* For context-dependent calls, see ContextualCheckBlockHeader.
*/
static bool CheckBlockHeader(const CBlockHeader &block,
BlockValidationState &state,
const Consensus::Params ¶ms,
BlockValidationOptions validationOptions) {
// Check proof of work matches claimed amount
if (validationOptions.shouldValidatePoW() &&
!CheckProofOfWork(block.GetHash(), block.nBits, params)) {
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER,
"high-hash", "proof of work failed");
}
return true;
}
bool CheckBlock(const CBlock &block, BlockValidationState &state,
const Consensus::Params ¶ms,
BlockValidationOptions validationOptions) {
// These are checks that are independent of context.
if (block.fChecked) {
return true;
}
// Check that the header is valid (particularly PoW). This is mostly
// redundant with the call in AcceptBlockHeader.
if (!CheckBlockHeader(block, state, params, validationOptions)) {
return false;
}
// Check the merkle root.
if (validationOptions.shouldValidateMerkleRoot()) {
bool mutated;
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2) {
return state.Invalid(BlockValidationResult::BLOCK_MUTATED,
"bad-txnmrklroot", "hashMerkleRoot mismatch");
}
// Check for merkle tree malleability (CVE-2012-2459): repeating
// sequences of transactions in a block without affecting the merkle
// root of a block, while still invalidating it.
if (mutated) {
return state.Invalid(BlockValidationResult::BLOCK_MUTATED,
"bad-txns-duplicate", "duplicate transaction");
}
}
// All potential-corruption validation must be done before we do any
// transaction validation, as otherwise we may mark the header as invalid
// because we receive the wrong transactions for it.
// First transaction must be coinbase.
if (block.vtx.empty()) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-cb-missing", "first tx is not coinbase");
}
// Size limits.
auto nMaxBlockSize = validationOptions.getExcessiveBlockSize();
// Bail early if there is no way this block is of reasonable size.
if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-blk-length", "size limits failed");
}
auto currentBlockSize = ::GetSerializeSize(block, PROTOCOL_VERSION);
if (currentBlockSize > nMaxBlockSize) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-blk-length", "size limits failed");
}
// And a valid coinbase.
TxValidationState tx_state;
if (!CheckCoinbase(*block.vtx[0], tx_state)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
strprintf("Coinbase check failed (txid %s) %s",
block.vtx[0]->GetId().ToString(),
tx_state.GetDebugMessage()));
}
// Check transactions for regularity, skipping the first. Note that this
// is the first time we check that all after the first are !IsCoinBase.
for (size_t i = 1; i < block.vtx.size(); i++) {
auto *tx = block.vtx[i].get();
if (!CheckRegularTransaction(*tx, tx_state)) {
return state.Invalid(
BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
strprintf("Transaction check failed (txid %s) %s",
tx->GetId().ToString(), tx_state.GetDebugMessage()));
}
}
if (validationOptions.shouldValidatePoW() &&
validationOptions.shouldValidateMerkleRoot()) {
block.fChecked = true;
}
return true;
}
/**
* Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
* NOTE: This function is not currently invoked by ConnectBlock(), so we
* should consider upgrade issues if we change which consensus rules are
* enforced in this function (eg by adding a new consensus rule). See comment
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
static bool ContextualCheckBlockHeader(const CChainParams ¶ms,
const CBlockHeader &block,
BlockValidationState &state,
const CBlockIndex *pindexPrev,
int64_t nAdjustedTime)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, params)) {
LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight);
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER,
"bad-diffbits", "incorrect proof of work");
}
// Check against checkpoints
if (fCheckpointsEnabled) {
const CCheckpointData &checkpoints = params.Checkpoints();
// Check that the block chain matches the known block chain up to a
// checkpoint.
if (!Checkpoints::CheckBlock(checkpoints, nHeight, block.GetHash())) {
LogPrintf("ERROR: %s: rejected by checkpoint lock-in at %d\n",
__func__, nHeight);
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT,
"checkpoint mismatch");
}
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's
// in our BlockIndex().
CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints);
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint "
"(height %d)\n",
__func__, nHeight);
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT,
"bad-fork-prior-to-checkpoint");
}
}
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) {
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER,
"time-too-old", "block's timestamp is too early");
}
// Check timestamp
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) {
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE,
"time-too-new",
"block timestamp too far in the future");
}
// Reject outdated version blocks when 95% (75% on testnet) of the network
// has upgraded:
// check for version 2, 3 and 4 upgrades
const Consensus::Params &consensusParams = params.GetConsensus();
if ((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) {
return state.Invalid(
BlockValidationResult::BLOCK_INVALID_HEADER,
strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
}
return true;
}
bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms,
const CTransaction &tx,
TxValidationState &state,
int flags) {
AssertLockHeld(cs_main);
// By convention a negative value for flags indicates that the current
// network-enforced consensus rules should be used. In a future soft-fork
// scenario that would mean checking which rules would be enforced for the
// next block and setting the appropriate flags. At the present time no
// soft-forks are scheduled, so no flags are set.
flags = std::max(flags, 0);
// ContextualCheckTransactionForCurrentBlock() uses
// ::ChainActive().Height()+1 to evaluate nLockTime because when IsFinalTx()
// is called within CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a transaction can
// be part of the *next* block, we need to call ContextualCheckTransaction()
// with one more than ::ChainActive().Height().
const int nBlockHeight = ::ChainActive().Height() + 1;
// BIP113 will require that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
// When the next block is created its previous block will be the current
// chain tip, so we use that to calculate the median time passed to
// ContextualCheckTransaction() if LOCKTIME_MEDIAN_TIME_PAST is set.
const int64_t nMedianTimePast =
::ChainActive().Tip() == nullptr
? 0
: ::ChainActive().Tip()->GetMedianTimePast();
const int64_t nLockTimeCutoff = (flags & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: GetAdjustedTime();
return ContextualCheckTransaction(params, tx, state, nBlockHeight,
nLockTimeCutoff, nMedianTimePast);
}
/**
* NOTE: This function is not currently invoked by ConnectBlock(), so we
* should consider upgrade issues if we change which consensus rules are
* enforced in this function (eg by adding a new consensus rule). See comment
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
static bool ContextualCheckBlock(const CBlock &block,
BlockValidationState &state,
const Consensus::Params ¶ms,
const CBlockIndex *pindexPrev) {
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Start enforcing BIP113 (Median Time Past).
int nLockTimeFlags = 0;
if (nHeight >= params.CSVHeight) {
assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
const int64_t nMedianTimePast =
pindexPrev == nullptr ? 0 : pindexPrev->GetMedianTimePast();
const int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: block.GetBlockTime();
const bool fIsMagneticAnomalyEnabled =
IsMagneticAnomalyEnabled(params, pindexPrev);
// Check transactions:
// - canonical ordering
// - ensure they are finalized
// - perform a preliminary block-sigops count (they will be recounted more
// strictly during ConnectBlock).
// - perform a transaction-sigops check (again, a more strict check will
// happen in ConnectBlock).
const CTransaction *prevTx = nullptr;
for (const auto &ptx : block.vtx) {
const CTransaction &tx = *ptx;
if (fIsMagneticAnomalyEnabled) {
if (prevTx && (tx.GetId() <= prevTx->GetId())) {
if (tx.GetId() == prevTx->GetId()) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"tx-duplicate",
strprintf("Duplicated transaction %s",
tx.GetId().ToString()));
}
return state.Invalid(
BlockValidationResult::BLOCK_CONSENSUS, "tx-ordering",
strprintf("Transaction order is invalid (%s < %s)",
tx.GetId().ToString(),
prevTx->GetId().ToString()));
}
if (prevTx || !tx.IsCoinBase()) {
prevTx = &tx;
}
}
TxValidationState tx_state;
if (!ContextualCheckTransaction(params, tx, tx_state, nHeight,
nLockTimeCutoff, nMedianTimePast)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
tx_state.GetDebugMessage());
}
}
// Enforce rule that the coinbase starts with serialized block height
if (nHeight >= params.BIP34Height) {
CScript expect = CScript() << nHeight;
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(),
block.vtx[0]->vin[0].scriptSig.begin())) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
"bad-cb-height",
"block height mismatch in coinbase");
}
}
return true;
}
/**
* If the provided block header is valid, add it to the block index.
*
* Returns true if the block is successfully added to the block index.
*/
bool BlockManager::AcceptBlockHeader(const Config &config,
const CBlockHeader &block,
BlockValidationState &state,
CBlockIndex **ppindex) {
AssertLockHeld(cs_main);
const CChainParams &chainparams = config.GetChainParams();
// Check for duplicate
BlockHash hash = block.GetHash();
BlockMap::iterator miSelf = m_block_index.find(hash);
CBlockIndex *pindex = nullptr;
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != m_block_index.end()) {
// Block header is already known.
pindex = miSelf->second;
if (ppindex) {
*ppindex = pindex;
}
if (pindex->nStatus.isInvalid()) {
LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__,
hash.ToString());
return state.Invalid(
BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate");
}
return true;
}
if (!CheckBlockHeader(block, state, chainparams.GetConsensus(),
BlockValidationOptions(config))) {
LogPrint(BCLog::VALIDATION,
"%s: Consensus::CheckBlockHeader: %s, %s\n", __func__,
hash.ToString(), state.ToString());
return false;
}
// Get prev block index
BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock);
if (mi == m_block_index.end()) {
LogPrintf("ERROR: %s: prev block not found\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV,
"prev-blk-not-found");
}
CBlockIndex *pindexPrev = (*mi).second;
assert(pindexPrev);
if (pindexPrev->nStatus.isInvalid()) {
LogPrintf("ERROR: %s: prev block invalid\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV,
"bad-prevblk");
}
if (!ContextualCheckBlockHeader(chainparams, block, state, pindexPrev,
GetAdjustedTime())) {
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s",
__func__, hash.ToString(), state.ToString());
}
/* Determine if this block descends from any block which has been found
* invalid (m_failed_blocks), then mark pindexPrev and any blocks
* between them as failed. For example:
*
* D3
* /
* B2 - C2
* / \
* A D2 - E2 - F2
* \
* B1 - C1 - D1 - E1
*
* In the case that we attempted to reorg from E1 to F2, only to find
* C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
* but NOT D3 (it was not in any of our candidate sets at the time).
*
* In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
* in LoadBlockIndex.
*/
if (!pindexPrev->IsValid(BlockValidity::SCRIPTS)) {
// The above does not mean "invalid": it checks if the previous
// block hasn't been validated up to BlockValidity::SCRIPTS. This is
// a performance optimization, in the common case of adding a new
// block to the tip, we don't need to iterate over the failed blocks
// list.
for (const CBlockIndex *failedit : m_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus.hasFailed());
CBlockIndex *invalid_walk = pindexPrev;
while (invalid_walk != failedit) {
invalid_walk->nStatus =
invalid_walk->nStatus.withFailedParent();
setDirtyBlockIndex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
LogPrintf("ERROR: %s: prev block invalid\n", __func__);
return state.Invalid(
BlockValidationResult::BLOCK_INVALID_PREV,
"bad-prevblk");
}
}
}
}
if (pindex == nullptr) {
pindex = AddToBlockIndex(block);
}
if (ppindex) {
*ppindex = pindex;
}
return true;
}
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(
const Config &config, const std::vector<CBlockHeader> &headers,
BlockValidationState &state, const CBlockIndex **ppindex) {
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader &header : headers) {
// Use a temp pindex instead of ppindex to avoid a const_cast
CBlockIndex *pindex = nullptr;
bool accepted =
m_blockman.AcceptBlockHeader(config, header, state, &pindex);
::ChainstateActive().CheckBlockIndex(
config.GetChainParams().GetConsensus());
if (!accepted) {
return false;
}
if (ppindex) {
*ppindex = pindex;
}
}
}
if (NotifyHeaderTip()) {
if (::ChainstateActive().IsInitialBlockDownload() && ppindex &&
*ppindex) {
LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n",
(*ppindex)->nHeight,
100.0 /
((*ppindex)->nHeight +
(GetAdjustedTime() - (*ppindex)->GetBlockTime()) /
Params().GetConsensus().nPowTargetSpacing) *
(*ppindex)->nHeight);
}
}
return true;
}
/**
* Store block on disk. If dbp is non-nullptr, the file is known to already
* reside on disk.
*/
static FlatFilePos SaveBlockToDisk(const CBlock &block, int nHeight,
const CChainParams &chainparams,
const FlatFilePos *dbp) {
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
FlatFilePos blockPos;
if (dbp != nullptr) {
blockPos = *dbp;
}
if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(),
dbp != nullptr)) {
error("%s: FindBlockPos failed", __func__);
return FlatFilePos();
}
if (dbp == nullptr) {
if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) {
AbortNode("Failed to write block");
return FlatFilePos();
}
}
return blockPos;
}
/**
* Store a block on disk.
*
* @param[in] config The global config.
* @param[in-out] pblock The block we want to accept.
* @param[in] fRequested A boolean to indicate if this block was requested
* from our peers.
* @param[in] dbp If non-null, the disk position of the block.
* @param[in-out] fNewBlock True if block was first received via this call.
* @return True if the block is accepted as a valid block and written to disk.
*/
bool CChainState::AcceptBlock(const Config &config,
const std::shared_ptr<const CBlock> &pblock,
BlockValidationState &state, bool fRequested,
const FlatFilePos *dbp, bool *fNewBlock) {
AssertLockHeld(cs_main);
const CBlock &block = *pblock;
if (fNewBlock) {
*fNewBlock = false;
}
CBlockIndex *pindex = nullptr;
bool accepted_header =
m_blockman.AcceptBlockHeader(config, block, state, &pindex);
CheckBlockIndex(config.GetChainParams().GetConsensus());
if (!accepted_header) {
return false;
}
// Try to process all requested blocks that we don't have, but only
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus.hasData();
// TODO: deal better with return value and error conditions for duplicate
// and unrequested blocks.
if (fAlreadyHave) {
return true;
}
// Compare block header timestamps and received times of the block and the
// chaintip. If they have the same chain height, use these diffs as a
// tie-breaker, attempting to pick the more honestly-mined block.
int64_t newBlockTimeDiff = std::llabs(pindex->GetReceivedTimeDiff());
int64_t chainTipTimeDiff =
m_chain.Tip() ? std::llabs(m_chain.Tip()->GetReceivedTimeDiff()) : 0;
bool isSameHeight =
m_chain.Tip() && (pindex->nChainWork == m_chain.Tip()->nChainWork);
if (isSameHeight) {
LogPrintf("Chain tip timestamp-to-received-time difference: hash=%s, "
"diff=%d\n",
m_chain.Tip()->GetBlockHash().ToString(), chainTipTimeDiff);
LogPrintf("New block timestamp-to-received-time difference: hash=%s, "
"diff=%d\n",
pindex->GetBlockHash().ToString(), newBlockTimeDiff);
}
bool fHasMoreOrSameWork =
(m_chain.Tip() ? pindex->nChainWork >= m_chain.Tip()->nChainWork
: true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
// regardless of whether pruning is enabled; it should generally be safe to
// not process unrequested blocks.
bool fTooFarAhead =
(pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP));
// TODO: Decouple this function from the block download logic by removing
// fRequested
// This requires some new chain data structure to efficiently look up if a
// block is in a chain leading to a candidate for best tip, despite not
// being such a candidate itself.
// If we didn't ask for it:
if (!fRequested) {
// This is a previously-processed block that was pruned.
if (pindex->nTx != 0) {
return true;
}
// Don't process less-work chains.
if (!fHasMoreOrSameWork) {
return true;
}
// Block height is too high.
if (fTooFarAhead) {
return true;
}
// Protect against DoS attacks from low-work chains.
// If our tip is behind, a peer could try to send us
// low-work blocks on a fake chain that we would never
// request; don't process these.
if (pindex->nChainWork < nMinimumChainWork) {
return true;
}
}
const CChainParams &chainparams = config.GetChainParams();
const Consensus::Params &consensusParams = chainparams.GetConsensus();
if (!CheckBlock(block, state, consensusParams,
BlockValidationOptions(config)) ||
!ContextualCheckBlock(block, state, consensusParams, pindex->pprev)) {
if (state.IsInvalid() &&
state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus = pindex->nStatus.withFailed();
setDirtyBlockIndex.insert(pindex);
}
return error("%s: %s (block %s)", __func__, state.ToString(),
block.GetHash().ToString());
}
// If connecting the new block would require rewinding more than one block
// from the active chain (i.e., a "deep reorg"), then mark the new block as
// parked. If it has enough work then it will be automatically unparked
// later, during FindMostWorkChain. We mark the block as parked at the very
// last minute so we can make sure everything is ready to be reorged if
// needed.
if (gArgs.GetBoolArg("-parkdeepreorg", true)) {
const CBlockIndex *pindexFork = m_chain.FindFork(pindex);
if (pindexFork && pindexFork->nHeight + 1 < m_chain.Height()) {
LogPrintf("Park block %s as it would cause a deep reorg.\n",
pindex->GetBlockHash().ToString());
pindex->nStatus = pindex->nStatus.withParked();
setDirtyBlockIndex.insert(pindex);
}
}
// Header is valid/has work and the merkle tree is good.
// Relay now, but if it does not build on our best tip, let the
// SendMessages loop relay it.
if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev) {
GetMainSignals().NewPoWValidBlock(pindex, pblock);
}
// Write block to history file
if (fNewBlock) {
*fNewBlock = true;
}
try {
FlatFilePos blockPos =
SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf(
"%s: Failed to find position to write new block to disk",
__func__));
return false;
}
ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error &e) {
return AbortNode(state, std::string("System error: ") + e.what());
}
FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
CheckBlockIndex(consensusParams);
return true;
}
bool ChainstateManager::ProcessNewBlock(
const Config &config, const std::shared_ptr<const CBlock> pblock,
bool fForceProcessing, bool *fNewBlock) {
AssertLockNotHeld(cs_main);
{
if (fNewBlock) {
*fNewBlock = false;
}
BlockValidationState state;
// CheckBlock() does not support multi-threaded block validation
// because CBlock::fChecked can cause data race.
// Therefore, the following critical section must include the
// CheckBlock() call as well.
LOCK(cs_main);
// Ensure that CheckBlock() passes before calling AcceptBlock, as
// belt-and-suspenders.
bool ret =
CheckBlock(*pblock, state, config.GetChainParams().GetConsensus(),
BlockValidationOptions(config));
if (ret) {
// Store to disk
ret = ::ChainstateActive().AcceptBlock(
config, pblock, state, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
return error("%s: AcceptBlock FAILED (%s)", __func__,
state.ToString());
}
}
NotifyHeaderTip();
// Only used to report errors, not invalidity - ignore it
BlockValidationState state;
if (!::ChainstateActive().ActivateBestChain(config, state, pblock)) {
return error("%s: ActivateBestChain failed (%s)", __func__,
state.ToString());
}
return true;
}
bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms,
const CBlock &block, CBlockIndex *pindexPrev,
BlockValidationOptions validationOptions) {
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
BlockHash block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(params, block, state, pindexPrev,
GetAdjustedTime())) {
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__,
state.ToString());
}
if (!CheckBlock(block, state, params.GetConsensus(), validationOptions)) {
return error("%s: Consensus::CheckBlock: %s", __func__,
state.ToString());
}
if (!ContextualCheckBlock(block, state, params.GetConsensus(),
pindexPrev)) {
return error("%s: Consensus::ContextualCheckBlock: %s", __func__,
state.ToString());
}
if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew,
params, validationOptions, true)) {
return false;
}
assert(state.IsValid());
return true;
}
/**
* BLOCK PRUNING CODE
*/
/**
* Calculate the amount of disk space the block & undo files currently use.
*/
uint64_t CalculateCurrentUsage() {
LOCK(cs_LastBlockFile);
uint64_t retval = 0;
for (const CBlockFileInfo &file : vinfoBlockFile) {
retval += file.nSize + file.nUndoSize;
}
return retval;
}
void BlockManager::PruneOneBlockFile(const int fileNumber) {
AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile);
for (const auto &entry : m_block_index) {
CBlockIndex *pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus = pindex->nStatus.withData(false).withUndo(false);
pindex->nFile = 0;
pindex->nDataPos = 0;
pindex->nUndoPos = 0;
setDirtyBlockIndex.insert(pindex);
// Prune from m_blocks_unlinked -- any block we prune would have
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates.
auto range = m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it =
range.first;
range.first++;
if (_it->second == pindex) {
m_blocks_unlinked.erase(_it);
}
}
}
}
vinfoBlockFile[fileNumber].SetNull();
setDirtyFileInfo.insert(fileNumber);
}
void UnlinkPrunedFiles(const std::set<int> &setFilesToPrune) {
for (const int i : setFilesToPrune) {
FlatFilePos pos(i, 0);
fs::remove(BlockFileSeq().FileName(pos));
fs::remove(UndoFileSeq().FileName(pos));
LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, i);
}
}
void BlockManager::FindFilesToPruneManual(std::set<int> &setFilesToPrune,
int nManualPruneHeight,
int chain_tip_height) {
assert(fPruneMode && nManualPruneHeight > 0);
LOCK2(cs_main, cs_LastBlockFile);
if (chain_tip_height < 0) {
return;
}
// last block to prune is the lesser of (user-specified height,
// MIN_BLOCKS_TO_KEEP from the tip)
unsigned int nLastBlockWeCanPrune = std::min(
(unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
int count = 0;
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
if (vinfoBlockFile[fileNumber].nSize == 0 ||
vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
}
PruneOneBlockFile(fileNumber);
setFilesToPrune.insert(fileNumber);
count++;
}
LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n",
nLastBlockWeCanPrune, count);
}
/* This function is called from the RPC code for pruneblockchain */
void PruneBlockFilesManual(int nManualPruneHeight) {
BlockValidationState state;
const CChainParams &chainparams = Params();
if (!::ChainstateActive().FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__,
state.ToString());
}
}
void BlockManager::FindFilesToPrune(std::set<int> &setFilesToPrune,
uint64_t nPruneAfterHeight,
int chain_tip_height, bool is_ibd) {
LOCK2(cs_main, cs_LastBlockFile);
if (chain_tip_height < 0 || nPruneTarget == 0) {
return;
}
if (uint64_t(chain_tip_height) <= nPruneAfterHeight) {
return;
}
unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files,
// so we should leave a buffer under our target to account for another
// allocation before the next pruning.
uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
uint64_t nBytesToPrune;
int count = 0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
// On a prune event, the chainstate DB is flushed.
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune
// too soon.
if (is_ibd) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize +
vinfoBlockFile[fileNumber].nUndoSize;
if (vinfoBlockFile[fileNumber].nSize == 0) {
continue;
}
// are we below our target?
if (nCurrentUsage + nBuffer < nPruneTarget) {
break;
}
// don't prune files that could have a block within
// MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
}
PruneOneBlockFile(fileNumber);
// Queue up the files for removal
setFilesToPrune.insert(fileNumber);
nCurrentUsage -= nBytesToPrune;
count++;
}
}
LogPrint(BCLog::PRUNE,
"Prune: target=%dMiB actual=%dMiB diff=%dMiB "
"max_prune_height=%d removed %d blk/rev pairs\n",
nPruneTarget / 1024 / 1024, nCurrentUsage / 1024 / 1024,
((int64_t)nPruneTarget - (int64_t)nCurrentUsage) / 1024 / 1024,
nLastBlockWeCanPrune, count);
}
CBlockIndex *BlockManager::InsertBlockIndex(const BlockHash &hash) {
AssertLockHeld(cs_main);
if (hash.IsNull()) {
return nullptr;
}
// Return existing
BlockMap::iterator mi = m_block_index.find(hash);
if (mi != m_block_index.end()) {
return (*mi).second;
}
// Create new
CBlockIndex *pindexNew = new CBlockIndex();
mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
}
bool BlockManager::LoadBlockIndex(
const Consensus::Params ¶ms, CBlockTreeDB &blocktree,
std::set<CBlockIndex *, CBlockIndexWorkComparator>
&block_index_candidates) {
AssertLockHeld(cs_main);
if (!blocktree.LoadBlockIndexGuts(
params, [this](const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(
cs_main) { return this->InsertBlockIndex(hash); })) {
return false;
}
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex *>> vSortedByHeight;
vSortedByHeight.reserve(m_block_index.size());
for (const std::pair<const BlockHash, CBlockIndex *> &item :
m_block_index) {
CBlockIndex *pindex = item.second;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) {
if (ShutdownRequested()) {
return false;
}
CBlockIndex *pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) +
GetBlockProof(*pindex);
pindex->nTimeMax =
(pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime)
: pindex->nTime);
// We can link the chain of blocks for which we've received transactions
// at some point. Pruned nodes may have deleted the block.
if (pindex->nTx > 0) {
if (!pindex->UpdateChainStats() && pindex->pprev) {
m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
}
}
if (!pindex->nStatus.hasFailed() && pindex->pprev &&
pindex->pprev->nStatus.hasFailed()) {
pindex->nStatus = pindex->nStatus.withFailedParent();
setDirtyBlockIndex.insert(pindex);
}
if (pindex->IsValid(BlockValidity::TRANSACTIONS) &&
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) {
block_index_candidates.insert(pindex);
}
if (pindex->nStatus.isInvalid() &&
(!pindexBestInvalid ||
pindex->nChainWork > pindexBestInvalid->nChainWork)) {
pindexBestInvalid = pindex;
}
if (pindex->nStatus.isOnParkedChain() &&
(!pindexBestParked ||
pindex->nChainWork > pindexBestParked->nChainWork)) {
pindexBestParked = pindex;
}
if (pindex->pprev) {
pindex->BuildSkip();
}
if (pindex->IsValid(BlockValidity::TREE) &&
(pindexBestHeader == nullptr ||
CBlockIndexWorkComparator()(pindexBestHeader, pindex))) {
pindexBestHeader = pindex;
}
}
return true;
}
void BlockManager::Unload() {
m_failed_blocks.clear();
m_blocks_unlinked.clear();
for (const BlockMap::value_type &entry : m_block_index) {
delete entry.second;
}
m_block_index.clear();
}
static bool LoadBlockIndexDB(ChainstateManager &chainman,
const Consensus::Params ¶ms)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
if (!chainman.m_blockman.LoadBlockIndex(
params, *pblocktree,
::ChainstateActive().setBlockIndexCandidates)) {
return false;
}
// Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile);
vinfoBlockFile.resize(nLastBlockFile + 1);
LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
}
LogPrintf("%s: last block file info: %s\n", __func__,
vinfoBlockFile[nLastBlockFile].ToString());
for (int nFile = nLastBlockFile + 1; true; nFile++) {
CBlockFileInfo info;
if (pblocktree->ReadBlockFileInfo(nFile, info)) {
vinfoBlockFile.push_back(info);
} else {
break;
}
}
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
for (const std::pair<const BlockHash, CBlockIndex *> &item :
chainman.BlockIndex()) {
CBlockIndex *pindex = item.second;
if (pindex->nStatus.hasData()) {
setBlkDataFiles.insert(pindex->nFile);
}
}
for (const int i : setBlkDataFiles) {
FlatFilePos pos(i, 0);
if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION)
.IsNull()) {
return false;
}
}
// Check whether we have ever pruned block & undo files
pblocktree->ReadFlag("prunedblockfiles", fHavePruned);
if (fHavePruned) {
LogPrintf(
"LoadBlockIndexDB(): Block files have previously been pruned\n");
}
// Check whether we need to continue reindexing
if (pblocktree->IsReindexing()) {
fReindex = true;
}
return true;
}
void CChainState::LoadMempool(const Config &config, const ArgsManager &args) {
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
::LoadMempool(config, m_mempool);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
}
bool CChainState::LoadChainTip(const CChainParams &chainparams) {
AssertLockHeld(cs_main);
const CCoinsViewCache &coins_cache = CoinsTip();
// Never called when the coins view is empty
assert(!coins_cache.GetBestBlock().IsNull());
const CBlockIndex *tip = m_chain.Tip();
if (tip && tip->GetBlockHash() == coins_cache.GetBestBlock()) {
return true;
}
// Load pointer to end of best chain
CBlockIndex *pindex = LookupBlockIndex(coins_cache.GetBestBlock());
if (!pindex) {
return false;
}
m_chain.SetTip(pindex);
PruneBlockIndexCandidates();
tip = m_chain.Tip();
LogPrintf(
"Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
tip->GetBlockHash().ToString(), m_chain.Height(),
FormatISO8601DateTime(tip->GetBlockTime()),
GuessVerificationProgress(chainparams.TxData(), tip));
return true;
}
CVerifyDB::CVerifyDB() {
uiInterface.ShowProgress(_("Verifying blocks...").translated, 0, false);
}
CVerifyDB::~CVerifyDB() {
uiInterface.ShowProgress("", 100, false);
}
bool CVerifyDB::VerifyDB(const Config &config, CCoinsView *coinsview,
int nCheckLevel, int nCheckDepth) {
LOCK(cs_main);
const CChainParams ¶ms = config.GetChainParams();
const Consensus::Params &consensusParams = params.GetConsensus();
if (::ChainActive().Tip() == nullptr ||
::ChainActive().Tip()->pprev == nullptr) {
return true;
}
// Verify blocks in the best chain
if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height()) {
nCheckDepth = ::ChainActive().Height();
}
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth,
nCheckLevel);
CCoinsViewCache coins(coinsview);
CBlockIndex *pindex;
CBlockIndex *pindexFailure = nullptr;
int nGoodTransactions = 0;
BlockValidationState state;
int reportDone = 0;
LogPrintfToBeContinued("[0%%]...");
for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev;
pindex = pindex->pprev) {
const int percentageDone =
std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() -
pindex->nHeight)) /
(double)nCheckDepth *
(nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone / 10) {
// report every 10% step
LogPrintfToBeContinued("[%d%%]...", percentageDone);
reportDone = percentageDone / 10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated,
percentageDone, false);
if (pindex->nHeight <= ::ChainActive().Height() - nCheckDepth) {
break;
}
if (fPruneMode && !pindex->nStatus.hasData()) {
// If pruning, only go back as far as we have data.
LogPrintf("VerifyDB(): block verification stopping at height %d "
"(pruning, no data)\n",
pindex->nHeight);
break;
}
CBlock block;
// check level 0: read from disk
if (!ReadBlockFromDisk(block, pindex, consensusParams)) {
return error(
"VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s",
pindex->nHeight, pindex->GetBlockHash().ToString());
}
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state, consensusParams,
BlockValidationOptions(config))) {
return error("%s: *** found bad block at %d, hash=%s (%s)\n",
__func__, pindex->nHeight,
pindex->GetBlockHash().ToString(), state.ToString());
}
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
if (!pindex->GetUndoPos().IsNull()) {
if (!UndoReadFromDisk(undo, pindex)) {
return error(
"VerifyDB(): *** found bad undo data at %d, hash=%s\n",
pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
}
// check level 3: check for inconsistencies during memory-only
// disconnect of tip blocks
if (nCheckLevel >= 3 &&
(coins.DynamicMemoryUsage() +
::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <=
::ChainstateActive().m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res =
::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DisconnectResult::FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in "
"block data at %d, hash=%s",
pindex->nHeight,
pindex->GetBlockHash().ToString());
}
if (res == DisconnectResult::UNCLEAN) {
nGoodTransactions = 0;
pindexFailure = pindex;
} else {
nGoodTransactions += block.vtx.size();
}
}
if (ShutdownRequested()) {
return true;
}
}
if (pindexFailure) {
return error("VerifyDB(): *** coin database inconsistencies found "
"(last %i blocks, %i good transactions before that)\n",
::ChainActive().Height() - pindexFailure->nHeight + 1,
nGoodTransactions);
}
// store block count as we move pindex at check level >= 4
int block_count = ::ChainActive().Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
while (pindex != ::ChainActive().Tip()) {
const int percentageDone = std::max(
1, std::min(99, 100 - int(double(::ChainActive().Height() -
pindex->nHeight) /
double(nCheckDepth) * 50)));
if (reportDone < percentageDone / 10) {
// report every 10% step
LogPrintfToBeContinued("[%d%%]...", percentageDone);
reportDone = percentageDone / 10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated,
percentageDone, false);
pindex = ::ChainActive().Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, consensusParams)) {
return error(
"VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s",
pindex->nHeight, pindex->GetBlockHash().ToString());
}
if (!::ChainstateActive().ConnectBlock(
block, state, pindex, coins, params,
BlockValidationOptions(config))) {
return error("VerifyDB(): *** found unconnectable block at %d, "
"hash=%s (%s)",
pindex->nHeight, pindex->GetBlockHash().ToString(),
state.ToString());
}
if (ShutdownRequested()) {
return true;
}
}
}
LogPrintf("[DONE].\n");
LogPrintf("No coin database inconsistencies in last %i blocks (%i "
"transactions)\n",
block_count, nGoodTransactions);
return true;
}
/**
* Apply the effects of a block on the utxo cache, ignoring that it may already
* have been applied.
*/
bool CChainState::RollforwardBlock(const CBlockIndex *pindex,
CCoinsViewCache &view,
const Consensus::Params ¶ms) {
// TODO: merge with ConnectBlock
CBlock block;
if (!ReadBlockFromDisk(block, pindex, params)) {
return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s",
pindex->nHeight, pindex->GetBlockHash().ToString());
}
for (const CTransactionRef &tx : block.vtx) {
// Pass check = true as every addition may be an overwrite.
AddCoins(view, *tx, pindex->nHeight, true);
}
for (const CTransactionRef &tx : block.vtx) {
if (tx->IsCoinBase()) {
continue;
}
for (const CTxIn &txin : tx->vin) {
view.SpendCoin(txin.prevout);
}
}
return true;
}
bool CChainState::ReplayBlocks(const Consensus::Params ¶ms) {
LOCK(cs_main);
CCoinsView &db = this->CoinsDB();
CCoinsViewCache cache(&db);
std::vector<BlockHash> hashHeads = db.GetHeadBlocks();
if (hashHeads.empty()) {
// We're already in a consistent state.
return true;
}
if (hashHeads.size() != 2) {
return error("ReplayBlocks(): unknown inconsistent state");
}
uiInterface.ShowProgress(_("Replaying blocks...").translated, 0, false);
LogPrintf("Replaying blocks\n");
// Old tip during the interrupted flush.
const CBlockIndex *pindexOld = nullptr;
// New tip during the interrupted flush.
const CBlockIndex *pindexNew;
// Latest block common to both the old and the new tip.
const CBlockIndex *pindexFork = nullptr;
if (m_blockman.m_block_index.count(hashHeads[0]) == 0) {
return error(
"ReplayBlocks(): reorganization to unknown block requested");
}
pindexNew = m_blockman.m_block_index[hashHeads[0]];
if (!hashHeads[1].IsNull()) {
// The old tip is allowed to be 0, indicating it's the first flush.
if (m_blockman.m_block_index.count(hashHeads[1]) == 0) {
return error(
"ReplayBlocks(): reorganization from unknown block requested");
}
pindexOld = m_blockman.m_block_index[hashHeads[1]];
pindexFork = LastCommonAncestor(pindexOld, pindexNew);
assert(pindexFork != nullptr);
}
// Rollback along the old branch.
while (pindexOld != pindexFork) {
if (pindexOld->nHeight > 0) {
// Never disconnect the genesis block.
CBlock block;
if (!ReadBlockFromDisk(block, pindexOld, params)) {
return error("RollbackBlock(): ReadBlockFromDisk() failed at "
"%d, hash=%s",
pindexOld->nHeight,
pindexOld->GetBlockHash().ToString());
}
LogPrintf("Rolling back %s (%i)\n",
pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);
DisconnectResult res = DisconnectBlock(block, pindexOld, cache);
if (res == DisconnectResult::FAILED) {
return error(
"RollbackBlock(): DisconnectBlock failed at %d, hash=%s",
pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
}
// If DisconnectResult::UNCLEAN is returned, it means a non-existing
// UTXO was deleted, or an existing UTXO was overwritten. It
// corresponds to cases where the block-to-be-disconnect never had
// all its operations applied to the UTXO set. However, as both
// writing a UTXO and deleting a UTXO are idempotent operations, the
// result is still a version of the UTXO set with the effects of
// that block undone.
}
pindexOld = pindexOld->pprev;
}
// Roll forward from the forking point to the new tip.
int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight;
++nHeight) {
const CBlockIndex *pindex = pindexNew->GetAncestor(nHeight);
LogPrintf("Rolling forward %s (%i)\n",
pindex->GetBlockHash().ToString(), nHeight);
uiInterface.ShowProgress(_("Replaying blocks...").translated,
(int)((nHeight - nForkHeight) * 100.0 /
(pindexNew->nHeight - nForkHeight)),
false);
if (!RollforwardBlock(pindex, cache, params)) {
return false;
}
}
cache.SetBestBlock(pindexNew->GetBlockHash());
cache.Flush();
uiInterface.ShowProgress("", 100, false);
return true;
}
// May NOT be used after any connections are up as much of the peer-processing
// logic assumes a consistent block index state
void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1;
setBlockIndexCandidates.clear();
// Do not point to CBlockIndex that will be free'd
m_finalizedBlockIndex = nullptr;
}
// May NOT be used after any connections are up as much
// of the peer-processing logic assumes a consistent
// block index state
void UnloadBlockIndex(CTxMemPool *mempool, ChainstateManager &chainman) {
LOCK(cs_main);
chainman.Unload();
pindexBestInvalid = nullptr;
pindexBestParked = nullptr;
pindexBestHeader = nullptr;
pindexBestForkTip = nullptr;
pindexBestForkBase = nullptr;
ResetASERTAnchorBlockCache();
if (mempool) {
mempool->clear();
}
vinfoBlockFile.clear();
nLastBlockFile = 0;
setDirtyBlockIndex.clear();
setDirtyFileInfo.clear();
fHavePruned = false;
}
bool ChainstateManager::LoadBlockIndex(const Consensus::Params ¶ms) {
AssertLockHeld(cs_main);
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
bool ret = LoadBlockIndexDB(*this, params);
if (!ret) {
return false;
}
needs_init = m_blockman.m_block_index.empty();
}
if (needs_init) {
// Everything here is for *new* reindex/DBs. Thus, though
// LoadBlockIndexDB may have set fReindex if we shut down
// mid-reindex previously, we don't check fReindex and
// instead only check it prior to LoadBlockIndexDB to set
// needs_init.
LogPrintf("Initializing databases...\n");
}
return true;
}
bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) {
LOCK(cs_main);
// Check whether we're already initialized by checking for genesis in
// m_blockman.m_block_index. Note that we can't use m_chain here, since it
// is set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash())) {
return true;
}
try {
const CBlock &block = chainparams.GenesisBlock();
FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed", __func__);
}
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error &e) {
return error("%s: failed to write genesis block: %s", __func__,
e.what());
}
return true;
}
bool LoadGenesisBlock(const CChainParams &chainparams) {
return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
void LoadExternalBlockFile(const Config &config, FILE *fileIn,
FlatFilePos *dbp) {
// Map of disk positions for blocks with unknown parent (only used for
// reindex)
static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
const CChainParams &chainparams = config.GetChainParams();
int nLoaded = 0;
try {
// This takes over fileIn and calls fclose() on it in the CBufferedFile
// destructor. Make sure we have at least 2*MAX_TX_SIZE space in there
// so any transaction can fit in the buffer.
CBufferedFile blkdat(fileIn, 2 * MAX_TX_SIZE, MAX_TX_SIZE + 8, SER_DISK,
CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) {
if (ShutdownRequested()) {
return;
}
blkdat.SetPos(nRewind);
// Start one byte further next time, in case of failure.
nRewind++;
// Remove former limit.
blkdat.SetLimit();
unsigned int nSize = 0;
try {
// Locate a header.
uint8_t buf[CMessageHeader::MESSAGE_START_SIZE];
blkdat.FindByte(chainparams.DiskMagic()[0]);
nRewind = blkdat.GetPos() + 1;
blkdat >> buf;
if (memcmp(buf, chainparams.DiskMagic().data(),
CMessageHeader::MESSAGE_START_SIZE)) {
continue;
}
// Read size.
blkdat >> nSize;
if (nSize < 80) {
continue;
}
} catch (const std::exception &) {
// No valid block header found; don't complain.
break;
}
try {
// read block
uint64_t nBlockPos = blkdat.GetPos();
if (dbp) {
dbp->nPos = nBlockPos;
}
blkdat.SetLimit(nBlockPos + nSize);
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock &block = *pblock;
blkdat >> block;
nRewind = blkdat.GetPos();
const BlockHash hash = block.GetHash();
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
if (hash != chainparams.GetConsensus().hashGenesisBlock &&
!LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(
BCLog::REINDEX,
"%s: Out of order block %s, parent %s not known\n",
__func__, hash.ToString(),
block.hashPrevBlock.ToString());
if (dbp) {
mapBlocksUnknownParent.insert(
std::make_pair(block.hashPrevBlock, *dbp));
}
continue;
}
// process in case the block isn't known yet
CBlockIndex *pindex = LookupBlockIndex(hash);
if (!pindex || !pindex->nStatus.hasData()) {
BlockValidationState state;
if (::ChainstateActive().AcceptBlock(
config, pblock, state, true, dbp, nullptr)) {
nLoaded++;
}
if (state.IsError()) {
break;
}
} else if (hash != chainparams.GetConsensus()
.hashGenesisBlock &&
pindex->nHeight % 1000 == 0) {
LogPrint(
BCLog::REINDEX,
"Block Import: already had block %s at height %d\n",
hash.ToString(), pindex->nHeight);
}
}
// Activate the genesis block so normal node progress can
// continue
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
if (!ActivateBestChain(config, state, nullptr)) {
break;
}
}
NotifyHeaderTip();
// Recursively process earlier encountered successors of this
// block
std::deque<uint256> queue;
queue.push_back(hash);
while (!queue.empty()) {
uint256 head = queue.front();
queue.pop_front();
std::pair<std::multimap<uint256, FlatFilePos>::iterator,
std::multimap<uint256, FlatFilePos>::iterator>
range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
std::multimap<uint256, FlatFilePos>::iterator it =
range.first;
std::shared_ptr<CBlock> pblockrecursive =
std::make_shared<CBlock>();
if (ReadBlockFromDisk(*pblockrecursive, it->second,
chainparams.GetConsensus())) {
LogPrint(
BCLog::REINDEX,
"%s: Processing out of order child %s of %s\n",
__func__, pblockrecursive->GetHash().ToString(),
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
if (::ChainstateActive().AcceptBlock(
config, pblockrecursive, dummy, true,
&it->second, nullptr)) {
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
}
}
range.first++;
mapBlocksUnknownParent.erase(it);
NotifyHeaderTip();
}
}
} catch (const std::exception &e) {
LogPrintf("%s: Deserialize or I/O error - %s\n", __func__,
e.what());
}
}
} catch (const std::runtime_error &e) {
AbortNode(std::string("System error: ") + e.what());
}
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded,
GetTimeMillis() - nStart);
}
void CChainState::CheckBlockIndex(const Consensus::Params &consensusParams) {
if (!fCheckBlockIndex) {
return;
}
LOCK(cs_main);
// During a reindex, we read the genesis block and call CheckBlockIndex
// before ActivateBestChain, so we have the genesis block in
// m_blockman.m_block_index but no active chain. (A few of the tests when
// iterating the block tree require that m_chain has been initialized.)
if (m_chain.Height() < 0) {
assert(m_blockman.m_block_index.size() <= 1);
return;
}
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex *, CBlockIndex *> forward;
for (const auto &entry : m_blockman.m_block_index) {
forward.emplace(entry.second->pprev, entry.second);
}
assert(forward.size() == m_blockman.m_block_index.size());
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator,
std::multimap<CBlockIndex *, CBlockIndex *>::iterator>
rangeGenesis = forward.equal_range(nullptr);
CBlockIndex *pindex = rangeGenesis.first->second;
rangeGenesis.first++;
// There is only one index entry with parent nullptr.
assert(rangeGenesis.first == rangeGenesis.second);
// Iterate over the entire block tree, using depth-first search.
// Along the way, remember whether there are blocks on the path from genesis
// block being explored which are the first to have certain properties.
size_t nNodes = 0;
int nHeight = 0;
// Oldest ancestor of pindex which is invalid.
CBlockIndex *pindexFirstInvalid = nullptr;
// Oldest ancestor of pindex which is parked.
CBlockIndex *pindexFirstParked = nullptr;
// Oldest ancestor of pindex which does not have data available.
CBlockIndex *pindexFirstMissing = nullptr;
// Oldest ancestor of pindex for which nTx == 0.
CBlockIndex *pindexFirstNeverProcessed = nullptr;
// Oldest ancestor of pindex which does not have BLOCK_VALID_TREE
// (regardless of being valid or not).
CBlockIndex *pindexFirstNotTreeValid = nullptr;
// Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS
// (regardless of being valid or not).
CBlockIndex *pindexFirstNotTransactionsValid = nullptr;
// Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN
// (regardless of being valid or not).
CBlockIndex *pindexFirstNotChainValid = nullptr;
// Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS
// (regardless of being valid or not).
CBlockIndex *pindexFirstNotScriptsValid = nullptr;
while (pindex != nullptr) {
nNodes++;
if (pindexFirstInvalid == nullptr && pindex->nStatus.hasFailed()) {
pindexFirstInvalid = pindex;
}
if (pindexFirstParked == nullptr && pindex->nStatus.isParked()) {
pindexFirstParked = pindex;
}
if (pindexFirstMissing == nullptr && !pindex->nStatus.hasData()) {
pindexFirstMissing = pindex;
}
if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) {
pindexFirstNeverProcessed = pindex;
}
if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr &&
pindex->nStatus.getValidity() < BlockValidity::TREE) {
pindexFirstNotTreeValid = pindex;
}
if (pindex->pprev != nullptr &&
pindexFirstNotTransactionsValid == nullptr &&
pindex->nStatus.getValidity() < BlockValidity::TRANSACTIONS) {
pindexFirstNotTransactionsValid = pindex;
}
if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr &&
pindex->nStatus.getValidity() < BlockValidity::CHAIN) {
pindexFirstNotChainValid = pindex;
}
if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr &&
pindex->nStatus.getValidity() < BlockValidity::SCRIPTS) {
pindexFirstNotScriptsValid = pindex;
}
// Begin: actual consistency checks.
if (pindex->pprev == nullptr) {
// Genesis block checks.
// Genesis block's hash must match.
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock);
// The current active chain's genesis block must be this block.
assert(pindex == m_chain.Genesis());
}
if (!pindex->HaveTxsDownloaded()) {
// nSequenceId can't be set positive for blocks that aren't linked
// (negative is used for preciousblock)
assert(pindex->nSequenceId <= 0);
}
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or
// not pruning has occurred). HAVE_DATA is only equivalent to nTx > 0
// (or VALID_TRANSACTIONS) if no pruning has occurred.
if (!fHavePruned) {
// If we've never pruned, then HAVE_DATA should be equivalent to nTx
// > 0
assert(pindex->nStatus.hasData() == (pindex->nTx > 0));
assert(pindexFirstMissing == pindexFirstNeverProcessed);
} else if (pindex->nStatus.hasData()) {
// If we have pruned, then we can only say that HAVE_DATA implies
// nTx > 0
assert(pindex->nTx > 0);
}
if (pindex->nStatus.hasUndo()) {
assert(pindex->nStatus.hasData());
}
// This is pruning-independent.
assert((pindex->nStatus.getValidity() >= BlockValidity::TRANSACTIONS) ==
(pindex->nTx > 0));
// All parents having had data (at some point) is equivalent to all
// parents being VALID_TRANSACTIONS, which is equivalent to
// HaveTxsDownloaded(). All parents having had data (at some point) is
// equivalent to all parents being VALID_TRANSACTIONS, which is
// equivalent to HaveTxsDownloaded().
assert((pindexFirstNeverProcessed == nullptr) ==
(pindex->HaveTxsDownloaded()));
assert((pindexFirstNotTransactionsValid == nullptr) ==
(pindex->HaveTxsDownloaded()));
// nHeight must be consistent.
assert(pindex->nHeight == nHeight);
// For every block except the genesis block, the chainwork must be
// larger than the parent's.
assert(pindex->pprev == nullptr ||
pindex->nChainWork >= pindex->pprev->nChainWork);
// The pskip pointer must point back for all but the first 2 blocks.
assert(nHeight < 2 ||
(pindex->pskip && (pindex->pskip->nHeight < nHeight)));
// All m_blockman.m_block_index entries must at least be TREE valid
assert(pindexFirstNotTreeValid == nullptr);
if (pindex->nStatus.getValidity() >= BlockValidity::TREE) {
// TREE valid implies all parents are TREE valid
assert(pindexFirstNotTreeValid == nullptr);
}
if (pindex->nStatus.getValidity() >= BlockValidity::CHAIN) {
// CHAIN valid implies all parents are CHAIN valid
assert(pindexFirstNotChainValid == nullptr);
}
if (pindex->nStatus.getValidity() >= BlockValidity::SCRIPTS) {
// SCRIPTS valid implies all parents are SCRIPTS valid
assert(pindexFirstNotScriptsValid == nullptr);
}
if (pindexFirstInvalid == nullptr) {
// Checks for not-invalid blocks.
// The failed mask cannot be set for blocks without invalid parents.
assert(!pindex->nStatus.isInvalid());
}
if (pindexFirstParked == nullptr) {
// Checks for not-parked blocks.
// The parked mask cannot be set for blocks without parked parents.
// (i.e., hasParkedParent only if an ancestor is properly parked).
assert(!pindex->nStatus.isOnParkedChain());
}
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) &&
pindexFirstNeverProcessed == nullptr) {
if (pindexFirstInvalid == nullptr) {
// If this block sorts at least as good as the current tip and
// is valid and we have all data for its parents, it must be in
// setBlockIndexCandidates or be parked.
if (pindexFirstMissing == nullptr) {
assert(pindex->nStatus.isOnParkedChain() ||
setBlockIndexCandidates.count(pindex));
}
// m_chain.Tip() must also be there even if some data has
// been pruned.
if (pindex == m_chain.Tip()) {
assert(setBlockIndexCandidates.count(pindex));
}
// If some parent is missing, then it could be that this block
// was in setBlockIndexCandidates but had to be removed because
// of the missing data. In this case it must be in
// m_blocks_unlinked -- see test below.
}
} else {
// If this block sorts worse than the current tip or some ancestor's
// block has never been seen, it cannot be in
// setBlockIndexCandidates.
assert(setBlockIndexCandidates.count(pindex) == 0);
}
// Check whether this block is in m_blocks_unlinked.
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator,
std::multimap<CBlockIndex *, CBlockIndex *>::iterator>
rangeUnlinked =
m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
bool foundInUnlinked = false;
while (rangeUnlinked.first != rangeUnlinked.second) {
assert(rangeUnlinked.first->first == pindex->pprev);
if (rangeUnlinked.first->second == pindex) {
foundInUnlinked = true;
break;
}
rangeUnlinked.first++;
}
if (pindex->pprev && pindex->nStatus.hasData() &&
pindexFirstNeverProcessed != nullptr &&
pindexFirstInvalid == nullptr) {
// If this block has block data available, some parent was never
// received, and has no invalid parents, it must be in
// m_blocks_unlinked.
assert(foundInUnlinked);
}
if (!pindex->nStatus.hasData()) {
// Can't be in m_blocks_unlinked if we don't HAVE_DATA
assert(!foundInUnlinked);
}
if (pindexFirstMissing == nullptr) {
// We aren't missing data for any parent -- cannot be in
// m_blocks_unlinked.
assert(!foundInUnlinked);
}
if (pindex->pprev && pindex->nStatus.hasData() &&
pindexFirstNeverProcessed == nullptr &&
pindexFirstMissing != nullptr) {
// We HAVE_DATA for this block, have received data for all parents
// at some point, but we're currently missing data for some parent.
// We must have pruned.
assert(fHavePruned);
// This block may have entered m_blocks_unlinked if:
// - it has a descendant that at some point had more work than the
// tip, and
// - we tried switching to that descendant but were missing
// data for some intermediate block between m_chain and the
// tip.
// So if this block is itself better than m_chain.Tip() and it
// wasn't in
// setBlockIndexCandidates, then it must be in m_blocks_unlinked.
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) &&
setBlockIndexCandidates.count(pindex) == 0) {
if (pindexFirstInvalid == nullptr) {
assert(foundInUnlinked);
}
}
}
// Perhaps too slow
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash());
// End: actual consistency checks.
// Try descending into the first subnode.
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator,
std::multimap<CBlockIndex *, CBlockIndex *>::iterator>
range = forward.equal_range(pindex);
if (range.first != range.second) {
// A subnode was found.
pindex = range.first->second;
nHeight++;
continue;
}
// This is a leaf node. Move upwards until we reach a node of which we
// have not yet visited the last child.
while (pindex) {
// We are going to either move to a parent or a sibling of pindex.
// If pindex was the first with a certain property, unset the
// corresponding variable.
if (pindex == pindexFirstInvalid) {
pindexFirstInvalid = nullptr;
}
if (pindex == pindexFirstParked) {
pindexFirstParked = nullptr;
}
if (pindex == pindexFirstMissing) {
pindexFirstMissing = nullptr;
}
if (pindex == pindexFirstNeverProcessed) {
pindexFirstNeverProcessed = nullptr;
}
if (pindex == pindexFirstNotTreeValid) {
pindexFirstNotTreeValid = nullptr;
}
if (pindex == pindexFirstNotTransactionsValid) {
pindexFirstNotTransactionsValid = nullptr;
}
if (pindex == pindexFirstNotChainValid) {
pindexFirstNotChainValid = nullptr;
}
if (pindex == pindexFirstNotScriptsValid) {
pindexFirstNotScriptsValid = nullptr;
}
// Find our parent.
CBlockIndex *pindexPar = pindex->pprev;
// Find which child we just visited.
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator,
std::multimap<CBlockIndex *, CBlockIndex *>::iterator>
rangePar = forward.equal_range(pindexPar);
while (rangePar.first->second != pindex) {
// Our parent must have at least the node we're coming from as
// child.
assert(rangePar.first != rangePar.second);
rangePar.first++;
}
// Proceed to the next one.
rangePar.first++;
if (rangePar.first != rangePar.second) {
// Move to the sibling.
pindex = rangePar.first->second;
break;
} else {
// Move up further.
pindex = pindexPar;
nHeight--;
continue;
}
}
}
// Check that we actually traversed the entire map.
assert(nNodes == forward.size());
}
std::string CChainState::ToString() {
CBlockIndex *tip = m_chain.Tip();
return strprintf("Chainstate [%s] @ height %d (%s)",
m_from_snapshot_blockhash.IsNull() ? "ibd" : "snapshot",
tip ? tip->nHeight : -1,
tip ? tip->GetBlockHash().ToString() : "null");
}
bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) {
if (coinstip_size == m_coinstip_cache_size_bytes &&
coinsdb_size == m_coinsdb_cache_size_bytes) {
// Cache sizes are unchanged, no need to continue.
return true;
}
size_t old_coinstip_size = m_coinstip_cache_size_bytes;
m_coinstip_cache_size_bytes = coinstip_size;
m_coinsdb_cache_size_bytes = coinsdb_size;
CoinsDB().ResizeCache(coinsdb_size);
LogPrintf("[%s] resized coinsdb cache to %.1f MiB\n", this->ToString(),
coinsdb_size * (1.0 / 1024 / 1024));
LogPrintf("[%s] resized coinstip cache to %.1f MiB\n", this->ToString(),
coinstip_size * (1.0 / 1024 / 1024));
BlockValidationState state;
const CChainParams &chainparams = Params();
bool ret;
if (coinstip_size > old_coinstip_size) {
// Likely no need to flush if cache sizes have grown.
ret = FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED);
} else {
// Otherwise, flush state to disk and deallocate the in-memory coins
// map.
ret = FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
CoinsTip().ReallocateCache();
}
return ret;
}
std::string CBlockFileInfo::ToString() const {
return strprintf(
"CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)",
nBlocks, nSize, nHeightFirst, nHeightLast,
FormatISO8601DateTime(nTimeFirst), FormatISO8601DateTime(nTimeLast));
}
CBlockFileInfo *GetBlockFileInfo(size_t n) {
LOCK(cs_LastBlockFile);
return &vinfoBlockFile.at(n);
}
static ThresholdState VersionBitsStateImpl(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos,
const CBlockIndex *pindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return VersionBitsState(pindex, params, pos, versionbitscache);
}
ThresholdState VersionBitsTipState(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos) {
LOCK(cs_main);
return VersionBitsStateImpl(params, pos, ::ChainActive().Tip());
}
ThresholdState VersionBitsBlockState(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos,
const CBlockIndex *pindex) {
LOCK(cs_main);
return VersionBitsStateImpl(params, pos, pindex);
}
BIP9Stats VersionBitsTipStatistics(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos) {
LOCK(cs_main);
return VersionBitsStatistics(::ChainActive().Tip(), params, pos);
}
int VersionBitsTipStateSinceHeight(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos) {
LOCK(cs_main);
return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos,
versionbitscache);
}
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(const Config &config, CTxMemPool &pool) {
int64_t nExpiryTimeout =
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE *filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf(
"Failed to open mempool file from disk. Continuing anyway.\n");
return false;
}
int64_t count = 0;
int64_t expired = 0;
int64_t failed = 0;
int64_t already_there = 0;
int64_t unbroadcast = 0;
int64_t nNow = GetTime();
try {
uint64_t version;
file >> version;
if (version != MEMPOOL_DUMP_VERSION) {
return false;
}
uint64_t num;
file >> num;
while (num--) {
CTransactionRef tx;
int64_t nTime;
int64_t nFeeDelta;
file >> tx;
file >> nTime;
file >> nFeeDelta;
Amount amountdelta = nFeeDelta * SATOSHI;
if (amountdelta != Amount::zero()) {
pool.PrioritiseTransaction(tx->GetId(), amountdelta);
}
TxValidationState state;
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
AcceptToMemoryPoolWithTime(config, pool, state, tx, nTime,
false /* bypass_limits */,
false /* test_accept */);
if (state.IsValid()) {
++count;
} else {
// mempool may contain the transaction already, e.g. from
// wallet(s) having loaded it while we were processing
// mempool transactions; consider these as valid, instead of
// failed, but mark them as 'already there'
if (pool.exists(tx->GetId())) {
++already_there;
} else {
++failed;
}
}
} else {
++expired;
}
if (ShutdownRequested()) {
return false;
}
}
std::map<TxId, Amount> mapDeltas;
file >> mapDeltas;
for (const auto &i : mapDeltas) {
pool.PrioritiseTransaction(i.first, i.second);
}
// TODO: remove this try...catch after May 15th 2021,
// when no one is running v0.22.11 or lower anymore.
// This will be done by backporting PR20854.
try {
std::set<TxId> unbroadcast_txids;
file >> unbroadcast_txids;
unbroadcast = unbroadcast_txids.size();
for (const auto &txid : unbroadcast_txids) {
pool.AddUnbroadcastTx(txid);
}
} catch (const std::exception &) {
// mempool.dat files created prior to v0.22.12 will not have an
// unbroadcast set. No need to log a failure if parsing fails here.
}
} catch (const std::exception &e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing "
"anyway.\n",
e.what());
return false;
}
LogPrintf("Imported mempool transactions from disk: %i succeeded, %i "
"failed, %i expired, %i already there, %i waiting for initial "
"broadcast\n",
count, failed, expired, already_there, unbroadcast);
return true;
}
bool DumpMempool(const CTxMemPool &pool) {
int64_t start = GetTimeMicros();
std::map<uint256, Amount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
std::set<TxId> unbroadcast_txids;
static Mutex dump_mutex;
LOCK(dump_mutex);
{
LOCK(pool.cs);
for (const auto &i : pool.mapDeltas) {
mapDeltas[i.first] = i.second;
}
vinfo = pool.infoAll();
unbroadcast_txids = pool.GetUnbroadcastTxs();
}
int64_t mid = GetTimeMicros();
try {
FILE *filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
if (!filestr) {
return false;
}
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
uint64_t version = MEMPOOL_DUMP_VERSION;
file << version;
file << uint64_t(vinfo.size());
for (const auto &i : vinfo) {
file << *(i.tx);
file << int64_t(count_seconds(i.m_time));
file << i.nFeeDelta;
mapDeltas.erase(i.tx->GetId());
}
file << mapDeltas;
LogPrintf("Writing %d unbroadcast transactions to disk.\n",
unbroadcast_txids.size());
file << unbroadcast_txids;
if (!FileCommit(file.Get())) {
throw std::runtime_error("FileCommit failed");
}
file.fclose();
if (!RenameOver(GetDataDir() / "mempool.dat.new",
GetDataDir() / "mempool.dat")) {
throw std::runtime_error("Rename failed");
}
int64_t last = GetTimeMicros();
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n",
(mid - start) * MICRO, (last - mid) * MICRO);
} catch (const std::exception &e) {
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
return false;
}
return true;
}
bool IsBlockPruned(const CBlockIndex *pblockindex) {
return (fHavePruned && !pblockindex->nStatus.hasData() &&
pblockindex->nTx > 0);
}
//! Guess how far we are in the verification process at the given block index
//! require cs_main if pindex has not been validated yet (because the chain's
//! transaction count might be unset) This conditional lock requirement might be
//! confusing, see: https://github.com/bitcoin/bitcoin/issues/15994
double GuessVerificationProgress(const ChainTxData &data,
const CBlockIndex *pindex) {
if (pindex == nullptr) {
return 0.0;
}
int64_t nNow = time(nullptr);
double fTxTotal;
if (pindex->GetChainTxCount() <= data.nTxCount) {
fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate;
} else {
fTxTotal = pindex->GetChainTxCount() +
(nNow - pindex->GetBlockTime()) * data.dTxRate;
}
return std::min<double>(pindex->GetChainTxCount() / fTxTotal, 1.0);
}
std::optional<BlockHash> ChainstateManager::SnapshotBlockhash() const {
if (m_active_chainstate != nullptr) {
// If a snapshot chainstate exists, it will always be our active.
return m_active_chainstate->m_from_snapshot_blockhash;
}
return {};
}
std::vector<CChainState *> ChainstateManager::GetAll() {
std::vector<CChainState *> out;
if (!IsSnapshotValidated() && m_ibd_chainstate) {
out.push_back(m_ibd_chainstate.get());
}
if (m_snapshot_chainstate) {
out.push_back(m_snapshot_chainstate.get());
}
return out;
}
CChainState &
ChainstateManager::InitializeChainstate(CTxMemPool &mempool,
const BlockHash &snapshot_blockhash) {
bool is_snapshot = !snapshot_blockhash.IsNull();
std::unique_ptr<CChainState> &to_modify =
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
m_active_chainstate = to_modify.get();
} else {
throw std::logic_error("unexpected chainstate activation");
}
return *to_modify;
}
CChainState &ChainstateManager::ActiveChainstate() const {
assert(m_active_chainstate);
return *m_active_chainstate;
}
bool ChainstateManager::IsSnapshotActive() const {
return m_snapshot_chainstate &&
m_active_chainstate == m_snapshot_chainstate.get();
}
CChainState &ChainstateManager::ValidatedChainstate() const {
if (m_snapshot_chainstate && IsSnapshotValidated()) {
return *m_snapshot_chainstate.get();
}
assert(m_ibd_chainstate);
return *m_ibd_chainstate.get();
}
bool ChainstateManager::IsBackgroundIBD(CChainState *chainstate) const {
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
}
void ChainstateManager::Unload() {
for (CChainState *chainstate : this->GetAll()) {
chainstate->m_chain.SetTip(nullptr);
chainstate->UnloadBlockIndex();
}
m_blockman.Unload();
}
void ChainstateManager::Reset() {
m_ibd_chainstate.reset();
m_snapshot_chainstate.reset();
m_active_chainstate = nullptr;
m_snapshot_validated = false;
}
void ChainstateManager::MaybeRebalanceCaches() {
if (m_ibd_chainstate && !m_snapshot_chainstate) {
LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n");
// Allocate everything to the IBD chainstate.
m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache,
m_total_coinsdb_cache);
} else if (m_snapshot_chainstate && !m_ibd_chainstate) {
LogPrintf(
"[snapshot] allocating all cache to the snapshot chainstate\n");
// Allocate everything to the snapshot chainstate.
m_snapshot_chainstate->ResizeCoinsCaches(m_total_coinstip_cache,
m_total_coinsdb_cache);
} else if (m_ibd_chainstate && m_snapshot_chainstate) {
// If both chainstates exist, determine who needs more cache based on
// IBD status.
//
// Note: shrink caches first so that we don't inadvertently overwhelm
// available memory.
if (m_snapshot_chainstate->IsInitialBlockDownload()) {
m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache * 0.05,
m_total_coinsdb_cache * 0.05);
m_snapshot_chainstate->ResizeCoinsCaches(
m_total_coinstip_cache * 0.95, m_total_coinsdb_cache * 0.95);
} else {
m_snapshot_chainstate->ResizeCoinsCaches(
m_total_coinstip_cache * 0.05, m_total_coinsdb_cache * 0.05);
m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache * 0.95,
m_total_coinsdb_cache * 0.95);
}
}
}
diff --git a/src/validation.h b/src/validation.h
index 04d99488c..6b8402654 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -1,1319 +1,1324 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2017-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_VALIDATION_H
#define BITCOIN_VALIDATION_H
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <amount.h>
#include <blockfileinfo.h>
#include <blockindexworkcomparator.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <disconnectresult.h>
#include <flatfile.h>
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageMagic
#include <script/script_error.h>
#include <script/script_metrics.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h> // For CTxMemPool::cs
#include <versionbits.h>
#include <atomic>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
class BlockValidationState;
class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
class CChainParams;
class CChain;
class CChainState;
class CConnman;
class CInv;
class ChainstateManager;
class Config;
class CScriptCheck;
class CTxMemPool;
class CTxUndo;
class DisconnectedBlockTransactions;
class TxValidationState;
struct ChainTxData;
struct FlatFilePos;
struct PrecomputedTransactionData;
struct LockPoints;
namespace Consensus {
struct Params;
}
#define MIN_TRANSACTION_SIZE \
(::GetSerializeSize(CTransaction(), PROTOCOL_VERSION))
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const Amount DEFAULT_MIN_RELAY_TX_FEE_PER_KB(1000 * SATOSHI);
/** Default for -excessutxocharge for transactions transactions */
static const Amount DEFAULT_UTXO_FEE = Amount::zero();
/**
* Default for -mempoolexpiry, expiration time for mempool transactions in
* hours.
*/
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336;
/** The maximum size of a blk?????.dat file (since 0.8) */
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
/** Maximum number of dedicated script-checking threads allowed */
static const int MAX_SCRIPTCHECK_THREADS = 15;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const char *const DEFAULT_BLOCKFILTERINDEX = "0";
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
/** Default for using fee filter */
static const bool DEFAULT_FEEFILTER = true;
static const bool DEFAULT_PEERBLOOMFILTERS = true;
/** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0;
/** Default for -maxreorgdepth */
static const int DEFAULT_MAX_REORG_DEPTH = 10;
/**
* Default for -finalizationdelay
* This is the minimum time between a block header reception and the block
* finalization.
* This value should be >> block propagation and validation time
*/
static const int64_t DEFAULT_MIN_FINALIZATION_DELAY = 2 * 60 * 60;
/**
* Block files containing a block-height within MIN_BLOCKS_TO_KEEP of
* ::ChainActive().Tip() will not be pruned.
*/
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
static const signed int DEFAULT_CHECKBLOCKS = 6;
static const unsigned int DEFAULT_CHECKLEVEL = 3;
/**
* Require that user allocate at least 550 MiB for block & undo files
* (blk???.dat and rev???.dat)
* At 1MB per block, 288 blocks = 288MB.
* Add 15% for Undo data = 331MB
* Add 20% for Orphan block rate = 397MB
* We want the low water mark after pruning to be at least 397 MB and since we
* prune in full block file chunks, we need the high water mark which triggers
* the prune to be one 128MB block file + added 15% undo data = 147MB greater
* for a total of 545MB
* Setting the target to >= 550 MiB will make it likely we can respect the
* target.
*/
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
/** Current sync state passed to tip changed callbacks. */
enum class SynchronizationState { INIT_REINDEX, INIT_DOWNLOAD, POST_INIT };
extern RecursiveMutex cs_main;
typedef std::unordered_map<BlockHash, CBlockIndex *, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
extern uint256 g_best_block;
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
/**
* A fee rate smaller than this is considered zero fee (for relaying, mining and
* transaction creation)
*/
extern CFeeRate minRelayTxFee;
/**
* If the tip is older than this (in seconds), the node is considered to be in
* initial block download.
*/
extern int64_t nMaxTipAge;
/**
* Block hash whose ancestors we will assume to have valid scripts without
* checking them.
*/
extern BlockHash hashAssumeValid;
/**
* Minimum work we will assume exists on some valid chain.
*/
extern arith_uint256 nMinimumChainWork;
/**
* Best header we've seen so far (used for getheaders queries' starting points).
*/
extern CBlockIndex *pindexBestHeader;
/** Pruning-related variables and constants */
/** True if any block files have ever been pruned. */
extern bool fHavePruned;
/** True if we're running in -prune mode. */
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
/** Documentation for argument 'checklevel'. */
extern const std::vector<std::string> CHECKLEVEL_DOC;
class BlockValidationOptions {
private:
uint64_t excessiveBlockSize;
bool checkPoW : 1;
bool checkMerkleRoot : 1;
public:
// Do full validation by default
explicit BlockValidationOptions(const Config &config);
explicit BlockValidationOptions(uint64_t _excessiveBlockSize,
bool _checkPow = true,
bool _checkMerkleRoot = true)
: excessiveBlockSize(_excessiveBlockSize), checkPoW(_checkPow),
checkMerkleRoot(_checkMerkleRoot) {}
BlockValidationOptions withCheckPoW(bool _checkPoW = true) const {
BlockValidationOptions ret = *this;
ret.checkPoW = _checkPoW;
return ret;
}
BlockValidationOptions
withCheckMerkleRoot(bool _checkMerkleRoot = true) const {
BlockValidationOptions ret = *this;
ret.checkMerkleRoot = _checkMerkleRoot;
return ret;
}
bool shouldValidatePoW() const { return checkPoW; }
bool shouldValidateMerkleRoot() const { return checkMerkleRoot; }
uint64_t getExcessiveBlockSize() const { return excessiveBlockSize; }
};
/**
* Import blocks from an external file.
*/
void LoadExternalBlockFile(const Config &config, FILE *fileIn,
FlatFilePos *dbp = nullptr);
/**
* Ensures we have a genesis block in the block tree, possibly writing one to
* disk.
*/
bool LoadGenesisBlock(const CChainParams &chainparams);
/**
* Unload database information.
*/
void UnloadBlockIndex(CTxMemPool *mempool, ChainstateManager &chainman);
/**
- * Run an instance of the script checking thread.
+ * Run instances of script checking worker threads
*/
-void ThreadScriptCheck(int worker_num);
+void StartScriptCheckWorkerThreads(int threads_num);
+
+/**
+ * Stop all of the script checking worker threads
+ */
+void StopScriptCheckWorkerThreads();
/**
* Return transaction from the block at block_index.
* If block_index is not provided, fall back to mempool.
* If mempool is not provided or the tx couldn't be found in mempool, fall back
* to g_txindex.
*
* @param[in] block_index The block to read from disk, or nullptr
* @param[in] mempool If block_index is not provided, look in the
* mempool, if provided
* @param[in] txid The txid
* @param[in] consensusParams The params
* @param[out] hashBlock The hash of block_index, if the tx was found via
* block_index
* @returns The tx if found, otherwise nullptr
*/
CTransactionRef GetTransaction(const CBlockIndex *const block_index,
const CTxMemPool *const mempool,
const TxId &txid,
const Consensus::Params &consensusParams,
BlockHash &hashBlock);
/**
* Find the best known block, and make it the tip of the block chain
*
* May not be called with cs_main held. May not be called in a
* validationinterface callback.
*/
bool ActivateBestChain(
const Config &config, BlockValidationState &state,
std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams);
/**
* Guess verification progress (as a fraction between 0.0=genesis and
* 1.0=current tip).
*/
double GuessVerificationProgress(const ChainTxData &data,
const CBlockIndex *pindex);
/**
* Calculate the amount of disk space the block & undo files currently use.
*/
uint64_t CalculateCurrentUsage();
/**
* Actually unlink the specified files
*/
void UnlinkPrunedFiles(const std::set<int> &setFilesToPrune);
/** Prune block files up to a given height */
void PruneBlockFilesManual(int nManualPruneHeight);
/**
* (try to) add transaction to memory pool
* @param[out] fee_out optional argument to return tx fee to the caller
*/
bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool,
TxValidationState &state, const CTransactionRef &tx,
bool bypass_limits, bool test_accept = false,
Amount *fee_out = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Simple class for regulating resource usage during CheckInputScripts (and
* CScriptCheck), atomic so as to be compatible with parallel validation.
*/
class CheckInputsLimiter {
protected:
std::atomic<int64_t> remaining;
public:
explicit CheckInputsLimiter(int64_t limit) : remaining(limit) {}
bool consume_and_check(int consumed) {
auto newvalue = (remaining -= consumed);
return newvalue >= 0;
}
bool check() { return remaining >= 0; }
};
class TxSigCheckLimiter : public CheckInputsLimiter {
public:
TxSigCheckLimiter() : CheckInputsLimiter(MAX_TX_SIGCHECKS) {}
// Let's make this bad boy copiable.
TxSigCheckLimiter(const TxSigCheckLimiter &rhs)
: CheckInputsLimiter(rhs.remaining.load()) {}
TxSigCheckLimiter &operator=(const TxSigCheckLimiter &rhs) {
remaining = rhs.remaining.load();
return *this;
}
static TxSigCheckLimiter getDisabled() {
TxSigCheckLimiter txLimiter;
// Historically, there has not been a transaction with more than 20k sig
// checks on testnet or mainnet, so this effectively disable sigchecks.
txLimiter.remaining = 20000;
return txLimiter;
}
};
class ConnectTrace;
/**
* Check whether all of this transaction's input scripts succeed.
*
* This involves ECDSA signature checks so can be computationally intensive.
* This function should only be called after the cheap sanity checks in
* CheckTxInputs passed.
*
* If pvChecks is not nullptr, script checks are pushed onto it instead of being
* performed inline. Any script checks which are not necessary (eg due to script
* execution cache hits) are, obviously, not pushed onto pvChecks/run.
*
* Upon success nSigChecksOut will be filled in with either:
* - correct total for all inputs, or,
* - 0, in the case when checks were pushed onto pvChecks (i.e., a cache miss
* with pvChecks non-null), in which case the total can be found by executing
* pvChecks and adding the results.
*
* Setting sigCacheStore/scriptCacheStore to false will remove elements from the
* corresponding cache which are matched. This is useful for checking blocks
* where we will likely never need the cache entry again.
*
* pLimitSigChecks can be passed to limit the sigchecks count either in parallel
* or serial validation. With pvChecks null (serial validation), breaking the
* pLimitSigChecks limit will abort evaluation early and return false. With
* pvChecks not-null (parallel validation): the cached nSigChecks may itself
* break the limit in which case false is returned, OR, each entry in the
* returned pvChecks must be executed exactly once in order to probe the limit
* accurately.
*/
bool CheckInputScripts(const CTransaction &tx, TxValidationState &state,
const CCoinsViewCache &view, const uint32_t flags,
bool sigCacheStore, bool scriptCacheStore,
const PrecomputedTransactionData &txdata,
int &nSigChecksOut, TxSigCheckLimiter &txLimitSigChecks,
CheckInputsLimiter *pBlockLimitSigChecks,
std::vector<CScriptCheck> *pvChecks)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Handy shortcut to full fledged CheckInputScripts call.
*/
static inline bool
CheckInputScripts(const CTransaction &tx, TxValidationState &state,
const CCoinsViewCache &view, const uint32_t flags,
bool sigCacheStore, bool scriptCacheStore,
const PrecomputedTransactionData &txdata, int &nSigChecksOut)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
TxSigCheckLimiter nSigChecksTxLimiter;
return CheckInputScripts(tx, state, view, flags, sigCacheStore,
scriptCacheStore, txdata, nSigChecksOut,
nSigChecksTxLimiter, nullptr, nullptr);
}
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos);
/** Get the BIP9 state for a given deployment at a given block. */
ThresholdState VersionBitsBlockState(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos,
const CBlockIndex *pindex);
/**
* Get the numerical statistics for the BIP9 state for a given deployment at the
* current tip.
*/
BIP9Stats VersionBitsTipStatistics(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos);
/**
* Get the block height at which the BIP9 deployment switched into the state for
* the block building on the current tip.
*/
int VersionBitsTipStateSinceHeight(const Consensus::Params ¶ms,
Consensus::DeploymentPos pos);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, int nHeight);
/**
* Mark all the coins corresponding to a given transaction inputs as spent.
*/
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo,
int nHeight);
/**
* Apply the effects of this transaction on the UTXO set represented by view.
*/
void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, int nHeight);
void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo,
int nHeight);
/**
* Test whether the LockPoints height and time are still valid on the current
* chain.
*/
bool TestLockPointValidity(const LockPoints *lp)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Check if transaction will be BIP 68 final in the next block to be created.
*
* Simulates calling SequenceLocks() with data from the tip of the current
* active chain. Optionally stores in LockPoints the resulting height and time
* calculated and the hash of the block needed for calculation or skips the
* calculation and uses the LockPoints passed in for evaluation. The LockPoints
* should not be considered valid if CheckSequenceLocks returns false.
*
* See consensus/consensus.h for flag definitions.
*/
bool CheckSequenceLocks(const CTxMemPool &pool, const CTransaction &tx,
int flags, LockPoints *lp = nullptr,
bool useExistingLockPoints = false)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
/**
* Closure representing one script verification.
* Note that this stores references to the spending transaction.
*
* Note that if pLimitSigChecks is passed, then failure does not imply that
* scripts have failed.
*/
class CScriptCheck {
private:
CTxOut m_tx_out;
const CTransaction *ptxTo;
unsigned int nIn;
uint32_t nFlags;
bool cacheStore;
ScriptError error;
ScriptExecutionMetrics metrics;
PrecomputedTransactionData txdata;
TxSigCheckLimiter *pTxLimitSigChecks;
CheckInputsLimiter *pBlockLimitSigChecks;
public:
CScriptCheck()
: ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false),
error(ScriptError::UNKNOWN), txdata(), pTxLimitSigChecks(nullptr),
pBlockLimitSigChecks(nullptr) {}
CScriptCheck(const CTxOut &outIn, const CTransaction &txToIn,
unsigned int nInIn, uint32_t nFlagsIn, bool cacheIn,
const PrecomputedTransactionData &txdataIn,
TxSigCheckLimiter *pTxLimitSigChecksIn = nullptr,
CheckInputsLimiter *pBlockLimitSigChecksIn = nullptr)
: m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn),
cacheStore(cacheIn), error(ScriptError::UNKNOWN), txdata(txdataIn),
pTxLimitSigChecks(pTxLimitSigChecksIn),
pBlockLimitSigChecks(pBlockLimitSigChecksIn) {}
bool operator()();
void swap(CScriptCheck &check) {
std::swap(ptxTo, check.ptxTo);
std::swap(m_tx_out, check.m_tx_out);
std::swap(nIn, check.nIn);
std::swap(nFlags, check.nFlags);
std::swap(cacheStore, check.cacheStore);
std::swap(error, check.error);
std::swap(metrics, check.metrics);
std::swap(txdata, check.txdata);
std::swap(pTxLimitSigChecks, check.pTxLimitSigChecks);
std::swap(pBlockLimitSigChecks, check.pBlockLimitSigChecks);
}
ScriptError GetScriptError() const { return error; }
ScriptExecutionMetrics GetScriptExecutionMetrics() const { return metrics; }
};
bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex);
/** Functions for validating blocks and updating the block tree */
/**
* Context-independent validity checks.
*
* Returns true if the provided block is valid (has valid header,
* transactions are valid, block is a valid size, etc.)
*/
bool CheckBlock(const CBlock &block, BlockValidationState &state,
const Consensus::Params ¶ms,
BlockValidationOptions validationOptions);
/**
* This is a variant of ContextualCheckTransaction which computes the contextual
* check for a transaction based on the chain tip.
*
* See consensus/consensus.h for flag definitions.
*/
bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms,
const CTransaction &tx,
TxValidationState &state,
int flags = -1)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Check a block is completely valid from start to finish (only works on top of
* our current best block)
*/
bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms,
const CBlock &block, CBlockIndex *pindexPrev,
BlockValidationOptions validationOptions)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* RAII wrapper for VerifyDB: Verify consistency of the block and coin
* databases.
*/
class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
bool VerifyDB(const Config &config, CCoinsView *coinsview, int nCheckLevel,
int nCheckDepth);
};
CBlockIndex *LookupBlockIndex(const BlockHash &hash)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex *FindForkInGlobalIndex(const CChain &chain,
const CBlockLocator &locator)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** @see CChainState::FlushStateToDisk */
enum class FlushStateMode { NONE, IF_NEEDED, PERIODIC, ALWAYS };
/** Global variable that points to the active CCoinsView (protected by cs_main)
*/
extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
/**
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
* to determine where the most-work tip is.
*
* This data is used mostly in `CChainState` - information about, e.g.,
* candidate tips is not maintained here.
*/
class BlockManager {
friend CChainState;
private:
/**
* Calculate the block/rev files to delete based on height specified
* by user with RPC command pruneblockchain
*/
void FindFilesToPruneManual(std::set<int> &setFilesToPrune,
int nManualPruneHeight, int chain_tip_height);
/**
* Prune block and undo files (blk???.dat and undo???.dat) so that the disk
* space used is less than a user-defined target. The user sets the target
* (in MB) on the command line or in config file. This will be run on
* startup and whenever new space is allocated in a block or undo file,
* staying below the target. Changing back to unpruned requires a reindex
* (which in this case means the blockchain must be re-downloaded.)
*
* Pruning functions are called from FlushStateToDisk when the global
* fCheckForPruning flag has been set. Block and undo files are deleted in
* lock-step (when blk00003.dat is deleted, so is rev00003.dat.) Pruning
* cannot take place until the longest chain is at least a certain length
* (100000 on mainnet, 1000 on testnet, 1000 on regtest). Pruning will never
* delete a block within a defined distance (currently 288) from the active
* chain's tip. The block index is updated by unsetting HAVE_DATA and
* HAVE_UNDO for any blocks that were stored in the deleted files. A db flag
* records the fact that at least some block files have been pruned.
*
* @param[out] setFilesToPrune The set of file indices that can be
* unlinked will be returned
*/
void FindFilesToPrune(std::set<int> &setFilesToPrune,
uint64_t nPruneAfterHeight, int chain_tip_height,
bool is_ibd);
public:
BlockMap m_block_index GUARDED_BY(cs_main);
/**
* In order to efficiently track invalidity of headers, we keep the set of
* blocks which we tried to connect and found to be invalid here (ie which
* were set to BLOCK_FAILED_VALID since the last restart). We can then
* walk this set and check if a new header is a descendant of something in
* this set, preventing us from having to walk m_block_index when we try
* to connect a bad block and fail.
*
* While this is more complicated than marking everything which descends
* from an invalid block as invalid at the time we discover it to be
* invalid, doing so would require walking all of m_block_index to find all
* descendants. Since this case should be very rare, keeping track of all
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
* well.
*
* Because we already walk m_block_index in height-order at startup, we go
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that
* time, instead of putting things in this set.
*/
std::set<CBlockIndex *> m_failed_blocks;
/**
* All pairs A->B, where A (or one of its ancestors) misses transactions,
* but B has transactions. Pruned nodes may have entries where B is missing
* data.
*/
std::multimap<CBlockIndex *, CBlockIndex *> m_blocks_unlinked;
/**
* Load the blocktree off disk and into memory. Populate certain metadata
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as
* peripheral collections like setDirtyBlockIndex.
*
* @param[out] block_index_candidates Fill this set with any valid blocks
* for which we've downloaded all transactions.
*/
bool LoadBlockIndex(const Consensus::Params &consensus_params,
CBlockTreeDB &blocktree,
std::set<CBlockIndex *, CBlockIndexWorkComparator>
&block_index_candidates)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex *AddToBlockIndex(const CBlockHeader &block)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex *InsertBlockIndex(const BlockHash &hash)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Mark one block file as pruned (modify associated database entries)
void PruneOneBlockFile(const int fileNumber)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it,
* ensure that it doesn't descend from an invalid block, and then add it to
* m_block_index.
*/
bool AcceptBlockHeader(const Config &config, const CBlockHeader &block,
BlockValidationState &state, CBlockIndex **ppindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
~BlockManager() { Unload(); }
};
/**
* A convenience class for constructing the CCoinsView* hierarchy used
* to facilitate access to the UTXO set.
*
* This class consists of an arrangement of layered CCoinsView objects,
* preferring to store and retrieve coins in memory via `m_cacheview` but
* ultimately falling back on cache misses to the canonical store of UTXOs on
* disk, `m_dbview`.
*/
class CoinsViews {
public:
//! The lowest level of the CoinsViews cache hierarchy sits in a leveldb
//! database on disk. All unspent coins reside in this store.
CCoinsViewDB m_dbview GUARDED_BY(cs_main);
//! This view wraps access to the leveldb instance and handles read errors
//! gracefully.
CCoinsViewErrorCatcher m_catcherview GUARDED_BY(cs_main);
//! This is the top layer of the cache hierarchy - it keeps as many coins in
//! memory as can fit per the dbcache setting.
std::unique_ptr<CCoinsViewCache> m_cacheview GUARDED_BY(cs_main);
//! This constructor initializes CCoinsViewDB and CCoinsViewErrorCatcher
//! instances, but it *does not* create a CCoinsViewCache instance by
//! default. This is done separately because the presence of the cache has
//! implications on whether or not we're allowed to flush the cache's state
//! to disk, which should not be done until the health of the database is
//! verified.
//!
//! All arguments forwarded onto CCoinsViewDB.
CoinsViews(std::string ldb_name, size_t cache_size_bytes, bool in_memory,
bool should_wipe);
//! Initialize the CCoinsViewCache member.
void InitCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
};
enum class CoinsCacheSizeState {
//! The coins cache is in immediate need of a flush.
CRITICAL = 2,
//! The cache is at >= 90% capacity.
LARGE = 1,
OK = 0
};
/**
* CChainState stores and provides an API to update our local knowledge of the
* current best chain.
*
* Eventually, the API here is targeted at being exposed externally as a
* consumable libconsensus library, so any functions added must only call
* other class member functions, pure functions in other parts of the consensus
* library, callbacks via the validation interface, or read/write-to-disk
* functions (eventually this will also be via callbacks).
*
* Anything that is contingent on the current tip of the chain is stored here,
* whereas block information and metadata independent of the current tip is
* kept in `BlockMetadataManager`.
*/
class CChainState {
private:
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in
* ActivateBestChain()
*/
RecursiveMutex m_cs_chainstate;
/**
* Every received block is assigned a unique and increasing identifier, so
* we know which one to give priority in case of a fork.
* Blocks loaded from disk are assigned id 0, so start the counter at 1.
*/
std::atomic<int32_t> nBlockSequenceId{1};
/** Decreasing counter (used by subsequent preciousblock calls). */
int32_t nBlockReverseSequenceId = -1;
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;
/**
* Whether this chainstate is undergoing initial block download.
*
* Mutable because we need to be able to mark IsInitialBlockDownload()
* const, which latches this for caching purposes.
*/
mutable std::atomic<bool> m_cached_finished_ibd{false};
//! Reference to a BlockManager instance which itself is shared across all
//! CChainState instances. Keeping a local reference allows us to test more
//! easily as opposed to referencing a global.
BlockManager &m_blockman;
//! mempool that is kept in sync with the chain
CTxMemPool &m_mempool;
//! Manages the UTXO set, which is a reflection of the contents of
//! `m_chain`.
std::unique_ptr<CoinsViews> m_coins_views;
/**
* The best finalized block.
* This block cannot be reorged in any way except by explicit user action.
*/
const CBlockIndex *m_finalizedBlockIndex GUARDED_BY(cs_main) = nullptr;
public:
explicit CChainState(CTxMemPool &mempool, BlockManager &blockman,
BlockHash from_snapshot_blockhash = BlockHash());
/**
* Initialize the CoinsViews UTXO set database management data structures.
* The in-memory cache is initialized separately.
*
* All parameters forwarded to CoinsViews.
*/
void InitCoinsDB(size_t cache_size_bytes, bool in_memory, bool should_wipe,
std::string leveldb_name = "chainstate");
//! Initialize the in-memory coins cache (to be done after the health of the
//! on-disk database is verified).
void InitCoinsCache(size_t cache_size_bytes)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! @returns whether or not the CoinsViews object has been fully initialized
//! and we can
//! safely flush this object to disk.
bool CanFlushToDisk() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return m_coins_views && m_coins_views->m_cacheview;
}
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
/**
* The blockhash which is the base of the snapshot this chainstate was
* created from.
*
* IsNull() if this chainstate was not created from a snapshot.
*/
const BlockHash m_from_snapshot_blockhash{};
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for
* itself and all ancestors) and as good as our current tip or better.
* Entries may be failed, though, and pruning nodes may be missing the data
* for the block.
*/
std::set<CBlockIndex *, CBlockIndexWorkComparator> setBlockIndexCandidates;
//! @returns A reference to the in-memory cache of the UTXO set.
CCoinsViewCache &CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
assert(m_coins_views->m_cacheview);
return *m_coins_views->m_cacheview.get();
}
//! @returns A reference to the on-disk UTXO set database.
CCoinsViewDB &CoinsDB() { return m_coins_views->m_dbview; }
//! @returns A reference to a wrapped view of the in-memory UTXO set that
//! handles disk read errors gracefully.
CCoinsViewErrorCatcher &CoinsErrorCatcher()
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return m_coins_views->m_catcherview;
}
//! Destructs all objects related to accessing the UTXO set.
void ResetCoinsViews() { m_coins_views.reset(); }
//! The cache size of the on-disk coins view.
size_t m_coinsdb_cache_size_bytes{0};
//! The cache size of the in-memory coins view.
size_t m_coinstip_cache_size_bytes{0};
//! Resize the CoinsViews caches dynamically and flush state to disk.
//! @returns true unless an error occurred during the flush.
bool ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called
* with if they're too large, if it's been a while since the last write, or
* always and in all cases if we're in prune mode and are deleting files.
*
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do
* anything besides checking if we need to prune.
*
* @returns true unless a system error occurred
*/
bool FlushStateToDisk(const CChainParams &chainparams,
BlockValidationState &state, FlushStateMode mode,
int nManualPruneHeight = 0);
//! Unconditionally flush all changes to disk.
void ForceFlushStateToDisk();
//! Prune blockfiles from the disk if necessary and then flush chainstate
//! changes if we pruned.
void PruneAndFlush();
/**
* Make the best chain active, in multiple steps. The result is either
* failure or an activated best chain. pblock is either nullptr or a pointer
* to a block that is already loaded (to avoid loading it again from disk).
*
* ActivateBestChain is split into steps (see ActivateBestChainStep) so that
* we avoid holding cs_main for an extended period of time; the length of
* this call may be quite long during reindexing or a substantial reorg.
*
* May not be called with cs_main held. May not be called in a
* validationinterface callback.
*
* @returns true unless a system error occurred
*/
bool ActivateBestChain(
const Config &config, BlockValidationState &state,
std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>())
LOCKS_EXCLUDED(cs_main);
bool AcceptBlock(const Config &config,
const std::shared_ptr<const CBlock> &pblock,
BlockValidationState &state, bool fRequested,
const FlatFilePos *dbp, bool *fNewBlock)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock &block,
const CBlockIndex *pindex,
CCoinsViewCache &view);
bool ConnectBlock(const CBlock &block, BlockValidationState &state,
CBlockIndex *pindex, CCoinsViewCache &view,
const CChainParams ¶ms,
BlockValidationOptions options, bool fJustCheck = false)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block disconnection on our pcoinsTip:
bool DisconnectTip(const CChainParams ¶ms, BlockValidationState &state,
DisconnectedBlockTransactions *disconnectpool)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
bool PreciousBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
bool InvalidateBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main)
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_chainstate);
/** Park a block. */
bool ParkBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main)
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_chainstate);
/**
* Finalize a block.
* A finalized block can not be reorged in any way.
*/
bool FinalizeBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main)
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_chainstate);
/** Return the currently finalized block index. */
const CBlockIndex *GetFinalizedBlock() const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Checks if a block is finalized.
*/
bool IsBlockFinalized(const CBlockIndex *pindex) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ResetBlockFailureFlags(CBlockIndex *pindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
template <typename F>
bool UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
template <typename F, typename C, typename AC>
void UpdateFlags(CBlockIndex *pindex, CBlockIndex *&pindexReset, F f,
C fChild, AC fAncestorWasChanged)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove parked status from a block and its descendants. */
void UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const Consensus::Params ¶ms);
bool LoadGenesisBlock(const CChainParams &chainparams);
void PruneBlockIndexCandidates();
void UnloadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Check whether we are doing an initial block download (synchronizing from
* disk or network)
*/
bool IsInitialBlockDownload() const;
/**
* Make various assertions about the state of the block index.
*
* By default this only executes fully when using the Regtest chain; see:
* fCheckBlockIndex.
*/
void CheckBlockIndex(const Consensus::Params &consensusParams);
/** Load the persisted mempool from disk */
void LoadMempool(const Config &config, const ArgsManager &args);
/** Update the chain tip based on database information, i.e. CoinsTip()'s
* best block. */
bool LoadChainTip(const CChainParams &chainparams)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Dictates whether we need to flush the cache to disk or not.
//!
//! @return the state of the size of the coins cache.
CoinsCacheSizeState GetCoinsCacheSizeState(const CTxMemPool *tx_pool)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CoinsCacheSizeState GetCoinsCacheSizeState(
const CTxMemPool *tx_pool, size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
bool ActivateBestChainStep(const Config &config,
BlockValidationState &state,
CBlockIndex *pindexMostWork,
const std::shared_ptr<const CBlock> &pblock,
bool &fInvalidFound, ConnectTrace &connectTrace)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
bool ConnectTip(const Config &config, BlockValidationState &state,
CBlockIndex *pindexNew,
const std::shared_ptr<const CBlock> &pblock,
ConnectTrace &connectTrace,
DisconnectedBlockTransactions &disconnectpool)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
void InvalidBlockFound(CBlockIndex *pindex,
const BlockValidationState &state)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void InvalidChainFound(CBlockIndex *pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex *FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool MarkBlockAsFinal(BlockValidationState &state,
const CBlockIndex *pindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock &block, CBlockIndex *pindexNew,
const FlatFilePos &pos)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &inputs,
const Consensus::Params ¶ms)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool UnwindBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex, bool invalidate)
EXCLUSIVE_LOCKS_REQUIRED(m_cs_chainstate);
friend ChainstateManager;
};
/**
* Mark a block as precious and reorganize.
*
* May not be called in a validationinterface callback.
*/
bool PreciousBlock(const Config &config, BlockValidationState &state,
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex *pindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove parked status from a block and its descendants. */
void UnparkBlockAndChildren(CBlockIndex *pindex)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove parked status from a block. */
void UnparkBlock(CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Provides an interface for creating and interacting with one or two
* chainstates: an IBD chainstate generated by downloading blocks, and
* an optional snapshot chainstate loaded from a UTXO snapshot. Managed
* chainstates can be maintained at different heights simultaneously.
*
* This class provides abstractions that allow the retrieval of the current
* most-work chainstate ("Active") as well as chainstates which may be in
* background use to validate UTXO snapshots.
*
* Definitions:
*
* *IBD chainstate*: a chainstate whose current state has been "fully"
* validated by the initial block download process.
*
* *Snapshot chainstate*: a chainstate populated by loading in an
* assumeutxo UTXO snapshot.
*
* *Active chainstate*: the chainstate containing the current most-work
* chain. Consulted by most parts of the system (net_processing,
* wallet) as a reflection of the current chain and UTXO set.
* This may either be an IBD chainstate or a snapshot chainstate.
*
* *Background IBD chainstate*: an IBD chainstate for which the
* IBD process is happening in the background while use of the
* active (snapshot) chainstate allows the rest of the system to function.
*
* *Validated chainstate*: the most-work chainstate which has been validated
* locally via initial block download. This will be the snapshot chainstate
* if a snapshot was loaded and all blocks up to the snapshot starting point
* have been downloaded and validated (via background validation), otherwise
* it will be the IBD chainstate.
*/
class ChainstateManager {
private:
//! The chainstate used under normal operation (i.e. "regular" IBD) or, if
//! a snapshot is in use, for background validation.
//!
//! Its contents (including on-disk data) will be deleted *upon shutdown*
//! after background validation of the snapshot has completed. We do not
//! free the chainstate contents immediately after it finishes validation
//! to cautiously avoid a case where some other part of the system is still
//! using this pointer (e.g. net_processing).
//!
//! Once this pointer is set to a corresponding chainstate, it will not
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
//! the contents of this pointer with ::cs_main held, release the lock,
//! and then use the reference without concern of it being deconstructed.
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
std::unique_ptr<CChainState> m_ibd_chainstate;
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
//! non-null, it is always our active chainstate.
//!
//! Once this pointer is set to a corresponding chainstate, it will not
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
//! the contents of this pointer with ::cs_main held, release the lock,
//! and then use the reference without concern of it being deconstructed.
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
std::unique_ptr<CChainState> m_snapshot_chainstate;
//! Points to either the ibd or snapshot chainstate; indicates our
//! most-work chain.
//!
//! Once this pointer is set to a corresponding chainstate, it will not
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
//! the contents of this pointer with ::cs_main held, release the lock,
//! and then use the reference without concern of it being deconstructed.
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
CChainState *m_active_chainstate{nullptr};
//! If true, the assumed-valid chainstate has been fully validated
//! by the background validation chainstate.
bool m_snapshot_validated{false};
// For access to m_active_chainstate.
friend CChainState &ChainstateActive();
friend CChain &ChainActive();
public:
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
BlockManager m_blockman GUARDED_BY(::cs_main);
//! The total number of bytes available for us to use across all in-memory
//! coins caches. This will be split somehow across chainstates.
int64_t m_total_coinstip_cache{0};
//
//! The total number of bytes available for us to use across all leveldb
//! coins databases. This will be split somehow across chainstates.
int64_t m_total_coinsdb_cache{0};
//! Instantiate a new chainstate and assign it based upon whether it is
//! from a snapshot.
//!
//! @param[in] mempool The mempool to pass to the chainstate
// constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
CChainState &
InitializeChainstate(CTxMemPool &mempool,
const BlockHash &snapshot_blockhash = BlockHash())
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
std::vector<CChainState *> GetAll();
//! The most-work chain.
CChainState &ActiveChainstate() const;
CChain &ActiveChain() const { return ActiveChainstate().m_chain; }
int ActiveHeight() const { return ActiveChain().Height(); }
CBlockIndex *ActiveTip() const { return ActiveChain().Tip(); }
BlockMap &BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return m_blockman.m_block_index;
}
bool IsSnapshotActive() const;
std::optional<BlockHash> SnapshotBlockhash() const;
//! Is there a snapshot in use and has it been fully validated?
bool IsSnapshotValidated() const { return m_snapshot_validated; }
//! @returns true if this chainstate is being used to validate an active
//! snapshot in the background.
bool IsBackgroundIBD(CChainState *chainstate) const;
//! Return the most-work chainstate that has been fully validated.
//!
//! During background validation of a snapshot, this is the IBD chain. After
//! background validation has completed, this is the snapshot chain.
CChainState &ValidatedChainstate() const;
CChain &ValidatedChain() const { return ValidatedChainstate().m_chain; }
CBlockIndex *ValidatedTip() const { return ValidatedChain().Tip(); }
/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
*
* If you want to *possibly* get feedback on whether pblock is valid, you
* must install a CValidationInterface (see validationinterface.h) - this
* will have its BlockChecked method called whenever *any* block completes
* validation.
*
* Note that we guarantee that either the proof-of-work is valid on pblock,
* or (and possibly also) BlockChecked will have been called.
*
* May not be called in a validationinterface callback.
*
* @param[in] config The global config.
* @param[in] pblock The block we want to process.
* @param[in] fForceProcessing Process this block even if unrequested;
* used for non-network block sources.
* @param[out] fNewBlock A boolean which is set to indicate if the block
* was first received via this call.
* @returns If the block was processed, independently of block validity
*/
bool ProcessNewBlock(const Config &config,
const std::shared_ptr<const CBlock> pblock,
bool fForceProcessing, bool *fNewBlock)
LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
*
* May not be called in a validationinterface callback.
*
* @param[in] config The config.
* @param[in] block The block headers themselves.
* @param[out] state This may be set to an Error state if any error
* occurred processing them.
* @param[out] ppindex If set, the pointer will be set to point to the
* last new block index object for the given
* headers.
* @return True if block headers were accepted as valid.
*/
bool ProcessNewBlockHeaders(const Config &config,
const std::vector<CBlockHeader> &block,
BlockValidationState &state,
const CBlockIndex **ppindex = nullptr)
LOCKS_EXCLUDED(cs_main);
//! Load the block tree and coins database from disk, initializing state if
//! we're running with -reindex
bool LoadBlockIndex(const Consensus::Params ¶ms)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Unload block index and chain data before shutdown.
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Clear (deconstruct) chainstate data.
void Reset();
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
};
/**
* DEPRECATED! Please use node.chainman instead. May only be used in
* validation.cpp internally
*/
extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
/** Please prefer the identical ChainstateManager::ActiveChainstate */
CChainState &ChainstateActive();
/** Please prefer the identical ChainstateManager::ActiveChain */
CChain &ChainActive();
/**
* Global variable that points to the active block tree (protected by cs_main)
*/
extern std::unique_ptr<CBlockTreeDB> pblocktree;
/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
* While checking, GetBestBlock() refers to the parent block. (protected by
* cs_main)
* This is also true for mempool checks.
*/
int GetSpendHeight(const CCoinsViewCache &inputs);
/**
* Determine what nVersion a new block should use.
*/
int32_t ComputeBlockVersion(const CBlockIndex *pindexPrev,
const Consensus::Params ¶ms);
/** Get block file info entry for one block file */
CBlockFileInfo *GetBlockFileInfo(size_t n);
/** Dump the mempool to disk. */
bool DumpMempool(const CTxMemPool &pool);
/** Load the mempool from disk. */
bool LoadMempool(const Config &config, CTxMemPool &pool);
//! Check whether the block associated with this index entry is pruned or not.
bool IsBlockPruned(const CBlockIndex *pblockindex);
#endif // BITCOIN_VALIDATION_H
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Feb 7, 15:50 (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082639
Default Alt Text
(509 KB)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment