diff --git a/src/blockindex.cpp b/src/blockindex.cpp index 0bb45e117..7a0b1453f 100644 --- a/src/blockindex.cpp +++ b/src/blockindex.cpp @@ -1,87 +1,84 @@ // Copyright (c) 2009-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 <blockindex.h> #include <tinyformat.h> /** * Turn the lowest '1' bit in the binary representation of a number into a '0'. */ static inline int InvertLowestOne(int n) { return n & (n - 1); } /** Compute what height to jump back to with the CBlockIndex::pskip pointer. */ static inline int GetSkipHeight(int height) { if (height < 2) { return 0; } // Determine which height to jump back to. Any number strictly lower than // height is acceptable, but the following expression seems to perform well // in simulations (max 110 steps to go back up to 2**18 blocks). return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); } std::string CBlockIndex::ToString() const { return strprintf( "CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, nHeight, hashMerkleRoot.ToString(), GetBlockHash().ToString()); } bool CBlockIndex::UpdateChainStats() { if (pprev == nullptr) { nChainTx = nTx; - nChainSize = nSize; return true; } if (pprev->nChainTx > 0) { nChainTx = pprev->nChainTx + nTx; - nChainSize = pprev->nChainSize + nSize; return true; } nChainTx = 0; - nChainSize = 0; return false; } const CBlockIndex *CBlockIndex::GetAncestor(int height) const { if (height > nHeight || height < 0) { return nullptr; } const CBlockIndex *pindexWalk = this; int heightWalk = nHeight; while (heightWalk > height) { int heightSkip = GetSkipHeight(heightWalk); int heightSkipPrev = GetSkipHeight(heightWalk - 1); if (pindexWalk->pskip != nullptr && (heightSkip == height || (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && heightSkipPrev >= height)))) { // Only follow pskip if pprev->pskip isn't better than pskip->pprev. pindexWalk = pindexWalk->pskip; heightWalk = heightSkip; } else { assert(pindexWalk->pprev); pindexWalk = pindexWalk->pprev; heightWalk--; } } return pindexWalk; } CBlockIndex *CBlockIndex::GetAncestor(int height) { return const_cast<CBlockIndex *>( const_cast<const CBlockIndex *>(this)->GetAncestor(height)); } void CBlockIndex::BuildSkip() { if (pprev) { pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); } } diff --git a/src/blockindex.h b/src/blockindex.h index edc4ff97b..15eb265db 100644 --- a/src/blockindex.h +++ b/src/blockindex.h @@ -1,261 +1,249 @@ // Copyright (c) 2009-2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_BLOCKINDEX_H #define BITCOIN_BLOCKINDEX_H #include <arith_uint256.h> #include <blockstatus.h> #include <flatfile.h> #include <kernel/cs_main.h> #include <primitives/block.h> #include <sync.h> #include <uint256.h> #include <util/time.h> struct BlockHash; /** * The block chain is a tree shaped structure starting with the genesis block at * the root, with each block potentially having multiple candidates to be the * next block. A blockindex may have multiple pprev pointing to it, but at most * one of them can be part of the currently active branch. */ class CBlockIndex { public: //! pointer to the hash of the block, if any. Memory is owned by this //! CBlockIndex const BlockHash *phashBlock{nullptr}; //! pointer to the index of the predecessor of this block CBlockIndex *pprev{nullptr}; //! pointer to the index of some further predecessor of this block CBlockIndex *pskip{nullptr}; //! height of the entry in the chain. The genesis block has height 0 int nHeight{0}; //! Which # file this block is stored in (blk?????.dat) int nFile GUARDED_BY(::cs_main){0}; //! Byte offset within blk?????.dat where this block's data is stored unsigned int nDataPos GUARDED_BY(::cs_main){0}; //! Byte offset within rev?????.dat where this block's undo data is stored unsigned int nUndoPos GUARDED_BY(::cs_main){0}; //! (memory only) Total amount of work (expected number of hashes) in the //! chain up to and including this block arith_uint256 nChainWork{}; //! Number of transactions in this block. //! Note: in a potential headers-first mode, this number cannot be relied //! upon //! Note: this value is faked during UTXO snapshot load to ensure that //! LoadBlockIndex() will load index entries for blocks that we lack data //! for. //! @sa ActivateSnapshot unsigned int nTx{0}; //! Size of this block. //! Note: in a potential headers-first mode, this number cannot be relied //! upon unsigned int nSize{0}; //! (memory only) Number of transactions in the chain up to and including //! this block. //! This value will be non-zero only if and only if transactions for this //! block and all its parents are available. Change to 64-bit type when //! necessary; won't happen before 2030 //! //! Note: this value is faked during use of a UTXO snapshot because we don't //! have the underlying block data available during snapshot load. //! @sa AssumeutxoData //! @sa ActivateSnapshot unsigned int nChainTx{0}; -private: - //! (memory only) Size of all blocks in the chain up to and including this - //! block. This value will be non-zero only if and only if transactions for - //! this block and all its parents are available. - uint64_t nChainSize{0}; - -public: //! Verification status of this block. See enum BlockStatus BlockStatus nStatus GUARDED_BY(::cs_main){}; //! block header int32_t nVersion{0}; uint256 hashMerkleRoot{}; uint32_t nTime{0}; uint32_t nBits{0}; uint32_t nNonce{0}; //! (memory only) Sequential id assigned to distinguish order in which //! blocks are received. int32_t nSequenceId{0}; //! (memory only) block header metadata int64_t nTimeReceived{0}; //! (memory only) Maximum nTime in the chain up to and including this block. unsigned int nTimeMax{0}; explicit CBlockIndex() = default; explicit CBlockIndex(const CBlockHeader &block) : nVersion{block.nVersion}, hashMerkleRoot{block.hashMerkleRoot}, nTime{block.nTime}, nBits{block.nBits}, nNonce{block.nNonce}, nTimeReceived{0} {} FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); FlatFilePos ret; if (nStatus.hasData()) { ret.nFile = nFile; ret.nPos = nDataPos; } return ret; } FlatFilePos GetUndoPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); FlatFilePos ret; if (nStatus.hasUndo()) { ret.nFile = nFile; ret.nPos = nUndoPos; } return ret; } CBlockHeader GetBlockHeader() const { CBlockHeader block; block.nVersion = nVersion; if (pprev) { block.hashPrevBlock = pprev->GetBlockHash(); } block.hashMerkleRoot = hashMerkleRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; return block; } BlockHash GetBlockHash() const { assert(phashBlock != nullptr); return *phashBlock; } /** * Get the number of transaction in the chain so far. */ int64_t GetChainTxCount() const { return nChainTx; } - /** - * Get the size of all the blocks in the chain so far. - */ - uint64_t GetChainSize() const { return nChainSize; } - /** * Update chain tx stats. */ bool UpdateChainStats(); /** * Check whether this block's and all previous blocks' transactions have * been downloaded (and stored to disk) at some point. * * Does not imply the transactions are consensus-valid (ConnectTip might * fail) Does not imply the transactions are still stored on disk. * (IsBlockPruned might return true) * * Note that this will be true for the snapshot base block, if one is * loaded (and all subsequent assumed-valid blocks) since its nChainTx * value will have been set manually based on the related AssumeutxoData * entry. */ bool HaveNumChainTxs() const { return GetChainTxCount() != 0; } NodeSeconds Time() const { return NodeSeconds{std::chrono::seconds{nTime}}; } int64_t GetBlockTime() const { return int64_t(nTime); } int64_t GetBlockTimeMax() const { return int64_t(nTimeMax); } int64_t GetHeaderReceivedTime() const { return nTimeReceived; } int64_t GetReceivedTimeDiff() const { return GetHeaderReceivedTime() - GetBlockTime(); } static constexpr int nMedianTimeSpan = 11; int64_t GetMedianTimePast() const { int64_t pmedian[nMedianTimeSpan]; int64_t *pbegin = &pmedian[nMedianTimeSpan]; int64_t *pend = &pmedian[nMedianTimeSpan]; const CBlockIndex *pindex = this; for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) { *(--pbegin) = pindex->GetBlockTime(); } std::sort(pbegin, pend); return pbegin[(pend - pbegin) / 2]; } std::string ToString() const; //! Check whether this block index entry is valid up to the passed validity //! level. bool IsValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); return nStatus.isValid(nUpTo); } //! @returns true if the block is assumed-valid; this means it is queued //! to be validated by a background chainstate. bool IsAssumedValid() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); return nStatus.isAssumedValid(); } //! Raise the validity level of this block index entry. //! Returns true if the validity was changed. bool RaiseValidity(enum BlockValidity nUpTo) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); // Only validity flags allowed. if (nStatus.isInvalid()) { return false; } if (nStatus.getValidity() >= nUpTo) { return false; } // If this block had been marked assumed-valid and we're raising // its validity to a certain point, there is no longer an assumption. if (IsAssumedValid() && nUpTo >= BlockValidity::SCRIPTS) { nStatus = nStatus.withClearedAssumedValidFlags(); } nStatus = nStatus.withValidity(nUpTo); return true; } //! Build the skiplist pointer for this entry. void BuildSkip(); //! Efficiently find an ancestor of this block. CBlockIndex *GetAncestor(int height); const CBlockIndex *GetAncestor(int height) const; }; #endif // BITCOIN_BLOCKINDEX_H diff --git a/src/blockvalidity.h b/src/blockvalidity.h index cb130866e..8f1551ec6 100644 --- a/src/blockvalidity.h +++ b/src/blockvalidity.h @@ -1,50 +1,50 @@ // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_BLOCKVALIDITY_H #define BITCOIN_BLOCKVALIDITY_H #include <cstdint> enum class BlockValidity : uint32_t { /** * Unused. */ UNKNOWN = 0, /** * Reserved (was HEADER). */ RESERVED = 1, /** * All parent headers found, difficulty matches, timestamp >= median * previous, checkpoint. Implies all parents are also at least TREE. */ TREE = 2, /** * Only first tx is coinbase, 2 <= coinbase input script length <= 100, * transactions valid, no duplicate txids, size, merkle root. * Implies all parents are at least TREE but not necessarily TRANSACTIONS. - * When all parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx and - * CBlockIndex::nChainSize will be set. + * When all parent blocks also have TRANSACTIONS, and CBlockIndex::nChainTx + * will be set. */ TRANSACTIONS = 3, /** * Outputs do not overspend inputs, no double spends, coinbase output ok, no * immature coinbase spends, BIP30. * Implies all parents are at least CHAIN, or are ASSUMED_VALID. */ CHAIN = 4, /** * Scripts & signatures ok. Implies all parents are either at least SCRIPTS, * or are ASSUMED_VALID. */ SCRIPTS = 5, }; #endif // BITCOIN_BLOCKVALIDITY_H diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp index 70784108e..5a0db72e1 100644 --- a/src/test/fuzz/chain.cpp +++ b/src/test/fuzz/chain.cpp @@ -1,108 +1,107 @@ // Copyright (c) 2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chain.h> #include <primitives/blockhash.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <cstdint> #include <optional> #include <vector> FUZZ_TARGET(chain) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::optional<CDiskBlockIndex> disk_block_index = ConsumeDeserializable<CDiskBlockIndex>(fuzzed_data_provider); if (!disk_block_index) { return; } const BlockHash zero{}; disk_block_index->phashBlock = &zero; { LOCK(::cs_main); (void)disk_block_index->ConstructBlockHash(); (void)disk_block_index->GetBlockPos(); (void)disk_block_index->GetBlockTime(); (void)disk_block_index->GetBlockTimeMax(); - (void)disk_block_index->GetChainSize(); (void)disk_block_index->GetChainTxCount(); (void)disk_block_index->GetHeaderReceivedTime(); (void)disk_block_index->GetMedianTimePast(); (void)disk_block_index->GetReceivedTimeDiff(); (void)disk_block_index->GetUndoPos(); (void)disk_block_index->HaveNumChainTxs(); (void)disk_block_index->IsValid(); (void)disk_block_index->UpdateChainStats(); } const CBlockHeader block_header = disk_block_index->GetBlockHeader(); (void)CDiskBlockIndex{*disk_block_index}; (void)disk_block_index->BuildSkip(); while (fuzzed_data_provider.ConsumeBool()) { const BlockValidity block_validity = fuzzed_data_provider.PickValueInArray({ BlockValidity::UNKNOWN, BlockValidity::RESERVED, BlockValidity::TREE, BlockValidity::TRANSACTIONS, BlockValidity::CHAIN, BlockValidity::SCRIPTS, }); const BlockStatus base; bool has_data = fuzzed_data_provider.ConsumeBool(); bool has_undo = fuzzed_data_provider.ConsumeBool(); bool has_failed = fuzzed_data_provider.ConsumeBool(); bool has_failed_parent = fuzzed_data_provider.ConsumeBool(); bool is_parked = fuzzed_data_provider.ConsumeBool(); bool has_parked_parent = fuzzed_data_provider.ConsumeBool(); bool is_assumed_valid = fuzzed_data_provider.ConsumeBool(); const BlockStatus block_status = base.withValidity(block_validity) .withData(has_data) .withUndo(has_undo) .withFailed(has_failed) .withFailedParent(has_failed_parent) .withParked(is_parked) .withParkedParent(has_parked_parent) .withAssumedValid(is_assumed_valid); assert(block_status.hasData() == has_data); assert(block_status.hasUndo() == has_undo); assert(block_status.hasFailed() == has_failed); assert(block_status.hasFailedParent() == has_failed_parent); assert(block_status.isParked() == is_parked); assert(block_status.hasParkedParent() == has_parked_parent); assert(block_status.isAssumedValid() == is_assumed_valid); assert(block_status.isInvalid() == has_failed || has_failed_parent); const BlockStatus valid_block = block_status.withClearedFailureFlags(); assert(!valid_block.isInvalid()); assert(block_status.isOnParkedChain() == is_parked || has_parked_parent); const BlockStatus unparked_block = block_status.withClearedParkedFlags(); assert(!unparked_block.isOnParkedChain()); const BlockStatus unassumed_valid_block = block_status.withClearedAssumedValidFlags(); assert(!unassumed_valid_block.isAssumedValid()); if (!block_status.isValid()) { continue; } WITH_LOCK(::cs_main, (void)disk_block_index->RaiseValidity(block_validity)); } CBlockIndex block_index{block_header}; block_index.phashBlock = &zero; (void)block_index.GetBlockHash(); (void)block_index.ToString(); }