// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2011 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_MAIN_H #define BITCOIN_MAIN_H #include "bignum.h" #include "net.h" #include "key.h" #include "script.h" #include "db.h" #include class CBlock; class CBlockIndex; class CWalletTx; class CWallet; class CKeyItem; class CReserveKey; class CWalletDB; class CAddress; class CInv; class CRequestTracker; class CNode; static const int CLIENT_VERSION = 59900; static const bool VERSION_IS_BETA = true; extern const std::string CLIENT_NAME; static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const int64 COIN = 100000000; static const int64 CENT = 1000000; static const int64 MIN_TX_FEE = 50000; static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; // Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. static const int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; #else static const int fHaveUPnP = false; #endif extern CCriticalSection cs_main; extern std::map mapBlockIndex; extern uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; extern CBigNum bnBestChainWork; extern CBigNum bnBestInvalidWork; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern uint64 nPooledTx; extern unsigned int nTransactionsUpdated; extern uint64 nLastBlockTx; extern uint64 nLastBlockSize; extern double dHashesPerSec; extern int64 nHPSTimerStart; extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set setpwalletRegistered; // Settings extern int fGenerateBitcoins; extern int64 nTransactionFee; extern int fLimitProcessors; extern int nLimitProcessors; extern int fMinimizeToTray; extern int fMinimizeOnClose; extern int fUseUPnP; class CReserveKey; class CTxDB; class CTxIndex; void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); bool ProcessBlock(CNode* pfrom, CBlock* pblock); bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); CBlock* CreateNewBlock(CReserveKey& reservekey); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); bool CheckProofOfWork(uint256 hash, unsigned int nBits); unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); int GetNumBlocksOfPeers(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); template bool WriteSetting(const std::string& strKey, const T& value) { bool fOk = false; BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) { std::string strWalletFile; if (!GetWalletFile(pwallet, strWalletFile)) continue; fOk |= CWalletDB(strWalletFile).WriteSetting(strKey, value); } return fOk; } 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 = -1; nBlockPos = 0; nTxPos = 0; } bool IsNull() const { return (nFile == -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 strprintf("null"); else return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); } void print() const { printf("%s", ToString().c_str()); } }; class CInPoint { public: CTransaction* ptx; unsigned int n; CInPoint() { SetNull(); } CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } void SetNull() { ptx = NULL; n = -1; } bool IsNull() const { return (ptx == NULL && n == -1); } }; class COutPoint { public: uint256 hash; unsigned int n; COutPoint() { SetNull(); } COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) void SetNull() { hash = 0; n = -1; } bool IsNull() const { return (hash == 0 && n == -1); } friend bool operator<(const COutPoint& a, const COutPoint& b) { return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); } friend bool operator==(const COutPoint& a, const COutPoint& b) { return (a.hash == b.hash && a.n == b.n); } friend bool operator!=(const COutPoint& a, const COutPoint& b) { return !(a == b); } std::string ToString() const { return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); } void print() const { printf("%s\n", ToString().c_str()); } }; // // An input of a transaction. It contains the location of the previous // transaction's output that it claims and a signature that matches the // output's public key. // class CTxIn { public: COutPoint prevout; CScript scriptSig; unsigned int nSequence; CTxIn() { nSequence = std::numeric_limits::max(); } explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) { prevout = prevoutIn; scriptSig = scriptSigIn; nSequence = nSequenceIn; } CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) { prevout = COutPoint(hashPrevTx, nOut); scriptSig = scriptSigIn; nSequence = nSequenceIn; } IMPLEMENT_SERIALIZE ( READWRITE(prevout); READWRITE(scriptSig); READWRITE(nSequence); ) bool IsFinal() const { return (nSequence == std::numeric_limits::max()); } friend bool operator==(const CTxIn& a, const CTxIn& b) { return (a.prevout == b.prevout && a.scriptSig == b.scriptSig && a.nSequence == b.nSequence); } friend bool operator!=(const CTxIn& a, const CTxIn& b) { return !(a == b); } std::string ToString() const { std::string str; str += strprintf("CTxIn("); str += prevout.ToString(); if (prevout.IsNull()) str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); else str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); if (nSequence != std::numeric_limits::max()) str += strprintf(", nSequence=%u", nSequence); str += ")"; return str; } void print() const { printf("%s\n", ToString().c_str()); } }; // // An output of a transaction. It contains the public key that the next input // must be able to sign with to claim it. // class CTxOut { public: int64 nValue; CScript scriptPubKey; CTxOut() { SetNull(); } CTxOut(int64 nValueIn, CScript scriptPubKeyIn) { nValue = nValueIn; scriptPubKey = scriptPubKeyIn; } IMPLEMENT_SERIALIZE ( READWRITE(nValue); READWRITE(scriptPubKey); ) void SetNull() { nValue = -1; scriptPubKey.clear(); } bool IsNull() { return (nValue == -1); } uint256 GetHash() const { return SerializeHash(*this); } friend bool operator==(const CTxOut& a, const CTxOut& b) { return (a.nValue == b.nValue && a.scriptPubKey == b.scriptPubKey); } friend bool operator!=(const CTxOut& a, const CTxOut& b) { return !(a == b); } std::string ToString() const { if (scriptPubKey.size() < 6) return "CTxOut(error)"; return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); } void print() const { printf("%s\n", ToString().c_str()); } }; enum GetMinFee_mode { GMF_BLOCK, GMF_RELAY, GMF_SEND, }; // // The basic transaction that is broadcasted on the network and contained in // blocks. A transaction can contain multiple inputs and outputs. // class CTransaction { public: int nVersion; std::vector vin; std::vector 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(vin); READWRITE(vout); READWRITE(nLockTime); ) void SetNull() { nVersion = 1; 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 nBlockTime=0) const { // 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)nLockTime < (nLockTime < LOCKTIME_THRESHOLD ? (int64)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 (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::max(); for (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()); } bool IsStandard() const; bool AreInputsStandard(std::map > mapInputs) const; int64 GetValueOut() const { int64 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; } static bool AllowFree(double dPriority) { // Large (in bytes) low-priority (new, small-coin) transactions // need a fee. return dPriority > COIN * 144 / 250; } int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; if (fAllowFree) { if (nBlockSize == 1) { // Transactions under 10K are free // (about 4500bc if made of 50bc inputs) if (nBytes < 10000) nMinFee = 0; } else { // Free transaction area if (nNewBlockSize < 27000) nMinFee = 0; } } // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 if (nMinFee < nBaseFee) BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) nMinFee = nBaseFee; // Raise the price as the block approaches full if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) { if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) return MAX_MONEY; nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); } if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; } bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) { CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); if (!filein) return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); // Read transaction if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) return error("CTransaction::ReadFromDisk() : fseek failed"); filein >> *this; // 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.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 += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", GetHash().ToString().substr(0,10).c_str(), nVersion, vin.size(), vout.size(), nLockTime); for (int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (int i = 0; i < vout.size(); i++) str += " " + vout[i].ToString() + "\n"; return str; } void print() const { printf("%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. bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, bool fBlock, bool fMiner, std::map >& inputsRet); bool ConnectInputs(std::map > inputs, std::map& mapTestPool, CDiskTxPos posThisTx, CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); protected: bool AddToMemoryPoolUnchecked(); public: bool RemoveFromMemoryPool(); }; // // A transaction with a merkle branch linking it to the block chain // class CMerkleTx : public CTransaction { public: uint256 hashBlock; std::vector vMerkleBranch; int nIndex; // memory only mutable char 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); int GetDepthInMainChain(CBlockIndex* &pindexRet) const; int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); 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 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 GetDepthInMainChain() 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 CBlock { public: // header int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; unsigned int nNonce; // network and disk std::vector vtx; // memory only mutable std::vector vMerkleTree; // Denial-of-service detection: mutable int nDoS; bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } CBlock() { SetNull(); } IMPLEMENT_SERIALIZE ( READWRITE(this->nVersion); nVersion = this->nVersion; READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); // ConnectBlock depends on vtx being last so it can calculate offset if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) READWRITE(vtx); else if (fRead) const_cast(this)->vtx.clear(); ) void SetNull() { nVersion = 1; hashPrevBlock = 0; hashMerkleRoot = 0; nTime = 0; nBits = 0; nNonce = 0; vtx.clear(); vMerkleTree.clear(); nDoS = 0; } bool IsNull() const { return (nBits == 0); } uint256 GetHash() const { return Hash(BEGIN(nVersion), END(nNonce)); } int64 GetBlockTime() const { return (int64)nTime; } 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 GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) BuildMerkleTree(); std::vector 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& 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 = AppendBlockFile(nFileRet); if (!fileout) return error("CBlock::WriteToDisk() : AppendBlockFile failed"); // Write index header unsigned int nSize = fileout.GetSerializeSize(*this); fileout << FLATDATA(pchMessageStart) << nSize; // Write block nBlockPosRet = ftell(fileout); if (nBlockPosRet == -1) return error("CBlock::WriteToDisk() : ftell failed"); fileout << *this; // Flush stdio buffers and commit to disk before returning fflush(fileout); if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) { #ifdef WIN32 _commit(_fileno(fileout)); #else fsync(fileno(fileout)); #endif } return true; } bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) { SetNull(); // Open history file to read CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); if (!filein) return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); if (!fReadTransactions) filein.nType |= SER_BLOCKHEADERONLY; // Read block filein >> *this; // Check the header if (!CheckProofOfWork(GetHash(), nBits)) return error("CBlock::ReadFromDisk() : errors in block header"); return true; } void print() const { printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", GetHash().ToString().substr(0,20).c_str(), nVersion, hashPrevBlock.ToString().substr(0,20).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(), nTime, nBits, nNonce, vtx.size()); for (int i = 0; i < vtx.size(); i++) { printf(" "); vtx[i].print(); } printf(" vMerkleTree: "); for (int i = 0; i < vMerkleTree.size(); i++) printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str()); printf("\n"); } bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); bool CheckBlock() const; bool AcceptBlock(); }; // // 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; int nHeight; CBigNum bnChainWork; // 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; bnChainWork = 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; bnChainWork = 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; } uint256 GetBlockHash() const { return *phashBlock; } int64 GetBlockTime() const { return (int64)nTime; } CBigNum GetBlockWork() const { CBigNum bnTarget; bnTarget.SetCompact(nBits); if (bnTarget <= 0) return 0; return (CBigNum(1)<<256) / (bnTarget+1); } bool IsInMainChain() const { return (pnext || this == pindexBest); } bool CheckIndex() const { return CheckProofOfWork(GetBlockHash(), nBits); } bool EraseBlockFromDisk() { // Open history file CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); if (!fileout) return false; // Overwrite with empty null block CBlock block; block.SetNull(); fileout << block; return true; } enum { nMedianTimeSpan=11 }; int64 GetMedianTimePast() const { int64 pmedian[nMedianTimeSpan]; int64* pbegin = &pmedian[nMedianTimeSpan]; int64* 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]; } int64 GetMedianTime() const { const CBlockIndex* pindex = this; for (int i = 0; i < nMedianTimeSpan/2; i++) { if (!pindex->pnext) return GetBlockTime(); pindex = pindex->pnext; } return pindex->GetMedianTimePast(); } std::string ToString() const { return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", pprev, pnext, nFile, nBlockPos, nHeight, hashMerkleRoot.ToString().substr(0,10).c_str(), GetBlockHash().ToString().substr(0,20).c_str()); } void print() const { printf("%s\n", ToString().c_str()); } }; // // Used to marshal pointers into hashes for db storage. // class CDiskBlockIndex : public CBlockIndex { public: uint256 hashPrev; uint256 hashNext; CDiskBlockIndex() { hashPrev = 0; hashNext = 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); // block header READWRITE(this->nVersion); READWRITE(hashPrev); READWRITE(hashMerkleRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); ) 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(); } std::string ToString() const { std::string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", GetBlockHash().ToString().c_str(), hashPrev.ToString().substr(0,20).c_str(), hashNext.ToString().substr(0,20).c_str()); return str; } void print() const { printf("%s\n", ToString().c_str()); } }; // // 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 vHave; public: CBlockLocator() { } explicit CBlockLocator(const CBlockIndex* pindex) { Set(pindex); } explicit CBlockLocator(uint256 hashBlock) { std::map::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end()) Set((*mi).second); } CBlockLocator(const std::vector& 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(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::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::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::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; if (pindex->IsInMainChain()) return hash; } } return hashGenesisBlock; } int GetHeight() { CBlockIndex* pindex = GetBlockIndex(); if (!pindex) return 0; return pindex->nHeight; } }; // // Alerts are for notifying old versions if they become too obsolete and // need to upgrade. The message is displayed in the status bar. // Alert messages are broadcast as a vector of signed data. Unserializing may // not read the entire buffer if the alert is for a newer version, but older // versions can still relay the original data. // class CUnsignedAlert { public: int nVersion; int64 nRelayUntil; // when newer nodes stop relaying to newer nodes int64 nExpiration; int nID; int nCancel; std::set setCancel; int nMinVer; // lowest version inclusive int nMaxVer; // highest version inclusive std::set setSubVer; // empty matches all int nPriority; // Actions std::string strComment; std::string strStatusBar; std::string strReserved; IMPLEMENT_SERIALIZE ( READWRITE(this->nVersion); nVersion = this->nVersion; READWRITE(nRelayUntil); READWRITE(nExpiration); READWRITE(nID); READWRITE(nCancel); READWRITE(setCancel); READWRITE(nMinVer); READWRITE(nMaxVer); READWRITE(setSubVer); READWRITE(nPriority); READWRITE(strComment); READWRITE(strStatusBar); READWRITE(strReserved); ) void SetNull() { nVersion = 1; nRelayUntil = 0; nExpiration = 0; nID = 0; nCancel = 0; setCancel.clear(); nMinVer = 0; nMaxVer = 0; setSubVer.clear(); nPriority = 0; strComment.clear(); strStatusBar.clear(); strReserved.clear(); } std::string ToString() const { std::string strSetCancel; BOOST_FOREACH(int n, setCancel) strSetCancel += strprintf("%d ", n); std::string strSetSubVer; BOOST_FOREACH(std::string str, setSubVer) strSetSubVer += "\"" + str + "\" "; return strprintf( "CAlert(\n" " nVersion = %d\n" " nRelayUntil = %"PRI64d"\n" " nExpiration = %"PRI64d"\n" " nID = %d\n" " nCancel = %d\n" " setCancel = %s\n" " nMinVer = %d\n" " nMaxVer = %d\n" " setSubVer = %s\n" " nPriority = %d\n" " strComment = \"%s\"\n" " strStatusBar = \"%s\"\n" ")\n", nVersion, nRelayUntil, nExpiration, nID, nCancel, strSetCancel.c_str(), nMinVer, nMaxVer, strSetSubVer.c_str(), nPriority, strComment.c_str(), strStatusBar.c_str()); } void print() const { printf("%s", ToString().c_str()); } }; class CAlert : public CUnsignedAlert { public: std::vector vchMsg; std::vector vchSig; CAlert() { SetNull(); } IMPLEMENT_SERIALIZE ( READWRITE(vchMsg); READWRITE(vchSig); ) void SetNull() { CUnsignedAlert::SetNull(); vchMsg.clear(); vchSig.clear(); } bool IsNull() const { return (nExpiration == 0); } uint256 GetHash() const { return SerializeHash(*this); } bool IsInEffect() const { return (GetAdjustedTime() < nExpiration); } bool Cancels(const CAlert& alert) const { if (!IsInEffect()) return false; // this was a no-op before 31403 return (alert.nID <= nCancel || setCancel.count(alert.nID)); } bool AppliesTo(int nVersion, std::string strSubVerIn) const { // TODO: rework for client-version-embedded-in-strSubVer ? return (IsInEffect() && nMinVer <= nVersion && nVersion <= nMaxVer && (setSubVer.empty() || setSubVer.count(strSubVerIn))); } bool AppliesToMe() const { return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); } bool RelayTo(CNode* pnode) const { if (!IsInEffect()) return false; // returns true if wasn't already contained in the set if (pnode->setKnown.insert(GetHash()).second) { if (AppliesTo(pnode->nVersion, pnode->strSubVer) || AppliesToMe() || GetAdjustedTime() < nRelayUntil) { pnode->PushMessage("alert", *this); return true; } } return false; } bool CheckSignature() { CKey key; if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"))) return error("CAlert::CheckSignature() : SetPubKey failed"); if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) return error("CAlert::CheckSignature() : verify signature failed"); // Now unserialize the data CDataStream sMsg(vchMsg); sMsg >> *(CUnsignedAlert*)this; return true; } bool ProcessAlert(); }; #endif