aboutsummaryrefslogtreecommitdiff
path: root/src/main.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.h')
-rw-r--r--src/main.h1127
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