diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -505,6 +505,7 @@ banman.cpp blockencodings.cpp blockfilter.cpp + blockindex.cpp chain.cpp checkpoints.cpp config.cpp diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -127,6 +127,7 @@ blockencodings.h \ blockfileinfo.h \ blockfilter.h \ + blockindex.h \ blockindexworkcomparator.h \ blockstatus.h \ blockvalidity.h \ @@ -309,6 +310,7 @@ banman.cpp \ blockencodings.cpp \ blockfilter.cpp \ + blockindex.cpp \ chain.cpp \ checkpoints.cpp \ config.cpp \ diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -6,6 +6,7 @@ #include <avalanche/peermanager.h> #include <avalanche/test/util.h> +#include <chain.h> #include <config.h> #include <net_processing.h> // For PeerLogicValidation #include <util/time.h> diff --git a/src/blockindex.h b/src/blockindex.h new file mode 100644 --- /dev/null +++ b/src/blockindex.h @@ -0,0 +1,212 @@ +// 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 <primitives/block.h> +#include <tinyformat.h> +#include <uint256.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 = 0; + + //! Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos = 0; + + //! Byte offset within rev?????.dat where this block's undo data is stored + unsigned int nUndoPos = 0; + + //! (memory only) Total amount of work (expected number of hashes) in the + //! chain up to and including this block + arith_uint256 nChainWork = arith_uint256(); + + //! Number of transactions in this block. + //! Note: in a potential headers-first mode, this number cannot be relied + //! upon + unsigned int nTx = 0; + +private: + //! (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 + unsigned int nChainTx = 0; + +public: + //! Verification status of this block. See enum BlockStatus + BlockStatus nStatus = BlockStatus(); + + //! block header + int32_t nVersion = 0; + uint256 hashMerkleRoot = uint256(); + 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 + uint64_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) : CBlockIndex() { + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nTimeReceived = 0; + nBits = block.nBits; + nNonce = block.nNonce; + } + + FlatFilePos GetBlockPos() const { + FlatFilePos ret; + if (nStatus.hasData()) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } + return ret; + } + + FlatFilePos GetUndoPos() const { + 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 { return *phashBlock; } + + /** + * Get the number of transaction in the chain so far. + */ + int64_t GetChainTxCount() const { return nChainTx; } + + /** + * 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) + */ + bool HaveTxsDownloaded() const { return GetChainTxCount() != 0; } + + 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 { + return strprintf( + "CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, + nHeight, hashMerkleRoot.ToString(), GetBlockHash().ToString()); + } + + //! Check whether this block index entry is valid up to the passed validity + //! level. + bool IsValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { + return nStatus.isValid(nUpTo); + } + + //! Raise the validity level of this block index entry. + //! Returns true if the validity was changed. + bool RaiseValidity(enum BlockValidity nUpTo) { + // Only validity flags allowed. + if (nStatus.isInvalid()) { + return false; + } + + if (nStatus.getValidity() >= nUpTo) { + return false; + } + + 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/blockindex.cpp b/src/blockindex.cpp new file mode 100644 --- /dev/null +++ b/src/blockindex.cpp @@ -0,0 +1,77 @@ +// 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> + +/** + * 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); +} + +bool CBlockIndex::UpdateChainStats() { + if (pprev == nullptr) { + nChainTx = nTx; + return true; + } + + if (pprev->HaveTxsDownloaded()) { + nChainTx = pprev->nChainTx + nTx; + return true; + } + + nChainTx = 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/blockindexworkcomparator.h b/src/blockindexworkcomparator.h --- a/src/blockindexworkcomparator.h +++ b/src/blockindexworkcomparator.h @@ -5,8 +5,7 @@ #ifndef BITCOIN_BLOCKINDEXWORKCOMPARATOR_H #define BITCOIN_BLOCKINDEXWORKCOMPARATOR_H -// TODO: Split chain.h apart and only include CBlockIndex -#include <chain.h> +#include <blockindex.h> struct CBlockIndexWorkComparator { bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const { diff --git a/src/chain.h b/src/chain.h --- a/src/chain.h +++ b/src/chain.h @@ -7,6 +7,7 @@ #define BITCOIN_CHAIN_H #include <arith_uint256.h> +#include <blockindex.h> #include <blockstatus.h> #include <blockvalidity.h> #include <consensus/params.h> @@ -42,201 +43,6 @@ */ static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60; -/** - * 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 = 0; - - //! Byte offset within blk?????.dat where this block's data is stored - unsigned int nDataPos = 0; - - //! Byte offset within rev?????.dat where this block's undo data is stored - unsigned int nUndoPos = 0; - - //! (memory only) Total amount of work (expected number of hashes) in the - //! chain up to and including this block - arith_uint256 nChainWork = arith_uint256(); - - //! Number of transactions in this block. - //! Note: in a potential headers-first mode, this number cannot be relied - //! upon - unsigned int nTx = 0; - -private: - //! (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 - unsigned int nChainTx = 0; - -public: - //! Verification status of this block. See enum BlockStatus - BlockStatus nStatus = BlockStatus(); - - //! block header - int32_t nVersion = 0; - uint256 hashMerkleRoot = uint256(); - 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 - uint64_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) : CBlockIndex() { - nVersion = block.nVersion; - hashMerkleRoot = block.hashMerkleRoot; - nTime = block.nTime; - nTimeReceived = 0; - nBits = block.nBits; - nNonce = block.nNonce; - } - - FlatFilePos GetBlockPos() const { - FlatFilePos ret; - if (nStatus.hasData()) { - ret.nFile = nFile; - ret.nPos = nDataPos; - } - return ret; - } - - FlatFilePos GetUndoPos() const { - 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 { return *phashBlock; } - - /** - * Get the number of transaction in the chain so far. - */ - int64_t GetChainTxCount() const { return nChainTx; } - - /** - * 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) - */ - bool HaveTxsDownloaded() const { return GetChainTxCount() != 0; } - - 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 { - return strprintf( - "CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, - nHeight, hashMerkleRoot.ToString(), GetBlockHash().ToString()); - } - - //! Check whether this block index entry is valid up to the passed validity - //! level. - bool IsValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { - return nStatus.isValid(nUpTo); - } - - //! Raise the validity level of this block index entry. - //! Returns true if the validity was changed. - bool RaiseValidity(enum BlockValidity nUpTo) { - // Only validity flags allowed. - if (nStatus.isInvalid()) { - return false; - } - - if (nStatus.getValidity() >= nUpTo) { - return false; - } - - 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; -}; - /** * Maintain a map of CBlockIndex for all known headers. */ diff --git a/src/chain.cpp b/src/chain.cpp --- a/src/chain.cpp +++ b/src/chain.cpp @@ -77,78 +77,6 @@ return (lower == vChain.end() ? nullptr : *lower); } -bool CBlockIndex::UpdateChainStats() { - if (pprev == nullptr) { - nChainTx = nTx; - return true; - } - - if (pprev->HaveTxsDownloaded()) { - nChainTx = pprev->nChainTx + nTx; - return true; - } - - nChainTx = 0; - return false; -} - -/** - * 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); -} - -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)); - } -} - arith_uint256 GetBlockProof(const CBlockIndex &block) { arith_uint256 bnTarget; bool fNegative;