diff --git a/src/test/activation_tests.cpp b/src/test/activation_tests.cpp index 12ebbd923..0ba409e75 100644 --- a/src/test/activation_tests.cpp +++ b/src/test/activation_tests.cpp @@ -1,74 +1,130 @@ // Copyright (c) 2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chain.h> #include <chainparams.h> +#include <config.h> #include <consensus/activation.h> +#include <script/script.h> +#include <txmempool.h> +#include <util/check.h> #include <util/system.h> +#include <validation.h> #include <test/util/blockindex.h> +#include <test/util/mining.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> BOOST_FIXTURE_TEST_SUITE(activation_tests, BasicTestingSetup) static void testPastActivation( std::function<bool(const Consensus::Params &, const CBlockIndex *)> func, const Consensus::Params ¶ms, int activationHeight) { BOOST_CHECK(!func(params, nullptr)); std::array<CBlockIndex, 4> blocks; blocks[0].nHeight = activationHeight - 2; for (size_t i = 1; i < blocks.size(); ++i) { blocks[i].pprev = &blocks[i - 1]; blocks[i].nHeight = blocks[i - 1].nHeight + 1; } BOOST_CHECK(!func(params, &blocks[0])); BOOST_CHECK(!func(params, &blocks[1])); BOOST_CHECK(func(params, &blocks[2])); BOOST_CHECK(func(params, &blocks[3])); } BOOST_AUTO_TEST_CASE(test_previous_activations_by_height) { const auto params = CreateChainParams(CBaseChainParams::MAIN); const auto consensus = params->GetConsensus(); testPastActivation(IsGravitonEnabled, consensus, consensus.gravitonHeight); testPastActivation(IsPhononEnabled, consensus, consensus.phononHeight); testPastActivation(IsAxionEnabled, consensus, consensus.axionHeight); testPastActivation(IsGluonEnabled, consensus, consensus.gluonHeight); } BOOST_AUTO_TEST_CASE(iswellingtonenabled) { const Consensus::Params ¶ms = Params().GetConsensus(); const auto activation = gArgs.GetIntArg("-wellingtonactivationtime", params.wellingtonActivationTime); SetMockTime(activation - 1000000); BOOST_CHECK(!IsWellingtonEnabled(params, nullptr)); std::array<CBlockIndex, 12> blocks; for (size_t i = 1; i < blocks.size(); ++i) { blocks[i].pprev = &blocks[i - 1]; } BOOST_CHECK(!IsWellingtonEnabled(params, &blocks.back())); BOOST_CHECK( !IsWellingtonEnabled(params, blocks.back().GetMedianTimePast())); SetMTP(blocks, activation - 1); BOOST_CHECK(!IsWellingtonEnabled(params, &blocks.back())); BOOST_CHECK(!IsWellingtonEnabled(params, activation - 1)); SetMTP(blocks, activation); BOOST_CHECK(IsWellingtonEnabled(params, &blocks.back())); BOOST_CHECK(IsWellingtonEnabled(params, activation)); SetMTP(blocks, activation + 1); BOOST_CHECK(IsWellingtonEnabled(params, &blocks.back())); BOOST_CHECK(IsWellingtonEnabled(params, activation + 1)); } +BOOST_FIXTURE_TEST_CASE(wellington_latch, RegTestingSetup) { + const Consensus::Params ¶ms = Params().GetConsensus(); + const auto activation = gArgs.GetIntArg("-wellingtonactivationtime", + params.wellingtonActivationTime); + SetMockTime(activation - 1000000); + + const Config &config = GetConfig(); + auto &mempool = *Assert(m_node.mempool); + auto &chainman = *Assert(m_node.chainman); + auto &activeChainstate = chainman.ActiveChainstate(); + + std::array<CBlockIndex, 12> blocks; + for (size_t i = 1; i < blocks.size() - 1; ++i) { + blocks[i].pprev = &blocks[i - 1]; + } + // Hack: we need to call MineBlock to avoid null pointer dereference in + // ATMP. Let's make sure it's our tip so SetMTP works as expected. + MineBlock(config, m_node, CScript() << OP_TRUE); + blocks[11] = *activeChainstate.m_chain.Tip(); + blocks[11].pprev = &blocks[10]; + activeChainstate.m_chain.SetTip(&blocks[11]); + + auto submitTxToMemPool = [&config, &activeChainstate]() { + LOCK(cs_main); + return AcceptToMemoryPool(config, activeChainstate, + MakeTransactionRef(), + /*accept_time=*/0, + /*bypass_limits=*/false); + }; + + BOOST_CHECK(!IsWellingtonEnabled(params, &blocks.back())); + submitTxToMemPool(); + BOOST_CHECK(!mempool.wellingtonLatched); + + // Activate Wellington + SetMTP(blocks, activation); + BOOST_CHECK(IsWellingtonEnabled(params, &blocks.back())); + + // Upon first tx submission, the flag is latched + BOOST_CHECK(!mempool.wellingtonLatched); + submitTxToMemPool(); + BOOST_CHECK(mempool.wellingtonLatched); + + // Simulate a reorg and check the flag did latch + SetMTP(blocks, activation - 1); + BOOST_CHECK(mempool.wellingtonLatched); + submitTxToMemPool(); + BOOST_CHECK(mempool.wellingtonLatched); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txmempool.h b/src/txmempool.h index fcf02a3d8..6bdd6fc6e 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -1,970 +1,979 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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_TXMEMPOOL_H #define BITCOIN_TXMEMPOOL_H #include <coins.h> #include <consensus/amount.h> #include <core_memusage.h> #include <indirectmap.h> #include <policy/packages.h> #include <primitives/transaction.h> #include <sync.h> #include <util/epochguard.h> #include <util/hasher.h> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index_container.hpp> #include <atomic> #include <map> #include <optional> #include <set> #include <string> #include <unordered_map> #include <utility> #include <vector> class CBlockIndex; class CChain; class Chainstate; class Config; extern RecursiveMutex cs_main; /** * Fake height value used in Coins to signify they are only in the memory * pool(since 0.8) */ static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF; struct LockPoints { // Will be set to the blockchain height and median time past values that // would be necessary to satisfy all relative locktime constraints (BIP68) // of this tx given our view of block chain history int height{0}; int64_t time{0}; // As long as the current chain descends from the highest height block // containing one of the inputs used in the calculation, then the cached // values are still valid even after a reorg. CBlockIndex *maxInputBlock{nullptr}; }; /** * Test whether the LockPoints height and time are still valid on the current * chain. */ bool TestLockPointValidity(const CChain &active_chain, const LockPoints &lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); struct CompareIteratorById { // SFINAE for T where T is either a pointer type (e.g., a txiter) or a // reference_wrapper<T> (e.g. a wrapped CTxMemPoolEntry&) template <typename T> bool operator()(const std::reference_wrapper<T> &a, const std::reference_wrapper<T> &b) const { return a.get().GetTx().GetId() < b.get().GetTx().GetId(); } template <typename T> bool operator()(const T &a, const T &b) const { return a->GetTx().GetId() < b->GetTx().GetId(); } }; /** \class CTxMemPoolEntry * * CTxMemPoolEntry stores data about the corresponding transaction, as well as * data about all in-mempool transactions that depend on the transaction * ("descendant" transactions). * * When a new entry is added to the mempool, we update the descendant state * (nCountWithDescendants, nSizeWithDescendants, and nModFeesWithDescendants) * for all ancestors of the newly added transaction. */ class CTxMemPoolEntry { public: typedef std::reference_wrapper<const CTxMemPoolEntry> CTxMemPoolEntryRef; // two aliases, should the types ever diverge typedef std::set<CTxMemPoolEntryRef, CompareIteratorById> Parents; typedef std::set<CTxMemPoolEntryRef, CompareIteratorById> Children; private: //! Unique identifier -- used for topological sorting uint64_t entryId = 0; const CTransactionRef tx; mutable Parents m_parents; mutable Children m_children; //! Cached to avoid expensive parent-transaction lookups const Amount nFee; //! ... and avoid recomputing tx size const size_t nTxSize; //! ... and total memory usage const size_t nUsageSize; //! Local time when entering the mempool const int64_t nTime; //! Chain height when entering the mempool const unsigned int entryHeight; //! keep track of transactions that spend a coinbase const bool spendsCoinbase; //! Total sigChecks const int64_t sigChecks; //! Used for determining the priority of the transaction for mining in a //! block Amount feeDelta{Amount::zero()}; //! Track the height and time at which tx was final LockPoints lockPoints; // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these // descendants as well. //! number of descendant transactions uint64_t nCountWithDescendants{1}; //! ... and size uint64_t nSizeWithDescendants; //! ... and total fees (all including us) Amount nModFeesWithDescendants; //! ... and sichecks int64_t nSigChecksWithDescendants; // Analogous statistics for ancestor transactions uint64_t nCountWithAncestors{1}; uint64_t nSizeWithAncestors; Amount nModFeesWithAncestors; int64_t nSigChecksWithAncestors; public: CTxMemPoolEntry(const CTransactionRef &_tx, const Amount fee, int64_t time, unsigned int entry_height, bool spends_coinbase, int64_t sigchecks, LockPoints lp); uint64_t GetEntryId() const { return entryId; } //! This should only be set by addUnchecked() before entry insertion into //! mempool void SetEntryId(uint64_t eid) { entryId = eid; } const CTransaction &GetTx() const { return *this->tx; } CTransactionRef GetSharedTx() const { return this->tx; } Amount GetFee() const { return nFee; } size_t GetTxSize() const { return nTxSize; } size_t GetTxVirtualSize() const; std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } unsigned int GetHeight() const { return entryHeight; } int64_t GetSigChecks() const { return sigChecks; } Amount GetModifiedFee() const { return nFee + feeDelta; } CFeeRate GetModifiedFeeRate() const { return CFeeRate(GetModifiedFee(), GetTxVirtualSize()); } size_t DynamicMemoryUsage() const { return nUsageSize; } const LockPoints &GetLockPoints() const { return lockPoints; } // Adjusts the descendant state. void UpdateDescendantState(int64_t modifySize, Amount modifyFee, int64_t modifyCount, int64_t modifySigChecks); // Adjusts the ancestor state void UpdateAncestorState(int64_t modifySize, Amount modifyFee, int64_t modifyCount, int64_t modifySigChecks); // Updates the fee delta used for mining priority score, and the // modified fees with descendants. void UpdateFeeDelta(Amount feeDelta); // Update the LockPoints after a reorg void UpdateLockPoints(const LockPoints &lp); uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } uint64_t GetVirtualSizeWithDescendants() const; Amount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } int64_t GetSigChecksWithDescendants() const { return nSigChecksWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } uint64_t GetVirtualSizeWithAncestors() const; Amount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigChecksWithAncestors() const { return nSigChecksWithAncestors; } const Parents &GetMemPoolParentsConst() const { return m_parents; } const Children &GetMemPoolChildrenConst() const { return m_children; } Parents &GetMemPoolParents() const { return m_parents; } Children &GetMemPoolChildren() const { return m_children; } //! epoch when last touched, useful for graph algorithms mutable Epoch::Marker m_epoch_marker; }; // extracts a transaction id from CTxMemPoolEntry or CTransactionRef struct mempoolentry_txid { typedef TxId result_type; result_type operator()(const CTxMemPoolEntry &entry) const { return entry.GetTx().GetId(); } result_type operator()(const CTransactionRef &tx) const { return tx->GetId(); } }; // used by the entry_time index struct CompareTxMemPoolEntryByEntryTime { bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) const { return a.GetTime() < b.GetTime(); } }; // used by the entry_id index struct CompareTxMemPoolEntryByEntryId { bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) const { return a.GetEntryId() < b.GetEntryId(); } }; /** * \class CompareTxMemPoolEntryByModifiedFeeRate * * Sort by feerate of entry (modfee/vsize) in descending order. * This is used by the block assembler (mining). */ struct CompareTxMemPoolEntryByModifiedFeeRate { bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) const { const CFeeRate frA = a.GetModifiedFeeRate(); const CFeeRate frB = b.GetModifiedFeeRate(); // Sort by modified fee rate first if (frA != frB) { return frA > frB; } // Ties are broken by whichever is topologically earlier // (this helps mining code avoid some backtracking). if (a.GetEntryId() != b.GetEntryId()) { return a.GetEntryId() < b.GetEntryId(); } // If nothing else, sort by txid (this should never happen as entryID is // expected to be unique). return a.GetSharedTx()->GetId() < b.GetSharedTx()->GetId(); } }; // Multi_index tag names struct entry_time {}; struct modified_feerate {}; struct entry_id {}; /** * Information about a mempool transaction. */ struct TxMempoolInfo { /** The transaction itself */ CTransactionRef tx; /** Time the transaction entered the mempool. */ std::chrono::seconds m_time; /** Fee of the transaction. */ Amount fee; /** Virtual size of the transaction. */ size_t vsize; /** The fee delta. */ Amount nFeeDelta; }; /** * Reason why a transaction was removed from the mempool, this is passed to the * notification signal. */ enum class MemPoolRemovalReason { //! Expired from mempool EXPIRY, //! Removed in size limiting SIZELIMIT, //! Removed for reorganization REORG, //! Removed for block BLOCK, //! Removed for conflict with in-block transaction CONFLICT, //! Removed for replacement REPLACED }; /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions that * may be included in the next block. * * Transactions are added when they are seen on the network (or created by the * local node), but not all transactions seen are added to the pool. For * example, the following new transactions will not be added to the mempool: * - a transaction which doesn't meet the minimum fee requirements. * - a new transaction that double-spends an input of a transaction already in * the pool. * - a non-standard transaction. * * CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping: * * mapTx is a boost::multi_index that sorts the mempool on 3 criteria: * - transaction hash * - time in mempool * - entry id (this is a topological index) * * Note: the term "descendant" refers to in-mempool transactions that depend on * this one, while "ancestor" refers to in-mempool transactions that a given * transaction depends on. * * When a new transaction is added to the mempool, it has no in-mempool children * (because any such children would be an orphan). So in addUnchecked(), we: * - update a new entry's setMemPoolParents to include all in-mempool parents * - update the new entry's direct parents to include the new tx as a child * - update all ancestors of the transaction to include the new tx's size/fee * * When a transaction is removed from the mempool, we must: * - update all in-mempool parents to not track the tx in setMemPoolChildren * - update all ancestors to not include the tx's size/fees in descendant state * - update all in-mempool children to not include it as a parent * * These happen in UpdateForRemoveFromMempool(). (Note that when removing a * transaction along with its descendants, we must calculate that set of * transactions to be removed before doing the removal, or else the mempool can * be in an inconsistent state where it's impossible to walk the ancestors of a * transaction.) * * In the event of a reorg, the invariant that all newly-added tx's have no * in-mempool children must be maintained. On top of this, we use a topological * index (GetEntryId). As such, we always dump mempool tx's into a disconnect * pool on reorg, and simply add them one by one, along with tx's from * disconnected blocks, when the reorg is complete. * * Computational limits: * * Updating all in-mempool ancestors of a newly added transaction can be slow, * if no bound exists on how many in-mempool ancestors there may be. * CalculateMemPoolAncestors() takes configurable limits that are designed to * prevent these calculations from being too CPU intensive. */ class CTxMemPool { private: //! Value n means that 1 times in n we check. const int m_check_ratio; //! Used by getblocktemplate to trigger CreateNewBlock() invocation std::atomic<uint32_t> nTransactionsUpdated{0}; //! sum of all mempool tx's sizes. uint64_t totalTxSize GUARDED_BY(cs); //! sum of all mempool tx's fees (NOT modified fee) Amount m_total_fee GUARDED_BY(cs); //! sum of dynamic memory usage of all the map elements (NOT the maps //! themselves) uint64_t cachedInnerUsage GUARDED_BY(cs); mutable int64_t lastRollingFeeUpdate GUARDED_BY(cs); mutable bool blockSinceLastRollingFeeBump GUARDED_BY(cs); //! minimum fee to get into the pool, decreases exponentially mutable double rollingMinimumFeeRate GUARDED_BY(cs); mutable Epoch m_epoch GUARDED_BY(cs); // In-memory counter for external mempool tracking purposes. // This number is incremented once every time a transaction // is added or removed from the mempool for any reason. mutable uint64_t m_sequence_number GUARDED_BY(cs){1}; void trackPackageRemoved(const CFeeRate &rate) EXCLUSIVE_LOCKS_REQUIRED(cs); bool m_is_loaded GUARDED_BY(cs){false}; //! Used by addUnchecked to generate ever-increasing //! CTxMemPoolEntry::entryId's uint64_t nextEntryId GUARDED_BY(cs) = 1; public: // public only for testing static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; typedef boost::multi_index_container< CTxMemPoolEntry, boost::multi_index::indexed_by< // indexed by txid boost::multi_index::hashed_unique< mempoolentry_txid, SaltedTxIdHasher>, // sorted by fee rate boost::multi_index::ordered_non_unique< boost::multi_index::tag<modified_feerate>, boost::multi_index::identity<CTxMemPoolEntry>, CompareTxMemPoolEntryByModifiedFeeRate>, // sorted by entry time boost::multi_index::ordered_non_unique< boost::multi_index::tag<entry_time>, boost::multi_index::identity<CTxMemPoolEntry>, CompareTxMemPoolEntryByEntryTime>, // sorted topologically (insertion order) boost::multi_index::ordered_unique< boost::multi_index::tag<entry_id>, boost::multi_index::identity<CTxMemPoolEntry>, CompareTxMemPoolEntryByEntryId>>> indexed_transaction_set; /** * This mutex needs to be locked when accessing `mapTx` or other members * that are guarded by it. * * @par Consistency guarantees * * By design, it is guaranteed that: * * 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool * that is consistent with current chain tip (`ActiveChain()` and * `CoinsTip()`) and is fully populated. Fully populated means that if * the current active chain is missing transactions that were present in * a previously active chain, all the missing transactions will have been * re-added to the mempool and should be present if they meet size and * consistency constraints. * * 2. Locking `mempool.cs` without `cs_main` will give a view of a mempool * consistent with some chain that was active since `cs_main` was last * locked, and that is fully populated as described above. It is ok for * code that only needs to query or remove transactions from the mempool * to lock just `mempool.cs` without `cs_main`. * * To provide these guarantees, it is necessary to lock both `cs_main` and * `mempool.cs` whenever adding transactions to the mempool and whenever * changing the chain tip. It's necessary to keep both mutexes locked until * the mempool is consistent with the new chain tip and fully populated. */ mutable RecursiveMutex cs; indexed_transaction_set mapTx GUARDED_BY(cs); using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator; typedef std::set<txiter, CompareIteratorById> setEntries; uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs); private: void UpdateParent(txiter entry, txiter parent, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs); void UpdateChild(txiter entry, txiter child, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Track locally submitted transactions to periodically retry initial * broadcast */ std::set<TxId> m_unbroadcast_txids GUARDED_BY(cs); /** * Helper function to calculate all in-mempool ancestors of staged_ancestors * and apply ancestor and descendant limits (including staged_ancestors * themselves, entry_size and entry_count). * param@[in] entry_size Virtual size to include in the limits. * param@[in] entry_count How many entries to include in the * limits. * param@[in] staged_ancestors Should contain entries in the mempool. * param@[out] setAncestors Will be populated with all mempool * ancestors. */ bool CalculateAncestorsAndCheckLimits( size_t entry_size, size_t entry_count, setEntries &setAncestors, CTxMemPoolEntry::Parents &staged_ancestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs); public: indirectmap<COutPoint, const CTransaction *> mapNextTx GUARDED_BY(cs); std::map<TxId, Amount> mapDeltas GUARDED_BY(cs); + /** + * Wellington activation latch. This is latched permanently to true in + * AcceptToMemoryPool the first time a tx arrives and + * IsWellingtonActivated() returns true. This should be removed after + * wellington is checkpointed and its mempool-accept/relay rules become + * retroactively permanent. + */ + std::atomic<bool> wellingtonLatched{false}; + /** * Create a new CTxMemPool. * Sanity checks will be off by default for performance, because otherwise * accepting transactions becomes O(N^2) where N is the number of * transactions in the pool. * * @param[in] check_ratio is the ratio used to determine how often sanity * checks will run. */ CTxMemPool(int check_ratio = 0); ~CTxMemPool(); /** * If sanity-checking is turned on, check makes sure the pool is consistent * (does not contain two transactions that spend the same inputs, all inputs * are in the mapNextTx array). If sanity-checking is turned off, check does * nothing. */ void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of // addUnchecked can be used to have it call CalculateMemPoolAncestors(), and // then invoke the second version. // Note that addUnchecked is ONLY called from ATMP outside of tests // and any other callers may break wallet's in-mempool tracking (due to // lack of CValidationInterface::TransactionAddedToMempool callbacks). void addUnchecked(const CTxMemPoolEntry &entry) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); void addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeForBlock(const std::vector<CTransactionRef> &vtx) EXCLUSIVE_LOCKS_REQUIRED(cs); void clear(); // lock free void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); bool CompareTopologically(const TxId &txida, const TxId &txidb) const; void getAllTxIds(std::vector<TxId> &vtxid) const; bool isSpent(const COutPoint &outpoint) const; unsigned int GetTransactionsUpdated() const; void AddTransactionsUpdated(unsigned int n); /** * Check that none of this transactions inputs are in the mempool, and thus * the tx is not dependent on other mempool transactions to be included in a * block. */ bool HasNoInputsOf(const CTransaction &tx) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** Affect CreateNewBlock prioritisation of transactions */ void PrioritiseTransaction(const TxId &txid, const Amount nFeeDelta); void ApplyDelta(const TxId &txid, Amount &nFeeDelta) const EXCLUSIVE_LOCKS_REQUIRED(cs); void ClearPrioritisation(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Get the transaction in the pool that spends the same prevout */ const CTransaction *GetConflictTx(const COutPoint &prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** Returns an iterator to the given txid, if found */ std::optional<txiter> GetIter(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Translate a set of txids into a set of pool iterators to avoid repeated * lookups. */ setEntries GetIterSet(const std::set<TxId> &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Remove a set of transactions from the mempool. If a transaction is in * this set, then all in-mempool descendants must also be in the set, unless * this transaction is being removed for being in a block. Set * updateDescendants to true when removing a tx that was in a block, so that * any in-mempool descendants have their ancestor state updated. */ void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Try to calculate all in-mempool ancestors of entry. * (these are all calculated including the tx itself) * limitAncestorCount = max number of ancestors * limitAncestorSize = max size of ancestors * limitDescendantCount = max number of descendants any ancestor can have * limitDescendantSize = max size of descendants any ancestor can have * errString = populated with error reason if any limits are hit * fSearchForParents = whether to search a tx's vin for in-mempool parents, * or look up parents from m_parents. Must be true for entries not in the * mempool */ bool CalculateMemPoolAncestors( const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Calculate all in-mempool ancestors of a set of transactions not already * in the mempool and check ancestor and descendant limits. Heuristics are * used to estimate the ancestor and descendant count of all entries if the * package were to be added to the mempool. The limits are applied to the * union of all package transactions. For example, if the package has 3 * transactions and limitAncestorCount = 50, the union of all 3 sets of * ancestors (including the transactions themselves) must be <= 47. * @param[in] package Transaction package being * evaluated for acceptance to mempool. The transactions need not be * direct ancestors/descendants of each other. * @param[in] limitAncestorCount Max number of txns including * ancestors. * @param[in] limitAncestorSize Max size including ancestors. * @param[in] limitDescendantCount Max number of txns including * descendants. * @param[in] limitDescendantSize Max size including descendants. * @param[out] errString Populated with error reason if * a limit is hit. */ bool CheckPackageLimits(const Package &package, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Populate setDescendants with all in-mempool descendants of hash. * Assumes that setDescendants includes all in-mempool descendants of * anything already in it. */ void CalculateDescendants(txiter it, setEntries &setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** * The minimum fee to get into the mempool, which may itself not be enough * for larger-sized transactions. The incrementalRelayFee policy variable is * used to bound the time it takes the fee rate to go back down all the way * to 0. When the feerate would otherwise be half of this, it is set to 0 * instead. */ CFeeRate GetMinFee(size_t sizelimit) const; /** * Remove transactions from the mempool until its dynamic size is <= * sizelimit. pvNoSpendsRemaining, if set, will be populated with the list * of outpoints which are not in mempool which no longer have any spends in * this mempool. */ void TrimToSize(size_t sizelimit, std::vector<COutPoint> *pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Expire all transaction (and their dependencies) in the mempool older than * time. Return the number of removed transactions. */ int Expire(std::chrono::seconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Reduce the size of the mempool by expiring and then trimming the mempool. */ void LimitSize(CCoinsViewCache &coins_cache, size_t limit, std::chrono::seconds age) EXCLUSIVE_LOCKS_REQUIRED(cs, ::cs_main); /** * Calculate the ancestor and descendant count for the given transaction. * The counts include the transaction itself. * When ancestors is non-zero (ie, the transaction itself is in the * mempool), ancestorsize and ancestorfees will also be set to the * appropriate values. */ void GetTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants, size_t *ancestorsize = nullptr, Amount *ancestorfees = nullptr) const; /** @returns true if the mempool is fully loaded */ bool IsLoaded() const; /** Sets the current loaded state */ void SetIsLoaded(bool loaded); unsigned long size() const { LOCK(cs); return mapTx.size(); } uint64_t GetTotalTxSize() const EXCLUSIVE_LOCKS_REQUIRED(cs) { AssertLockHeld(cs); return totalTxSize; } Amount GetTotalFee() const EXCLUSIVE_LOCKS_REQUIRED(cs) { AssertLockHeld(cs); return m_total_fee; } bool exists(const TxId &txid) const { LOCK(cs); return mapTx.count(txid) != 0; } CTransactionRef get(const TxId &txid) const; TxMempoolInfo info(const TxId &txid) const; std::vector<TxMempoolInfo> infoAll() const; CFeeRate estimateFee() const; size_t DynamicMemoryUsage() const; /** Adds a transaction to the unbroadcast set */ void AddUnbroadcastTx(const TxId &txid) { LOCK(cs); // Sanity check the transaction is in the mempool & insert into // unbroadcast set. if (exists(txid)) { m_unbroadcast_txids.insert(txid); } } /** Removes a transaction from the unbroadcast set */ void RemoveUnbroadcastTx(const TxId &txid, const bool unchecked = false); /** Returns transactions in unbroadcast set */ std::set<TxId> GetUnbroadcastTxs() const { LOCK(cs); return m_unbroadcast_txids; } /** Returns whether a txid is in the unbroadcast set */ bool IsUnbroadcastTx(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs) { AssertLockHeld(cs); return (m_unbroadcast_txids.count(txid) != 0); } /** Guards this internal counter for external reporting */ uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) { return m_sequence_number++; } uint64_t GetSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) { return m_sequence_number; } private: /** * Update ancestors of hash to add/remove it as a descendant transaction. */ void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Set ancestor state for an entry */ void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * For each transaction being removed, update ancestors and any direct * children. If updateDescendants is true, then also update in-mempool * descendants' ancestor state. */ void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Sever link between specified transaction and direct children. */ void UpdateChildrenForRemoval(txiter entry) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Before calling removeUnchecked for a given transaction, * UpdateForRemoveFromMempool must be called on the entire (dependent) set * of transactions being removed at the same time. We use each * CTxMemPoolEntry's setMemPoolParents in order to walk ancestors of a given * transaction that is removed, so we can't remove intermediate transactions * in a chain before we've updated all the state for the removal. */ void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs); public: /** * visited marks a CTxMemPoolEntry as having been traversed * during the lifetime of the most recently created Epoch::Guard * and returns false if we are the first visitor, true otherwise. * * An Epoch::Guard must be held when visited is called or an assert will be * triggered. * */ bool visited(const txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) { return m_epoch.visited(it->m_epoch_marker); } bool visited(std::optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) { // verify guard even when it==nullopt assert(m_epoch.guarded()); return !it || visited(*it); } }; /** * CCoinsView that brings transactions from a mempool into view. * It does not check for spendings by memory pool transactions. * Instead, it provides access to all Coins which are either unspent in the * base CCoinsView, are outputs from any mempool transaction, or are * tracked temporarily to allow transaction dependencies in package validation. * This allows transaction replacement to work as expected, as you want to * have all inputs "available" to check signatures, and any cycles in the * dependency graph are checked directly in AcceptToMemoryPool. * It also allows you to sign a double-spend directly in * signrawtransactionwithkey and signrawtransactionwithwallet, * as long as the conflicting transaction is not yet confirmed. */ class CCoinsViewMemPool : public CCoinsViewBacked { /** * Coins made available by transactions being validated. Tracking these * allows for package validation, since we can access transaction outputs * without submitting them to mempool. */ std::unordered_map<COutPoint, Coin, SaltedOutpointHasher> m_temp_added; protected: const CTxMemPool &mempool; public: CCoinsViewMemPool(CCoinsView *baseIn, const CTxMemPool &mempoolIn); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; /** * Add the coins created by this transaction. These coins are only * temporarily stored in m_temp_added and cannot be flushed to the back end. * Only used for package validation. */ void PackageAddTransaction(const CTransactionRef &tx); }; /** * DisconnectedBlockTransactions * * During the reorg, it's desirable to re-add previously confirmed transactions * to the mempool, so that anything not re-confirmed in the new chain is * available to be mined. However, it's more efficient to wait until the reorg * is complete and process all still-unconfirmed transactions at that time, * since we expect most confirmed transactions to (typically) still be * confirmed in the new chain, and re-accepting to the memory pool is expensive * (and therefore better to not do in the middle of reorg-processing). * Instead, store the disconnected transactions (in order!) as we go, remove any * that are included in blocks in the new chain, and then process the remaining * still-unconfirmed transactions at the end. * * It also enables efficient reprocessing of current mempool entries, useful * when (de)activating forks that result in in-mempool transactions becoming * invalid */ // multi_index tag names struct txid_index {}; struct insertion_order {}; class DisconnectedBlockTransactions { private: typedef boost::multi_index_container< CTransactionRef, boost::multi_index::indexed_by< // hashed by txid boost::multi_index::hashed_unique< boost::multi_index::tag<txid_index>, mempoolentry_txid, SaltedTxIdHasher>, // sorted by order in the blockchain boost::multi_index::sequenced< boost::multi_index::tag<insertion_order>>>> indexed_disconnected_transactions; indexed_disconnected_transactions queuedTx; uint64_t cachedInnerUsage = 0; struct TxInfo { const std::chrono::seconds time; const Amount feeDelta; const unsigned height; }; using TxInfoMap = std::unordered_map<TxId, TxInfo, SaltedTxIdHasher>; /// populated by importMempool(); the original tx entry times and feeDeltas TxInfoMap txInfo; void addTransaction(const CTransactionRef &tx) { queuedTx.insert(tx); cachedInnerUsage += RecursiveDynamicUsage(tx); } /// @returns a pointer into the txInfo map if tx->GetId() exists in the map, /// or nullptr otherwise. Note that the returned pointer is only valid for /// as long as its underlying map node is valid. const TxInfo *getTxInfo(const CTransactionRef &tx) const; public: // It's almost certainly a logic bug if we don't clear out queuedTx before // destruction, as we add to it while disconnecting blocks, and then we // need to re-process remaining transactions to ensure mempool consistency. // For now, assert() that we've emptied out this object on destruction. // This assert() can always be removed if the reorg-processing code were // to be refactored such that this assumption is no longer true (for // instance if there was some other way we cleaned up the mempool after a // reorg, besides draining this object). ~DisconnectedBlockTransactions() { assert(queuedTx.empty()); } // Estimate the overhead of queuedTx to be 6 pointers + an allocation, as // no exact formula for boost::multi_index_contained is implemented. size_t DynamicMemoryUsage() const { return memusage::MallocUsage(sizeof(CTransactionRef) + 6 * sizeof(void *)) * queuedTx.size() + memusage::DynamicUsage(txInfo) + cachedInnerUsage; } const indexed_disconnected_transactions &GetQueuedTx() const { return queuedTx; } // Import mempool entries in topological order into queuedTx and clear the // mempool. Caller should call updateMempoolForReorg to reprocess these // transactions void importMempool(CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs); // Add entries for a block while reconstructing the topological ordering so // they can be added back to the mempool simply. void addForBlock(const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs); // Remove entries based on txid_index, and update memory usage. void removeForBlock(const std::vector<CTransactionRef> &vtx) { // Short-circuit in the common case of a block being added to the tip if (queuedTx.empty()) { return; } for (auto const &tx : vtx) { auto it = queuedTx.find(tx->GetId()); if (it != queuedTx.end()) { cachedInnerUsage -= RecursiveDynamicUsage(*it); queuedTx.erase(it); txInfo.erase(tx->GetId()); } } } // Remove an entry by insertion_order index, and update memory usage. void removeEntry(indexed_disconnected_transactions::index< insertion_order>::type::iterator entry) { cachedInnerUsage -= RecursiveDynamicUsage(*entry); txInfo.erase((*entry)->GetId()); queuedTx.get<insertion_order>().erase(entry); } bool isEmpty() const { return queuedTx.empty(); } void clear() { cachedInnerUsage = 0; queuedTx.clear(); txInfo.clear(); } /** * Make mempool consistent after a reorg, by re-adding or recursively * erasing disconnected block transactions from the mempool, and also * removing any other transactions from the mempool that are no longer valid * given the new tip/height. * * Note: we assume that disconnectpool only contains transactions that are * NOT confirmed in the current chain nor already in the mempool (otherwise, * in-mempool descendants of such transactions would be removed). * * Passing fAddToMempool=false will skip trying to add the transactions * back, and instead just erase from the mempool as needed. */ void updateMempoolForReorg(const Config &config, Chainstate &active_chainstate, bool fAddToMempool, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs); }; #endif // BITCOIN_TXMEMPOOL_H diff --git a/src/validation.cpp b/src/validation.cpp index 0ce54f8c7..e4c43d1c7 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1,6157 +1,6169 @@ // 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 <blockvalidity.h> #include <chainparams.h> #include <checkpoints.h> #include <checkqueue.h> #include <config.h> #include <consensus/activation.h> #include <consensus/amount.h> #include <consensus/merkle.h> #include <consensus/tx_check.h> #include <consensus/tx_verify.h> #include <consensus/validation.h> #include <deploymentstatus.h> #include <hash.h> #include <index/blockfilterindex.h> #include <logging.h> #include <logging/timer.h> #include <minerfund.h> #include <node/blockstorage.h> #include <node/coinstats.h> #include <node/ui_interface.h> #include <node/utxo_snapshot.h> #include <policy/mempool.h> #include <policy/policy.h> #include <policy/settings.h> #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/strencodings.h> #include <util/system.h> #include <util/trace.h> #include <util/translation.h> #include <validationinterface.h> #include <warnings.h> #include <boost/algorithm/string/replace.hpp> #include <algorithm> +#include <atomic> #include <numeric> #include <optional> #include <string> #include <thread> using node::BLOCKFILE_CHUNK_SIZE; using node::BlockManager; using node::BlockMap; using node::CCoinsStats; using node::CoinStatsHashType; using node::fImporting; using node::fPruneMode; using node::fReindex; using node::GetUTXOStats; using node::nPruneTarget; using node::OpenBlockFile; using node::ReadBlockFromDisk; using node::SnapshotMetadata; using node::UNDOFILE_CHUNK_SIZE; using node::UndoReadFromDisk; using node::UnlinkPrunedFiles; #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", }; /** * 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; Mutex g_best_block_mutex; std::condition_variable g_best_block_cv; uint256 g_best_block; bool fRequireStandard = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; BlockHash hashAssumeValid; arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB); BlockValidationOptions::BlockValidationOptions(const Config &config) : excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true), checkMerkleRoot(true) {} const CBlockIndex * Chainstate::FindForkInGlobalIndex(const CBlockLocator &locator) const { 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) { const CBlockIndex *pindex{m_blockman.LookupBlockIndex(hash)}; if (pindex) { if (m_chain.Contains(pindex)) { return pindex; } if (pindex->GetAncestor(m_chain.Height()) == m_chain.Tip()) { return m_chain.Tip(); } } } return m_chain.Genesis(); } static uint32_t GetNextBlockScriptFlags(const Consensus::Params ¶ms, const CBlockIndex *pindex); bool CheckSequenceLocksAtTip(CBlockIndex *tip, const CCoinsView &coins_view, const CTransaction &tx, LockPoints *lp, bool useExistingLockPoints) { assert(tip != nullptr); CBlockIndex index; index.pprev = tip; // CheckSequenceLocksAtTip() uses active_chainstate.m_chain.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 active_chainstate.m_chain.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 { 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 (!coins_view.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, STANDARD_LOCKTIME_VERIFY_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 // CheckSequenceLocksAtTip 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.GetIntArg("-replayprotectionactivationtime", params.wellingtonActivationTime); } static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { if (pindexPrev == nullptr) { return false; } return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast()); } /** * Checks to avoid mempool polluting consensus critical paths since cached * signature and script validity results will be reused if we validate this * transaction again during block validation. */ static bool CheckInputsFromMempoolAndCache( const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags, PrecomputedTransactionData &txdata, int &nSigChecksOut, CCoinsViewCache &coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) { AssertLockHeld(cs_main); AssertLockHeld(pool.cs); assert(!tx.IsCoinBase()); for (const CTxIn &txin : tx.vin) { const Coin &coin = view.AccessCoin(txin.prevout); // This coin was checked in PreChecks and MemPoolAccept // has been holding cs_main since then. Assume(!coin.IsSpent()); if (coin.IsSpent()) { return false; } // If the Coin is available, there are 2 possibilities: // it is available in our current ChainstateActive UTXO set, // or it's a UTXO provided by a transaction in our mempool. // Ensure the scriptPubKeys in Coins from CoinsView are correct. 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 &coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout); assert(!coinFromUTXOSet.IsSpent()); assert(coinFromUTXOSet.GetTxOut() == coin.GetTxOut()); } } // Call CheckInputScripts() to cache signature and script validity against // current tip consensus rules. return CheckInputScripts(tx, state, view, flags, /*sigCacheStore=*/true, /*scriptCacheStore=*/true, txdata, nSigChecksOut); } namespace { class MemPoolAccept { public: MemPoolAccept(CTxMemPool &mempool, Chainstate &active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate), m_limit_ancestors( gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)), m_limit_ancestor_size(gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000), m_limit_descendants(gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)), m_limit_descendant_size( gArgs.GetIntArg("-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; 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; const unsigned int m_heightOverride; /** * When true, the mempool will not be trimmed when individual * transactions are submitted in Finalize(). Instead, limits should be * enforced at the end to ensure the package is not partially submitted. */ const bool m_package_submission; /** Parameters for single transaction mempool validation. */ static ATMPArgs SingleAccept(const Config &config, int64_t accept_time, bool bypass_limits, std::vector<COutPoint> &coins_to_uncache, bool test_accept, unsigned int heightOverride) { return ATMPArgs{config, accept_time, bypass_limits, coins_to_uncache, test_accept, heightOverride, /*m_package_submission=*/false}; } /** * Parameters for test package mempool validation through * testmempoolaccept. */ static ATMPArgs PackageTestAccept(const Config &config, int64_t accept_time, std::vector<COutPoint> &coins_to_uncache) { return ATMPArgs{config, accept_time, /*m_bypass_limits=*/false, coins_to_uncache, /*m_test_accept=*/true, /*m_heightOverride=*/0, // not submitting to mempool /*m_package_submission=*/false}; } /** Parameters for child-with-unconfirmed-parents package validation. */ static ATMPArgs PackageChildWithParents(const Config &config, int64_t accept_time, std::vector<COutPoint> &coins_to_uncache) { return ATMPArgs{config, accept_time, /*m_bypass_limits=*/false, coins_to_uncache, /*m_test_accept=*/false, /*m_heightOverride=*/0, /*m_package_submission=*/true}; } // No default ctor to avoid exposing details to clients and allowing the // possibility of mixing up the order of the arguments. Use static // functions above instead. ATMPArgs() = delete; }; // Single transaction acceptance MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef &ptx, ATMPArgs &args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Multiple transaction acceptance. Transactions may or may not be * interdependent, but must not conflict with each other, and the * transactions cannot already be in the mempool. Parents must come * before children if any dependencies exist. */ PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef> &txns, ATMPArgs &args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Package (more specific than just multiple transactions) acceptance. * Package must be a child with all of its unconfirmed parents, and * topologically sorted. */ PackageMempoolAcceptResult AcceptPackage(const Package &package, 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) { } /** All mempool ancestors of this transaction. */ CTxMemPool::setEntries m_ancestors; /** * Mempool entry constructed for this transaction. * Constructed in PreChecks() but not inserted into the mempool until * Finalize(). */ std::unique_ptr<CTxMemPoolEntry> m_entry; /** * Virtual size of the transaction as used by the mempool, calculated * using serialized size of the transaction and sigchecks. */ int64_t m_vsize; /** * Fees paid by this transaction: total input amounts subtracted by * total output amounts. */ Amount m_base_fees; /** * Base fees + any fee delta set by the user with * prioritisetransaction. */ Amount m_modified_fees; const CTransactionRef &m_ptx; TxValidationState m_state; /** * A temporary cache containing serialized transaction data for * signature verification. * Reused across PreChecks and ConsensusScriptChecks. */ PrecomputedTransactionData m_precomputed_txdata; // 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); // Enforce package mempool ancestor/descendant limits (distinct from // individual ancestor/descendant limits done in PreChecks). bool PackageMempoolChecks(const std::vector<CTransactionRef> &txns, PackageValidationState &package_state) 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(const ATMPArgs &args, Workspace &ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // 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(const ATMPArgs &args, Workspace &ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Submit all transactions to the mempool and call ConsensusScriptChecks to // add to the script cache - should only be called after successful // validation of all transactions in the package. // The package may end up partially-submitted after size limiting; // returns true if all transactions are successfully added to the mempool, // false otherwise. bool SubmitPackage(const ATMPArgs &args, std::vector<Workspace> &workspaces, PackageValidationState &package_state, std::map<const TxId, const MempoolAcceptResult> &results) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); private: CTxMemPool &m_pool; CCoinsViewCache m_view; CCoinsViewMemPool m_viewmempool; CCoinsView m_dummy; Chainstate &m_active_chainstate; // 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) { AssertLockHeld(cs_main); AssertLockHeld(m_pool.cs); 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 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; const unsigned int heightOverride = args.m_heightOverride; // Alias what we need out of ws TxValidationState &state = ws.m_state; std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; // 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( m_active_chainstate.m_chain.Tip(), args.m_config.GetChainParams().GetConsensus(), tx, ctxState)) { // 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); const CCoinsViewCache &coins_cache = m_active_chainstate.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); assert(m_active_chainstate.m_blockman.LookupBlockIndex( m_view.GetBestBlock()) == m_active_chainstate.m_chain.Tip()); // 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. // Pass in m_view which has all of the relevant inputs cached. Note that, // since m_view's backend was removed, it no longer pulls coins from the // mempool. if (!CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx, &lp)) { return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final"); } // The mempool holds txs for the next block, so pass height+1 to // CheckTxInputs if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) { // state filled in by CheckTxInputs return false; } // 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"); } // ws.m_modified_fess includes any fee deltas from PrioritiseTransaction ws.m_modified_fees = ws.m_base_fees; m_pool.ApplyDelta(txid, ws.m_modified_fees); // 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 && ws.m_modified_fees < minRelayTxFee.GetFee(nSize)) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", ws.m_modified_fees, ::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; ws.m_precomputed_txdata = PrecomputedTransactionData{tx}; if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, ws.m_sig_checks_standard)) { // State filled in by CheckInputScripts return false; } entry.reset(new CTxMemPoolEntry( ptx, ws.m_base_fees, nAcceptTime, heightOverride ? heightOverride : m_active_chainstate.m_chain.Height(), fSpendsCoinbase, ws.m_sig_checks_standard, lp)); ws.m_vsize = entry->GetTxVirtualSize(); Amount mempoolRejectFee = m_pool .GetMinFee( gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000) .GetFee(ws.m_vsize); if (!bypass_limits && mempoolRejectFee > Amount::zero() && ws.m_modified_fees < mempoolRejectFee) { return state.Invalid( TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", ws.m_modified_fees, mempoolRejectFee)); } // Calculate in-mempool ancestors, up to a limit. std::string errString; if (!m_pool.CalculateMemPoolAncestors( *entry, ws.m_ancestors, 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::PackageMempoolChecks( const std::vector<CTransactionRef> &txns, PackageValidationState &package_state) { AssertLockHeld(cs_main); AssertLockHeld(m_pool.cs); // CheckPackageLimits expects the package transactions to not already be in // the mempool. assert(std::all_of(txns.cbegin(), txns.cend(), [this](const auto &tx) { return !m_pool.exists(tx->GetId()); })); std::string err_string; if (!m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, err_string)) { // This is a package-wide error, separate from an individual transaction // error. return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string); } return true; } bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs &args, Workspace &ws) { AssertLockHeld(cs_main); AssertLockHeld(m_pool.cs); const CTransaction &tx = *ws.m_ptx; const TxId &txid = tx.GetId(); TxValidationState &state = ws.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, ws.m_precomputed_txdata, nSigChecksConsensus, m_active_chainstate.CoinsTip())) { // 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). LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against " "latest-block but not STANDARD flags %s, %s\n", txid.ToString(), state.ToString()); return Assume(false); } 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(const ATMPArgs &args, Workspace &ws) { AssertLockHeld(cs_main); AssertLockHeld(m_pool.cs); const TxId &txid = ws.m_ptx->GetId(); TxValidationState &state = ws.m_state; const bool bypass_limits = args.m_bypass_limits; std::unique_ptr<CTxMemPoolEntry> &entry = ws.m_entry; // Store transaction in memory. m_pool.addUnchecked(*entry, ws.m_ancestors); // Trim mempool and check if tx was trimmed. // If we are validating a package, don't trim here because we could evict a // previous transaction in the package. LimitMempoolSize() should be called // at the very end to make sure the mempool is still within limits and // package submission happens atomically. if (!args.m_package_submission && !bypass_limits) { m_pool.LimitSize( m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{ gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); if (!m_pool.exists(txid)) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full"); } } return true; } bool MemPoolAccept::SubmitPackage( const ATMPArgs &args, std::vector<Workspace> &workspaces, PackageValidationState &package_state, std::map<const TxId, const MempoolAcceptResult> &results) { AssertLockHeld(cs_main); AssertLockHeld(m_pool.cs); // Sanity check: none of the transactions should be in the mempool. assert(std::all_of( workspaces.cbegin(), workspaces.cend(), [this](const auto &ws) { return !m_pool.exists(ws.m_ptx->GetId()); })); bool all_submitted = true; // ConsensusScriptChecks adds to the script cache and is therefore // consensus-critical; CheckInputsFromMempoolAndCache asserts that // transactions only spend coins available from the mempool or UTXO set. // Submit each transaction to the mempool immediately after calling // ConsensusScriptChecks to make the outputs available for subsequent // transactions. for (Workspace &ws : workspaces) { if (!ConsensusScriptChecks(args, ws)) { results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Failure(ws.m_state)); // Since PreChecks() passed, this should never fail. all_submitted = Assume(false); } // Re-calculate mempool ancestors to call addUnchecked(). They may have // changed since the last calculation done in PreChecks, since package // ancestors have already been submitted. std::string unused_err_string; if (!m_pool.CalculateMemPoolAncestors( *ws.m_entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, unused_err_string)) { results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Failure(ws.m_state)); // Since PreChecks() and PackageMempoolChecks() both enforce limits, // this should never fail. all_submitted = Assume(false); } // If we call LimitMempoolSize() for each individual Finalize(), the // mempool will not take the transaction's descendant feerate into // account because it hasn't seen them yet. Also, we risk evicting a // transaction that a subsequent package transaction depends on. // Instead, allow the mempool to temporarily bypass limits, the maximum // package size) while submitting transactions individually and then // trim at the very end. if (!Finalize(args, ws)) { results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Failure(ws.m_state)); // Since LimitMempoolSize() won't be called, this should never fail. all_submitted = Assume(false); } } // It may or may not be the case that all the transactions made it into the // mempool. Regardless, make sure we haven't exceeded max mempool size. m_pool.LimitSize( m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{ gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); if (!all_submitted) { return false; } // Find the txids of the transactions that made it into the mempool. Allow // partial submission, but don't report success unless they all made it into // the mempool. for (Workspace &ws : workspaces) { if (m_pool.exists(ws.m_ptx->GetId())) { results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Success( ws.m_vsize, ws.m_base_fees)); GetMainSignals().TransactionAddedToMempool( ws.m_ptx, m_pool.GetAndIncrementSequence()); } else { all_submitted = false; ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full"); results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Failure(ws.m_state)); } } return all_submitted; } MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef &ptx, ATMPArgs &args) { AssertLockHeld(cs_main); // mempool "read lock" (held through // GetMainSignals().TransactionAddedToMempool()) LOCK(m_pool.cs); - Workspace ws(ptx, GetNextBlockScriptFlags( - args.m_config.GetChainParams().GetConsensus(), - m_active_chainstate.m_chain.Tip())); + const Consensus::Params &consensusParams = + args.m_config.GetChainParams().GetConsensus(); + const CBlockIndex *tip = m_active_chainstate.m_chain.Tip(); + + // After wellington we will stop computing the ancestors and descendant + // limits, and stop enforcing them. Because the mempool has no idea what the + // current tip is (and it should not), we will use an activation latch so it + // knows when the statistics can be skipped. This also makes an hypothetical + // reorg event easier to handle. + // Note that |= operator is not defined for atomic bool ! + m_pool.wellingtonLatched = + m_pool.wellingtonLatched || IsWellingtonEnabled(consensusParams, tip); + + Workspace ws(ptx, GetNextBlockScriptFlags(consensusParams, tip)); // Perform the inexpensive checks first and avoid hashing and signature // verification unless those checks pass, to mitigate CPU exhaustion // denial-of-service attacks. if (!PreChecks(args, ws)) { return MempoolAcceptResult::Failure(ws.m_state); } if (!ConsensusScriptChecks(args, ws)) { return MempoolAcceptResult::Failure(ws.m_state); } const TxId txid = ptx->GetId(); // Mempool sanity check -- in our new mempool no tx can be added if its // outputs are already spent in the mempool (that is, no children before // parents allowed; the mempool must be consistent at all times). // // This means that on reorg, the disconnectpool *must* always import // the existing mempool tx's, clear the mempool, and then re-add // remaining tx's in topological order via this function. Our new mempool // has fast adds, so this is ok. if (auto it = m_pool.mapNextTx.lower_bound(COutPoint{txid, 0}); it != m_pool.mapNextTx.end() && it->first->GetTxId() == txid) { LogPrintf("%s: BUG! PLEASE REPORT THIS! Attempt to add txid %s, but " "its outputs are already spent in the " "mempool\n", __func__, txid.ToString()); ws.m_state.Invalid(TxValidationResult::TX_CONFLICT, "txn-mempool-conflict"); return MempoolAcceptResult::Failure(ws.m_state); } // Tx was accepted, but not added if (args.m_test_accept) { return MempoolAcceptResult::Success(ws.m_vsize, ws.m_base_fees); } if (!Finalize(args, ws)) { return MempoolAcceptResult::Failure(ws.m_state); } GetMainSignals().TransactionAddedToMempool( ptx, m_pool.GetAndIncrementSequence()); return MempoolAcceptResult::Success(ws.m_vsize, ws.m_base_fees); } PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions( const std::vector<CTransactionRef> &txns, ATMPArgs &args) { AssertLockHeld(cs_main); // These context-free package limits can be done before taking the mempool // lock. PackageValidationState package_state; if (!CheckPackage(txns, package_state)) { return PackageMempoolAcceptResult(package_state, {}); } std::vector<Workspace> workspaces{}; workspaces.reserve(txns.size()); std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces), [&args, this](const auto &tx) { return Workspace( tx, GetNextBlockScriptFlags( args.m_config.GetChainParams().GetConsensus(), m_active_chainstate.m_chain.Tip())); }); std::map<const TxId, const MempoolAcceptResult> results; LOCK(m_pool.cs); // Do all PreChecks first and fail fast to avoid running expensive script // checks when unnecessary. for (Workspace &ws : workspaces) { if (!PreChecks(args, ws)) { package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed"); // Exit early to avoid doing pointless work. Update the failed tx // result; the rest are unfinished. results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Failure(ws.m_state)); return PackageMempoolAcceptResult(package_state, std::move(results)); } // Make the coins created by this transaction available for subsequent // transactions in the package to spend. m_viewmempool.PackageAddTransaction(ws.m_ptx); if (args.m_test_accept) { // When test_accept=true, transactions that pass PreChecks // are valid because there are no further mempool checks (passing // PreChecks implies passing ConsensusScriptChecks). results.emplace(ws.m_ptx->GetId(), MempoolAcceptResult::Success( ws.m_vsize, ws.m_base_fees)); } } // Apply package mempool ancestor/descendant limits. Skip if there is only // one transaction, because it's unnecessary. Also, CPFP carve out can // increase the limit for individual transactions, but this exemption is // not extended to packages in CheckPackageLimits(). std::string err_string; if (txns.size() > 1 && !PackageMempoolChecks(txns, package_state)) { return PackageMempoolAcceptResult(package_state, std::move(results)); } if (args.m_test_accept) { return PackageMempoolAcceptResult(package_state, std::move(results)); } if (!SubmitPackage(args, workspaces, package_state, results)) { package_state.Invalid(PackageValidationResult::PCKG_TX, "submission failed"); return PackageMempoolAcceptResult(package_state, std::move(results)); } return PackageMempoolAcceptResult(package_state, std::move(results)); } PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package &package, ATMPArgs &args) { AssertLockHeld(cs_main); PackageValidationState package_state; // Check that the package is well-formed. If it isn't, we won't try to // validate any of the transactions and thus won't return any // MempoolAcceptResults, just a package-wide error. // Context-free package checks. if (!CheckPackage(package, package_state)) { return PackageMempoolAcceptResult(package_state, {}); } // All transactions in the package must be a parent of the last transaction. // This is just an opportunity for us to fail fast on a context-free check // without taking the mempool lock. if (!IsChildWithParents(package)) { package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-child-with-parents"); return PackageMempoolAcceptResult(package_state, {}); } // IsChildWithParents() guarantees the package is > 1 transactions. assert(package.size() > 1); // The package must be 1 child with all of its unconfirmed parents. The // package is expected to be sorted, so the last transaction is the child. const auto &child = package.back(); std::unordered_set<TxId, SaltedTxIdHasher> unconfirmed_parent_txids; std::transform( package.cbegin(), package.cend() - 1, std::inserter(unconfirmed_parent_txids, unconfirmed_parent_txids.end()), [](const auto &tx) { return tx->GetId(); }); // All child inputs must refer to a preceding package transaction or a // confirmed UTXO. The only way to verify this is to look up the child's // inputs in our current coins view (not including mempool), and enforce // that all parents not present in the package be available at chain tip. // Since this check can bring new coins into the coins cache, keep track of // these coins and uncache them if we don't end up submitting this package // to the mempool. const CCoinsViewCache &coins_tip_cache = m_active_chainstate.CoinsTip(); for (const auto &input : child->vin) { if (!coins_tip_cache.HaveCoinInCache(input.prevout)) { args.m_coins_to_uncache.push_back(input.prevout); } } // Using the MemPoolAccept m_view cache allows us to look up these same // coins faster later. This should be connecting directly to CoinsTip, not // to m_viewmempool, because we specifically require inputs to be confirmed // if they aren't in the package. m_view.SetBackend(m_active_chainstate.CoinsTip()); const auto package_or_confirmed = [this, &unconfirmed_parent_txids]( const auto &input) { return unconfirmed_parent_txids.count(input.prevout.GetTxId()) > 0 || m_view.HaveCoin(input.prevout); }; if (!std::all_of(child->vin.cbegin(), child->vin.cend(), package_or_confirmed)) { package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-child-with-unconfirmed-parents"); return PackageMempoolAcceptResult(package_state, {}); } // Protect against bugs where we pull more inputs from disk that miss being // added to coins_to_uncache. The backend will be connected again when // needed in PreChecks. m_view.SetBackend(m_dummy); LOCK(m_pool.cs); std::map<const TxId, const MempoolAcceptResult> results; // Node operators are free to set their mempool policies however they // please, nodes may receive transactions in different orders, and malicious // counterparties may try to take advantage of policy differences to pin or // delay propagation of transactions. As such, it's possible for some // package transaction(s) to already be in the mempool, and we don't want to // reject the entire package in that case (as that could be a censorship // vector). De-duplicate the transactions that are already in the mempool, // and only call AcceptMultipleTransactions() with the new transactions. // This ensures we don't double-count transaction counts and sizes when // checking ancestor/descendant limits, or double-count transaction fees for // fee-related policy. std::vector<CTransactionRef> txns_new; for (const auto &tx : package) { const auto &txid = tx->GetId(); // An already confirmed tx is treated as one not in mempool, because all // we know is that the inputs aren't available. if (m_pool.exists(txid)) { // Exact transaction already exists in the mempool. auto iter = m_pool.GetIter(txid); assert(iter != std::nullopt); results.emplace( txid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee())); } else { // Transaction does not already exist in the mempool. txns_new.push_back(tx); } } // Nothing to do if the entire package has already been submitted. if (txns_new.empty()) { return PackageMempoolAcceptResult(package_state, std::move(results)); } // Validate the (deduplicated) transactions as a package. auto submission_result = AcceptMultipleTransactions(txns_new, args); // Include already-in-mempool transaction results in the final result. for (const auto &[txid, mempoolaccept_res] : results) { submission_result.m_tx_results.emplace(txid, mempoolaccept_res); } return submission_result; } } // namespace MempoolAcceptResult AcceptToMemoryPool(const Config &config, Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept, unsigned int heightOverride) { AssertLockHeld(::cs_main); assert(active_chainstate.GetMempool() != nullptr); CTxMemPool &pool{*active_chainstate.GetMempool()}; std::vector<COutPoint> coins_to_uncache; auto args = MemPoolAccept::ATMPArgs::SingleAccept( config, accept_time, bypass_limits, coins_to_uncache, test_accept, heightOverride); const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate) .AcceptSingleTransaction(tx, args); if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { // 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) { active_chainstate.CoinsTip().Uncache(outpoint); } } // After we've (potentially) uncached entries, ensure our coins cache is // still within its size limits BlockValidationState stateDummy; active_chainstate.FlushStateToDisk(stateDummy, FlushStateMode::PERIODIC); return result; } PackageMempoolAcceptResult ProcessNewPackage(const Config &config, Chainstate &active_chainstate, CTxMemPool &pool, const Package &package, bool test_accept) { AssertLockHeld(cs_main); assert(!package.empty()); assert(std::all_of(package.cbegin(), package.cend(), [](const auto &tx) { return tx != nullptr; })); std::vector<COutPoint> coins_to_uncache; const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (test_accept) { auto args = MemPoolAccept::ATMPArgs::PackageTestAccept( config, GetTime(), coins_to_uncache); return MemPoolAccept(pool, active_chainstate) .AcceptMultipleTransactions(package, args); } else { auto args = MemPoolAccept::ATMPArgs::PackageChildWithParents( config, GetTime(), coins_to_uncache); return MemPoolAccept(pool, active_chainstate) .AcceptPackage(package, args); } }(); // Uncache coins pertaining to transactions that were not submitted to the // mempool. if (test_accept || result.m_state.IsInvalid()) { for (const COutPoint &hashTx : coins_to_uncache) { active_chainstate.CoinsTip().Uncache(hashTx); } } // Ensure the coins cache is still within limits. BlockValidationState state_dummy; active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); return result; } 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(gArgs.GetDataDirNet() / ldb_name, cache_size_bytes, in_memory, should_wipe), m_catcherview(&m_dbview) {} void CoinsViews::InitCache() { AssertLockHeld(::cs_main); m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview); } Chainstate::Chainstate(CTxMemPool *mempool, BlockManager &blockman, ChainstateManager &chainman, std::optional<BlockHash> from_snapshot_blockhash) : m_mempool(mempool), m_blockman(blockman), m_params(::Params()), m_chainman(chainman), m_from_snapshot_blockhash(from_snapshot_blockhash) { } void Chainstate::InitCoinsDB(size_t cache_size_bytes, bool in_memory, bool should_wipe, std::string leveldb_name) { if (m_from_snapshot_blockhash) { leveldb_name += "_" + m_from_snapshot_blockhash->ToString(); } m_coins_views = std::make_unique<CoinsViews>(leveldb_name, cache_size_bytes, in_memory, should_wipe); } void Chainstate::InitCoinsCache(size_t cache_size_bytes) { AssertLockHeld(::cs_main); 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 Chainstate*`) can call it. // bool Chainstate::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 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 } void Chainstate::CheckForkWarningConditions() { 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 (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 (m_best_fork_tip && m_chain.Height() - m_best_fork_tip->nHeight >= 72) { m_best_fork_tip = nullptr; } if (m_best_fork_tip || (m_chainman.m_best_invalid && m_chainman.m_best_invalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6))) { if (!GetfLargeWorkForkFound() && m_best_fork_base) { std::string warning = std::string("'Warning: Large-work fork detected, forking after " "block ") + m_best_fork_base->phashBlock->ToString() + std::string("'"); AlertNotify(warning); } if (m_best_fork_tip && m_best_fork_base) { 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__, m_best_fork_base->nHeight, m_best_fork_base->phashBlock->ToString(), m_best_fork_tip->nHeight, m_best_fork_tip->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); } } void Chainstate::CheckForkWarningConditionsOnNewFork( CBlockIndex *pindexNewForkTip) { AssertLockHeld(cs_main); // If we are on a fork that is sufficiently large, set a warning flag. const CBlockIndex *pfork = m_chain.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 && (!m_best_fork_tip || pindexNewForkTip->nHeight > m_best_fork_tip->nHeight) && pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && m_chain.Height() - pindexNewForkTip->nHeight < 72) { m_best_fork_tip = pindexNewForkTip; m_best_fork_base = pfork; } CheckForkWarningConditions(); } // Called both upon regular invalid block discovery *and* InvalidateBlock void Chainstate::InvalidChainFound(CBlockIndex *pindexNew) { AssertLockHeld(cs_main); if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) { m_chainman.m_best_invalid = pindexNew; } if (m_chainman.m_best_header != nullptr && m_chainman.m_best_header->GetAncestor(pindexNew->nHeight) == pindexNew) { m_chainman.m_best_header = m_chain.Tip(); } // If the invalid chain found is supposed to be finalized, we need to move // back the finalization point. if (IsBlockAvalancheFinalized(pindexNew)) { LOCK(cs_avalancheFinalizedBlockIndex); m_avalancheFinalizedBlockIndex = 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 = m_chain.Tip(); assert(tip); LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n", __func__, tip->GetBlockHash().ToString(), m_chain.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 Chainstate::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) { AssertLockHeld(cs_main); if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus = pindex->nStatus.withFailed(); m_chainman.m_failed_blocks.insert(pindex); m_blockman.m_dirty_blockindex.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); } 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; } 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; } bool AbortNode(BlockValidationState &state, const std::string &strMessage, const bilingual_str &userMessage) { 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 Chainstate::DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view) { AssertLockHeld(::cs_main); 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 CCheckQueue<CScriptCheck> scriptcheckqueue(128); void StartScriptCheckWorkerThreads(int threads_num) { scriptcheckqueue.StartWorkerThreads(threads_num); } void StopScriptCheckWorkerThreads() { scriptcheckqueue.StopWorkerThreads(); } // 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; // Enforce P2SH (BIP16) if (DeploymentActiveAfter(pindex, params, Consensus::DEPLOYMENT_P2SH)) { flags |= SCRIPT_VERIFY_P2SH; } // Enforce the DERSIG (BIP66) rule. if (DeploymentActiveAfter(pindex, params, Consensus::DEPLOYMENT_DERSIG)) { flags |= SCRIPT_VERIFY_DERSIG; } // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule. if (DeploymentActiveAfter(pindex, params, Consensus::DEPLOYMENT_CLTV)) { flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } // Start enforcing CSV (BIP68, BIP112 and BIP113) rule. if (DeploymentActiveAfter(pindex, params, Consensus::DEPLOYMENT_CSV)) { 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_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 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 Chainstate::ConnectBlock(const CBlock &block, BlockValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, BlockValidationOptions options, bool fJustCheck) { AssertLockHeld(cs_main); assert(pindex); const BlockHash block_hash{block.GetHash()}; assert(*pindex->phashBlock == block_hash); int64_t nTimeStart = GetTimeMicros(); const Consensus::Params &consensusParams = m_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_hash == 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 && m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex && m_chainman.m_best_header->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( *m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, 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 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"); } } } } // Enforce BIP68 (sequence locks). int nLockTimeFlags = 0; if (DeploymentActiveAt(*pindex, consensusParams, Consensus::DEPLOYMENT_CSV)) { 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; // nSigChecksRet may be accurate (found in cache) or 0 (checks were // deferred into vChecks). int nSigChecksRet; 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; 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); const 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"); } if (!CheckMinerFund(consensusParams, pindex->pprev, block.vtx[0]->vout, blockReward)) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-minerfund"); } 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 (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) { return false; } if (!pindex->IsValid(BlockValidity::SCRIPTS)) { pindex->RaiseValidity(BlockValidity::SCRIPTS); m_blockman.m_dirty_blockindex.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); TRACE6(validation, block_connected, block_hash.data(), pindex->nHeight, block.vtx.size(), nInputs, nSigChecksRet, // in microseconds (µs) nTime5 - nTimeStart); return true; } CoinsCacheSizeState Chainstate::GetCoinsCacheSizeState() { AssertLockHeld(::cs_main); return this->GetCoinsCacheSizeState( m_coinstip_cache_size_bytes, gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); } CoinsCacheSizeState Chainstate::GetCoinsCacheSizeState(size_t max_coins_cache_size_bytes, size_t max_mempool_size_bytes) { AssertLockHeld(::cs_main); int64_t nMempoolUsage = m_mempool ? m_mempool->DynamicMemoryUsage() : 0; int64_t cacheSize = CoinsTip().DynamicMemoryUsage(); int64_t nTotalSpace = max_coins_cache_size_bytes + std::max<int64_t>(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 Chainstate::FlushStateToDisk(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(); LOCK(m_blockman.cs_LastBlockFile); if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) { // Make sure we don't prune above the blockfilterindexes // bestblocks. Pruning is height-based. int last_prune = m_chain.Height(); ForEachBlockFilterIndex([&](BlockFilterIndex &index) { last_prune = std::max( 1, std::min(last_prune, index.GetSummary().best_block_height)); }); if (nManualPruneHeight > 0) { LOG_TIME_MILLIS_WITH_CATEGORY( "find files to prune (manual)", BCLog::BENCH); m_blockman.FindFilesToPruneManual( setFilesToPrune, std::min(last_prune, nManualPruneHeight), m_chain.Height()); } else { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH); m_blockman.FindFilesToPrune( setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload()); m_blockman.m_check_for_pruning = false; } if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!m_blockman.m_have_pruned) { m_blockman.m_block_tree_db->WriteFlag( "prunedblockfiles", true); m_blockman.m_have_pruned = 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) { // 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. m_blockman.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); if (!m_blockman.WriteBlockIndexDB()) { 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_MILLIS_WITH_CATEGORY( strprintf("write coins cache to disk (%d coins, %.2fkB)", coins_count, coins_mem_usage / 1000), BCLog::BENCH); // 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(gArgs.GetDataDirNet(), 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; } TRACE5(utxocache, flush, // in microseconds (µs) GetTimeMicros() - nNow.count(), uint32_t(mode), coins_count, uint64_t(coins_mem_usage), fFlushForPrune); } 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 Chainstate::ForceFlushStateToDisk() { BlockValidationState state; if (!this->FlushStateToDisk(state, FlushStateMode::ALWAYS)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } void Chainstate::PruneAndFlush() { BlockValidationState state; m_blockman.m_check_for_pruning = true; if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } static void UpdateTipLog(const CCoinsViewCache &coins_tip, const CBlockIndex *tip, const CChainParams ¶ms, const std::string &func_name, const std::string &prefix) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%ld " "date='%s' progress=%f cache=%.1fMiB(%utxo)\n", prefix, func_name, tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion, log(tip->nChainWork.getdouble()) / log(2.0), tip->GetChainTxCount(), FormatISO8601DateTime(tip->GetBlockTime()), GuessVerificationProgress(params.TxData(), tip), coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)), coins_tip.GetCacheSize()); } void Chainstate::UpdateTip(const CBlockIndex *pindexNew) { AssertLockHeld(::cs_main); const auto &coins_tip = CoinsTip(); // The remainder of the function isn't relevant if we are not acting on // the active chainstate, so return if need be. if (this != &m_chainman.ActiveChainstate()) { // Only log every so often so that we don't bury log messages at the // tip. constexpr int BACKGROUND_LOG_INTERVAL = 2000; if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) { UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] "); } return; } // New best block if (m_mempool) { m_mempool->AddTransactionsUpdated(1); } { LOCK(g_best_block_mutex); g_best_block = pindexNew->GetBlockHash(); g_best_block_cv.notify_all(); } UpdateTipLog(coins_tip, pindexNew, m_params, __func__, ""); } /** * 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 Chainstate::DisconnectTip(BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) { AssertLockHeld(cs_main); if (m_mempool) { AssertLockHeld(m_mempool->cs); } CBlockIndex *pindexDelete = m_chain.Tip(); const Consensus::Params &consensusParams = m_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(state, FlushStateMode::IF_NEEDED)) { return false; } if (m_mempool) { // 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); } } m_chain.SetTip(pindexDelete->pprev); UpdateTip(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; } }; /** * 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 added to connectTrace if connection succeeds. */ bool Chainstate::ConnectTip(const Config &config, BlockValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr<const CBlock> &pblock, ConnectTrace &connectTrace, DisconnectedBlockTransactions &disconnectpool) { AssertLockHeld(cs_main); if (m_mempool) { AssertLockHeld(m_mempool->cs); } const Consensus::Params &consensusParams = m_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, 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()); } 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(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.; if (m_mempool) { m_mempool->removeForBlock(blockConnecting.vtx); 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(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 *Chainstate::FindMostWorkChain( std::vector<const CBlockIndex *> &blocksToReconcile) { 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 an avalanche finalized block to be reorged, // then we park it. { LOCK(cs_avalancheFinalizedBlockIndex); if (m_avalancheFinalizedBlockIndex && !AreOnTheSameFork(pindexNew, m_avalancheFinalizedBlockIndex)) { LogPrintf("Park block %s because it forks prior to the " "avalanche finalized chaintip.\n", pindexNew->GetBlockHash().ToString()); pindexNew->nStatus = pindexNew->nStatus.withParked(); m_blockman.m_dirty_blockindex.insert(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 && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork)) { m_chainman.m_best_invalid = pindexNew; } if (fParkedChain && (m_chainman.m_best_parked == nullptr || pindexNew->nChainWork > m_chainman.m_best_parked->nChainWork)) { m_chainman.m_best_parked = 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) { blocksToReconcile.push_back(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 Chainstate::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 Chainstate::ActivateBestChainStep( const Config &config, BlockValidationState &state, CBlockIndex *pindexMostWork, const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) { AssertLockHeld(cs_main); if (m_mempool) { 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 (!fBlocksDisconnected) { // Import and clear mempool; we must do this to preserve // topological ordering in the mempool index. This is ok since // inserts into the mempool are very fast now in our new // implementation. disconnectpool.importMempool(*m_mempool); } if (!DisconnectTip(state, &disconnectpool)) { // This is likely a fatal error, but keep the mempool consistent, // just in case. Only remove from the mempool in this case. if (m_mempool) { disconnectpool.updateMempoolForReorg(config, *this, 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. if (m_mempool) { disconnectpool.updateMempoolForReorg(config, *this, 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 (m_mempool) { 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, *this, true, *m_mempool); } m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1); } // 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(Chainstate &chainstate) LOCKS_EXCLUDED(cs_main) { bool fNotify = false; bool fInitialBlockDownload = false; static CBlockIndex *pindexHeaderOld = nullptr; CBlockIndex *pindexHeader = nullptr; { LOCK(cs_main); pindexHeader = chainstate.m_chainman.m_best_header; if (pindexHeader != pindexHeaderOld) { fNotify = true; fInitialBlockDownload = chainstate.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 Chainstate::ActivateBestChain(const Config &config, BlockValidationState &state, std::shared_ptr<const CBlock> pblock) { AssertLockNotHeld(m_chainstate_mutex); // 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); // 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_chainstate_mutex to enforce // mutual exclusion so that only one caller may execute this function at a // time LOCK(m_chainstate_mutex); CBlockIndex *pindexMostWork = nullptr; CBlockIndex *pindexNewTip = nullptr; int nStopAtHeight = gArgs.GetIntArg("-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(); std::vector<const CBlockIndex *> blocksToReconcile; bool blocks_connected = false; { LOCK(cs_main); // Lock transaction pool for at least as long as it takes for // connectTrace to be consumed LOCK(MempoolMutex()); CBlockIndex *starting_tip = m_chain.Tip(); 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(blocksToReconcile); } // 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(); if (blocks_connected) { 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). for (const CBlockIndex *pindex : blocksToReconcile) { g_avalanche->addToReconcile(pindex); } if (!blocks_connected) { return true; } 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(state, FlushStateMode::PERIODIC)) { return false; } return true; } bool Chainstate::PreciousBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { AssertLockNotHeld(m_chainstate_mutex); AssertLockNotHeld(::cs_main); { 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); } namespace { // Leverage RAII to run a functor at scope end template <typename Func> struct Defer { Func func; Defer(Func &&f) : func(std::move(f)) {} ~Defer() { func(); } }; } // namespace bool Chainstate::UnwindBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex, bool invalidate) { // Genesis block can't be invalidated or parked assert(pindex); if (pindex->nHeight == 0) { return false; } CBlockIndex *to_mark_failed_or_parked = pindex; bool pindex_was_in_chain = false; int disconnected = 0; // 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_chainstate_mutex) in the Park, Invalidate and FinalizeBlock // functions due to differences in our code) AssertLockHeld(m_chainstate_mutex); // 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 (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)); } } } { 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(MempoolMutex()); constexpr int maxDisconnectPoolBlocks = 10; bool ret = false; DisconnectedBlockTransactions disconnectpool; // After 10 blocks this becomes nullptr, so that DisconnectTip will // stop giving us unwound block txs if we are doing a deep unwind. DisconnectedBlockTransactions *optDisconnectPool = &disconnectpool; // Disable thread safety analysis because we can't require m_mempool->cs // as m_mempool can be null. We keep the runtime analysis though. Defer deferred([&]() NO_THREAD_SAFETY_ANALYSIS { AssertLockHeld(cs_main); if (m_mempool && !disconnectpool.isEmpty()) { AssertLockHeld(m_mempool->cs); // DisconnectTip will add transactions to disconnectpool. // When all unwinding is done and we are on a new tip, we must // add all transactions back to the mempool against the new tip. disconnectpool.updateMempoolForReorg(config, *this, /* fAddToMempool = */ ret, *m_mempool); } }); // Disconnect (descendants of) pindex, and mark them invalid. while (true) { if (ShutdownRequested()) { break; } // Make sure the queue of validation callbacks doesn't grow // unboundedly. // FIXME this commented code is a regression and could cause OOM if // a very old block is invalidated via the invalidateblock RPC. // This can be uncommented if the main signals are moved away from // cs_main or this code is refactored so that cs_main can be // released at this point. // // LimitValidationInterfaceQueue(); if (!m_chain.Contains(pindex)) { break; } if (m_mempool && disconnected == 0) { // On first iteration, we grab all the mempool txs to preserve // topological ordering. This has the side-effect of temporarily // clearing the mempool, but we will re-add later in // updateMempoolForReorg() (above). This technique guarantees // mempool consistency as well as ensures that our topological // entry_id index is always correct. disconnectpool.importMempool(*m_mempool); } 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. ret = DisconnectTip(state, optDisconnectPool); ++disconnected; if (optDisconnectPool && disconnected > maxDisconnectPoolBlocks) { // Stop using the disconnect pool after 10 blocks. After 10 // blocks we no longer add block tx's to the disconnectpool. // However, when this scope ends we will reconcile what's // in the pool with the new tip (in the deferred d'tor above). optDisconnectPool = nullptr; } 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(); m_blockman.m_dirty_blockindex.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()); m_blockman.m_dirty_blockindex.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(); { 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(); m_blockman.m_dirty_blockindex.insert(to_mark_failed_or_parked); if (invalidate) { m_chainman.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 (auto &[_, block_index] : m_blockman.m_block_index) { if (block_index.IsValid(BlockValidity::TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) { setBlockIndexCandidates.insert(&block_index); } } 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 Chainstate::InvalidateBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { AssertLockNotHeld(m_chainstate_mutex); AssertLockNotHeld(::cs_main); // See 'Note for backport of Core PR16849' in Chainstate::UnwindBlock LOCK(m_chainstate_mutex); return UnwindBlock(config, state, pindex, true); } bool Chainstate::ParkBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { AssertLockNotHeld(m_chainstate_mutex); AssertLockNotHeld(::cs_main); // See 'Note for backport of Core PR16849' in Chainstate::UnwindBlock LOCK(m_chainstate_mutex); return UnwindBlock(config, state, pindex, false); } template <typename F> bool Chainstate::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; m_blockman.m_dirty_blockindex.insert(pindex); if (newStatus.isValid()) { m_chainman.m_failed_blocks.erase(pindex); } if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), pindex)) { setBlockIndexCandidates.insert(pindex); } return true; } return false; } template <typename F, typename C, typename AC> void Chainstate::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. for (auto &[_, block_index] : m_blockman.m_block_index) { UpdateFlagsForBlock(pindex, &block_index, fChild); UpdateFlagsForBlock(pindexDeepestChanged, &block_index, fAncestorWasChanged); } } void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) { AssertLockHeld(cs_main); UpdateFlags( pindex, m_chainman.m_best_invalid, [](const BlockStatus status) { return status.withClearedFailureFlags(); }, [](const BlockStatus status) { return status.withClearedFailureFlags(); }, [](const BlockStatus status) { return status.withFailedParent(false); }); } void Chainstate::UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren) { AssertLockHeld(cs_main); UpdateFlags( pindex, m_chainman.m_best_parked, [](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 Chainstate::UnparkBlockAndChildren(CBlockIndex *pindex) { return UnparkBlockImpl(pindex, true); } void Chainstate::UnparkBlock(CBlockIndex *pindex) { return UnparkBlockImpl(pindex, false); } bool Chainstate::AvalancheFinalizeBlock(CBlockIndex *pindex) { if (!pindex) { return false; } if (!m_chain.Contains(pindex)) { LogPrint(BCLog::AVALANCHE, "The block to mark finalized by avalanche is not on the " "active chain: %s\n", pindex->GetBlockHash().ToString()); return false; } if (IsBlockAvalancheFinalized(pindex)) { return true; } LOCK(cs_avalancheFinalizedBlockIndex); m_avalancheFinalizedBlockIndex = pindex; GetMainSignals().BlockFinalized(pindex); return true; } void Chainstate::ClearAvalancheFinalizedBlock() { LOCK(cs_avalancheFinalizedBlockIndex); m_avalancheFinalizedBlockIndex = nullptr; } bool Chainstate::IsBlockAvalancheFinalized(const CBlockIndex *pindex) const { LOCK(cs_avalancheFinalizedBlockIndex); return pindex && m_avalancheFinalizedBlockIndex && m_avalancheFinalizedBlockIndex->GetAncestor(pindex->nHeight) == pindex; } /** * Mark a block as having its data received and checked (up to * BLOCK_VALID_TRANSACTIONS). */ void Chainstate::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); m_blockman.m_dirty_blockindex.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)); } } /** * 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, BlockManager &blockman, const CBlockIndex *pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::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())) { LogPrint(BCLog::VALIDATION, "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(). const CBlockIndex *pcheckpoint = blockman.GetLastCheckpoint(checkpoints); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { LogPrint(BCLog::VALIDATION, "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"); } const Consensus::Params &consensusParams = params.GetConsensus(); // Reject blocks with outdated version if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB)) || (block.nVersion < 3 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DERSIG)) || (block.nVersion < 4 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CLTV))) { 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 CBlockIndex *active_chain_tip, const Consensus::Params ¶ms, const CTransaction &tx, TxValidationState &state) { AssertLockHeld(cs_main); // TODO: Make active_chain_tip a reference assert(active_chain_tip); // ContextualCheckTransactionForCurrentBlock() uses // active_chain_tip.Height()+1 to evaluate nLockTime because when // IsFinalTx() is called within 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 // active_chain_tip.Height(). const int nBlockHeight = active_chain_tip->nHeight + 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(). // This time can also be used for consensus upgrades. const int64_t nMedianTimePast{active_chain_tip->GetMedianTimePast()}; return ContextualCheckTransaction(params, tx, state, nBlockHeight, 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; // Enforce BIP113 (Median Time Past). int nLockTimeFlags = 0; if (DeploymentActiveAfter(pindexPrev, params, Consensus::DEPLOYMENT_CSV)) { 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 // - check they have the minimum size 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)) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), tx_state.GetDebugMessage()); } } // Enforce rule that the coinbase starts with serialized block height if (DeploymentActiveAfter(pindexPrev, params, Consensus::DEPLOYMENT_HEIGHTINCB)) { 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 ChainstateManager::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_blockman.m_block_index.find(hash)}; if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (miSelf != m_blockman.m_block_index.end()) { // Block header is already known. CBlockIndex *pindex = &(miSelf->second); if (ppindex) { *ppindex = pindex; } if (pindex->nStatus.isInvalid()) { LogPrint(BCLog::VALIDATION, "%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_blockman.m_block_index.find(block.hashPrevBlock)}; if (mi == m_blockman.m_block_index.end()) { LogPrint(BCLog::VALIDATION, "header %s has prev block not found: %s\n", hash.ToString(), block.hashPrevBlock.ToString()); return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found"); } CBlockIndex *pindexPrev = &((*mi).second); assert(pindexPrev); if (pindexPrev->nStatus.isInvalid()) { LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString()); return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); } if (!ContextualCheckBlockHeader(chainparams, block, state, m_blockman, pindexPrev, GetAdjustedTime())) { LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString()); return false; } /* 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(); m_blockman.m_dirty_blockindex.insert(invalid_walk); invalid_walk = invalid_walk->pprev; } LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString()); return state.Invalid( BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); } } } } CBlockIndex *pindex{m_blockman.AddToBlockIndex(block, m_best_header)}; 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 = AcceptBlockHeader(config, header, state, &pindex); ActiveChainstate().CheckBlockIndex(); if (!accepted) { return false; } if (ppindex) { *ppindex = pindex; } } } if (NotifyHeaderTip(ActiveChainstate())) { if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) { const CBlockIndex &last_accepted{**ppindex}; const int64_t blocks_left{ (GetTime() - last_accepted.GetBlockTime()) / config.GetChainParams().GetConsensus().nPowTargetSpacing}; const double progress{100.0 * last_accepted.nHeight / (last_accepted.nHeight + blocks_left)}; LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", last_accepted.nHeight, progress); } } return true; } /** * 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 Chainstate::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_chainman.AcceptBlockHeader(config, block, state, &pindex)}; CheckBlockIndex(); 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 > m_chain.Height() + int(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. // Note that this would break the getblockfrompeer RPC // 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 Consensus::Params &consensusParams = m_params.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(); m_blockman.m_dirty_blockindex.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(); m_blockman.m_dirty_blockindex.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{m_blockman.SaveBlockToDisk( block, pindex->nHeight, m_chain, m_params, 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(state, FlushStateMode::NONE); CheckBlockIndex(); return true; } bool ChainstateManager::ProcessNewBlock( const Config &config, const std::shared_ptr<const CBlock> &block, bool force_processing, bool *new_block) { AssertLockNotHeld(cs_main); { if (new_block) { *new_block = 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); // Skipping AcceptBlock() for CheckBlock() failures means that we will // never mark a block as invalid if CheckBlock() fails. This is // protective against consensus failure if there are any unknown form // s of block malleability that cause CheckBlock() to fail; see e.g. // CVE-2012-2459 and // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. // Because CheckBlock() is not very expensive, the anti-DoS benefits of // caching failure (of a definitely-invalid block) are not substantial. bool ret = CheckBlock(*block, state, config.GetChainParams().GetConsensus(), BlockValidationOptions(config)); if (ret) { // Store to disk ret = ActiveChainstate().AcceptBlock( config, block, state, force_processing, nullptr, new_block); } if (!ret) { GetMainSignals().BlockChecked(*block, state); return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString()); } } NotifyHeaderTip(ActiveChainstate()); // Only used to report errors, not invalidity - ignore it BlockValidationState state; if (!ActiveChainstate().ActivateBestChain(config, state, block)) { return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString()); } return true; } MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef &tx, bool test_accept) { AssertLockHeld(cs_main); Chainstate &active_chainstate = ActiveChainstate(); if (!active_chainstate.GetMempool()) { TxValidationState state; state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool"); return MempoolAcceptResult::Failure(state); } // Use GetConfig() temporarily. It will be removed in a follow-up by // making AcceptToMemoryPool take a CChainParams instead of a Config. // This avoids passing an extra Config argument to this function that will // be removed soon. auto result = AcceptToMemoryPool(::GetConfig(), active_chainstate, tx, GetTime(), /*bypass_limits=*/false, test_accept); active_chainstate.GetMempool()->check( active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1); return result; } bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, Chainstate &chainstate, const CBlock &block, CBlockIndex *pindexPrev, BlockValidationOptions validationOptions) { AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip()); CCoinsViewCache viewNew(&chainstate.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, chainstate.m_blockman, 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 (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, validationOptions, true)) { return false; } assert(state.IsValid()); return true; } /* This function is called from the RPC code for pruneblockchain */ void PruneBlockFilesManual(Chainstate &active_chainstate, int nManualPruneHeight) { BlockValidationState state; if (active_chainstate.FlushStateToDisk(state, FlushStateMode::NONE, nManualPruneHeight)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } void Chainstate::LoadMempool(const Config &config, const ArgsManager &args) { if (!m_mempool) { return; } if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { ::LoadMempool(config, *m_mempool, *this); } m_mempool->SetIsLoaded(!ShutdownRequested()); } bool Chainstate::LoadChainTip() { 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 = m_blockman.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(m_params.TxData(), tip)); return true; } CVerifyDB::CVerifyDB() { uiInterface.ShowProgress(_("Verifying blocks...").translated, 0, false); } CVerifyDB::~CVerifyDB() { uiInterface.ShowProgress("", 100, false); } bool CVerifyDB::VerifyDB(Chainstate &chainstate, const Config &config, CCoinsView &coinsview, int nCheckLevel, int nCheckDepth) { AssertLockHeld(cs_main); const CChainParams ¶ms = config.GetChainParams(); const Consensus::Params &consensusParams = params.GetConsensus(); if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr) { return true; } // Verify blocks in the best chain if (nCheckDepth <= 0 || nCheckDepth > chainstate.m_chain.Height()) { nCheckDepth = chainstate.m_chain.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%%]..."); const bool is_snapshot_cs{!chainstate.m_from_snapshot_blockhash}; for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { const int percentageDone = std::max( 1, std::min(99, (int)(((double)(chainstate.m_chain.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 <= chainstate.m_chain.Height() - nCheckDepth) { break; } if ((fPruneMode || is_snapshot_cs) && !pindex->nStatus.hasData()) { // If pruning or running under an assumeutxo snapshot, 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 size_t curr_coins_usage = coins.DynamicMemoryUsage() + chainstate.CoinsTip().DynamicMemoryUsage(); if (nCheckLevel >= 3 && curr_coins_usage <= chainstate.m_coinstip_cache_size_bytes) { assert(coins.GetBestBlock() == pindex->GetBlockHash()); DisconnectResult res = chainstate.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", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions); } // store block count as we move pindex at check level >= 4 int block_count = chainstate.m_chain.Height() - pindex->nHeight; // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { while (pindex != chainstate.m_chain.Tip()) { const int percentageDone = std::max( 1, std::min(99, 100 - int(double(chainstate.m_chain.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 = chainstate.m_chain.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, consensusParams)) { return error( "VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } if (!chainstate.ConnectBlock(block, state, pindex, coins, 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 Chainstate::RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &view) { AssertLockHeld(cs_main); // TODO: merge with ConnectBlock CBlock block; if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) { 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 Chainstate::ReplayBlocks() { 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, m_params.GetConsensus())) { 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)) { 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 Chainstate::UnloadBlockIndex() { AssertLockHeld(::cs_main); nBlockSequenceId = 1; m_best_fork_tip = nullptr; m_best_fork_base = nullptr; setBlockIndexCandidates.clear(); } bool ChainstateManager::LoadBlockIndex() { AssertLockHeld(cs_main); // Load block index from databases bool needs_init = fReindex; if (!fReindex) { bool ret = m_blockman.LoadBlockIndexDB(); if (!ret) { return false; } std::vector<CBlockIndex *> vSortedByHeight{ m_blockman.GetAllBlockIndices()}; std::sort(vSortedByHeight.begin(), vSortedByHeight.end(), CBlockIndexHeightOnlyComparator()); // Find start of assumed-valid region. int first_assumed_valid_height = std::numeric_limits<int>::max(); for (const CBlockIndex *block : vSortedByHeight) { if (block->IsAssumedValid()) { auto chainstates = GetAll(); // If we encounter an assumed-valid block index entry, ensure // that we have one chainstate that tolerates assumed-valid // entries and another that does not (i.e. the background // validation chainstate), since assumed-valid entries should // always be pending validation by a fully-validated chainstate. auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); }; assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); })); assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); })); first_assumed_valid_height = block->nHeight; break; } } for (CBlockIndex *pindex : vSortedByHeight) { if (ShutdownRequested()) { return false; } if (pindex->IsAssumedValid() || (pindex->IsValid(BlockValidity::TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) { // Fill each chainstate's block candidate set. Only add // assumed-valid blocks to the tip candidate set if the // chainstate is allowed to rely on assumed-valid blocks. // // If all setBlockIndexCandidates contained the assumed-valid // blocks, the background chainstate's ActivateBestChain() call // would add assumed-valid blocks to the chain (based on how // FindMostWorkChain() works). Obviously we don't want this // since the purpose of the background validation chain is to // validate assumed-valid blocks. // // Note: This is considering all blocks whose height is greater // or equal to the first assumed-valid block to be assumed-valid // blocks, and excluding them from the background chainstate's // setBlockIndexCandidates set. This does mean that some blocks // which are not technically assumed-valid (later blocks on a // fork beginning before the first assumed-valid block) might // not get added to the background chainstate, but this is ok, // because they will still be attached to the active chainstate // if they actually contain more work. // // Instead of this height-based approach, an earlier attempt was // made at detecting "holistically" whether the block index // under consideration relied on an assumed-valid ancestor, but // this proved to be too slow to be practical. for (Chainstate *chainstate : GetAll()) { if (chainstate->reliesOnAssumedValid() || pindex->nHeight < first_assumed_valid_height) { chainstate->setBlockIndexCandidates.insert(pindex); } } } if (pindex->nStatus.isInvalid() && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) { m_best_invalid = pindex; } if (pindex->nStatus.isOnParkedChain() && (!m_best_parked || pindex->nChainWork > m_best_parked->nChainWork)) { m_best_parked = pindex; } if (pindex->IsValid(BlockValidity::TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex))) { m_best_header = pindex; } } 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 Chainstate::LoadGenesisBlock() { 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(m_params.GenesisBlock().GetHash())) { return true; } try { const CBlock &block = m_params.GenesisBlock(); FlatFilePos blockPos{ m_blockman.SaveBlockToDisk(block, 0, m_chain, m_params, nullptr)}; if (blockPos.IsNull()) { return error("%s: writing genesis block to disk failed", __func__); } CBlockIndex *pindex = m_blockman.AddToBlockIndex(block, m_chainman.m_best_header); ReceivedBlockTransactions(block, pindex, blockPos); } catch (const std::runtime_error &e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } return true; } void Chainstate::LoadExternalBlockFile(const Config &config, FILE *fileIn, FlatFilePos *dbp) { AssertLockNotHeld(m_chainstate_mutex); // Map of disk positions for blocks with unknown parent (only used for // reindex) static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); 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(char(m_params.DiskMagic()[0])); nRewind = blkdat.GetPos() + 1; blkdat >> buf; if (memcmp(buf, m_params.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 != m_params.GetConsensus().hashGenesisBlock && !m_blockman.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 const CBlockIndex *pindex = m_blockman.LookupBlockIndex(hash); if (!pindex || !pindex->nStatus.hasData()) { BlockValidationState state; if (AcceptBlock(config, pblock, state, true, dbp, nullptr)) { nLoaded++; } if (state.IsError()) { break; } } else if (hash != m_params.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 == m_params.GetConsensus().hashGenesisBlock) { BlockValidationState state; if (!ActivateBestChain(config, state, nullptr)) { break; } } NotifyHeaderTip(*this); // 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, m_params.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 (AcceptBlock(config, pblockrecursive, dummy, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); } } range.first++; mapBlocksUnknownParent.erase(it); NotifyHeaderTip(*this); } } } 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 Chainstate::CheckBlockIndex() { 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 (auto &[_, block_index] : m_blockman.m_block_index) { forward.emplace(block_index.pprev, &block_index); } 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; } // Assumed-valid index entries will not have data since we haven't // downloaded the full block yet. if (pindexFirstMissing == nullptr && !pindex->nStatus.hasData() && !pindex->IsAssumedValid()) { 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 && !pindex->IsAssumedValid()) { if (pindexFirstNotTransactionsValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::TRANSACTIONS) { pindexFirstNotTransactionsValid = pindex; } if (pindexFirstNotChainValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::CHAIN) { pindexFirstNotChainValid = pindex; } if (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() == m_params.GetConsensus().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. // Unless these indexes are assumed valid and pending block download on // a background chainstate. if (!m_blockman.m_have_pruned && !pindex->IsAssumedValid()) { // 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()); } if (pindex->IsAssumedValid()) { // Assumed-valid blocks should have some nTx value. assert(pindex->nTx > 0); // Assumed-valid blocks should connect to the main chain. assert(pindex->nStatus.getValidity() >= BlockValidity::TREE); } else { // Otherwise there should only be an nTx value if we have // actually seen a block's transactions. // 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) { // Don't perform this check for the background chainstate since // its setBlockIndexCandidates shouldn't have some entries (i.e. // those past the snapshot block) which do exist in the block // index for the active chainstate. if (this == &m_chainman.ActiveChainstate()) { // 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(m_blockman.m_have_pruned); // 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 Chainstate::ToString() { AssertLockHeld(::cs_main); CBlockIndex *tip = m_chain.Tip(); return strprintf("Chainstate [%s] @ height %d (%s)", m_from_snapshot_blockhash ? "snapshot" : "ibd", tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null"); } bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) { AssertLockHeld(::cs_main); 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; bool ret; if (coinstip_size > old_coinstip_size) { // Likely no need to flush if cache sizes have grown. ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED); } else { // Otherwise, flush state to disk and deallocate the in-memory coins // map. ret = FlushStateToDisk(state, FlushStateMode::ALWAYS); CoinsTip().ReallocateCache(); } return ret; } static const uint64_t MEMPOOL_DUMP_VERSION = 1; bool LoadMempool(const Config &config, CTxMemPool &pool, Chainstate &active_chainstate) { int64_t nExpiryTimeout = gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; FILE *filestr = fsbridge::fopen(gArgs.GetDataDirNet() / "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) { --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); } if (nTime > nNow - nExpiryTimeout) { LOCK(cs_main); const auto &accepted = AcceptToMemoryPool(config, active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false); if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) { ++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); } std::set<TxId> unbroadcast_txids; file >> unbroadcast_txids; unbroadcast = unbroadcast_txids.size(); for (const auto &txid : unbroadcast_txids) { // Ensure transactions were accepted to mempool then add to // unbroadcast set. if (pool.get(txid) != nullptr) { pool.AddUnbroadcastTx(txid); } } } 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(gArgs.GetDataDirNet() / "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(gArgs.GetDataDirNet() / "mempool.dat.new", gArgs.GetDataDirNet() / "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; } //! 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 { LOCK(::cs_main); if (m_active_chainstate && m_active_chainstate->m_from_snapshot_blockhash) { // If a snapshot chainstate exists, it will always be our active. return m_active_chainstate->m_from_snapshot_blockhash; } return std::nullopt; } std::vector<Chainstate *> ChainstateManager::GetAll() { LOCK(::cs_main); std::vector<Chainstate *> 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; } Chainstate &ChainstateManager::InitializeChainstate( CTxMemPool *mempool, const std::optional<BlockHash> &snapshot_blockhash) { AssertLockHeld(::cs_main); bool is_snapshot = snapshot_blockhash.has_value(); std::unique_ptr<Chainstate> &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 Chainstate(mempool, m_blockman, *this, 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; } const AssumeutxoData *ExpectedAssumeutxo(const int height, const CChainParams &chainparams) { const MapAssumeutxo &valid_assumeutxos_map = chainparams.Assumeutxo(); const auto assumeutxo_found = valid_assumeutxos_map.find(height); if (assumeutxo_found != valid_assumeutxos_map.end()) { return &assumeutxo_found->second; } return nullptr; } bool ChainstateManager::ActivateSnapshot(CAutoFile &coins_file, const SnapshotMetadata &metadata, bool in_memory) { BlockHash base_blockhash = metadata.m_base_blockhash; if (this->SnapshotBlockhash()) { LogPrintf("[snapshot] can't activate a snapshot-based chainstate more " "than once\n"); return false; } int64_t current_coinsdb_cache_size{0}; int64_t current_coinstip_cache_size{0}; // Cache percentages to allocate to each chainstate. // // These particular percentages don't matter so much since they will only be // relevant during snapshot activation; caches are rebalanced at the // conclusion of this function. We want to give (essentially) all available // cache capacity to the snapshot to aid the bulk load later in this // function. static constexpr double IBD_CACHE_PERC = 0.01; static constexpr double SNAPSHOT_CACHE_PERC = 0.99; { LOCK(::cs_main); // Resize the coins caches to ensure we're not exceeding memory limits. // // Allocate the majority of the cache to the incoming snapshot // chainstate, since (optimistically) getting to its tip will be the top // priority. We'll need to call `MaybeRebalanceCaches()` once we're done // with this function to ensure the right allocation (including the // possibility that no snapshot was activated and that we should restore // the active chainstate caches to their original size). // current_coinsdb_cache_size = this->ActiveChainstate().m_coinsdb_cache_size_bytes; current_coinstip_cache_size = this->ActiveChainstate().m_coinstip_cache_size_bytes; // Temporarily resize the active coins cache to make room for the // newly-created snapshot chain. this->ActiveChainstate().ResizeCoinsCaches( static_cast<size_t>(current_coinstip_cache_size * IBD_CACHE_PERC), static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC)); } auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<Chainstate>( /* mempool */ nullptr, m_blockman, *this, base_blockhash)); { LOCK(::cs_main); snapshot_chainstate->InitCoinsDB( static_cast<size_t>(current_coinsdb_cache_size * SNAPSHOT_CACHE_PERC), in_memory, false, "chainstate"); snapshot_chainstate->InitCoinsCache(static_cast<size_t>( current_coinstip_cache_size * SNAPSHOT_CACHE_PERC)); } const bool snapshot_ok = this->PopulateAndValidateSnapshot( *snapshot_chainstate, coins_file, metadata); if (!snapshot_ok) { WITH_LOCK(::cs_main, this->MaybeRebalanceCaches()); return false; } { LOCK(::cs_main); assert(!m_snapshot_chainstate); m_snapshot_chainstate.swap(snapshot_chainstate); const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(); assert(chaintip_loaded); m_active_chainstate = m_snapshot_chainstate.get(); LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString()); LogPrintf("[snapshot] (%.2f MB)\n", m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000)); this->MaybeRebalanceCaches(); } return true; } static void FlushSnapshotToDisk(CCoinsViewCache &coins_cache, bool snapshot_loaded) { LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE( strprintf("%s (%.2f MB)", snapshot_loaded ? "saving snapshot chainstate" : "flushing coins cache", coins_cache.DynamicMemoryUsage() / (1000 * 1000)), BCLog::LogFlags::ALL); coins_cache.Flush(); } bool ChainstateManager::PopulateAndValidateSnapshot( Chainstate &snapshot_chainstate, CAutoFile &coins_file, const SnapshotMetadata &metadata) { // It's okay to release cs_main before we're done using `coins_cache` // because we know that nothing else will be referencing the newly created // snapshot_chainstate yet. CCoinsViewCache &coins_cache = *WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsTip()); BlockHash base_blockhash = metadata.m_base_blockhash; CBlockIndex *snapshot_start_block = WITH_LOCK( ::cs_main, return m_blockman.LookupBlockIndex(base_blockhash)); if (!snapshot_start_block) { // Needed for GetUTXOStats and ExpectedAssumeutxo to determine the // height and to avoid a crash when base_blockhash.IsNull() LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n", base_blockhash.ToString()); return false; } int base_height = snapshot_start_block->nHeight; auto maybe_au_data = ExpectedAssumeutxo(base_height, ::Params()); if (!maybe_au_data) { LogPrintf("[snapshot] assumeutxo height in snapshot metadata not " "recognized (%d) - refusing to load snapshot\n", base_height); return false; } const AssumeutxoData &au_data = *maybe_au_data; COutPoint outpoint; Coin coin; const uint64_t coins_count = metadata.m_coins_count; uint64_t coins_left = metadata.m_coins_count; LogPrintf("[snapshot] loading coins from snapshot %s\n", base_blockhash.ToString()); int64_t coins_processed{0}; while (coins_left > 0) { try { coins_file >> outpoint; coins_file >> coin; } catch (const std::ios_base::failure &) { LogPrintf("[snapshot] bad snapshot format or truncated snapshot " "after deserializing %d coins\n", coins_count - coins_left); return false; } if (coin.GetHeight() > uint32_t(base_height) || // Avoid integer wrap-around in coinstats.cpp:ApplyHash outpoint.GetN() >= std::numeric_limits<decltype(outpoint.GetN())>::max()) { LogPrintf( "[snapshot] bad snapshot data after deserializing %d coins\n", coins_count - coins_left); return false; } coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin)); --coins_left; ++coins_processed; if (coins_processed % 1000000 == 0) { LogPrintf("[snapshot] %d coins loaded (%.2f%%, %.2f MB)\n", coins_processed, static_cast<float>(coins_processed) * 100 / static_cast<float>(coins_count), coins_cache.DynamicMemoryUsage() / (1000 * 1000)); } // Batch write and flush (if we need to) every so often. // // If our average Coin size is roughly 41 bytes, checking every 120,000 // coins means <5MB of memory imprecision. if (coins_processed % 120000 == 0) { if (ShutdownRequested()) { return false; } const auto snapshot_cache_state = WITH_LOCK( ::cs_main, return snapshot_chainstate.GetCoinsCacheSizeState()); if (snapshot_cache_state >= CoinsCacheSizeState::CRITICAL) { // This is a hack - we don't know what the actual best block is, // but that doesn't matter for the purposes of flushing the // cache here. We'll set this to its correct value // (`base_blockhash`) below after the coins are loaded. coins_cache.SetBestBlock(BlockHash{GetRandHash()}); // No need to acquire cs_main since this chainstate isn't being // used yet. FlushSnapshotToDisk(coins_cache, /*snapshot_loaded=*/false); } } } // Important that we set this. This and the coins_cache accesses above are // sort of a layer violation, but either we reach into the innards of // CCoinsViewCache here or we have to invert some of the Chainstate to // embed them in a snapshot-activation-specific CCoinsViewCache bulk load // method. coins_cache.SetBestBlock(base_blockhash); bool out_of_coins{false}; try { coins_file >> outpoint; } catch (const std::ios_base::failure &) { // We expect an exception since we should be out of coins. out_of_coins = true; } if (!out_of_coins) { LogPrintf("[snapshot] bad snapshot - coins left over after " "deserializing %d coins\n", coins_count); return false; } LogPrintf("[snapshot] loaded %d (%.2f MB) coins from snapshot %s\n", coins_count, coins_cache.DynamicMemoryUsage() / (1000 * 1000), base_blockhash.ToString()); // No need to acquire cs_main since this chainstate isn't being used yet. FlushSnapshotToDisk(coins_cache, /*snapshot_loaded=*/true); assert(coins_cache.GetBestBlock() == base_blockhash); CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ }; // As above, okay to immediately release cs_main here since no other context // knows about the snapshot_chainstate. CCoinsViewDB *snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB()); if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) { LogPrintf("[snapshot] failed to generate coins stats\n"); return false; } // Assert that the deserialized chainstate contents match the expected // assumeutxo value. if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) { LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n", au_data.hash_serialized.ToString(), stats.hashSerialized.ToString()); return false; } snapshot_chainstate.m_chain.SetTip(snapshot_start_block); // The remainder of this function requires modifying data protected by // cs_main. LOCK(::cs_main); // Fake various pieces of CBlockIndex state: CBlockIndex *index = nullptr; // Don't make any modifications to the genesis block. // This is especially important because we don't want to erroneously // apply ASSUMED_VALID_FLAG to genesis, which would happen if we didn't // skip it here (since it apparently isn't BlockValidity::SCRIPTS). constexpr int AFTER_GENESIS_START{1}; for (int i = AFTER_GENESIS_START; i <= snapshot_chainstate.m_chain.Height(); ++i) { index = snapshot_chainstate.m_chain[i]; // Fake nTx so that LoadBlockIndex() loads assumed-valid CBlockIndex // entries (among other things) if (!index->nTx) { index->nTx = 1; } // Fake nChainTx so that GuessVerificationProgress reports accurately index->nChainTx = index->pprev->nChainTx + index->nTx; // Mark unvalidated block index entries beneath the snapshot base block // as assumed-valid. if (!index->IsValid(BlockValidity::SCRIPTS)) { // This flag will be removed once the block is fully validated by a // background chainstate. index->nStatus = index->nStatus.withAssumedValid(); } m_blockman.m_dirty_blockindex.insert(index); // Changes to the block index will be flushed to disk after this call // returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is // called, since we've added a snapshot chainstate and therefore will // have to downsize the IBD chainstate, which will result in a call to // `FlushStateToDisk(ALWAYS)`. } assert(index); index->nChainTx = au_data.nChainTx; snapshot_chainstate.setBlockIndexCandidates.insert(snapshot_start_block); LogPrintf("[snapshot] validated snapshot (%.2f MB)\n", coins_cache.DynamicMemoryUsage() / (1000 * 1000)); return true; } Chainstate &ChainstateManager::ActiveChainstate() const { LOCK(::cs_main); assert(m_active_chainstate); return *m_active_chainstate; } bool ChainstateManager::IsSnapshotActive() const { LOCK(::cs_main); return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get(); } void ChainstateManager::MaybeRebalanceCaches() { AssertLockHeld(::cs_main); 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); } } }