Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
432 lines (358 sloc) 12.6 KB
// 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_CHAIN_H
#define BITCOIN_CHAIN_H
#include <arith_uint256.h>
#include <blockstatus.h>
#include <blockvalidity.h>
#include <consensus/params.h>
#include <crypto/common.h> // for ReadLE64
#include <flatfile.h>
#include <primitives/block.h>
#include <sync.h>
#include <tinyformat.h>
#include <uint256.h>
#include <unordered_map>
#include <vector>
/**
* Maximum amount of time that a block timestamp is allowed to exceed the
* current network-adjusted time before the block will be accepted.
*/
static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60;
/**
* Timestamp window used as a grace period by code that compares external
* timestamps (such as timestamps passed to RPCs, or wallet key creation times)
* to block timestamps. This should be set at least as high as
* MAX_FUTURE_BLOCK_TIME.
*/
static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
/**
* Maximum gap between node time and block time used
* for the "Catching up..." mode in GUI.
*
* Ref: https://github.com/bitcoin/bitcoin/pull/1026
*/
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 uint256 *phashBlock;
//! pointer to the index of the predecessor of this block
CBlockIndex *pprev;
//! pointer to the index of some further predecessor of this block
CBlockIndex *pskip;
//! height of the entry in the chain. The genesis block has height 0
int nHeight;
//! Which # file this block is stored in (blk?????.dat)
int nFile;
//! Byte offset within blk?????.dat where this block's data is stored
unsigned int nDataPos;
//! Byte offset within rev?????.dat where this block's undo data is stored
unsigned int nUndoPos;
//! (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
unsigned int nTx;
//! (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;
//! Verification status of this block. See enum BlockStatus
BlockStatus nStatus;
//! block header
int32_t nVersion;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
//! (memory only) Sequential id assigned to distinguish order in which
//! blocks are received.
int32_t nSequenceId;
//! (memory only) block header metadata
uint64_t nTimeReceived;
//! (memory only) Maximum nTime in the chain up to and including this block.
unsigned int nTimeMax;
void SetNull() {
phashBlock = nullptr;
pprev = nullptr;
pskip = nullptr;
nHeight = 0;
nFile = 0;
nDataPos = 0;
nUndoPos = 0;
nChainWork = arith_uint256();
nTx = 0;
nChainTx = 0;
nStatus = BlockStatus();
nSequenceId = 0;
nTimeMax = 0;
nVersion = 0;
hashMerkleRoot = uint256();
nTime = 0;
nTimeReceived = 0;
nBits = 0;
nNonce = 0;
}
CBlockIndex() { SetNull(); }
explicit CBlockIndex(const CBlockHeader &block) {
SetNull();
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;
}
uint256 GetBlockHash() const { return *phashBlock; }
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.
*/
struct BlockHasher {
// this used to call `GetCheapHash()` in uint256, which was later moved; the
// cheap hash function simply calls ReadLE64() however, so the end result is
// identical
size_t operator()(const uint256 &hash) const {
return ReadLE64(hash.begin());
}
};
typedef std::unordered_map<uint256, CBlockIndex *, BlockHasher> BlockMap;
extern BlockMap &mapBlockIndex;
extern CCriticalSection cs_main;
inline CBlockIndex *LookupBlockIndex(const uint256 &hash) {
AssertLockHeld(cs_main);
BlockMap::const_iterator it = mapBlockIndex.find(hash);
return it == mapBlockIndex.end() ? nullptr : it->second;
}
arith_uint256 GetBlockProof(const CBlockIndex &block);
/**
* Return the time it would take to redo the work difference between from and
* to, assuming the current hashrate corresponds to the difficulty at tip, in
* seconds.
*/
int64_t GetBlockProofEquivalentTime(const CBlockIndex &to,
const CBlockIndex &from,
const CBlockIndex &tip,
const Consensus::Params &);
/**
* Find the forking point between two chain tips.
*/
const CBlockIndex *LastCommonAncestor(const CBlockIndex *pa,
const CBlockIndex *pb);
/**
* Check if two block index are on the same fork.
*/
bool AreOnTheSameFork(const CBlockIndex *pa, const CBlockIndex *pb);
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex {
public:
uint256 hashPrev;
CDiskBlockIndex() { hashPrev = uint256(); }
explicit CDiskBlockIndex(const CBlockIndex *pindex) : CBlockIndex(*pindex) {
hashPrev = (pprev ? pprev->GetBlockHash() : uint256());
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int _nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(VARINT(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
}
READWRITE(VARINT(nHeight, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(nStatus);
READWRITE(VARINT(nTx));
if (nStatus.hasData() || nStatus.hasUndo()) {
READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
}
if (nStatus.hasData()) {
READWRITE(VARINT(nDataPos));
}
if (nStatus.hasUndo()) {
READWRITE(VARINT(nUndoPos));
}
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
}
uint256 GetBlockHash() const {
CBlockHeader block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block.GetHash();
}
std::string ToString() const {
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s)",
GetBlockHash().ToString(), hashPrev.ToString());
return str;
}
};
/**
* An in-memory indexed chain of blocks.
*/
class CChain {
private:
std::vector<CBlockIndex *> vChain;
public:
/**
* Returns the index entry for the genesis block of this chain, or nullptr
* if none.
*/
CBlockIndex *Genesis() const {
return vChain.size() > 0 ? vChain[0] : nullptr;
}
/**
* Returns the index entry for the tip of this chain, or nullptr if none.
*/
CBlockIndex *Tip() const {
return vChain.size() > 0 ? vChain[vChain.size() - 1] : nullptr;
}
/**
* Returns the index entry at a particular height in this chain, or nullptr
* if no such height exists.
*/
CBlockIndex *operator[](int nHeight) const {
if (nHeight < 0 || nHeight >= (int)vChain.size()) {
return nullptr;
}
return vChain[nHeight];
}
/** Compare two chains efficiently. */
friend bool operator==(const CChain &a, const CChain &b) {
return a.vChain.size() == b.vChain.size() &&
a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
}
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
return (*this)[pindex->nHeight] == pindex;
}
/**
* Find the successor of a block in this chain, or nullptr if the given
* index is not found or is the tip.
*/
CBlockIndex *Next(const CBlockIndex *pindex) const {
if (!Contains(pindex)) {
return nullptr;
}
return (*this)[pindex->nHeight + 1];
}
/**
* Return the maximal height in the chain. Is equal to chain.Tip() ?
* chain.Tip()->nHeight : -1.
*/
int Height() const { return vChain.size() - 1; }
/** Set/initialize a chain with a given tip. */
void SetTip(CBlockIndex *pindex);
/**
* Return a CBlockLocator that refers to a block in this chain (by default
* the tip).
*/
CBlockLocator GetLocator(const CBlockIndex *pindex = nullptr) const;
/**
* Find the last common block between this chain and a block index entry.
*/
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
/**
* Find the earliest block with timestamp equal or greater than the given.
*/
CBlockIndex *FindEarliestAtLeast(int64_t nTime) const;
};
#endif // BITCOIN_CHAIN_H
You can’t perform that action at this time.