// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef BITCOIN_CHAIN_H
#define BITCOIN_CHAIN_H
+class CChainPower;
+
#include "arith_uint256.h"
#include "primitives/block.h"
#include "pow.h"
#include "tinyformat.h"
#include "uint256.h"
+#include "mmr.h"
#include <vector>
-#include <boost/foreach.hpp>
-
static const int SPROUT_VALUE_VERSION = 1001400;
+static const int SAPLING_VALUE_VERSION = 1010100;
+
+class CBlockFileInfo
+{
+public:
+ unsigned int nBlocks; //!< number of blocks stored in file
+ unsigned int nSize; //!< number of used bytes of block file
+ unsigned int nUndoSize; //!< number of used bytes in the undo file
+ unsigned int nHeightFirst; //!< lowest height of block in file
+ unsigned int nHeightLast; //!< highest height of block in file
+ uint64_t nTimeFirst; //!< earliest time of block in file
+ uint64_t nTimeLast; //!< latest time of block in file
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(VARINT(nBlocks));
+ READWRITE(VARINT(nSize));
+ READWRITE(VARINT(nUndoSize));
+ READWRITE(VARINT(nHeightFirst));
+ READWRITE(VARINT(nHeightLast));
+ READWRITE(VARINT(nTimeFirst));
+ READWRITE(VARINT(nTimeLast));
+ }
+
+ void SetNull() {
+ nBlocks = 0;
+ nSize = 0;
+ nUndoSize = 0;
+ nHeightFirst = 0;
+ nHeightLast = 0;
+ nTimeFirst = 0;
+ nTimeLast = 0;
+ }
+
+ CBlockFileInfo() {
+ SetNull();
+ }
+
+ std::string ToString() const;
+
+ /** update statistics (does not update nSize) */
+ void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) {
+ if (nBlocks==0 || nHeightFirst > nHeightIn)
+ nHeightFirst = nHeightIn;
+ if (nBlocks==0 || nTimeFirst > nTimeIn)
+ nTimeFirst = nTimeIn;
+ nBlocks++;
+ if (nHeightIn > nHeightLast)
+ nHeightLast = nHeightIn;
+ if (nTimeIn > nTimeLast)
+ nTimeLast = nTimeIn;
+ }
+};
struct CDiskBlockPos
{
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(nFile));
READWRITE(VARINT(nPos));
}
//! Blocks with this validity are assumed to satisfy all consensus rules.
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;
+class CBlockIndex;
+
+// This class provides an accumulator for both the chainwork and the chainPOS value
+// CChainPower's can be compared, and the comparison ensures that work and proof of stake power
+// are both used equally to determine which chain has the most work. This makes an attack
+// that involves mining in secret completely ineffective, even before dPOW, unless a large part
+// of the staking supply is also controlled. It also enables a faster deterministic convergence,
+// aided by both POS and POW.
+class CChainPower
+{
+ public:
+ arith_uint256 chainWork;
+ arith_uint256 chainStake;
+ int32_t nHeight;
+
+ CChainPower() : nHeight(0), chainStake(0), chainWork(0) {}
+ CChainPower(CBlockIndex *pblockIndex);
+ CChainPower(CBlockIndex *pblockIndex, const arith_uint256 &stake, const arith_uint256 &work);
+ CChainPower(int32_t height) : nHeight(height), chainStake(0), chainWork(0) {}
+ CChainPower(int32_t height, const arith_uint256 &stake, const arith_uint256 &work) :
+ nHeight(height), chainStake(stake), chainWork(work) {}
+
+ CChainPower &operator=(const CChainPower &chainPower)
+ {
+ chainWork = chainPower.chainWork;
+ chainStake = chainPower.chainStake;
+ nHeight = chainPower.nHeight;
+ return *this;
+ }
+
+ CChainPower &operator+=(const CChainPower &chainPower)
+ {
+ this->chainWork += chainPower.chainWork;
+ this->chainStake += chainPower.chainStake;
+ return *this;
+ }
+
+ friend CChainPower operator+(const CChainPower &chainPowerA, const CChainPower &chainPowerB)
+ {
+ CChainPower result = CChainPower(chainPowerA);
+ result.chainWork += chainPowerB.chainWork;
+ result.chainStake += chainPowerB.chainStake;
+ return result;
+ }
+
+ friend CChainPower operator-(const CChainPower &chainPowerA, const CChainPower &chainPowerB)
+ {
+ CChainPower result = CChainPower(chainPowerA);
+ result.chainWork -= chainPowerB.chainWork;
+ result.chainStake -= chainPowerB.chainStake;
+ return result;
+ }
+
+ friend CChainPower operator*(const CChainPower &chainPower, int32_t x)
+ {
+ CChainPower result = CChainPower(chainPower);
+ result.chainWork *= x;
+ result.chainStake *= x;
+ return result;
+ }
+
+ CChainPower &addStake(const arith_uint256 &nChainStake)
+ {
+ chainStake += nChainStake;
+ return *this;
+ }
+
+ CChainPower &addWork(const arith_uint256 &nChainWork)
+ {
+ chainWork += nChainWork;
+ return *this;
+ }
+
+ friend bool operator==(const CChainPower &p1, const CChainPower &p2);
+
+ friend bool operator!=(const CChainPower &p1, const CChainPower &p2)
+ {
+ return !(p1 == p2);
+ }
+
+ friend bool operator<(const CChainPower &p1, const CChainPower &p2);
+
+ friend bool operator<=(const CChainPower &p1, const CChainPower &p2);
+
+ friend bool operator>(const CChainPower &p1, const CChainPower &p2)
+ {
+ return !(p1 <= p2);
+ }
+
+ friend bool operator>=(const CChainPower &p1, const CChainPower &p2)
+ {
+ return !(p1 < p2);
+ }
+
+ uint256 CompactChainPower() const;
+ static CChainPower ExpandCompactPower(uint256 compactPower, uint32_t height = 0);
+};
+
/** 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
//! 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;
- int64_t newcoins,zfunds;
+ int64_t newcoins;
+ int64_t zfunds;
+ int64_t immature; // how much in this block is immature
+ uint32_t maturity; // when do the immature funds in this block mature?
+
+ int8_t segid; // jl777 fields
+
//! Which # file this block is stored in (blk?????.dat)
int nFile;
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;
+ CChainPower chainPower;
//! Number of transactions in this block.
//! Note: in a potential headers-first mode, this number cannot be relied upon
boost::optional<uint32_t> nCachedBranchId;
//! The anchor for the tree state up to the start of this block
- uint256 hashAnchor;
+ uint256 hashSproutAnchor;
//! (memory only) The anchor for the tree state up to the end of this block
- uint256 hashAnchorEnd;
+ uint256 hashFinalSproutRoot;
//! Change in value held by the Sprout circuit over this block.
//! Will be boost::none for older blocks on old nodes until a reindex has taken place.
//! Will be boost::none if nChainTx is zero.
boost::optional<CAmount> nChainSproutValue;
+ //! Change in value held by the Sapling circuit over this block.
+ //! Not a boost::optional because this was added before Sapling activated, so we can
+ //! rely on the invariant that every block before this was added had nSaplingValue = 0.
+ CAmount nSaplingValue;
+
+ //! (memory only) Total value held by the Sapling circuit up to and including this block.
+ //! Will be boost::none if nChainTx is zero.
+ boost::optional<CAmount> nChainSaplingValue;
+
//! block header
int nVersion;
uint256 hashMerkleRoot;
- uint256 hashReserved;
+ uint256 hashFinalSaplingRoot;
unsigned int nTime;
unsigned int nBits;
uint256 nNonce;
{
phashBlock = NULL;
newcoins = zfunds = 0;
+ maturity = 0;
+ immature = 0;
+ segid = -2;
pprev = NULL;
pskip = NULL;
- nHeight = 0;
nFile = 0;
nDataPos = 0;
nUndoPos = 0;
- nChainWork = arith_uint256();
+ chainPower = CChainPower();
nTx = 0;
nChainTx = 0;
nStatus = 0;
nCachedBranchId = boost::none;
- hashAnchor = uint256();
- hashAnchorEnd = uint256();
+ hashSproutAnchor = uint256();
+ hashFinalSproutRoot = uint256();
nSequenceId = 0;
nSproutValue = boost::none;
nChainSproutValue = boost::none;
+ nSaplingValue = 0;
+ nChainSaplingValue = boost::none;
nVersion = 0;
hashMerkleRoot = uint256();
- hashReserved = uint256();
+ hashFinalSaplingRoot = uint256();
nTime = 0;
nBits = 0;
nNonce = uint256();
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
- hashReserved = block.hashReserved;
+ hashFinalSaplingRoot = block.hashFinalSaplingRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
nSolution = block.nSolution;
}
+ void SetHeight(int32_t height)
+ {
+ this->chainPower.nHeight = height;
+ }
+
+ inline int32_t GetHeight() const
+ {
+ return this->chainPower.nHeight;
+ }
+
CDiskBlockPos GetBlockPos() const {
CDiskBlockPos ret;
if (nStatus & BLOCK_HAVE_DATA) {
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
- block.hashReserved = hashReserved;
+ block.hashFinalSaplingRoot = hashFinalSaplingRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
std::string ToString() const
{
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
- pprev, nHeight,
+ pprev, this->chainPower.nHeight,
hashMerkleRoot.ToString(),
GetBlockHash().ToString());
}
{
return GetBlockHeader().IsVerusPOSBlock();
}
+
+ bool GetRawVerusPOSHash(uint256 &ret) const;
+ uint256 GetVerusEntropyHashComponent() const;
+
+ uint256 BlockMMRRoot() const
+ {
+ if (nVersion == CBlockHeader::VERUS_V2)
+ {
+ CPBaaSSolutionDescriptor descr = CConstVerusSolutionVector::GetDescriptor((nSolution));
+ if (descr.version >= CActivationHeight::ACTIVATE_PBAAS)
+ {
+ return descr.hashBlockMMRRoot;
+ }
+ }
+ return hashMerkleRoot;
+ }
+
+ uint256 PrevMMRRoot()
+ {
+ if (nVersion == CBlockHeader::VERUS_V2)
+ {
+ CPBaaSSolutionDescriptor descr = CConstVerusSolutionVector::GetDescriptor(nSolution);
+ if (descr.version >= CActivationHeight::ACTIVATE_PBAAS)
+ {
+ return descr.hashPrevMMRRoot;
+ }
+ }
+ return uint256();
+ }
+
+ // return a node from this block index as is, including hash of merkle root and block hash as well as compact chain power, to put into an MMR
+ ChainMMRNode GetBlockMMRNode() const
+ {
+ uint256 blockHash = GetBlockHash();
+
+ uint256 preHash = ChainMMRNode::HashObj(BlockMMRRoot(), blockHash);
+ uint256 power = ArithToUint256(GetCompactPower(nNonce, nBits, nVersion));
+
+ return ChainMMRNode(ChainMMRNode::HashObj(preHash, power), power);
+ }
+
+ CMMRNodeBranch MMRProofBridge()
+ {
+ // we need to add the block hash on the right, no change to index, as bit is zero
+ return CMMRNodeBranch(CMMRNodeBranch::BRANCH_MMRBLAKE_NODE, 2, 0, std::vector<uint256>({GetBlockHash()}));
+ }
+
+ CMMRNodeBranch BlockProofBridge()
+ {
+ // we need to add the merkle root on the left
+ return CMMRNodeBranch(CMMRNodeBranch::BRANCH_MMRBLAKE_NODE, 2, 1, std::vector<uint256>({BlockMMRRoot()}));
+ }
};
/** Used to marshal pointers into hashes for db storage. */
public:
uint256 hashPrev;
- CDiskBlockIndex() {
+ CDiskBlockIndex() : CBlockIndex() {
hashPrev = uint256();
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- if (!(nType & SER_GETHASH))
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+#ifdef VERUSHASHDEBUG
+ if (!ser_action.ForRead()) printf("Serializing block index %s, stream version: %x\n", ToString().c_str(), nVersion);
+#endif
+ if (!(s.GetType() & SER_GETHASH))
READWRITE(VARINT(nVersion));
- READWRITE(VARINT(nHeight));
+ if (ser_action.ForRead()) {
+ chainPower = CChainPower();
+ }
+ READWRITE(VARINT(chainPower.nHeight));
READWRITE(VARINT(nStatus));
READWRITE(VARINT(nTx));
if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO))
READWRITE(branchId);
}
}
- READWRITE(hashAnchor);
+ READWRITE(hashSproutAnchor);
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
- READWRITE(hashReserved);
+ READWRITE(hashFinalSaplingRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
// Only read/write nSproutValue if the client version used to create
// this index was storing them.
- if ((nType & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) {
+ if ((s.GetType() & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) {
READWRITE(nSproutValue);
}
+
+ // Only read/write nSaplingValue if the client version used to create
+ // this index was storing them.
+ if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) {
+ READWRITE(nSaplingValue);
+ }
+
+ // If you have just added new serialized fields above, remember to add
+ // them to CBlockTreeDB::LoadBlockIndexGuts() in txdb.cpp :)
}
uint256 GetBlockHash() const
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
- block.hashReserved = hashReserved;
+ block.hashFinalSaplingRoot = hashFinalSaplingRoot;
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());
+ std::string str = "CDiskBlockIndex:\n";
+
+ CBlockHeader block;
+ block.nVersion = nVersion;
+ block.hashPrevBlock = hashPrev;
+ block.hashMerkleRoot = hashMerkleRoot;
+ block.hashFinalSaplingRoot = hashFinalSaplingRoot;
+ block.nTime = nTime;
+ block.nBits = nBits;
+ block.nNonce = nNonce;
+ block.nSolution = nSolution;
+ CPBaaSPreHeader preBlock(block);
+
+ str += strprintf("block.nVersion=%x\npprev=%p\nnHeight=%d\nhashBlock=%s\nblock.hashPrevBlock=%s\nblock.hashMerkleRoot=%s\nblock.nBits=%d\nblock.nNonce=%s\nblock.nSolution=%s\npreBlock.hashPrevMMRRoot=%s\npreBlock.hashBlockMMRRoot=%s\n",
+ this->nVersion, pprev, this->chainPower.nHeight, GetBlockHash().ToString(), hashPrev.ToString(), hashMerkleRoot.ToString(), nBits, nNonce.ToString(), HexBytes(nSolution.data(), nSolution.size()), preBlock.hashPrevMMRRoot.ToString(), preBlock.hashBlockMMRRoot.ToString());
+
return str;
}
};
-/** An in-memory indexed chain of blocks. */
+class CChain;
+typedef CMerkleMountainRange<ChainMMRNode, CChunkedLayer<ChainMMRNode, 9>, COverlayNodeLayer<ChainMMRNode, CChain>> ChainMerkleMountainRange;
+typedef CMerkleMountainView<ChainMMRNode, CChunkedLayer<ChainMMRNode, 9>, COverlayNodeLayer<ChainMMRNode, CChain>> ChainMerkleMountainView;
+
+/** An in-memory indexed chain of blocks.
+ * With Verus and PBaaS chains, this also provides a complete Merkle Mountain Range (MMR) for the chain at all times,
+ * enabling proof of any transaction that can be exported and trusted on any chain that has a trusted oracle or other lite proof of this chain.
+*/
class CChain {
private:
std::vector<CBlockIndex*> vChain;
+ ChainMerkleMountainRange mmr;
CBlockIndex *lastTip;
public:
+ CChain() : vChain(), mmr(COverlayNodeLayer<ChainMMRNode, CChain>(*this)) {}
+
/** Returns the index entry for the genesis block of this chain, or NULL if none. */
CBlockIndex *Genesis() const {
return vChain.size() > 0 ? vChain[0] : NULL;
return vChain[nHeight];
}
+ uint256 GetVerusEntropyHash(int forHeight, int *pPOSheight=nullptr, int *pPOWheight=nullptr, int *pALTheight=nullptr) const;
+
+ /** Get the Merkle Mountain Range for this chain. */
+ const ChainMerkleMountainRange &GetMMR()
+ {
+ return mmr;
+ }
+
+ /** Get a Merkle Mountain Range view for this chain. */
+ ChainMerkleMountainView GetMMV()
+ {
+ return ChainMerkleMountainView(mmr, mmr.size());
+ }
+
+ ChainMMRNode GetMMRNode(int index) const
+ {
+ return vChain[index]->GetBlockMMRNode();
+ }
+
+ bool GetBlockProof(ChainMerkleMountainView &view, CMMRProof &retProof, int index) const;
+ bool GetMerkleProof(ChainMerkleMountainView &view, CMMRProof &retProof, int index) const;
+
/** Compare two chains efficiently. */
friend bool operator==(const CChain &a, const CChain &b) {
return a.vChain.size() == b.vChain.size() &&
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
- return (*this)[pindex->nHeight] == pindex;
+ return !pindex ? false : (*this)[pindex->GetHeight()] == pindex;
}
/** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */
CBlockIndex *Next(const CBlockIndex *pindex) const {
if (Contains(pindex))
- return (*this)[pindex->nHeight + 1];
+ return (*this)[pindex->GetHeight() + 1];
else
return NULL;
}
- /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */
+ /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->GetHeight() : -1. */
int Height() const {
return vChain.size() - 1;
}
+ uint64_t size()
+ {
+ return vChain.size();
+ }
+
/** Set/initialize a chain with a given tip. */
void SetTip(CBlockIndex *pindex);