src/main.h
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MAIN_H
#define BITCOIN_MAIN_H
#include "core.h"
#include "bignum.h"
#include "sync.h"
#include "txmempool.h"
#include "net.h"
#include "script.h"
#include "scrypt.h"
#include "state.h"
#include <list>
class CWallet;
class CWalletTx;
class CBlockHeader;
class CBlock;
class CBlockThin;
class CBlockIndex;
class CBlockThinIndex;
class CKeyItem;
class CReserveKey;
class CAddress;
class CInv;
class CRequestTracker;
class CNode;
static const unsigned int MAX_BLOCK_SIZE = 1000000;
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
/** Default for -maxorphanblocksmib, maximum number of memory to keep orphan blocks */
static const unsigned int DEFAULT_MAX_ORPHAN_BLOCKS = 40;
static const unsigned int MAX_INV_SZ = 50000;
static const unsigned int MAX_GETHEADERS_SZ = 2000;
static const unsigned int MAX_MULTI_BLOCK_SIZE = 5120000; // 5MiB, most likely to hit MAX_MULTI_BLOCK_ELEMNTS first
static const unsigned int MAX_MULTI_BLOCK_ELEMENTS = 64; // processing larger blocks is cpu intensive
static const unsigned int MAX_MULTI_BLOCK_THIN_ELEMENTS = 128;
/** No amount larger than this (in satoshi) is valid */
static const int64_t MAX_MONEY = std::numeric_limits<int64_t>::max();
inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
inline int64_t FutureDriftV1(int64_t nTime) { return nTime + 10 * 60; }
inline int64_t FutureDriftV2(int64_t nTime) { return nTime + 15; }
inline int64_t FutureDrift(int64_t nTime, int nHeight) { return Params().IsProtocolV2(nHeight) ? FutureDriftV2(nTime) : FutureDriftV1(nTime); }
inline unsigned int GetTargetSpacing(int nHeight) { return Params().IsProtocolV2(nHeight) ? 64 : 60; }
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
extern std::map<uint256, CBlockThinIndex*> mapBlockThinIndex;
extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeenOrphan;
extern CBlockIndex* pindexGenesisBlock;
extern CBlockThinIndex* pindexGenesisBlockThin;
extern CBlockThinIndex* pindexRear;
extern unsigned int nStakeMinAge;
extern unsigned int nNodeLifespan;
extern int nCoinbaseMaturity;
extern int nBestHeight;
extern int nHeightFilteredNeeded;
extern uint256 nBestChainTrust;
extern uint256 nBestInvalidTrust;
extern uint256 hashBestChain;
extern CBlockIndex* pindexBest;
extern CBlockThinIndex* pindexBestHeader;
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern int64_t nLastCoinStakeSearchInterval;
extern const std::string strMessageMagic;
extern int64_t nTimeBestReceived;
extern bool fImporting;
extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
struct COrphanBlock {
uint256 hashBlock;
uint256 hashPrev;
std::pair<COutPoint, unsigned int> stake;
std::vector<unsigned char> vchBlock;
};
extern std::map<uint256, COrphanBlock*> mapOrphanBlocks;
extern std::map<uint256, CBlockThin*> mapOrphanBlockThins;
extern std::map<int64_t, CAnonOutputCount> mapAnonOutputStats;
extern CTxMemPool mempool;
// Settings
extern int64_t nTransactionFee;
extern int64_t nReserveBalance;
extern int64_t nMinimumInputValue;
extern bool fUseFastIndex;
extern bool fEnforceCanonical;
// Minimum disk space required - used in CheckDiskSpace()
static const uint64_t nMinDiskSpace = 52428800;
class CReserveKey;
class CTxDB;
class CTxIndex;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true);
bool ProcessBlock(CNode* pfrom, CBlock* pblock, uint256& hash);
bool CheckDiskSpace(uint64_t nAdditionalBytes=0);
FILE* OpenBlockFile(bool fHeaderFile, unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
FILE* AppendBlockFile(bool fHeaderFile, unsigned int& nFileRet, const char* fmode = "ab");
int LoadBlockIndex(bool fAllowNew=true);
void PrintBlockTree();
CBlockIndex* FindBlockByHeight(int nHeight);
CBlockThinIndex* FindBlockThinByHeight(int nHeight);
bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, std::vector<CNode*> &vNodesCopy, bool fSendTrickle);
bool LoadExternalBlockFile(int nFile, FILE* fileIn);
void ThreadImport(std::vector<boost::filesystem::path> vImportFiles);
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
unsigned int GetNextTargetRequiredThin(const CBlockThinIndex* pindexLast, bool fProofOfStake);
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
bool IsConfirmedInNPrevBlocks(const CTxIndex& txindex, const CBlockIndex* pindexFrom, int nMaxDepth, int& nActualDepth);
std::string GetWarnings(std::string strFor);
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
bool GetTransactionBlockHash(const uint256 &hash, uint256 &hashBlock);
bool GetKeyImage(CTxDB* ptxdb, ec_point& keyImage, CKeyImageSpent& keyImageSpent, bool& fInMempool);
bool TxnHashInSystem(CTxDB* ptxdb, uint256& txnHash);
uint256 WantedByOrphan(const CBlock* pblockOrphan);
uint256 WantedByOrphanHeader(const CBlockThin* pblockOrphan);
const COrphanBlock* AddOrphanBlock(const CBlock* pblock);
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
const CBlockThinIndex* GetLastBlockThinIndex(const CBlockThinIndex* pindex, bool fProofOfStake);
void ResendWalletTransactions(bool fForce = false);
bool ChangeNodeState(int newState, bool fProcess = true);
bool AddDataToMerkleFilters(const std::vector<unsigned char>& vData);
bool AddKeyToMerkleFilters(const CTxDestination& address);
bool GetCoinAgeThin(CTransaction txCoinStake, uint64_t& nCoinAge, std::vector<const CWalletTx*> &vWtxPrev);
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
/** Position on disk for a particular transaction. */
class CDiskTxPos
{
public:
unsigned int nFile;
unsigned int nBlockPos;
unsigned int nTxPos;
CDiskTxPos()
{
SetNull();
}
CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn)
{
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nTxPos = nTxPosIn;
}
IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; }
bool IsNull() const { return (nFile == (unsigned int) -1); }
friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
{
return (a.nFile == b.nFile &&
a.nBlockPos == b.nBlockPos &&
a.nTxPos == b.nTxPos);
}
friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b)
{
return !(a == b);
}
std::string ToString() const
{
if (IsNull())
return "null";
else
return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos);
}
void print() const
{
LogPrintf("%s", ToString().c_str());
}
};
typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
public:
static const int CURRENT_VERSION=1;
int nVersion;
unsigned int nTime;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
unsigned int nLockTime;
// Denial-of-service detection:
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CTransaction()
{
SetNull();
}
IMPLEMENT_SERIALIZE
(
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nTime);
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
)
void SetNull()
{
nVersion = CTransaction::CURRENT_VERSION;
nTime = GetAdjustedTime();
vin.clear();
vout.clear();
nLockTime = 0;
nDoS = 0; // Denial-of-service prevention
}
bool IsNull() const
{
return (vin.empty() && vout.empty());
}
uint256 GetHash() const
{
return SerializeHash(*this);
}
bool IsFinal(int nBlockHeight=0, int64_t nBlockTime=0) const
{
AssertLockHeld(cs_main);
// Time based nLockTime implemented in 0.1.6
if (nLockTime == 0)
return true;
if (nBlockHeight == 0)
nBlockHeight = nBestHeight;
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
if ((int64_t)nLockTime < ((int64_t)nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
BOOST_FOREACH(const CTxIn& txin, vin)
if (!txin.IsFinal())
return false;
return true;
}
bool IsNewerThan(const CTransaction& old) const
{
if (vin.size() != old.vin.size())
return false;
for (unsigned int i = 0; i < vin.size(); i++)
if (vin[i].prevout != old.vin[i].prevout)
return false;
bool fNewer = false;
unsigned int nLowest = std::numeric_limits<unsigned int>::max();
for (unsigned int i = 0; i < vin.size(); i++)
{
if (vin[i].nSequence != old.vin[i].nSequence)
{
if (vin[i].nSequence <= nLowest)
{
fNewer = false;
nLowest = vin[i].nSequence;
};
if (old.vin[i].nSequence < nLowest)
{
fNewer = true;
nLowest = old.vin[i].nSequence;
};
};
};
return fNewer;
}
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1);
}
bool IsCoinStake() const
{
// ppcoin: the coin stake transaction is marked with the first output empty
return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
}
/** Check for standard transaction types
@return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandard() const;
/** Check for standard transaction types
@param[in] mapInputs Map of previous transactions that have outputs we're spending
@return True if all inputs (scriptSigs) use only standard transaction forms
@see CTransaction::FetchInputs
*/
bool AreInputsStandard(const MapPrevTx& mapInputs) const;
bool HasStealthOutput() const;
/** Count ECDSA signature operations the old-fashioned (pre-0.6) way
@return number of sigops this transaction's outputs will produce when spent
@see CTransaction::FetchInputs
*/
unsigned int GetLegacySigOpCount() const;
/** Count ECDSA signature operations in pay-to-script-hash inputs.
@param[in] mapInputs Map of previous transactions that have outputs we're spending
@return maximum number of sigops required to validate this transaction's inputs
@see CTransaction::FetchInputs
*/
unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
/** Amount of bitcoins spent by this transaction.
@return sum of all outputs (note: does not include fees)
*/
int64_t GetValueOut() const
{
int64_t nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, vout)
{
nValueOut += txout.nValue;
if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
throw std::runtime_error("CTransaction::GetValueOut() : value out of range");
}
return nValueOut;
}
/** Amount of bitcoins coming in to this transaction
Note that lightweight clients may not know anything besides the hash of previous transactions,
so may not be able to calculate this.
@param[in] mapInputs Map of previous transactions that have outputs we're spending
@return Sum of value of all inputs (scriptSigs)
@see CTransaction::FetchInputs
*/
int64_t GetValueIn(const MapPrevTx& mapInputs) const;
int64_t GetMinFee(unsigned int nBlockSize=1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const;
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
{
CAutoFile filein = CAutoFile(OpenBlockFile(false, pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION);
if (!filein)
return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
// Read transaction
if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
return error("CTransaction::ReadFromDisk() : fseek failed");
try {
filein >> *this;
} catch (std::exception &e)
{
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
};
// Return file pointer
if (pfileRet)
{
if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
return error("CTransaction::ReadFromDisk() : second fseek failed");
*pfileRet = filein.release();
};
return true;
}
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return (a.nVersion == b.nVersion &&
a.nTime == b.nTime &&
a.vin == b.vin &&
a.vout == b.vout &&
a.nLockTime == b.nLockTime);
}
friend bool operator!=(const CTransaction& a, const CTransaction& b)
{
return !(a == b);
}
std::string ToString() const
{
std::string str;
str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%d)\n",
GetHash().ToString(),
nTime,
nVersion,
vin.size(),
vout.size(),
nLockTime);
for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
return str;
}
void print() const
{
LogPrintf("%s", ToString().c_str());
}
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet);
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
bool ReadFromDisk(COutPoint prevout);
bool DisconnectInputs(CTxDB& txdb);
/** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
@param[in] txdb Transaction database
@param[in] mapTestPool List of pending changes to the transaction index database
@param[in] fBlock True if being called to add a new best-block to the chain
@param[in] fMiner True if being called by CreateNewBlock
@param[out] inputsRet Pointers to this transaction's inputs
@param[out] fInvalid returns true if transaction is invalid
@return Returns true if all inputs are in txdb or mapTestPool
*/
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
bool CheckAnonInputs(CTxDB& txdb, int64_t& nSumValue, bool& fInvalid, bool fCheckExists);
/** Sanity check previous transactions, then, if all checks succeed,
mark them as spent by this transaction.
@param[in] inputs Previous transactions (from FetchInputs)
@param[out] mapTestPool Keeps track of inputs that need to be updated on disk
@param[in] posThisTx Position of this transaction on disk
@param[in] pindexBlock
@param[in] fBlock true if called from ConnectBlock
@param[in] fMiner true if called from CreateNewBlock
@return Returns true if all checks succeed
*/
bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS);
bool CheckTransaction() const;
bool GetCoinAge(CTxDB& txdb, const CBlockIndex* pindexPrev, uint64_t& nCoinAge) const;
protected:
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
};
bool AcceptToMemoryPool(CTxMemPool &pool, CTransaction &tx, CTxDB& txdb, bool *pfMissingInputs=NULL);
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx : public CTransaction
{
private:
int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const;
int GetDepthInMainChainINTERNAL(CBlockThinIndex* &pindexRet) const;
public:
uint256 hashBlock;
std::vector<uint256> vMerkleBranch;
int nIndex;
// memory only
mutable bool fMerkleVerified;
CMerkleTx()
{
Init();
}
CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
{
Init();
}
void Init()
{
hashBlock = 0;
nIndex = -1;
fMerkleVerified = false;
}
IMPLEMENT_SERIALIZE
(
nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action);
nVersion = this->nVersion;
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
)
int SetMerkleBranch(const CBlock* pblock=NULL);
// Return depth of transaction in blockchain:
// -1 : not in blockchain, and not in memory pool (conflicted transaction)
// 0 : in memory pool, waiting to be included in a block
// >=1 : this many blocks deep in the main chain
int GetDepthInMainChain(CBlockIndex* &pindexRet) const;
int GetDepthInMainChain(CBlockThinIndex* &pindexRet) const;
int GetDepthInMainChain() const
{
if (nNodeMode == NT_FULL)
{
CBlockIndex *pindexRet;
return GetDepthInMainChain(pindexRet);
};
CBlockThinIndex *pindexRet;
return GetDepthInMainChain(pindexRet);
}
bool IsInMainChain() const
{
if (nNodeMode == NT_THIN)
{
CBlockThinIndex *pindexRet;
return GetDepthInMainChainINTERNAL(pindexRet) > 0;
};
CBlockIndex *pindexRet;
return GetDepthInMainChainINTERNAL(pindexRet) > 0;
}
int GetBlocksToMaturity() const;
bool AcceptToMemoryPool(CTxDB& txdb);
bool AcceptToMemoryPool();
};
/** A txdb record that contains the disk location of a transaction and the
* locations of transactions that spend its outputs. vSpent is really only
* used as a flag, but having the location is very helpful for debugging.
*/
class CTxIndex
{
public:
CDiskTxPos pos;
std::vector<CDiskTxPos> vSpent;
CTxIndex()
{
SetNull();
}
CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs)
{
pos = posIn;
vSpent.resize(nOutputs);
}
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(pos);
READWRITE(vSpent);
)
void SetNull()
{
pos.SetNull();
vSpent.clear();
}
bool IsNull()
{
return pos.IsNull();
}
friend bool operator==(const CTxIndex& a, const CTxIndex& b)
{
return (a.pos == b.pos &&
a.vSpent == b.vSpent);
}
friend bool operator!=(const CTxIndex& a, const CTxIndex& b)
{
return !(a == b);
}
int GetDepthInMainChainFromIndex() const;
};
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
* to everyone and the block is added to the block chain. The first transaction
* in the block is a special one that creates a new coin owned by the creator
* of the block.
*
* Blocks are appended to blk0001.dat files on disk. Their location on disk
* is indexed by CBlockIndex objects in memory.
*/
class CBlockHeader
{
public:
// header
static const int CURRENT_VERSION = 7;
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
CBlockHeader()
{
SetHdrNull();
}
void SetHdrNull()
{
nVersion = CBlockHeader::CURRENT_VERSION;
hashPrevBlock = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
}
IMPLEMENT_SERIALIZE
(
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
)
bool IsNull() const
{
return (nBits == 0);
}
uint256 GetHash() const
{
if (nVersion > 6)
return Hash(BEGIN(nVersion), END(nNonce));
else
return scrypt_blockhash(CVOIDBEGIN(nVersion));
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
CBlockHeader GetBlockHeaderOnly() const
{
CBlockHeader block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrevBlock;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
};
class CBlock : public CBlockHeader
{
public:
// network and disk
std::vector<CTransaction> vtx;
// ppcoin: block signature - signed by one of the coin base txout[N]'s owner
std::vector<unsigned char> vchBlockSig;
// memory only
mutable std::vector<uint256> vMerkleTree;
// Denial-of-service detection:
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CBlock()
{
SetNull();
}
IMPLEMENT_SERIALIZE
(
READWRITE(*(CBlockHeader*)this);
// ConnectBlock depends on vtx following header to generate CDiskTxPos
if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
{
READWRITE(vtx);
READWRITE(vchBlockSig);
}
else if (fRead)
{
const_cast<CBlock*>(this)->vtx.clear();
const_cast<CBlock*>(this)->vchBlockSig.clear();
}
)
void SetNull()
{
SetHdrNull();
vtx.clear();
vchBlockSig.clear();
vMerkleTree.clear();
nDoS = 0;
}
void UpdateTime(const CBlockIndex* pindexPrev);
// entropy bit for stake modifier if chosen by modifier
unsigned int GetStakeEntropyBit() const
{
// Take last bit of block hash as entropy bit
unsigned int nEntropyBit = ((GetHash().Get64()) & 1llu);
if (fDebugPoS)
LogPrintf("GetStakeEntropyBit: hashBlock=%s nEntropyBit=%u\n", GetHash().ToString().c_str(), nEntropyBit);
return nEntropyBit;
}
// ppcoin: two types of block: proof-of-work or proof-of-stake
bool IsProofOfStake() const
{
return (vtx.size() > 1 && vtx[1].IsCoinStake());
}
bool IsProofOfWork() const
{
return !IsProofOfStake();
}
std::pair<COutPoint, unsigned int> GetProofOfStake() const
{
return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0);
}
// ppcoin: get max transaction timestamp
int64_t GetMaxTransactionTime() const
{
int64_t maxTransactionTime = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
maxTransactionTime = std::max(maxTransactionTime, (int64_t)tx.nTime);
return maxTransactionTime;
}
uint256 BuildMerkleTree() const
{
vMerkleTree.clear();
BOOST_FOREACH(const CTransaction& tx, vtx)
vMerkleTree.push_back(tx.GetHash());
int j = 0;
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{
for (int i = 0; i < nSize; i += 2)
{
int i2 = std::min(i+1, nSize-1);
vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
}
j += nSize;
}
return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
}
std::vector<uint256> GetMerkleBranch(int nIndex) const
{
if (vMerkleTree.empty())
BuildMerkleTree();
std::vector<uint256> vMerkleBranch;
int j = 0;
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{
int i = std::min(nIndex^1, nSize-1);
vMerkleBranch.push_back(vMerkleTree[j+i]);
nIndex >>= 1;
j += nSize;
}
return vMerkleBranch;
}
static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
{
if (nIndex == -1)
return 0;
BOOST_FOREACH(const uint256& otherside, vMerkleBranch)
{
if (nIndex & 1)
hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
else
hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
nIndex >>= 1;
}
return hash;
}
bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet)
{
// Open history file to append
CAutoFile fileout = CAutoFile(AppendBlockFile(false, nFileRet), SER_DISK, CLIENT_VERSION);
if (!fileout)
return error("CBlock::WriteToDisk() : AppendBlockFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(Params().MessageStart()) << nSize;
// Write block
long fileOutPos = ftell(fileout);
if (fileOutPos < 0)
return error("CBlock::WriteToDisk() : ftell failed");
nBlockPosRet = fileOutPos;
fileout << *this;
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
FileCommit(fileout);
return true;
}
bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true)
{
SetNull();
// Open history file to read
CAutoFile filein = CAutoFile(OpenBlockFile(false, nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION);
if (!filein)
return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
if (!fReadTransactions)
filein.nType |= SER_BLOCKHEADERONLY;
// Read block
try {
filein >> *this;
}
catch (std::exception &e) {
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
// Check the header
if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return error("CBlock::ReadFromDisk() : errors in block header");
return true;
}
void print() const
{
LogPrintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u, vchBlockSig=%s)\n",
GetHash().ToString().c_str(),
nVersion,
hashPrevBlock.ToString().c_str(),
hashMerkleRoot.ToString().c_str(),
nTime, nBits, nNonce,
vtx.size(),
HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str());
for (unsigned int i = 0; i < vtx.size(); i++)
{
LogPrintf(" ");
vtx[i].print();
}
LogPrintf(" vMerkleTree: ");
for (unsigned int i = 0; i < vMerkleTree.size(); i++)
LogPrintf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str());
LogPrintf("\n");
}
CBlockThin GetBlockThinOnly() const;
bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false);
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProof);
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const;
bool AcceptBlock();
bool SignBlock(CWallet& keystore, int64_t nFees);
bool CheckBlockSignature() const;
bool GetHashProof(uint256& hashProof);
private:
bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
};
class CBlockThin : public CBlockHeader
{
public:
unsigned int nFlags; // PoS block index flags
uint256 hashProof;
// Denial-of-service detection:
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CBlockThin()
{
}
CBlockThin(CBlock &block)
{
nVersion = block.nVersion;
hashPrevBlock = block.hashPrevBlock;
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
nFlags = 0;
if (block.IsProofOfStake())
SetProofOfStake();
SetStakeEntropyBit(block.GetStakeEntropyBit());
}
IMPLEMENT_SERIALIZE
(
READWRITE(*(CBlockHeader*)this);
READWRITE(nFlags);
READWRITE(hashProof);
)
void SetProofOfStake()
{
nFlags |= BLOCK_PROOF_OF_STAKE;
}
bool SetStakeEntropyBit(unsigned int nEntropyBit)
{
if (nEntropyBit > 1)
return false;
nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
return true;
}
bool IsProofOfWork() const
{
return !(nFlags & BLOCK_PROOF_OF_STAKE);
}
bool IsProofOfStake() const
{
return (nFlags & BLOCK_PROOF_OF_STAKE);
}
bool WriteBlockThinToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet)
{
// Open history file to append
CAutoFile fileout = CAutoFile(AppendBlockFile(true, nFileRet), SER_DISK, CLIENT_VERSION);
if (!fileout)
return error("CBlockThin::WriteBlockThinToDisk() : AppendBlockFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(Params().MessageStart()) << nSize;
// Write block
long fileOutPos = ftell(fileout);
if (fileOutPos < 0)
return error("CBlockThin::WriteBlockThinToDisk() : ftell failed");
nBlockPosRet = fileOutPos;
fileout << *(CBlockThin*)this;
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
FileCommit(fileout);
return true;
}
bool ReadBlockThinFromDisk(unsigned int nFile, unsigned int nBlockPos)
{
SetHdrNull();
// Open history file to read
CAutoFile filein = CAutoFile(OpenBlockFile(false, nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION);
if (!filein)
return error("CBlockThin::ReadBlockThinFromDisk() : OpenBlockFile failed");
//filein.nType |= SER_BLOCKHEADERONLY;
// Read block
try {
filein >> *this;
} catch (std::exception &e)
{
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
return true;
}
bool AddToBlockThinIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProof);
bool CheckBlockThin(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const;
bool AcceptBlockThin();
bool DisconnectBlockThin(CTxDB& txdb, CBlockThinIndex* pindex);
bool ConnectBlockThin(CTxDB& txdb, CBlockThinIndex* pindex, bool fJustCheck=false);
bool SetBestThinChain(CTxDB& txdb, CBlockThinIndex* pindexNew);
private:
bool SetBestThinChainInner(CTxDB& txdb, CBlockThinIndex *pindexNew);
};
/** 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. pprev and pnext link a path through the
* main/longest chain. A blockindex may have multiple pprev pointing back
* to it, but pnext will only point forward to the longest branch, or will
* be null if the block is not part of the longest chain.
*/
class CBlockIndex
{
public:
const uint256* phashBlock;
CBlockIndex* pprev;
CBlockIndex* pnext;
unsigned int nFile;
unsigned int nBlockPos;
uint256 nChainTrust; // ppcoin: trust score of block chain
int nHeight;
int64_t nMint;
int64_t nMoneySupply;
int64_t nAnonSupply;
unsigned int nFlags; // ppcoin: block index flags
uint64_t nStakeModifier; // hash modifier for proof-of-stake
uint256 bnStakeModifierV2;
// proof-of-stake specific fields
COutPoint prevoutStake;
unsigned int nStakeTime;
uint256 hashProof;
// block header
int nVersion;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
CBlockIndex()
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = 0;
nBlockPos = 0;
nHeight = 0;
nChainTrust = 0;
nMint = 0;
nMoneySupply = 0;
nAnonSupply = 0;
nFlags = 0;
nStakeModifier = 0;
bnStakeModifierV2 = 0;
hashProof = 0;
prevoutStake.SetNull();
nStakeTime = 0;
nVersion = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
}
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block)
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0;
nChainTrust = 0;
nMint = 0;
nMoneySupply = 0;
nAnonSupply = 0;
nFlags = 0;
nStakeModifier = 0;
bnStakeModifierV2 = 0;
hashProof = 0;
if (block.IsProofOfStake())
{
SetProofOfStake();
prevoutStake = block.vtx[1].vin[0].prevout;
nStakeTime = block.vtx[1].nTime;
}
else
{
prevoutStake.SetNull();
nStakeTime = 0;
}
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
}
CBlock GetBlockHeader() const
{
CBlock block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
CBlockThin GetBlockThinOnly() const
{
CBlockThin block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.nFlags = nFlags;
block.hashProof = hashProof;
return block;
}
uint256 GetBlockHash() const
{
return *phashBlock;
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
uint256 GetBlockTrust() const;
bool IsInMainChain() const
{
return (pnext || this == pindexBest);
}
bool CheckIndex() const
{
return true;
}
int64_t GetPastTimeLimit() const
{
if (Params().IsProtocolV2(nHeight))
return GetBlockTime();
else
return GetMedianTimePast();
}
enum { 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];
}
/**
* Returns true if there are nRequired or more blocks of minVersion or above
* in the last nToCheck blocks, starting at pstart and going backwards.
*/
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
unsigned int nRequired, unsigned int nToCheck);
bool IsProofOfWork() const
{
return !(nFlags & BLOCK_PROOF_OF_STAKE);
}
bool IsProofOfStake() const
{
return (nFlags & BLOCK_PROOF_OF_STAKE);
}
void SetProofOfStake()
{
nFlags |= BLOCK_PROOF_OF_STAKE;
}
unsigned int GetStakeEntropyBit() const
{
return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
}
bool SetStakeEntropyBit(unsigned int nEntropyBit)
{
if (nEntropyBit > 1)
return false;
nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
return true;
}
bool GeneratedStakeModifier() const
{
return (nFlags & BLOCK_STAKE_MODIFIER);
}
void SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier)
{
nStakeModifier = nModifier;
if (fGeneratedStakeModifier)
nFlags |= BLOCK_STAKE_MODIFIER;
}
std::string ToString() const
{
return strprintf("CBlockIndex(nprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016x, hashProof=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
pprev, pnext, nFile, nBlockPos, nHeight,
FormatMoney(nMint), FormatMoney(nMoneySupply),
GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW",
nStakeModifier,
hashProof.ToString(),
prevoutStake.ToString(), nStakeTime,
hashMerkleRoot.ToString(),
GetBlockHash().ToString());
}
void print() const
{
LogPrintf("%s\n", ToString().c_str());
}
};
class CBlockThinIndex : public CBlockThin
{
public:
const uint256* phashBlock;
CBlockThinIndex* pprev;
CBlockThinIndex* pnext;
unsigned int nFile;
unsigned int nBlockPos;
int nHeight;
uint256 nChainTrust; // ppcoin: trust score of block chain
//unsigned int nFlags; // ppcoin: block index flags
uint64_t nStakeModifier; // hash modifier for proof-of-stake
uint256 hashProof;
CBlockThinIndex()
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = 0;
nBlockPos = 0;
nHeight = 0;
nChainTrust = 0;
nStakeModifier = 0;
hashProof = 0;
nVersion = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
nFlags = 0;
nStakeModifier = 0;
}
CBlockThinIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlockThin& block)
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0;
hashProof = 0;
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
nFlags = block.nFlags;
}
CBlockThin GetBlockThin() const
{
CBlockThin blockThin;
blockThin.nVersion = nVersion;
if (pprev)
blockThin.hashPrevBlock = pprev->GetBlockHash();
blockThin.hashMerkleRoot = hashMerkleRoot;
blockThin.nTime = nTime;
blockThin.nBits = nBits;
blockThin.nNonce = nNonce;
blockThin.nFlags = nFlags;
return blockThin;
}
CBlock GetBlock() const
{
CBlock 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;
}
uint256 GetBlockTrust() const;
bool IsInMainChain() const
{
return (pnext || this == pindexBestHeader);
}
bool CheckIndex() const
{
return true;
}
int64_t GetPastTimeLimit() const
{
if (Params().IsProtocolV2(nHeight))
return GetBlockTime();
else
return GetMedianTimePast();
}
enum { nMedianTimeSpan=11 };
int64_t GetMedianTimePast() const
{
int64_t pmedian[nMedianTimeSpan];
int64_t* pbegin = &pmedian[nMedianTimeSpan];
int64_t* pend = &pmedian[nMedianTimeSpan];
const CBlockThinIndex* 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];
}
/**
* Returns true if there are nRequired or more blocks of minVersion or above
* in the last nToCheck blocks, starting at pstart and going backwards.
*/
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
unsigned int nRequired, unsigned int nToCheck);
/*
void SetProofOfStake()
{
nFlags |= BLOCK_PROOF_OF_STAKE;
}
*/
unsigned int GetStakeEntropyBit() const
{
return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
}
/*
bool SetStakeEntropyBit(unsigned int nEntropyBit)
{
if (nEntropyBit > 1)
return false;
nFlags |= (nEntropyBit ? BLOCK_STAKE_ENTROPY : 0);
return true;
}
*/
void SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier)
{
nStakeModifier = nModifier;
if (fGeneratedStakeModifier)
nFlags |= BLOCK_STAKE_MODIFIER;
}
bool GeneratedStakeModifier() const
{
return (nFlags & BLOCK_STAKE_MODIFIER);
}
};
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex
{
private:
uint256 blockHash;
public:
uint256 hashPrev;
uint256 hashNext;
CDiskBlockIndex()
{
hashPrev = 0;
hashNext = 0;
blockHash = 0;
}
explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex)
{
hashPrev = (pprev ? pprev->GetBlockHash() : 0);
hashNext = (pnext ? pnext->GetBlockHash() : 0);
}
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(hashNext);
READWRITE(nFile);
READWRITE(nBlockPos);
READWRITE(nHeight);
READWRITE(nMint);
READWRITE(nMoneySupply);
READWRITE(nAnonSupply);
READWRITE(nFlags);
READWRITE(nStakeModifier);
READWRITE(bnStakeModifierV2);
if (IsProofOfStake())
{
READWRITE(prevoutStake);
READWRITE(nStakeTime);
}
else if (fRead)
{
const_cast<CDiskBlockIndex*>(this)->prevoutStake.SetNull();
const_cast<CDiskBlockIndex*>(this)->nStakeTime = 0;
}
READWRITE(hashProof);
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(blockHash);
)
uint256 GetBlockHash() const
{
if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0)
return blockHash;
CBlock block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
const_cast<CDiskBlockIndex*>(this)->blockHash = block.GetHash();
return blockHash;
}
std::string ToString() const
{
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
GetBlockHash().ToString(),
hashPrev.ToString(),
hashNext.ToString());
return str;
}
void print() const
{
LogPrintf("%s\n", ToString().c_str());
}
};
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockThinIndex : public CBlockThinIndex
{
public:
uint256 hashPrev;
uint256 hashNext;
CDiskBlockThinIndex()
{
hashPrev = 0;
hashNext = 0;
}
explicit CDiskBlockThinIndex(CBlockThinIndex* pindex) : CBlockThinIndex(*pindex)
{
hashPrev = (pprev ? pprev->GetBlockHash() : 0);
hashNext = (pnext ? pnext->GetBlockHash() : 0);
}
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(hashNext);
READWRITE(nFile);
READWRITE(nBlockPos);
READWRITE(nHeight);
READWRITE(nFlags);
READWRITE(nStakeModifier);
/*
if (IsProofOfStake())
{
READWRITE(prevoutStake);
READWRITE(nStakeTime);
} else
if (fRead)
{
const_cast<CDiskBlockIndex*>(this)->prevoutStake.SetNull();
const_cast<CDiskBlockIndex*>(this)->nStakeTime = 0;
}
*/
READWRITE(hashProof);
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
)
CBlock GetBlock()
{
CBlock block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
uint256 GetBlockHash() const
{
CBlock block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block.GetHash();
}
bool GeneratedStakeModifier() const
{
return (nFlags & BLOCK_STAKE_MODIFIER);
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
};
/** Describes a place in the block chain to another node such that if the
* other node doesn't have the same branch, it can find a recent common trunk.
* The further back it is, the further before the fork it may be.
*/
class CBlockLocator
{
protected:
std::vector<uint256> vHave;
public:
CBlockLocator()
{
}
explicit CBlockLocator(const CBlockIndex* pindex)
{
Set(pindex);
}
explicit CBlockLocator(uint256 hashBlock)
{
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi != mapBlockIndex.end())
Set((*mi).second);
}
CBlockLocator(const std::vector<uint256>& vHaveIn)
{
vHave = vHaveIn;
}
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vHave);
)
void SetNull()
{
vHave.clear();
}
bool IsNull()
{
return vHave.empty();
}
void Set(const CBlockIndex* pindex)
{
vHave.clear();
int nStep = 1;
while (pindex)
{
vHave.push_back(pindex->GetBlockHash());
// Exponentially larger steps back
for (int i = 0; pindex && i < nStep; i++)
pindex = pindex->pprev;
if (vHave.size() > 10)
nStep *= 2;
}
vHave.push_back(Params().HashGenesisBlock());
}
void SetThin(const uint256& fromHash)
{
// if the block is before the thin index 'window'
// it is probably in the chain index of the peer, and the other hashes won't be missed
vHave.clear();
vHave.push_back(fromHash);
vHave.push_back(Params().HashGenesisBlock());
}
int GetDistanceBack()
{
// Retrace how far back it was in the sender's branch
int nDistance = 0;
int nStep = 1;
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
{
CBlockIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return nDistance;
}
nDistance += nStep;
if (nDistance > 10)
nStep *= 2;
}
return nDistance;
}
CBlockIndex* GetBlockIndex()
{
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
{
CBlockIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return pindex;
}
}
return pindexGenesisBlock;
}
uint256 GetBlockHash()
{
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
{
CBlockIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return hash;
}
}
return Params().HashGenesisBlock();
}
int GetHeight()
{
CBlockIndex* pindex = GetBlockIndex();
if (!pindex)
return 0;
return pindex->nHeight;
}
};
/** Describes a place in the block chain to another node such that if the
* other node doesn't have the same branch, it can find a recent common trunk.
* The further back it is, the further before the fork it may be.
*/
class CBlockThinLocator
{
protected:
std::vector<uint256> vHave;
public:
CBlockThinLocator()
{
}
explicit CBlockThinLocator(const CBlockThinIndex* pindex)
{
Set(pindex);
}
explicit CBlockThinLocator(uint256 hashBlock)
{
std::map<uint256, CBlockThinIndex*>::iterator mi = mapBlockThinIndex.find(hashBlock);
if (mi != mapBlockThinIndex.end())
Set((*mi).second);
}
CBlockThinLocator(const std::vector<uint256>& vHaveIn)
{
vHave = vHaveIn;
}
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vHave);
)
void SetNull()
{
vHave.clear();
}
bool IsNull()
{
return vHave.empty();
}
void Set(const CBlockThinIndex* pindex)
{
vHave.clear();
int nStep = 1;
while (pindex)
{
vHave.push_back(pindex->GetBlockHash());
// Exponentially larger steps back
for (int i = 0; pindex && i < nStep; i++)
pindex = pindex->pprev;
if (vHave.size() > 10)
nStep *= 2;
}
vHave.push_back(Params().HashGenesisBlock());
}
int GetDistanceBack()
{
// Retrace how far back it was in the sender's branch
int nDistance = 0;
int nStep = 1;
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockThinIndex*>::iterator mi = mapBlockThinIndex.find(hash);
if (mi != mapBlockThinIndex.end())
{
CBlockThinIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return nDistance;
};
nDistance += nStep;
if (nDistance > 10)
nStep *= 2;
}
return nDistance;
}
CBlockThinIndex* GetBlockIndex()
{
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockThinIndex*>::iterator mi = mapBlockThinIndex.find(hash);
if (mi != mapBlockThinIndex.end())
{
CBlockThinIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return pindex;
}
}
return pindexGenesisBlockThin;
}
uint256 GetBlockHash()
{
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, vHave)
{
std::map<uint256, CBlockThinIndex*>::iterator mi = mapBlockThinIndex.find(hash);
if (mi != mapBlockThinIndex.end())
{
CBlockThinIndex* pindex = (*mi).second;
if (pindex->IsInMainChain())
return hash;
}
}
return (Params().HashGenesisBlock());
}
int GetHeight()
{
CBlockThinIndex* pindex = GetBlockIndex();
if (!pindex)
return 0;
return pindex->nHeight;
}
};
/** Data structure that represents a partial merkle tree.
*
* It respresents a subset of the txid's of a known block, in a way that
* allows recovery of the list of txid's and the merkle root, in an
* authenticated way.
*
* The encoding works as follows: we traverse the tree in depth-first order,
* storing a bit for each traversed node, signifying whether the node is the
* parent of at least one matched leaf txid (or a matched txid itself). In
* case we are at the leaf level, or this bit is 0, its merkle node hash is
* stored, and its children are not explorer further. Otherwise, no hash is
* stored, but we recurse into both (or the only) child branch. During
* decoding, the same depth-first traversal is performed, consuming bits and
* hashes as they written during encoding.
*
* The serialization is fixed and provides a hard guarantee about the
* encoded size:
*
* SIZE <= 10 + ceil(32.25*N)
*
* Where N represents the number of leaf nodes of the partial tree. N itself
* is bounded by:
*
* N <= total_transactions
* N <= 1 + matched_transactions*tree_height
*
* The serialization format:
* - uint32 total_transactions (4 bytes)
* - varint number of hashes (1-3 bytes)
* - uint256[] hashes in depth-first order (<= 32*N bytes)
* - varint number of bytes of flag bits (1-3 bytes)
* - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits)
* The size constraints follow from this.
*/
class CPartialMerkleTree
{
public:
// the total number of transactions in the block
unsigned int nTransactions;
// node-is-parent-of-matched-txid bits
std::vector<bool> vBits;
// txids and internal hashes
std::vector<uint256> vHash;
// flag set when encountering invalid data
bool fBad;
// helper function to efficiently calculate the number of nodes at given height in the merkle tree
unsigned int CalcTreeWidth(int height)
{
return (nTransactions+(1 << height)-1) >> height;
}
// calculate the hash of a node in the merkle tree (at leaf level: the txid's themself)
uint256 CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid);
// recursive function that traverses tree nodes, storing the data as bits and hashes
void TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch);
// recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild.
// it returns the hash of the respective node.
uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch);
// serialization implementation
IMPLEMENT_SERIALIZE(
READWRITE(nTransactions);
READWRITE(vHash);
std::vector<unsigned char> vBytes;
if (fRead)
{
READWRITE(vBytes);
CPartialMerkleTree &us = *(const_cast<CPartialMerkleTree*>(this));
us.vBits.resize(vBytes.size() * 8);
for (unsigned int p = 0; p < us.vBits.size(); p++)
us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0;
us.fBad = false;
} else
{
vBytes.resize((vBits.size()+7)/8);
for (unsigned int p = 0; p < vBits.size(); p++)
vBytes[p / 8] |= vBits[p] << (p % 8);
READWRITE(vBytes);
};
)
// Construct a partial merkle tree from a list of transaction id's, and a mask that selects a subset of them
CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch);
CPartialMerkleTree();
// extract the matching txid's represented by this partial merkle tree.
// returns the merkle root, or 0 in case of failure
uint256 ExtractMatches(std::vector<uint256> &vMatch);
};
/** Used to relay blocks as header + vector<merkle branch>
* to filtered nodes.
*/
class CMerkleBlock
{
public:
CBlockThin header;
CPartialMerkleTree txn;
// (not relayed)
std::vector<std::pair<unsigned int, uint256> > vMatchedTxn;
// Create from a CBlock, filtering transactions according to filter
// Note that this will call IsRelevantAndUpdate on the filter for each transaction,
// thus the filter will likely be modified.
CMerkleBlock(){};
CMerkleBlock(const CBlock& block, CBlockIndex *pBlockIndex, CBloomFilter& filter);
IMPLEMENT_SERIALIZE
(
READWRITE(header);
READWRITE(txn);
)
};
class CMerkleBlockIncoming : public CMerkleBlock
{
public:
unsigned int nProcessed;
int64_t nTimeRecv;
std::vector<uint256> vMatch;
CMerkleBlockIncoming()
{
};
CMerkleBlockIncoming(CMerkleBlock &mb)
{
header = mb.header;
txn = mb.txn;
//vMatchedTxn = mb.txn;
};
};
class CMBlkThinElement
{
// send txns with thin block in 'mblkt' message
public:
CMerkleBlock merkleBlock;
std::vector<CTransaction> vtx;
IMPLEMENT_SERIALIZE
(
READWRITE(merkleBlock);
READWRITE(vtx);
)
};
#endif