// Copyright (c) 2009-2010 Satoshi Nakamoto // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. class COutPoint; class CInPoint; class CDiskTxPos; class CCoinBase; class CTxIn; class CTxOut; class CTransaction; class CBlock; class CBlockIndex; class CWalletTx; class CKeyItem; static const unsigned int MAX_SIZE = 0x02000000; static const unsigned int MAX_BLOCK_SIZE = 1000000; static const int64 COIN = 100000000; static const int64 CENT = 1000000; static const int COINBASE_MATURITY = 100; static const CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); extern CCriticalSection cs_main; extern map mapBlockIndex; extern const uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; extern CBigNum bnBestChainWork; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; extern map mapRequestCount; extern CCriticalSection cs_mapRequestCount; extern map mapAddressBook; extern CCriticalSection cs_mapAddressBook; extern vector vchDefaultKey; // Settings extern int fGenerateBitcoins; extern int64 nTransactionFee; extern CAddress addrIncoming; extern int fLimitProcessors; extern int nLimitProcessors; extern int fMinimizeToTray; extern int fMinimizeOnClose; bool CheckDiskSpace(int64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); bool AddKey(const CKey& key); vector GenerateNewKey(); bool AddToWallet(const CWalletTx& wtxIn); void WalletUpdateSpent(const COutPoint& prevout); void ReacceptWalletTransactions(); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv); bool SendMessages(CNode* pto, bool fSendTrickle); int64 GetBalance(); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet); bool CommitTransaction(CWalletTx& wtxNew, const CKey& key); bool BroadcastTransaction(CWalletTx& wtxNew); string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); void GenerateBitcoins(bool fGenerate); void ThreadBitcoinMiner(void* parg); void BitcoinMiner(); bool IsInitialBlockDownload(); 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); } 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); } string ToString() const { return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,6).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 = UINT_MAX; } explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) { prevout = prevoutIn; scriptSig = scriptSigIn; nSequence = nSequenceIn; } CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) { prevout = COutPoint(hashPrevTx, nOut); scriptSig = scriptSigIn; nSequence = nSequenceIn; } IMPLEMENT_SERIALIZE ( READWRITE(prevout); READWRITE(scriptSig); READWRITE(nSequence); ) bool IsFinal() const { return (nSequence == UINT_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); } string ToString() const { string str; str += strprintf("CTxIn("); str += prevout.ToString(); if (prevout.IsNull()) str += strprintf(", coinbase %s", HexStr(scriptSig.begin(), scriptSig.end(), false).c_str()); else str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); if (nSequence != UINT_MAX) str += strprintf(", nSequence=%u", nSequence); str += ")"; return str; } void print() const { printf("%s\n", ToString().c_str()); } bool IsMine() const; int64 GetDebit() const; }; // // 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; public: 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); } bool IsMine() const { return ::IsMine(scriptPubKey); } int64 GetCredit() const { if (IsMine()) return nValue; return 0; } 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); } 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,24).c_str()); } void print() const { printf("%s\n", ToString().c_str()); } }; // // 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; vector vin; vector vout; unsigned int nLockTime; 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; } bool IsNull() const { return (vin.empty() && vout.empty()); } uint256 GetHash() const { return SerializeHash(*this); } bool IsFinal(int64 nBlockTime=0) const { // Time based nLockTime implemented in 0.1.6, // do not use time based until most 0.1.5 nodes have upgraded. if (nLockTime == 0) return true; if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); if (nLockTime < (nLockTime < 500000000 ? nBestHeight : nBlockTime)) return true; 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 = UINT_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 CheckTransaction() const { // Basic checks that don't depend on any context if (vin.empty() || vout.empty()) return error("CTransaction::CheckTransaction() : vin or vout empty"); // Check for negative values foreach(const CTxOut& txout, vout) if (txout.nValue < 0) return error("CTransaction::CheckTransaction() : txout.nValue negative"); if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) return error("CTransaction::CheckTransaction() : coinbase script size"); } else { foreach(const CTxIn& txin, vin) if (txin.prevout.IsNull()) return error("CTransaction::CheckTransaction() : prevout is null"); } return true; } bool IsMine() const { foreach(const CTxOut& txout, vout) if (txout.IsMine()) return true; return false; } int64 GetDebit() const { int64 nDebit = 0; foreach(const CTxIn& txin, vin) nDebit += txin.GetDebit(); return nDebit; } int64 GetCredit() const { int64 nCredit = 0; foreach(const CTxOut& txout, vout) nCredit += txout.GetCredit(); return nCredit; } int64 GetValueOut() const { int64 nValueOut = 0; foreach(const CTxOut& txout, vout) { if (txout.nValue < 0) throw runtime_error("CTransaction::GetValueOut() : negative value"); nValueOut += txout.nValue; } return nValueOut; } int64 GetMinFee(unsigned int nBlockSize=1) const { // Base fee is 1 cent per kilobyte unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); int64 nMinFee = (1 + (int64)nBytes / 1000) * CENT; // Transactions under 60K are free as long as block size is under 80K // (about 27,000bc if made of 50bc inputs) if (nBytes < 60000 && nBlockSize < 80000) nMinFee = 0; // Transactions under 3K are free as long as block size is under 200K if (nBytes < 3000 && nBlockSize < 200000) nMinFee = 0; // To limit dust spam, require a 0.01 fee if any output is less than 0.01 if (nMinFee < CENT) foreach(const CTxOut& txout, vout) if (txout.nValue < CENT) nMinFee = CENT; 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); } string ToString() const { string str; str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", GetHash().ToString().substr(0,6).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 DisconnectInputs(CTxDB& txdb); bool ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, int nHeight, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); bool ClientConnectInputs(); bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); bool AcceptTransaction(bool fCheckInputs=true, bool* pfMissingInputs=NULL) { CTxDB txdb("r"); return AcceptTransaction(txdb, fCheckInputs, pfMissingInputs); } protected: bool AddToMemoryPool(); public: bool RemoveFromMemoryPool(); }; // // A transaction with a merkle branch linking it to the block chain // class CMerkleTx : public CTransaction { public: uint256 hashBlock; vector vMerkleBranch; int nIndex; // memory only mutable bool fMerkleVerified; mutable bool fGetCreditCached; mutable int64 nGetCreditCached; CMerkleTx() { Init(); } CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) { Init(); } void Init() { hashBlock = 0; nIndex = -1; fMerkleVerified = false; fGetCreditCached = false; nGetCreditCached = 0; } IMPLEMENT_SERIALIZE ( nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); nVersion = this->nVersion; READWRITE(hashBlock); READWRITE(vMerkleBranch); READWRITE(nIndex); ) int64 GetCredit(bool fUseCache=false) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; // GetBalance can assume transactions in mapWallet won't change if (fUseCache && fGetCreditCached) return nGetCreditCached; nGetCreditCached = CTransaction::GetCredit(); fGetCreditCached = true; return nGetCreditCached; } int SetMerkleBranch(const CBlock* pblock=NULL); int GetDepthInMainChain(int& nHeightRet) const; int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); } bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true); bool AcceptTransaction() { CTxDB txdb("r"); return AcceptTransaction(txdb); } }; // // A transaction with a bunch of additional info that only the owner cares // about. It includes any unrecorded transactions needed to link it back // to the block chain. // class CWalletTx : public CMerkleTx { public: vector vtxPrev; map mapValue; vector > vOrderForm; unsigned int fTimeReceivedIsTxTime; unsigned int nTimeReceived; // time received by this node char fFromMe; char fSpent; //// probably need to sign the order info so know it came from payer // memory only UI hints mutable unsigned int nTimeDisplayed; mutable int nLinesDisplayed; CWalletTx() { Init(); } CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) { Init(); } CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) { Init(); } void Init() { fTimeReceivedIsTxTime = false; nTimeReceived = 0; fFromMe = false; fSpent = false; nTimeDisplayed = 0; nLinesDisplayed = 0; } IMPLEMENT_SERIALIZE ( nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action); nVersion = this->nVersion; READWRITE(vtxPrev); READWRITE(mapValue); READWRITE(vOrderForm); READWRITE(fTimeReceivedIsTxTime); READWRITE(nTimeReceived); READWRITE(fFromMe); READWRITE(fSpent); ) bool WriteToDisk() { return CWalletDB().WriteTx(GetHash(), *this); } int64 GetTxTime() const; int GetRequestCount() const; void AddSupportingTransactions(CTxDB& txdb); bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } void RelayWalletTransaction(CTxDB& txdb); void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } }; // // 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; 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) { if (a.pos != b.pos || a.vSpent.size() != b.vSpent.size()) return false; for (int i = 0; i < a.vSpent.size(); i++) if (a.vSpent[i] != b.vSpent[i]) return false; return true; } friend bool operator!=(const CTxIndex& a, const CTxIndex& b) { return !(a == b); } }; // // 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 vector vtx; // memory only mutable vector vMerkleTree; 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(); } bool IsNull() const { return (nBits == 0); } uint256 GetHash() const { return Hash(BEGIN(nVersion), END(nNonce)); } uint256 BuildMerkleTree() const { vMerkleTree.clear(); 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 = 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()); } vector GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) BuildMerkleTree(); vector vMerkleBranch; int j = 0; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { int i = min(nIndex^1, nSize-1); vMerkleBranch.push_back(vMerkleTree[j+i]); nIndex >>= 1; j += nSize; } return vMerkleBranch; } static uint256 CheckMerkleBranch(uint256 hash, const vector& vMerkleBranch, int nIndex) { if (nIndex == -1) return 0; 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(bool fWriteTransactions, unsigned int& nFileRet, unsigned int& nBlockPosRet) { // Open history file to append CAutoFile fileout = AppendBlockFile(nFileRet); if (!fileout) return error("CBlock::WriteToDisk() : AppendBlockFile failed"); if (!fWriteTransactions) fileout.nType |= SER_BLOCKHEADERONLY; // 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 __WXMSW__ _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 (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit) return error("CBlock::ReadFromDisk() : nBits errors in block header"); if (GetHash() > CBigNum().SetCompact(nBits).getuint256()) return error("CBlock::ReadFromDisk() : GetHash() 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,16).c_str(), nVersion, hashPrevBlock.ToString().substr(0,16).c_str(), hashMerkleRoot.ToString().substr(0,6).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,6).c_str()); printf("\n"); } int64 GetBlockValue(int64 nFees) const; bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions=true); 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; } uint256 GetBlockHash() const { return *phashBlock; } CBigNum GetBlockWork() const { return (CBigNum(1)<<256) / (CBigNum().SetCompact(nBits)+1); } bool IsInMainChain() const { return (pnext || this == pindexBest); } 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 { unsigned int pmedian[nMedianTimeSpan]; unsigned int* pbegin = &pmedian[nMedianTimeSpan]; unsigned int* pend = &pmedian[nMedianTimeSpan]; const CBlockIndex* pindex = this; for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) *(--pbegin) = pindex->nTime; 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 nTime; pindex = pindex->pnext; } return pindex->GetMedianTimePast(); } 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,6).c_str(), GetBlockHash().ToString().substr(0,16).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(); } string ToString() const { string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", GetBlockHash().ToString().c_str(), hashPrev.ToString().substr(0,16).c_str(), hashNext.ToString().substr(0,16).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: vector vHave; public: CBlockLocator() { } explicit CBlockLocator(const CBlockIndex* pindex) { Set(pindex); } explicit CBlockLocator(uint256 hashBlock) { map::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end()) Set((*mi).second); } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); READWRITE(vHave); ) 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; foreach(const uint256& hash, vHave) { 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 foreach(const uint256& hash, vHave) { 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 foreach(const uint256& hash, vHave) { 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; } }; // // Private key that includes an expiration date in case it never gets used. // class CWalletKey { public: CPrivKey vchPrivKey; int64 nTimeCreated; int64 nTimeExpires; string strComment; //// todo: add something to note what created it (user, getnewaddress, change) //// maybe should have a map property map CWalletKey(int64 nTimeExpiresIn=0) { nTimeCreated = (nTimeExpiresIn ? GetTime() : 0); nTimeExpires = nTimeExpiresIn; } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); READWRITE(vchPrivKey); READWRITE(nTimeCreated); READWRITE(nTimeExpires); READWRITE(strComment); ) }; extern map mapTransactions; extern map mapWallet; extern vector vWalletUpdated; extern CCriticalSection cs_mapWallet; extern map, CPrivKey> mapKeys; extern map > mapPubKeys; extern CCriticalSection cs_mapKeys; extern CKey keyUser;