diff options
Diffstat (limited to 'src/main.h')
-rw-r--r-- | src/main.h | 1127 |
1 files changed, 836 insertions, 291 deletions
diff --git a/src/main.h b/src/main.h index e61cbdd46b..fdaec3469e 100644 --- a/src/main.h +++ b/src/main.h @@ -20,20 +20,38 @@ class CReserveKey; class CAddress; class CInv; -class CRequestTracker; class CNode; +struct CBlockIndexWorkComparator; + +/** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; +/** The maximum size for mined blocks */ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +/** The maximum allowed number of signature check operations in a block (network rule) */ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +/** The maximum number of orphan transactions kept in memory */ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +/** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; +/** The maximum size of a blk?????.dat file (since 0.8) */ +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ +static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ static const int64 MIN_TX_FEE = 50000; +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ static const int64 MIN_RELAY_TX_FEE = 10000; +/** No amount larger than this (in satoshi) is valid */ static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; -// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +/** 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 #ifdef USE_UPNP static const int fHaveUPnP = true; @@ -51,6 +69,7 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; +extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; extern uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; @@ -68,6 +87,10 @@ extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set<CWallet*> setpwalletRegistered; extern unsigned char pchMessageStart[4]; +extern bool fImporting; +extern bool fReindex; +extern bool fBenchmark; +extern unsigned int nCoinCacheSize; // Settings extern int64 nTransactionFee; @@ -77,33 +100,71 @@ static const uint64 nMinDiskSpace = 52428800; class CReserveKey; -class CTxDB; -class CTxIndex; - +class CCoinsDB; +class CBlockTreeDB; +class CDiskBlockPos; +class CCoins; +class CTxUndo; +class CCoinsView; +class CCoinsViewCache; + +/** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); +/** Unregister a wallet from core */ void UnregisterWallet(CWallet* pwalletIn); -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); -bool ProcessBlock(CNode* pfrom, CBlock* pblock); +/** Push an updated transaction to all registered wallets */ +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +/** Process an incoming block */ +bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +/** Check whether enough disk space is available for an incoming block */ 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); +/** Open a block file (blk?????.dat) */ +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Open an undo file (rev?????.dat) */ +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(); +/** Print the loaded block tree */ void PrintBlockTree(); +/** Find a block by height in the currently-connected chain */ CBlockIndex* FindBlockByHeight(int nHeight); +/** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); +/** Send queued protocol messages to be sent to a give node */ bool SendMessages(CNode* pto, bool fSendTrickle); -bool LoadExternalBlockFile(FILE* fileIn); +/** Run the importer thread, which deals with reindexing, loading bootstrap.dat, and whatever is passed to -loadblock */ +void ThreadImport(void *parg); +/** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +/** Generate a new block, without valid proof-of-work */ CBlock* CreateNewBlock(CReserveKey& reservekey); +/** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +/** Do mining precalculation */ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +/** Check mined block */ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +/** Get the number of active peers */ int GetNumBlocksOfPeers(); +/** Check whether we are doin an inital block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); +/** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(std::string strFor); -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ +bool SetBestChain(CBlockIndex* pindexNew); +/** Find the best known block, and make it the tip of the block chain */ +bool ConnectBestBlock(); +/** Create a new block index entry for a given block hash */ +CBlockIndex * InsertBlockIndex(uint256 hash); + @@ -114,61 +175,48 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock); +static inline std::string BlockHashStr(const uint256& hash) +{ + return hash.ToString(); +} bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); -/** Position on disk for a particular transaction. */ -class CDiskTxPos +class CDiskBlockPos { public: - unsigned int nFile; - unsigned int nBlockPos; - unsigned int nTxPos; + int nFile; + unsigned int nPos; - CDiskTxPos() - { + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nFile)); + READWRITE(VARINT(nPos)); + ) + + CDiskBlockPos() { SetNull(); } - CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) - { + CDiskBlockPos(int nFileIn, unsigned int nPosIn) { nFile = nFileIn; - nBlockPos = nBlockPosIn; - nTxPos = nTxPosIn; + nPos = nPosIn; } - 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 CDiskBlockPos &a, const CDiskBlockPos &b) { + return (a.nFile == b.nFile && a.nPos == b.nPos); } - friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) - { + friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { return !(a == b); } - std::string ToString() const - { - if (IsNull()) - return "null"; - else - return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); - } - - void print() const - { - printf("%s", ToString().c_str()); - } + void SetNull() { nFile = -1; nPos = 0; } + bool IsNull() const { return (nFile == -1); } }; + /** An inpoint - a combination of a transaction and an index n into its vin */ class CInPoint { @@ -214,7 +262,7 @@ public: std::string ToString() const { - return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n); } void print() const @@ -336,7 +384,7 @@ public: scriptPubKey.clear(); } - bool IsNull() + bool IsNull() const { return (nValue == -1); } @@ -380,7 +428,13 @@ enum GetMinFee_mode GMF_SEND, }; -typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx; +// Modes for script/signature checking +enum CheckSig_mode +{ + CS_NEVER, // never validate scripts + CS_AFTER_CHECKPOINT, // validate scripts after the last checkpoint + CS_ALWAYS // always validate scripts +}; /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. @@ -492,7 +546,7 @@ public: @return True if all inputs (scriptSigs) use only standard transaction forms @see CTransaction::FetchInputs */ - bool AreInputsStandard(const MapPrevTx& mapInputs) const; + bool AreInputsStandard(CCoinsViewCache& mapInputs) const; /** Count ECDSA signature operations the old-fashioned (pre-0.6) way @return number of sigops this transaction's outputs will produce when spent @@ -506,7 +560,7 @@ public: @return maximum number of sigops required to validate this transaction's inputs @see CTransaction::FetchInputs */ - unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; + unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; /** Amount of bitcoins spent by this transaction. @return sum of all outputs (note: does not include fees) @@ -531,7 +585,7 @@ public: @return Sum of value of all inputs (scriptSigs) @see CTransaction::FetchInputs */ - int64 GetValueIn(const MapPrevTx& mapInputs) const; + int64 GetValueIn(CCoinsViewCache& mapInputs) const; static bool AllowFree(double dPriority) { @@ -540,80 +594,7 @@ public: 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, PROTOCOL_VERSION); - unsigned int nNewBlockSize = nBlockSize + nBytes; - int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; - - if (fAllowFree) - { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500 BTC if made of 50 BTC 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 = CAutoFile(OpenBlockFile(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; - } + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const; friend bool operator==(const CTransaction& a, const CTransaction& b) { @@ -632,7 +613,7 @@ public: std::string ToString() const { std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", GetHash().ToString().substr(0,10).c_str(), nVersion, vin.size(), @@ -651,48 +632,386 @@ public: } - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); - bool ReadFromDisk(COutPoint prevout); - bool DisconnectInputs(CTxDB& txdb); + // Do all possible client-mode checks + bool ClientCheckInputs() const; - /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + // Check whether all prevouts of this transaction are present in the UTXO set represented by view + bool HaveInputs(CCoinsViewCache &view) const; - @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); - - /** 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 - @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions - @return Returns true if all checks succeed - */ - bool ConnectInputs(MapPrevTx inputs, - std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true); - bool ClientConnectInputs(); + // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) + // This does not modify the UTXO set + bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC) const; + + // Apply the effects of this transaction on the UTXO set represented by view + bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; + + // Context-independent validity checks bool CheckTransaction() const; - bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + + // Try to accept this transaction into the memory pool + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); protected: - const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; + static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); +}; + +/** wrapper for CTxOut that provides a more compact serialization */ +class CTxOutCompressor +{ +private: + CTxOut &txout; + +public: + static uint64 CompressAmount(uint64 nAmount); + static uint64 DecompressAmount(uint64 nAmount); + + CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + + IMPLEMENT_SERIALIZE(({ + if (!fRead) { + uint64 nVal = CompressAmount(txout.nValue); + READWRITE(VARINT(nVal)); + } else { + uint64 nVal = 0; + READWRITE(VARINT(nVal)); + txout.nValue = DecompressAmount(nVal); + } + CScriptCompressor cscript(REF(txout.scriptPubKey)); + READWRITE(cscript); + });) +}; + +/** Undo information for a CTxIn + * + * Contains the prevout's CTxOut being spent, and if this was the + * last output of the affected transaction, its metadata as well + * (coinbase or not, height, transaction version) + */ +class CTxInUndo +{ +public: + CTxOut txout; // the txout data before being spent + bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase + unsigned int nHeight; // if the outpoint was the last unspent: its height + int nVersion; // if the outpoint was the last unspent: its version + + CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} + CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + + ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); + if (nHeight > 0) + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + ::Unserialize(s, VARINT(nCode), nType, nVersion); + nHeight = nCode / 2; + fCoinBase = nCode & 1; + if (nHeight > 0) + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); + } +}; + +/** Undo information for a CTransaction */ +class CTxUndo +{ +public: + // undo information for all txins + std::vector<CTxInUndo> vprevout; + + IMPLEMENT_SERIALIZE( + READWRITE(vprevout); + ) }; +/** Undo information for a CBlock */ +class CBlockUndo +{ +public: + std::vector<CTxUndo> vtxundo; // for all but the coinbase + IMPLEMENT_SERIALIZE( + READWRITE(vtxundo); + ) + + bool WriteToDisk(CDiskBlockPos &pos) + { + // Open history file to append + CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlockUndo::WriteToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } +}; + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector<CTxOut> vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + // calculate number of bytes for the bitmask, and its number of non-zero bytes + // each bit in the bitmask represents the availability of one output, but the + // availabilities of the first two outputs are encoded separately + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; + } + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b<nMaskSize; b++) { + unsigned char chAvail = 0; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) + if (!vout[2+b*8+i].IsNull()) + chAvail |= (1 << i); + ::Serialize(s, chAvail, nType, nVersion); + } + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) { + if (!vout[i].IsNull()) + ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); + } + // coinbase height + ::Serialize(s, VARINT(nHeight), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector<bool> vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; + } + + // mark a vout spent + bool Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); + } + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; @@ -741,66 +1060,15 @@ public: 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(); + bool AcceptToMemoryPool(bool fCheckInputs=true); }; -/** 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 GetDepthInMainChain() const; - -}; @@ -812,11 +1080,8 @@ public: * 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 +class CBlockHeader { public: // header @@ -828,17 +1093,7 @@ public: unsigned int nBits; unsigned int nNonce; - // network and disk - std::vector<CTransaction> vtx; - - // 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() + CBlockHeader() { SetNull(); } @@ -852,25 +1107,16 @@ public: 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<CBlock*>(this)->vtx.clear(); ) void SetNull() { - nVersion = CBlock::CURRENT_VERSION; + nVersion = CBlockHeader::CURRENT_VERSION; hashPrevBlock = 0; hashMerkleRoot = 0; nTime = 0; nBits = 0; nNonce = 0; - vtx.clear(); - vMerkleTree.clear(); - nDoS = 0; } bool IsNull() const @@ -889,7 +1135,45 @@ public: } void UpdateTime(const CBlockIndex* pindexPrev); +}; + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector<CTransaction> vtx; + + // 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(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *((CBlockHeader*)this) = header; + } + IMPLEMENT_SERIALIZE + ( + READWRITE(*(CBlockHeader*)this); + READWRITE(vtx); + ) + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + vMerkleTree.clear(); + nDoS = 0; + } uint256 BuildMerkleTree() const { @@ -910,6 +1194,12 @@ public: return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); } + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + std::vector<uint256> GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) @@ -942,12 +1232,12 @@ public: } - bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) + bool WriteToDisk(CDiskBlockPos &pos) { // Open history file to append - CAutoFile fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (!fileout) - return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + return error("CBlock::WriteToDisk() : OpenBlockFile failed"); // Write index header unsigned int nSize = fileout.GetSerializeSize(*this); @@ -957,27 +1247,25 @@ public: long fileOutPos = ftell(fileout); if (fileOutPos < 0) return error("CBlock::WriteToDisk() : ftell failed"); - nBlockPosRet = fileOutPos; + pos.nPos = (unsigned int)fileOutPos; fileout << *this; // Flush stdio buffers and commit to disk before returning fflush(fileout); - if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + if (!IsInitialBlockDownload()) FileCommit(fileout); return true; } - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) + bool ReadFromDisk(const CDiskBlockPos &pos) { SetNull(); // Open history file to read - CAutoFile filein = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION); + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (!filein) return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - if (!fReadTransactions) - filein.nType |= SER_BLOCKHEADERONLY; // Read block try { @@ -998,10 +1286,10 @@ public: 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(), + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", + BlockHashStr(GetHash()).c_str(), nVersion, - hashPrevBlock.ToString().substr(0,20).c_str(), + BlockHashStr(hashPrevBlock).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(), nTime, nBits, nNonce, vtx.size()); @@ -1017,22 +1305,104 @@ public: } - 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); + // Undo the effects of this block (with given index) on the UTXO set represented by coins + bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins); + + // Apply the effects of this block (with given index) on the UTXO set represented by coins + bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + + // Read a block from disk + bool ReadFromDisk(const CBlockIndex* pindex); + + // Add this block to the block index, and if necessary, switch the active block chain to this + bool AddToBlockIndex(const CDiskBlockPos &pos); + + // Context-independent validity checks bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; - bool AcceptBlock(); -private: - bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); + // Store block on disk + // if dbp is provided, the file is known to already reside on disk + bool AcceptBlock(CDiskBlockPos *dbp = NULL); }; +class CBlockFileInfo +{ +public: + unsigned int nBlocks; // number of blocks stored in file + unsigned int nSize; // number of used bytes of block file + unsigned int nUndoSize; // number of used bytes in the undo file + unsigned int nHeightFirst; // lowest height of block in file + unsigned int nHeightLast; // highest height of block in file + uint64 nTimeFirst; // earliest time of block in file + uint64 nTimeLast; // latest time of block in file + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nBlocks)); + READWRITE(VARINT(nSize)); + READWRITE(VARINT(nUndoSize)); + READWRITE(VARINT(nHeightFirst)); + READWRITE(VARINT(nHeightLast)); + READWRITE(VARINT(nTimeFirst)); + READWRITE(VARINT(nTimeLast)); + ) + + void SetNull() { + nBlocks = 0; + nSize = 0; + nUndoSize = 0; + nHeightFirst = 0; + nHeightLast = 0; + nTimeFirst = 0; + nTimeLast = 0; + } + + CBlockFileInfo() { + SetNull(); + } + + std::string ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u..%u, time=%s..%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + } + + // update statistics (does not update nSize) + void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { + if (nBlocks==0 || nHeightFirst > nHeightIn) + nHeightFirst = nHeightIn; + if (nBlocks==0 || nTimeFirst > nTimeIn) + nTimeFirst = nTimeIn; + nBlocks++; + if (nHeightIn > nHeightFirst) + nHeightLast = nHeightIn; + if (nTimeIn > nTimeLast) + nTimeLast = nTimeIn; + } +}; + +extern CCriticalSection cs_LastBlockFile; +extern CBlockFileInfo infoLastBlockFile; +extern int nLastBlockFile; + +enum BlockStatus { + BLOCK_VALID_UNKNOWN = 0, + BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint + BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root + BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 + BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + BLOCK_VALID_MASK = 7, + + BLOCK_HAVE_DATA = 8, // full block available in blk*.dat + BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat + BLOCK_HAVE_MASK = 24, + + BLOCK_FAILED_VALID = 32, // stage after last reached validness failed + BLOCK_FAILED_CHILD = 64, // descends from failed block + BLOCK_FAILED_MASK = 96 +}; /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple @@ -1044,14 +1414,40 @@ private: class CBlockIndex { public: + // pointer to the hash of the block, if any. memory is owned by this CBlockIndex const uint256* phashBlock; + + // pointer to the index of the predecessor of this block CBlockIndex* pprev; + + // (memory only) pointer to the index of the *active* successor of this block CBlockIndex* pnext; - unsigned int nFile; - unsigned int nBlockPos; + + // height of the entry in the chain. The genesis block has height 0 int nHeight; + + // Which # file this block is stored in (blk?????.dat) + int nFile; + + // Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos; + + // Byte offset within rev?????.dat where this block's undo data is stored + unsigned int nUndoPos; + + // (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block CBigNum bnChainWork; + // Number of transactions in this block. + // Note: in a potential headers-first mode, this number cannot be relied upon + unsigned int nTx; + + // (memory only) Number of transactions in the chain up to and including this block + unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030 + + // Verification status of this block. See enum BlockStatus + unsigned int nStatus; + // block header int nVersion; uint256 hashMerkleRoot; @@ -1065,10 +1461,14 @@ public: phashBlock = NULL; pprev = NULL; pnext = NULL; - nFile = 0; - nBlockPos = 0; nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; bnChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1077,15 +1477,19 @@ public: nNonce = 0; } - CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) + CBlockIndex(CBlockHeader& block) { phashBlock = NULL; pprev = NULL; pnext = NULL; - nFile = nFileIn; - nBlockPos = nBlockPosIn; nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; bnChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -1094,9 +1498,27 @@ public: nNonce = block.nNonce; } - CBlock GetBlockHeader() const + CDiskBlockPos GetBlockPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_DATA) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } + return ret; + } + + CDiskBlockPos GetUndoPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_UNDO) { + ret.nFile = nFile; + ret.nPos = nUndoPos; + } + return ret; + } + + CBlockHeader GetBlockHeader() const { - CBlock block; + CBlockHeader block; block.nVersion = nVersion; if (pprev) block.hashPrevBlock = pprev->GetBlockHash(); @@ -1171,13 +1593,12 @@ public: static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck); - std::string ToString() const { - return strprintf("CBlockIndex(pprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nFile, nBlockPos, nHeight, + return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nHeight, hashMerkleRoot.ToString().substr(0,10).c_str(), - GetBlockHash().ToString().substr(0,20).c_str()); + BlockHashStr(GetBlockHash()).c_str()); } void print() const @@ -1186,6 +1607,19 @@ public: } }; +struct CBlockIndexWorkComparator +{ + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->bnChainWork > pb->bnChainWork) return false; + if (pa->bnChainWork < pb->bnChainWork) return true; + + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + + return false; // identical blocks + } +}; + /** Used to marshal pointers into hashes for db storage. */ @@ -1193,29 +1627,29 @@ class CDiskBlockIndex : public CBlockIndex { public: uint256 hashPrev; - uint256 hashNext; - CDiskBlockIndex() - { + CDiskBlockIndex() { hashPrev = 0; - hashNext = 0; } - explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) - { + 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(VARINT(nVersion)); + + READWRITE(VARINT(nHeight)); + READWRITE(VARINT(nStatus)); + READWRITE(VARINT(nTx)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); // block header READWRITE(this->nVersion); @@ -1228,7 +1662,7 @@ public: uint256 GetBlockHash() const { - CBlock block; + CBlockHeader block; block.nVersion = nVersion; block.hashPrevBlock = hashPrev; block.hashMerkleRoot = hashMerkleRoot; @@ -1243,10 +1677,9 @@ public: { std::string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); - str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + str += strprintf("\n hashBlock=%s, hashPrev=%s)", GetBlockHash().ToString().c_str(), - hashPrev.ToString().substr(0,20).c_str(), - hashNext.ToString().substr(0,20).c_str()); + BlockHashStr(hashPrev).c_str()); return str; } @@ -1404,12 +1837,13 @@ public: std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; - bool accept(CTxDB& txdb, CTransaction &tx, - bool fCheckInputs, bool* pfMissingInputs); + bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); - bool remove(CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); void clear(); void queryHashes(std::vector<uint256>& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); unsigned long size() { @@ -1430,4 +1864,115 @@ public: extern CTxMemPool mempool; +struct CCoinsStats +{ + int nHeight; + uint64 nTransactions; + uint64 nTransactionOutputs; + uint64 nSerializedSize; + + CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0) {} +}; + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(uint256 txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(uint256 txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(uint256 txid); + + // Retrieve the block index whose state this CCoinsView currently represents + virtual CBlockIndex *GetBestBlock(); + + // Modify the currently active block index + virtual bool SetBestBlock(CBlockIndex *pindex); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) + virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Calculate statistics about the unspent transaction output set + virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} +}; + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(uint256 txid, CCoins &coins); + bool SetCoins(uint256 txid, const CCoins &coins); + bool HaveCoins(uint256 txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + CBlockIndex *pindexTip; + std::map<uint256,CCoins> cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods + bool GetCoins(uint256 txid, CCoins &coins); + bool SetCoins(uint256 txid, const CCoins &coins); + bool HaveCoins(uint256 txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(uint256 txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. + bool Flush(); + + // Calculate the size of the cache (in number of transactions) + unsigned int GetCacheSize(); + +private: + std::map<uint256,CCoins>::iterator FetchCoins(uint256 txid); +}; + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(uint256 txid, CCoins &coins); + bool HaveCoins(uint256 txid); +}; + +/** Global variable that points to the active CCoinsView (protected by cs_main) */ +extern CCoinsViewCache *pcoinsTip; + +/** Global variable that points to the active block tree (protected by cs_main) */ +extern CBlockTreeDB *pblocktree; + #endif |