#include "sync.h"
#include "net.h"
#include "script.h"
-#include "scrypt_mine.h"
+#include "scrypt.h"
+#include "zerocoin/Zerocoin.h"
#include <list>
class CInv;
class CRequestTracker;
class CNode;
+class CBlockIndexTrustComparator;
static const unsigned int MAX_BLOCK_SIZE = 1000000;
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
static const unsigned int MAX_INV_SZ = 50000;
+
+static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
+static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
+static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
+static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF;
+
static const int64 MIN_TX_FEE = CENT;
static const int64 MIN_RELAY_TX_FEE = CENT;
static const int64 MAX_MONEY = 2000000000 * COIN;
static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Sat, 20 Jul 2013 00:00:00 GMT
static const unsigned int CHAINCHECKS_SWITCH_TIME = 1379635200; // Fri, 20 Sep 2013 00:00:00 GMT
static const unsigned int STAKECURVE_SWITCH_TIME = 1382227200; // Sun, 20 Oct 2013 00:00:00 GMT
-static const unsigned int STAKEWEIGHT_SWITCH_TIME = 1388534400; // Wed, 01 Jan 2014 00:00:00 GMT
+static const unsigned int FEE_SWITCH_TIME = 1404172800; // Thu, 01 Jun 2014 00:00:00 GMT
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
static const uint256 hashGenesisBlock("0x00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4");
static const uint256 hashGenesisBlockTestNet("0x000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb");
-static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
+inline int64 PastDrift(int64 nTime) { return nTime - 2 * 60 * 60; } // up to 2 hours from the past
+inline int64 FutureDrift(int64 nTime) { return nTime + 2 * 60 * 60; } // up to 2 hours from the future
+extern libzerocoin::Params* ZCParams;
extern CScript COINBASE_FLAGS;
-
-
extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
+extern std::set<CBlockIndex*, CBlockIndexTrustComparator> setBlockIndexValid;
extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
extern CBlockIndex* pindexGenesisBlock;
extern unsigned int nStakeMinAge;
// Settings
extern int64 nTransactionFee;
-extern bool fStakeUsePooledKeys;
+extern int64 nMinimumInputValue;
+extern bool fUseFastIndex;
+extern unsigned int nDerivationMethodIndex;
+extern bool fEnforceCanonical;
// Minimum disk space required - used in CheckDiskSpace()
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;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
-void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true);
+void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true);
bool ProcessBlock(CNode* pfrom, CBlock* pblock);
bool CheckDiskSpace(uint64 nAdditionalBytes=0);
-FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
-FILE* AppendBlockFile(unsigned int& nFileRet);
+FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
+FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool LoadBlockIndex(bool fAllowNew=true);
void PrintBlockTree();
CBlockIndex* FindBlockByHeight(int nHeight);
bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, bool fSendTrickle);
bool LoadExternalBlockFile(FILE* fileIn);
-CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake=false);
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
-bool CheckStake(CBlock* pblock, CWallet& wallet);
+
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
+unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
int64 GetProofOfWorkReward(unsigned int nBits);
int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly=false);
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
-bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
+bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow);
+bool SetBestChain(CBlockIndex* pindexNew);
+bool ConnectBestBlock();
+CBlockIndex * InsertBlockIndex(uint256 hash);
uint256 WantedByOrphan(const CBlock* pblockOrphan);
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
void StakeMiner(CWallet *pwallet);
-void ResendWalletTransactions();
-
-
-
-
-
-
-
-
-
+void ResendWalletTransactions(bool fForce=false);
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;
-
- CDiskTxPos()
- {
- SetNull();
- }
-
- CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn)
- {
- nFile = nFileIn;
- nBlockPos = nBlockPosIn;
- nTxPos = nTxPosIn;
- }
+ int nFile;
+ unsigned int nPos;
- IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
- void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; }
- bool IsNull() const { return (nFile == (unsigned int) -1); }
+ IMPLEMENT_SERIALIZE(
+ READWRITE(VARINT(nFile));
+ READWRITE(VARINT(nPos));
+ )
- 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=%u, nBlockPos=%u, nTxPos=%u)", 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
{
scriptPubKey.clear();
}
- bool IsNull()
+ bool IsNull() const
{
return (nValue == -1);
}
std::string ToStringShort() const
{
- return strprintf(" out %s %s", FormatMoney(nValue).c_str(), scriptPubKey.ToString(true).c_str());
+ return strprintf(" out %s %s", FormatMoney(nValue).c_str(), scriptPubKey.ToString().substr(0, 10).c_str());
}
std::string ToString() const
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.
return SerializeHash(*this);
}
+ uint256 GetMetaHash() const
+ {
+ return SignatureHash(CScript(), *this, 0, SIGHASH_ALL);
+ }
+
bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const
{
// Time based nLockTime implemented in 0.1.6
@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
@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)
@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)
{
return dPriority > COIN * 144 / 250;
}
- int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const;
-
- 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=false, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const;
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
}
- 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(CTxDB& txdb, 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, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true, CBlock *pblock=NULL) const;
+
+ // Apply the effects of this transaction on the UTXO set represented by view
+ bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime, const uint256 &txhash) const;
+
+ // Context-independent validity checks
bool CheckTransaction() const;
- bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
- bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const; // ppcoin: get transaction coin age
+ // Try to accept this transaction into the memory pool
+ bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
+ bool GetCoinAge(uint64& nCoinAge) const; // Get transaction coin age
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
+ bool fCoinStake; // if the outpoint was the last unspent: whether it belonged to a coinstake
+ unsigned int nHeight; // if the outpoint was the last unspent: its height
+ int nVersion; // if the outpoint was the last unspent: its version
+ unsigned int nTime; // if the outpoint was the last unspent: its timestamp
+ unsigned int nBlockTime; // if the outpoint was the last unspent: its block timestamp
+
+ CTxInUndo() : txout(), fCoinBase(false), fCoinStake(false), nHeight(0), nVersion(0), nTime(0), nBlockTime(0) {}
+ CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, bool fCoinStakeIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0, int nTimeIn = 0, int nBlockTimeIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn), nVersion(nVersionIn), nTime(nTimeIn), nBlockTime(nBlockTimeIn) { }
+
+ unsigned int GetSerializeSize(int nType, int nVersion) const {
+ return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) +
+ ::GetSerializeSize(VARINT(nTime*2+(fCoinStake ? 1 : 0)), nType, nVersion) +
+ ::GetSerializeSize(VARINT(nBlockTime), 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);
+ ::Serialize(s, VARINT(nTime*2+(fCoinStake ? 1 : 0)), nType, nVersion);
+ ::Serialize(s, VARINT(nBlockTime), 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 nCodeHeight = 0, nCodeTime = 0;
+ ::Unserialize(s, VARINT(nCodeHeight), nType, nVersion);
+ nHeight = nCodeHeight / 2;
+ fCoinBase = nCodeHeight & 1;
+ ::Unserialize(s, VARINT(nCodeTime), nType, nVersion);
+ nTime = nCodeTime / 2;
+ fCoinStake = nCodeTime & 1;
+ ::Unserialize(s, VARINT(nBlockTime), nType, nVersion);
+ 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(nFlags)
+ * - VARINT(nCode)
+ * - unspentness bitvector, for vout[2] and further; least significant byte first
+ * - the non-spent CTxOuts (via CTxOutCompressor)
+ * - VARINT(nHeight)
+ * - VARINT(nTime + is_coinstake)
+ * - VARINT(nBlockTime)
+ *
+ * The nFlag value consists of:
+ * - bit 1: is coinbase
+ * - bit 2: is coinstake
+ * - bit 3: is pruned
+ *
+ * The nCode value consists of:
+ * - 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: 010004835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e40f1d75240f1d752
+ * <><><><--------------------------------------------><----><------><------>
+ * | | \ | / / /
+ * version | code vout[1] height timestamp block timestamp
+ * flags
+ *
+ * - version = 1
+ * - flags = 4
+ * - 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
+ * - time = 1389883712
+ * - is_coinbase = 0
+ * - is_coinstake = 0
+ * - block time = 1389883712
+ *
+ *
+ * Example: 010508044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b40f1d75240f1d752
+ * <><><><--><--------------------------------------------------><----------------------------------------------><----><------><------>
+ * / | \ \ | | / / /
+ * version | code unspentness vout[4] vout[16] height timestamp block timestamp
+ * flags
+ *
+ * - version = 1
+ * - flags = 5
+ * - code = 8 (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
+ * - time = 1389883712
+ * - is_coinbase = 1
+ * - is_coinstake = 0
+ * - block time = 1389883712
+ *
+ * Example: 010686af3b40f1d75240f1d752
+ * <><><----><------><------>
+ * / | \ | \
+ * version flags height timestamp block timestamp
+ *
+ * - version = 1
+ * - flags = 6 (00000110)
+ * - height = 120891
+ * - time = 1389883712
+ * - is_coinbase = 0
+ * - is_coinstake = 1
+ * - block time = 1389883712
+ */
+class CCoins
+{
+public:
+ // whether transaction is a coinbase
+ bool fCoinBase;
+
+ // whether transaction is a coinstake
+ bool fCoinStake;
+
+ // 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 blockchain
+ 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;
+
+ // transaction timestamp + coinstake flag
+ unsigned int nTime;
+
+ // block timestamp
+ unsigned int nBlockTime;
+
+ // construct a CCoins from a CTransaction, at a given height/timestamp
+ CCoins(const CTransaction &tx, int nHeightIn, int nBlockTimeIn) : fCoinBase(tx.IsCoinBase()), fCoinStake(tx.IsCoinStake()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion), nTime(tx.nTime), nBlockTime(nBlockTimeIn) { }
+
+ // empty constructor
+ CCoins() : fCoinBase(false), fCoinStake(false), vout(0), nHeight(0), nVersion(0), nTime(0), nBlockTime(0) { }
+
+ // remove spent outputs at the end of vout
+ void Cleanup() {
+ while (vout.size() > 0 && (vout.back().IsNull() || vout.back().IsEmpty()))
+ vout.pop_back();
+ }
+
+ // equality test
+ friend bool operator==(const CCoins &a, const CCoins &b) {
+ return a.fCoinBase == b.fCoinBase &&
+ a.fCoinStake == b.fCoinStake &&
+ a.nHeight == b.nHeight &&
+ a.nVersion == b.nVersion &&
+ a.nTime == b.nTime &&
+ a.nBlockTime == b.nBlockTime &&
+ 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;
+ }
+
+ bool IsCoinStake() const {
+ return fCoinStake;
+ }
+
+ unsigned int GetSerializeSize(int nType, int nVersion) const {
+ unsigned int nSize = 0;
+ bool fPruned = IsPruned();
+
+ // version
+ nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion);
+ unsigned char nFlags = 0;
+ // coinbase, coinstake and prune flags
+ nFlags = (fCoinBase ? 1 : 0)<<0 | (fCoinStake ? 1 : 0)<<1 | (fPruned ? 1 : 0)<<2;
+ // size of flags
+ nSize += ::GetSerializeSize(VARINT(nFlags), nType, nVersion);
+
+ if (!IsPruned()) {
+ 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)) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
+ // 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);
+ // timestamp and coinstake flag
+ nSize += ::GetSerializeSize(VARINT(nTime), nType, nVersion);
+ // block timestamp
+ nSize += ::GetSerializeSize(VARINT(nBlockTime), nType, nVersion);
+ }
+ else {
+ // size of height
+ nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion);
+ // size of timestamp
+ nSize += ::GetSerializeSize(VARINT(nTime), nType, nVersion);
+ // size of block timestamp
+ nSize += ::GetSerializeSize(VARINT(nBlockTime), nType, nVersion);
+ }
+
+ return nSize;
+ }
+
+ template<typename Stream>
+ void Serialize(Stream &s, int nType, int nVersion) const {
+ bool fPruned = IsPruned();
+ unsigned char nFlags = 0;
+ nFlags = (fCoinBase ? 1 : 0)<<0 | (fCoinStake ? 1 : 0)<<1 | (fPruned ? 1 : 0)<<2;
+
+ // version
+ ::Serialize(s, VARINT(this->nVersion), nType, nVersion);
+ // flags
+ ::Serialize(s, VARINT(nFlags), nType, nVersion);
+
+ if (!fPruned) {
+ 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)) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
+
+ // 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);
+ // transaction timestamp and coinstake flag
+ ::Serialize(s, VARINT(nTime), nType, nVersion);
+ // block timestamp
+ ::Serialize(s, VARINT(nBlockTime), nType, nVersion);
+ }
+ else {
+ // coinbase height
+ ::Serialize(s, VARINT(nHeight), nType, nVersion);
+ // transaction timestamp
+ ::Serialize(s, VARINT(nTime), nType, nVersion);
+ // block timestamp
+ ::Serialize(s, VARINT(nBlockTime), nType, nVersion);
+ }
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream &s, int nType, int nVersion) {
+ unsigned char nFlags = 0;
+
+ // version
+ ::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
+ // coinbase and coinstake flags
+ ::Unserialize(s, VARINT(nFlags), nType, nVersion);
+
+ fCoinBase = nFlags & (1<<0);
+ fCoinStake = nFlags & (1<<1);
+ bool fPruned = nFlags & (1<<2);
+
+ if (!fPruned) {
+ unsigned int nCode = 0;
+ // header code
+ ::Unserialize(s, VARINT(nCode), nType, nVersion);
+ 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);
+ // transaction timestamp
+ ::Unserialize(s, VARINT(nTime), nType, nVersion);
+ nTime = nTime;
+ // block timestamp
+ ::Unserialize(s, VARINT(nBlockTime), nType, nVersion);
+ }
+ else {
+ // coinbase height
+ ::Unserialize(s, VARINT(nHeight), nType, nVersion);
+ // transaction timestamp
+ ::Unserialize(s, VARINT(nTime), nType, nVersion);
+ // block timestamp
+ ::Unserialize(s, VARINT(nBlockTime), 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.nTime = nTime;
+ undo.nBlockTime = nBlockTime;
+ undo.fCoinBase = fCoinBase;
+ undo.fCoinStake = fCoinStake;
+ 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 {
+ if (vout.size() == 0)
+ return true;
+
+ BOOST_FOREACH(const CTxOut &out, vout)
+ if (!out.IsNull())
+ return false;
+
+ return true;
+ }
+};
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;
-
-};
-
-
-
-
-
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
* to everyone and the block is added to the block chain. The first transaction
* in the block is a special one that creates a new coin owned by the creator
* of the block.
- *
- * Blocks are appended to blk0001.dat files on disk. Their location on disk
- * is indexed by CBlockIndex objects in memory.
*/
class CBlock
{
// memory only
mutable std::vector<uint256> vMerkleTree;
- uint256 hashBlock;
// Denial-of-service detection:
mutable int nDoS;
vchBlockSig.clear();
vMerkleTree.clear();
nDoS = 0;
- hashBlock = 0;
}
bool IsNull() const
return (nBits == 0);
}
- uint256 GetHash(bool fRehash=false) const
+ uint256 GetHash() const
{
return scrypt_blockhash(CVOIDBEGIN(nVersion));
}
void UpdateTime(const CBlockIndex* pindexPrev);
- // ppcoin: entropy bit for stake modifier if chosen by modifier
- unsigned int GetStakeEntropyBit(unsigned int nTime) const
- {
- // Protocol switch to support p2pool at novacoin block #9689
- if (nTime >= ENTROPY_SWITCH_TIME || fTestNet)
- {
- // Take last bit of block hash as entropy bit
- unsigned int nEntropyBit = ((GetHash().Get64()) & 1llu);
- if (fDebug && GetBoolArg("-printstakemodifier"))
- printf("GetStakeEntropyBit: nTime=%u hashBlock=%s nEntropyBit=%u\n", nTime, GetHash().ToString().c_str(), nEntropyBit);
- return nEntropyBit;
- }
- // Before novacoin block #9689 - old protocol
- uint160 hashSig = Hash160(vchBlockSig);
- if (fDebug && GetBoolArg("-printstakemodifier"))
- printf("GetStakeEntropyBit: hashSig=%s", hashSig.ToString().c_str());
- hashSig >>= 159; // take the first bit of the hash
- if (fDebug && GetBoolArg("-printstakemodifier"))
- printf(" entropybit=%"PRI64d"\n", hashSig.Get64());
- return hashSig.Get64();
- }
+ unsigned int GetStakeEntropyBit(unsigned int nTime) const;
- // ppcoin: two types of block: proof-of-work or proof-of-stake
+ // two types of block: proof-of-work or proof-of-stake
bool IsProofOfStake() const
{
return (vtx.size() > 1 && vtx[1].IsCoinStake());
return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0);
}
- // ppcoin: get max transaction timestamp
+ // get max transaction timestamp
int64 GetMaxTransactionTime() const
{
int64 maxTransactionTime = 0;
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())
return hash;
}
-
- 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);
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, bool fReadTransactions = true)
{
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)
return true;
}
-
-
void print() const
{
printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu", vchBlockSig=%s)\n",
printf("\n");
}
+ // Undo the effects of this block (with given index) on the UTXO set represented by coins
+ bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins);
- bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
- bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false);
+ // 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, bool fReadTransactions=true);
- bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
- bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
- bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const;
+
+ // 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, bool fCheckSig=false) const;
+
+ // Store block on disk
bool AcceptBlock();
- bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block
- bool SignBlock(const CKeyStore& keystore);
- bool CheckBlockSignature(bool fProofOfStake) const;
-private:
- bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
+ // Get total coinage consumed
+ bool GetCoinAge(uint64& nCoinAge) const;
+
+ // Generate proof-of-stake block signature
+ bool SignBlock(CWallet& keystore);
+
+ // Get generator key
+ bool GetGenerator(CKey& GeneratorKey) const;
+
+ // Validate proof-of-stake block signature
+ bool CheckSignature(bool& fFatal, uint256& hashProofOfStake) const;
+
+ // Legacy proof-of-work signature
+ bool CheckLegacySignature() const;
};
+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
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;
- uint256 nChainTrust; // ppcoin: trust score of block chain
+
+ // 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) Trust score of block chain up to and including this block
+ uint256 nChainTrust;
+
+ // Number of transactions in this block.
+ unsigned int nTx;
+
+ // (memory only) Number of transactions in the chain up to and including this block
+ unsigned int nChainTx;
+
+ // Verification status of this block. See enum BlockStatus for detailed info
+ unsigned int nStatus;
+
+ // Coins amount created by this block
int64 nMint;
+
+ // Total coins created in this block chain up to and including this block
int64 nMoneySupply;
- unsigned int nFlags; // ppcoin: block index flags
- enum
+ // Block flags
+ unsigned int nFlags;
+ enum
{
- BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
- BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
- BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
+ // is proof-of-stake block
+ BLOCK_PROOF_OF_STAKE = (1 << 0),
+ // entropy bit for stake modifier
+ BLOCK_STAKE_ENTROPY = (1 << 1),
+ // regenerated stake modifier
+ BLOCK_STAKE_MODIFIER = (1 << 2),
};
- uint64 nStakeModifier; // hash modifier for proof-of-stake
- unsigned int nStakeModifierChecksum; // checksum of index; in-memeory only
+ // Hash modifier for proof-of-stake kernel
+ uint64 nStakeModifier;
- // proof-of-stake specific fields
+ // Checksum of index in-memory only
+ unsigned int nStakeModifierChecksum;
+
+ // Predecessor of coinstake transaction
COutPoint prevoutStake;
+
+ // Timestamp of coinstake transaction
unsigned int nStakeTime;
+
+ // Kernel hash
uint256 hashProofOfStake;
- // block header
+ // Block header
int nVersion;
uint256 hashMerkleRoot;
unsigned int nTime;
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
- nFile = 0;
- nBlockPos = 0;
nHeight = 0;
+ nFile = 0;
+ nDataPos = 0;
+ nUndoPos = 0;
nChainTrust = 0;
+ nTx = 0;
+ nChainTx = 0;
+ nStatus = 0;
nMint = 0;
nMoneySupply = 0;
nFlags = 0;
nNonce = 0;
}
- CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block)
+ CBlockIndex(CBlock& block)
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
- nFile = nFileIn;
- nBlockPos = nBlockPosIn;
nHeight = 0;
+ nFile = 0;
+ nDataPos = 0;
+ nUndoPos = 0;
nChainTrust = 0;
+ nTx = 0;
+ nChainTx = 0;
+ nStatus = 0;
nMint = 0;
nMoneySupply = 0;
nFlags = 0;
nNonce = block.nNonce;
}
+ CDiskBlockPos GetBlockPos() const {
+ CDiskBlockPos ret;
+ if (nStatus & BLOCK_HAVE_DATA) {
+ ret.nFile = nFile;
+ ret.nPos = nDataPos;
+ } else
+ ret.SetNull();
+ return ret;
+ }
+
+ CDiskBlockPos GetUndoPos() const {
+ CDiskBlockPos ret;
+ if (nStatus & BLOCK_HAVE_UNDO) {
+ ret.nFile = nFile;
+ ret.nPos = nUndoPos;
+ } else
+ ret.SetNull();
+ return ret;
+ }
+
CBlock GetBlockHeader() const
{
CBlock block;
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
unsigned int nRequired, unsigned int nToCheck);
-
bool IsProofOfWork() const
{
return !(nFlags & BLOCK_PROOF_OF_STAKE);
std::string ToString() const
{
- return strprintf("CBlockIndex(nprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016"PRI64x", nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
- pprev, pnext, nFile, nBlockPos, nHeight,
+ return strprintf("CBlockIndex(nprev=%p, pnext=%p nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016"PRI64x", nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
+ pprev, pnext, nHeight,
FormatMoney(nMint).c_str(), FormatMoney(nMoneySupply).c_str(),
GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW",
nStakeModifier, nStakeModifierChecksum,
GetBlockHash().ToString().c_str());
}
+
void print() const
{
printf("%s\n", ToString().c_str());
}
};
+struct CBlockIndexTrustComparator
+{
+ bool operator()(CBlockIndex *pa, CBlockIndex *pb) {
+ if (pa->nChainTrust > pb->nChainTrust) return false;
+ if (pa->nChainTrust < pb->nChainTrust) return true;
+ return false; // identical blocks
+ }
+};
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex
{
+private:
+ uint256 blockHash;
public:
uint256 hashPrev;
- uint256 hashNext;
- CDiskBlockIndex()
- {
+ CDiskBlockIndex() {
hashPrev = 0;
- hashNext = 0;
+ blockHash = 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));
READWRITE(nMint);
READWRITE(nMoneySupply);
READWRITE(nFlags);
const_cast<CDiskBlockIndex*>(this)->nStakeTime = 0;
const_cast<CDiskBlockIndex*>(this)->hashProofOfStake = 0;
}
+ READWRITE(blockHash);
// block header
READWRITE(this->nVersion);
uint256 GetBlockHash() const
{
+ if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0)
+ return blockHash;
+
CBlock block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
- return block.GetHash();
+
+ const_cast<CDiskBlockIndex*>(this)->blockHash = block.GetHash();
+
+ return blockHash;
}
{
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().c_str(),
- hashNext.ToString().c_str());
+ hashPrev.ToString().substr(0,20).c_str());
return str;
}
};
-
-
-
-
-
-
/** Describes a place in the block chain to another node such that if the
* other node doesn't have the same branch, it can find a recent common trunk.
* The further back it is, the further before the fork it may be.
-
-
-
class CTxMemPool
{
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);
void clear();
void queryHashes(std::vector<uint256>& vtxid);
+ void pruneSpent(const uint256& hash, CCoins &coins);
unsigned long size()
{
extern CTxMemPool mempool;
+struct CCoinsStats
+{
+ int nHeight;
+ uint64 nTransactions;
+ uint64 nPrunedTransactions;
+ uint64 nTransactionOutputs;
+ uint64 nSerializedSize;
+
+ CCoinsStats() : nHeight(0), nTransactions(0), nPrunedTransactions(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);
+ virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
+ virtual bool GetStats(CCoinsStats &stats);
+};
+
+/** 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;
+ std::map<uint256,CCoins> cacheCoinsReadOnly;
+
+public:
+ CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool GetCoinsReadOnly(uint256 txid, CCoins &coins);
+ bool SetCoins(uint256 txid, const CCoins &coins);
+ bool HaveCoins(uint256 txid);
+ CCoins &GetCoins(uint256 txid);
+ CBlockIndex *GetBestBlock();
+ bool SetBestBlock(CBlockIndex *pindex);
+ bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
+ bool Flush();
+ 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