diff options
Diffstat (limited to 'src/validation.h')
-rw-r--r-- | src/validation.h | 215 |
1 files changed, 80 insertions, 135 deletions
diff --git a/src/validation.h b/src/validation.h index 2609538ece..16f4bfe741 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,6 +15,7 @@ #include <chain.h> #include <consensus/amount.h> #include <fs.h> +#include <node/blockstorage.h> #include <policy/feerate.h> #include <policy/packages.h> #include <script/script_error.h> @@ -40,7 +41,6 @@ class CChainState; class CBlockTreeDB; class CChainParams; -struct CCheckpointData; class CTxMemPool; class ChainstateManager; class SnapshotMetadata; @@ -60,6 +60,16 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101; static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25; /** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; + +// If a package is submitted, it must be within the mempool's ancestor/descendant limits. Since a +// submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor +// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the +// defaults reflect this constraint. +static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT); +static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT); +static_assert(DEFAULT_ANCESTOR_SIZE_LIMIT >= MAX_PACKAGE_SIZE); +static_assert(DEFAULT_DESCENDANT_SIZE_LIMIT >= MAX_PACKAGE_SIZE); + /** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336; /** Maximum number of dedicated script-checking threads allowed */ @@ -97,7 +107,6 @@ enum class SynchronizationState { }; extern RecursiveMutex cs_main; -typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap; extern Mutex g_best_block_mutex; extern std::condition_variable g_best_block_cv; /** Used to notify getblocktemplate RPC of new tips. */ @@ -151,17 +160,19 @@ struct MempoolAcceptResult { enum class ResultType { VALID, //!> Fully validated, valid. INVALID, //!> Invalid. + MEMPOOL_ENTRY, //!> Valid, transaction was already in the mempool. }; const ResultType m_result_type; const TxValidationState m_state; - // The following fields are only present when m_result_type = ResultType::VALID + // The following fields are only present when m_result_type = ResultType::VALID or MEMPOOL_ENTRY /** Mempool transactions replaced by the tx per BIP 125 rules. */ const std::optional<std::list<CTransactionRef>> m_replaced_transactions; /** Virtual size as used by the mempool, calculated using serialized size and sigops. */ const std::optional<int64_t> m_vsize; /** Raw base fees in satoshis. */ const std::optional<CAmount> m_base_fees; + static MempoolAcceptResult Failure(TxValidationState state) { return MempoolAcceptResult(state); } @@ -170,6 +181,10 @@ struct MempoolAcceptResult { return MempoolAcceptResult(std::move(replaced_txns), vsize, fees); } + static MempoolAcceptResult MempoolTx(int64_t vsize, CAmount fees) { + return MempoolAcceptResult(vsize, fees); + } + // Private constructors. Use static methods MempoolAcceptResult::Success, etc. to construct. private: /** Constructor for failure case */ @@ -182,6 +197,10 @@ private: explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, int64_t vsize, CAmount fees) : m_result_type(ResultType::VALID), m_replaced_transactions(std::move(replaced_txns)), m_vsize{vsize}, m_base_fees(fees) {} + + /** Constructor for already-in-mempool case. It wouldn't replace any transactions. */ + explicit MempoolAcceptResult(int64_t vsize, CAmount fees) + : m_result_type(ResultType::MEMPOOL_ENTRY), m_vsize{vsize}, m_base_fees(fees) {} }; /** @@ -191,7 +210,7 @@ struct PackageMempoolAcceptResult { const PackageValidationState m_state; /** - * Map from wtxid to finished MempoolAcceptResults. The client is responsible + * Map from (w)txid to finished MempoolAcceptResults. The client is responsible * for keeping track of the transaction objects themselves. If a result is not * present, it means validation was unfinished for that transaction. If there * was a package-wide error (see result in m_state), m_tx_results will be empty. @@ -211,32 +230,26 @@ struct PackageMempoolAcceptResult * Try to add a transaction to the mempool. This is an internal function and is exposed only for testing. * Client code should use ChainstateManager::ProcessTransaction() * - * @param[in] pool Reference to the node's mempool. * @param[in] active_chainstate Reference to the active chainstate. * @param[in] tx The transaction to submit for mempool acceptance. - * @param[in] accept_time The timestamp for adding the transaction to the mempool. Usually - * the current system time, but may be different. + * @param[in] accept_time The timestamp for adding the transaction to the mempool. * It is also used to determine when the entry expires. * @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits. * @param[in] test_accept When true, run validation checks but don't submit to mempool. * * @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason. */ -MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, CChainState& active_chainstate, const CTransactionRef& tx, +MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTransactionRef& tx, int64_t accept_time, bool bypass_limits, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** -* Atomically test acceptance of a package. If the package only contains one tx, package rules still -* apply. Package validation does not allow BIP125 replacements, so the transaction(s) cannot spend -* the same inputs as any transaction in the mempool. -* @param[in] txns Group of transactions which may be independent or contain -* parent-child dependencies. The transactions must not conflict -* with each other, i.e., must not spend the same inputs. If any -* dependencies exist, parents must appear anywhere in the list -* before their children. +* Validate (and maybe submit) a package to the mempool. See doc/policy/packages.md for full details +* on package validation rules. +* @param[in] test_accept When true, run validation checks but don't submit to mempool. * @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction. -* If a transaction fails, validation will exit early and some results may be missing. +* If a transaction fails, validation will exit early and some results may be missing. It is also +* possible for the package to be partially submitted. */ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool, const Package& txns, bool test_accept) @@ -344,7 +357,7 @@ public: ~CVerifyDB(); bool VerifyDB( CChainState& chainstate, - const CChainParams& chainparams, + const Consensus::Params& consensus_params, CCoinsView& coinsview, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -367,122 +380,6 @@ enum class FlushStateMode { ALWAYS }; -struct CBlockIndexWorkComparator -{ - bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const; -}; - -/** - * Maintains a tree of blocks (stored in `m_block_index`) which is consulted - * to determine where the most-work tip is. - * - * This data is used mostly in `CChainState` - information about, e.g., - * candidate tips is not maintained here. - */ -class BlockManager -{ - friend CChainState; - -private: - /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ - void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height); - - /** - * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. - * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new - * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex - * (which in this case means the blockchain must be re-downloaded.) - * - * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set. - * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.) - * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest). - * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip. - * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files. - * A db flag records the fact that at least some block files have been pruned. - * - * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned - */ - void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd); - -public: - BlockMap m_block_index GUARDED_BY(cs_main); - - /** In order to efficiently track invalidity of headers, we keep the set of - * blocks which we tried to connect and found to be invalid here (ie which - * were set to BLOCK_FAILED_VALID since the last restart). We can then - * walk this set and check if a new header is a descendant of something in - * this set, preventing us from having to walk m_block_index when we try - * to connect a bad block and fail. - * - * While this is more complicated than marking everything which descends - * from an invalid block as invalid at the time we discover it to be - * invalid, doing so would require walking all of m_block_index to find all - * descendants. Since this case should be very rare, keeping track of all - * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as - * well. - * - * Because we already walk m_block_index in height-order at startup, we go - * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, - * instead of putting things in this set. - */ - std::set<CBlockIndex*> m_failed_blocks; - - /** - * All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions. - * Pruned nodes may have entries where B is missing data. - */ - std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked; - - std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main); - - bool LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkComparator>& setBlockIndexCandidates) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - - /** - * Load the blocktree off disk and into memory. Populate certain metadata - * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral - * collections like setDirtyBlockIndex. - * - * @param[out] block_index_candidates Fill this set with any valid blocks for - * which we've downloaded all transactions. - */ - bool LoadBlockIndex( - const Consensus::Params& consensus_params, - std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates) - EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - /** Clear all data members. */ - void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - /** Create a new block index entry for a given block hash */ - CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - //! Mark one block file as pruned (modify associated database entries) - void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - /** - * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure - * that it doesn't descend from an invalid block, and then add it to m_block_index. - */ - bool AcceptBlockHeader( - const CBlockHeader& block, - BlockValidationState& state, - const CChainParams& chainparams, - CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - /** Find the last common block between the parameter chain and a locator. */ - CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - //! Returns last CBlockIndex* that is a checkpoint - CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - - ~BlockManager() { - Unload(); - } -}; - /** * A convenience class for constructing the CCoinsView* hierarchy used * to facilitate access to the UTXO set. @@ -628,6 +525,10 @@ public: */ const std::optional<uint256> m_from_snapshot_blockhash; + //! Return true if this chainstate relies on blocks that are assumed-valid. In + //! practice this means it was created based on a UTXO snapshot. + bool reliesOnAssumedValid() { return m_from_snapshot_blockhash.has_value(); } + /** * The set of all CBlockIndex entries with either BLOCK_VALID_TRANSACTIONS (for * itself and all ancestors) *or* BLOCK_ASSUMED_VALID (if using background @@ -649,6 +550,12 @@ public: return m_coins_views->m_dbview; } + //! @returns A pointer to the mempool. + CTxMemPool* GetMempool() + { + return m_mempool; + } + //! @returns A reference to a wrapped view of the in-memory UTXO set that //! handles disk read errors gracefully. CCoinsViewErrorCatcher& CoinsErrorCatcher() EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -752,6 +659,9 @@ public: /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload() const; + /** Find the last common block of this chain and a locator. */ + CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** * Make various assertions about the state of the block index. * @@ -892,18 +802,53 @@ private: //! by the background validation chainstate. bool m_snapshot_validated{false}; + CBlockIndex* m_best_invalid; + friend bool BlockManager::LoadBlockIndex(const Consensus::Params&, ChainstateManager&); + //! Internal helper for ActivateSnapshot(). [[nodiscard]] bool PopulateAndValidateSnapshot( CChainState& snapshot_chainstate, CAutoFile& coins_file, const SnapshotMetadata& metadata); + /** + * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure + * that it doesn't descend from an invalid block, and then add it to m_block_index. + */ + bool AcceptBlockHeader( + const CBlockHeader& block, + BlockValidationState& state, + const CChainParams& chainparams, + CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + friend CChainState; + public: std::thread m_load_block; //! A single BlockManager instance is shared across each constructed //! chainstate to avoid duplicating block metadata. BlockManager m_blockman GUARDED_BY(::cs_main); + /** + * In order to efficiently track invalidity of headers, we keep the set of + * blocks which we tried to connect and found to be invalid here (ie which + * were set to BLOCK_FAILED_VALID since the last restart). We can then + * walk this set and check if a new header is a descendant of something in + * this set, preventing us from having to walk m_block_index when we try + * to connect a bad block and fail. + * + * While this is more complicated than marking everything which descends + * from an invalid block as invalid at the time we discover it to be + * invalid, doing so would require walking all of m_block_index to find all + * descendants. Since this case should be very rare, keeping track of all + * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as + * well. + * + * Because we already walk m_block_index in height-order at startup, we go + * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, + * instead of putting things in this set. + */ + std::set<CBlockIndex*> m_failed_blocks; + //! The total number of bytes available for us to use across all in-memory //! coins caches. This will be split somehow across chainstates. int64_t m_total_coinstip_cache{0}; |