// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MAIN_H
#define BITCOIN_MAIN_H
#include "bignum.h"
+#include "sync.h"
#include "net.h"
-#include "key.h"
#include "script.h"
-#include "db.h"
+#include "scrypt.h"
+#include "zerocoin/Zerocoin.h"
#include <list>
+class CWallet;
class CBlock;
class CBlockIndex;
-class CWalletTx;
-class CWallet;
class CKeyItem;
class CReserveKey;
-class CWalletDB;
+class COutPoint;
class CAddress;
class CInv;
class CRequestTracker;
class CNode;
-class CBlockIndex;
static const unsigned int MAX_BLOCK_SIZE = 1000000;
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
-static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
-static const int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
-static const int64 COIN = 100000000;
-static const int64 CENT = 1000000;
-static const int64 MIN_TX_FEE = 50000;
-static const int64 MIN_RELAY_TX_FEE = 10000;
-static const int64 MAX_MONEY = 21000000 * COIN;
+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 int64 MAX_MINT_PROOF_OF_WORK = 100 * COIN;
+static const int64 MAX_MINT_PROOF_OF_STAKE = 1 * COIN;
+static const int64 MIN_TXOUT_AMOUNT = MIN_TX_FEE;
+
+static const unsigned int ENTROPY_SWITCH_TIME = 1362791041; // Sat, 09 Mar 2013 01:04:01 GMT
+static const unsigned int STAKE_SWITCH_TIME = 1371686400; // Thu, 20 Jun 2013 00:00:00 GMT
+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 OUTPUT_SWITCH_TIME = 1398916800; // Thu, 01 May 2014 04:00:00 GMT
+
+
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
-static const int COINBASE_MATURITY = 100;
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
-static const int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
+static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
+
#ifdef USE_UPNP
static const int fHaveUPnP = true;
#else
static const int fHaveUPnP = false;
#endif
+static const uint256 hashGenesisBlock("0x00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4");
+static const uint256 hashGenesisBlockTestNet("0x000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb");
+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 uint256 hashGenesisBlock;
+extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
extern CBlockIndex* pindexGenesisBlock;
+extern unsigned int nStakeMinAge;
+extern unsigned int nNodeLifespan;
+extern int nCoinbaseMaturity;
extern int nBestHeight;
-extern CBigNum bnBestChainWork;
-extern CBigNum bnBestInvalidWork;
+extern uint256 nBestChainTrust;
+extern uint256 nBestInvalidTrust;
extern uint256 hashBestChain;
extern CBlockIndex* pindexBest;
extern unsigned int nTransactionsUpdated;
-extern double dHashesPerSec;
-extern int64 nHPSTimerStart;
+extern uint64 nLastBlockTx;
+extern uint64 nLastBlockSize;
+extern int64 nLastCoinStakeSearchInterval;
+extern const std::string strMessageMagic;
extern int64 nTimeBestReceived;
extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
+extern unsigned char pchMessageStart[4];
+extern std::map<uint256, CBlock*> mapOrphanBlocks;
// Settings
-extern int fGenerateBitcoins;
extern int64 nTransactionFee;
-extern int fLimitProcessors;
-extern int nLimitProcessors;
-extern int fMinimizeToTray;
-extern int fMinimizeOnClose;
-extern int fUseUPnP;
-
-
-
+extern int64 nMinimumInputValue;
+extern bool fUseFastIndex;
+extern unsigned int nDerivationMethodIndex;
+// Minimum disk space required - used in CheckDiskSpace()
+static const uint64 nMinDiskSpace = 52428800;
class CReserveKey;
-class CTxDB;
-class CTxIndex;
+class CCoinsDB;
+class CChainDB;
+class CDiskBlockPos;
+class CCoins;
+class CTxUndo;
+class CCoinsView;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
+void SyncWithWallets(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);
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
-CBlock* CreateNewBlock(CReserveKey& reservekey);
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
+bool LoadExternalBlockFile(FILE* fileIn);
+
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);
+unsigned int ComputeMinStake(unsigned int nBase, int64 nTime, unsigned int nBlockTime);
+int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
-
-
-
-
-
-
-
-
-
-
-
+bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow);
+uint256 WantedByOrphan(const CBlock* pblockOrphan);
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
+void StakeMiner(CWallet *pwallet);
+void ResendWalletTransactions();
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
-template<typename T>
-bool WriteSetting(const std::string& strKey, const T& value)
-{
- bool fOk = false;
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- {
- std::string strWalletFile;
- if (!GetWalletFile(pwallet, strWalletFile))
- continue;
- fOk |= CWalletDB(strWalletFile).WriteSetting(strKey, value);
- }
- return fOk;
-}
-
-
-class CDiskTxPos
+class CDiskBlockPos
{
public:
- unsigned int nFile;
- unsigned int nBlockPos;
- unsigned int nTxPos;
+ int nFile;
+ unsigned int nPos;
- CDiskTxPos()
- {
- SetNull();
- }
-
- CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn)
- {
- nFile = nFileIn;
- nBlockPos = nBlockPosIn;
- nTxPos = nTxPosIn;
- }
-
- IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
- void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; }
- bool IsNull() const { return (nFile == -1); }
+ 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 strprintf("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
{
public:
CInPoint() { SetNull(); }
CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; }
- void SetNull() { ptx = NULL; n = -1; }
- bool IsNull() const { return (ptx == NULL && n == -1); }
+ void SetNull() { ptx = NULL; n = (unsigned int) -1; }
+ bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); }
};
-
+/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
public:
COutPoint() { SetNull(); }
COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; }
IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
- void SetNull() { hash = 0; n = -1; }
- bool IsNull() const { return (hash == 0 && n == -1); }
+ void SetNull() { hash = 0; n = (unsigned int) -1; }
+ bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
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
-//
-// An input of a transaction. It contains the location of the previous
-// transaction's output that it claims and a signature that matches the
-// output's public key.
-//
+/** An input of a transaction. It contains the location of the previous
+ * transaction's output that it claims and a signature that matches the
+ * output's public key.
+ */
class CTxIn
{
public:
CTxIn()
{
- nSequence = UINT_MAX;
+ nSequence = std::numeric_limits<unsigned int>::max();
}
- explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
+ explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
{
prevout = prevoutIn;
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
- CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
+ CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
{
prevout = COutPoint(hashPrevTx, nOut);
scriptSig = scriptSigIn;
bool IsFinal() const
{
- return (nSequence == UINT_MAX);
+ return (nSequence == std::numeric_limits<unsigned int>::max());
}
friend bool operator==(const CTxIn& a, const CTxIn& b)
return !(a == b);
}
+ std::string ToStringShort() const
+ {
+ return strprintf(" %s %d", prevout.hash.ToString().c_str(), prevout.n);
+ }
+
std::string ToString() const
{
std::string str;
- str += strprintf("CTxIn(");
+ str += "CTxIn(";
str += prevout.ToString();
if (prevout.IsNull())
str += strprintf(", coinbase %s", HexStr(scriptSig).c_str());
else
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
- if (nSequence != UINT_MAX)
+ if (nSequence != std::numeric_limits<unsigned int>::max())
str += strprintf(", nSequence=%u", nSequence);
str += ")";
return str;
-//
-// An output of a transaction. It contains the public key that the next input
-// must be able to sign with to claim it.
-//
+/** An output of a transaction. It contains the public key that the next input
+ * must be able to sign with to claim it.
+ */
class CTxOut
{
public:
scriptPubKey.clear();
}
- bool IsNull()
+ bool IsNull() const
{
return (nValue == -1);
}
+ void SetEmpty()
+ {
+ nValue = 0;
+ scriptPubKey.clear();
+ }
+
+ bool IsEmpty() const
+ {
+ return (nValue == 0 && scriptPubKey.empty());
+ }
+
uint256 GetHash() const
{
return SerializeHash(*this);
return !(a == b);
}
+ std::string ToStringShort() const
+ {
+ return strprintf(" out %s %s", FormatMoney(nValue).c_str(), scriptPubKey.ToString().substr(0, 10).c_str());
+ }
+
std::string ToString() const
{
+ if (IsEmpty()) return "CTxOut(empty)";
if (scriptPubKey.size() < 6)
return "CTxOut(error)";
- return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
+ return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue).c_str(), scriptPubKey.ToString().c_str());
}
void print() const
};
-typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
-//
-// The basic transaction that is broadcasted on the network and contained in
-// blocks. A transaction can contain multiple inputs and outputs.
-//
+enum GetMinFee_mode
+{
+ GMF_BLOCK,
+ GMF_RELAY,
+ GMF_SEND,
+};
+
+// 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.
+ */
class CTransaction
{
public:
+ static const int CURRENT_VERSION=1;
int nVersion;
+ unsigned int nTime;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
unsigned int nLockTime;
+ // Denial-of-service detection:
+ mutable int nDoS;
+ bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CTransaction()
{
(
READWRITE(this->nVersion);
nVersion = this->nVersion;
+ READWRITE(nTime);
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
void SetNull()
{
- nVersion = 1;
+ nVersion = CTransaction::CURRENT_VERSION;
+ nTime = GetAdjustedTime();
vin.clear();
vout.clear();
nLockTime = 0;
+ nDoS = 0; // Denial-of-service prevention
}
bool IsNull() const
nBlockHeight = nBestHeight;
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
- if ((int64)nLockTime < (nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime))
+ if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime))
return true;
BOOST_FOREACH(const CTxIn& txin, vin)
if (!txin.IsFinal())
return false;
bool fNewer = false;
- unsigned int nLowest = UINT_MAX;
+ unsigned int nLowest = std::numeric_limits<unsigned int>::max();
for (unsigned int i = 0; i < vin.size(); i++)
{
if (vin[i].nSequence != old.vin[i].nSequence)
bool IsCoinBase() const
{
- return (vin.size() == 1 && vin[0].prevout.IsNull());
+ return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1);
}
- int GetSigOpCount() const
+ bool IsCoinStake() const
{
- int n = 0;
- BOOST_FOREACH(const CTxIn& txin, vin)
- n += txin.scriptSig.GetSigOpCount();
- BOOST_FOREACH(const CTxOut& txout, vout)
- n += txout.scriptPubKey.GetSigOpCount();
- return n;
+ // ppcoin: the coin stake transaction is marked with the first output empty
+ return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
}
- /** Count ECDSA signature operations in pay-to-script-hash inputs.
- This is a better measure of how expensive it is to process this transaction.
+ /** Check for standard transaction types
+ @return True if all outputs (scriptPubKeys) use only standard transaction forms
+ */
+ bool IsStandard() const;
- @param[in] mapInputsMap of previous transactions that have outputs we're spending
- @return maximum number of sigops required to validate this transaction's inputs
+ /** Check for standard transaction types
+ @param[in] mapInputs Map of previous transactions that have outputs we're spending
+ @return True if all inputs (scriptSigs) use only standard transaction forms
@see CTransaction::FetchInputs
*/
- int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
+ bool AreInputsStandard(CCoinsView& mapInputs) const;
- bool IsStandard() const
- {
- BOOST_FOREACH(const CTxIn& txin, vin)
- if (!txin.scriptSig.IsPushOnly())
- return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
- BOOST_FOREACH(const CTxOut& txout, vout)
- if (!::IsStandard(txout.scriptPubKey))
- return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
- return true;
- }
+ /** Count ECDSA signature operations the old-fashioned (pre-0.6) way
+ @return number of sigops this transaction's outputs will produce when spent
+ @see CTransaction::FetchInputs
+ */
+ unsigned int GetLegacySigOpCount() const;
+
+ /** Count ECDSA signature operations in pay-to-script-hash inputs.
+
+ @param[in] mapInputs Map of previous transactions that have outputs we're spending
+ @return maximum number of sigops required to validate this transaction's inputs
+ @see CTransaction::FetchInputs
+ */
+ unsigned int GetP2SHSigOpCount(CCoinsView& mapInputs) const;
+ /** Amount of bitcoins spent by this transaction.
+ @return sum of all outputs (note: does not include fees)
+ */
int64 GetValueOut() const
{
int64 nValueOut = 0;
Note that lightweight clients may not know anything besides the hash of previous transactions,
so may not be able to calculate this.
- @param[in] mapInputsMap of previous transactions that have outputs we're spending
- @returnSum of value of all inputs (scriptSigs)
+ @param[in] mapInputs Map of previous transactions that have outputs we're spending
+ @return Sum of value of all inputs (scriptSigs)
@see CTransaction::FetchInputs
- */
- int64 GetValueIn(const MapPrevTx& mapInputs) const;
+ */
+ int64 GetValueIn(CCoinsView& mapInputs) const;
static bool AllowFree(double dPriority)
{
return dPriority > COIN * 144 / 250;
}
- int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, bool fForRelay=false) const
- {
- // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
- int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
-
- unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
- unsigned int nNewBlockSize = nBlockSize + nBytes;
- int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
-
- if (fAllowFree)
- {
- if (nBlockSize == 1)
- {
- // Transactions under 10K are free
- // (about 4500bc if made of 50bc inputs)
- if (nBytes < 10000)
- nMinFee = 0;
- }
- else
- {
- // Free transaction area
- if (nNewBlockSize < 27000)
- nMinFee = 0;
- }
- }
-
- // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
- if (nMinFee < nBaseFee)
- {
- BOOST_FOREACH(const CTxOut& txout, vout)
- if (txout.nValue < CENT)
- nMinFee = nBaseFee;
- }
-
- // Raise the price as the block approaches full
- if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
- {
- if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
- return MAX_MONEY;
- nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
- }
-
- if (!MoneyRange(nMinFee))
- nMinFee = MAX_MONEY;
- return nMinFee;
- }
-
-
- bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
- {
- CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb");
- if (!filein)
- return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
-
- // Read transaction
- if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
- return error("CTransaction::ReadFromDisk() : fseek failed");
- filein >> *this;
-
- // Return file pointer
- if (pfileRet)
- {
- if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
- return error("CTransaction::ReadFromDisk() : second fseek failed");
- *pfileRet = filein.release();
- }
- return true;
- }
+ 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)
{
return (a.nVersion == b.nVersion &&
+ a.nTime == b.nTime &&
a.vin == b.vin &&
a.vout == b.vout &&
a.nLockTime == b.nLockTime);
return !(a == b);
}
+ std::string ToStringShort() const
+ {
+ std::string str;
+ str += strprintf("%s %s", GetHash().ToString().c_str(), IsCoinBase()? "base" : (IsCoinStake()? "stake" : "user"));
+ return str;
+ }
std::string ToString() const
{
std::string str;
- str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
+ str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
+ str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%d)\n",
GetHash().ToString().substr(0,10).c_str(),
+ nTime,
nVersion,
vin.size(),
vout.size(),
}
- 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(CCoinsView &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] inputsPrevious transactions (from FetchInputs)
- @param[out] mapTestPoolKeeps track of inputs that need to be updated on disk
- @param[in] posThisTxPosition 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(CCoinsView &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(CCoinsView &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime) 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);
+ bool GetCoinAge(uint64& nCoinAge) const; // Get transaction coin age
protected:
- const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
- bool AddToMemoryPoolUnchecked();
+ static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs);
+};
+
+
+/** wrapper for CTxOut that provides a more compact serialization */
+class CTxOutCompressor
+{
+private:
+ CTxOut &txout;
+
public:
- bool RemoveFromMemoryPool();
+ 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(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 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: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e40f1d75240f1d752
+ * <><><--------------------------------------------><----><------><------>
+ * | \ | / / /
+ * version code vout[1] height timestamp block timestamp
+ *
+ * - 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
+ * - time = 1389883712
+ * - is_coinstake = 0
+ * - block time = 1389883712
+ *
+ *
+ * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b40f1d75240f1d752
+ * <><><--><--------------------------------------------------><----------------------------------------------><----><------><------>
+ * / \ \ | | / / /
+ * version code unspentness vout[4] vout[16] height timestamp block timestamp
+ *
+ * - 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
+ * - time = 1389883712
+ * - is_coinstake = 0
+ * - 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.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;
+ 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) + (fCoinStake ? 1 : 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);
+ // timestamp and coinstake flag
+ nSize += ::GetSerializeSize(VARINT(nTime*2+(fCoinStake ? 1 : 0)), nType, nVersion);
+ // block timestamp
+ nSize += ::GetSerializeSize(VARINT(nBlockTime), 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);
+ // transaction timestamp and coinstake flag
+ ::Serialize(s, VARINT(nTime*2+(fCoinStake ? 1 : 0)), nType, nVersion);
+ // block time
+ ::Serialize(s, VARINT(nBlockTime), nType, nVersion);
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream &s, int nType, int nVersion) {
+ unsigned int nCode = 0, nCodeTime = 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);
+ // transaction timestamp
+ ::Unserialize(s, VARINT(nCodeTime), nType, nVersion);
+ nTime = nCodeTime / 2;
+ fCoinStake = nCodeTime & 1;
+ // 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 {
+ BOOST_FOREACH(const CTxOut &out, vout)
+ if (!out.IsNull())
+ return false;
+ return true;
+ }
+};
-//
-// A transaction with a merkle branch linking it to the block chain
-//
+
+/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx : public CTransaction
{
public:
int nIndex;
// memory only
- mutable char fMerkleVerified;
+ mutable bool fMerkleVerified;
CMerkleTx()
int SetMerkleBranch(const CBlock* pblock=NULL);
- int GetDepthInMainChain(int& nHeightRet) const;
- int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); }
+ int GetDepthInMainChain(CBlockIndex* &pindexRet) const;
+ int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const;
- bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
- bool AcceptToMemoryPool();
+ 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.
-//
+/** 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.
+ */
class CBlock
{
public:
// header
+ static const int CURRENT_VERSION=6;
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
// network and disk
std::vector<CTransaction> vtx;
+ // ppcoin: block signature - signed by one of the coin base txout[N]'s owner
+ std::vector<unsigned char> vchBlockSig;
+
// 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()
{
READWRITE(nBits);
READWRITE(nNonce);
- // ConnectBlock depends on vtx being last so it can calculate offset
+ // ConnectBlock depends on vtx following header to generate CDiskTxPos
if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
+ {
READWRITE(vtx);
+ READWRITE(vchBlockSig);
+ }
else if (fRead)
+ {
const_cast<CBlock*>(this)->vtx.clear();
+ const_cast<CBlock*>(this)->vchBlockSig.clear();
+ }
)
void SetNull()
{
- nVersion = 1;
+ nVersion = CBlock::CURRENT_VERSION;
hashPrevBlock = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
vtx.clear();
+ vchBlockSig.clear();
vMerkleTree.clear();
+ nDoS = 0;
}
bool IsNull() const
uint256 GetHash() const
{
- return Hash(BEGIN(nVersion), END(nNonce));
+ return scrypt_blockhash(CVOIDBEGIN(nVersion));
}
int64 GetBlockTime() const
return (int64)nTime;
}
- int GetSigOpCount() const
+ void UpdateTime(const CBlockIndex* pindexPrev);
+
+ // ppcoin: entropy bit for stake modifier if chosen by modifier
+ unsigned int GetStakeEntropyBit(unsigned int nTime) const
{
- int n = 0;
- BOOST_FOREACH(const CTransaction& tx, vtx)
- n += tx.GetSigOpCount();
- return n;
+ // 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();
}
- void UpdateTime(const CBlockIndex* pindexPrev);
+ // ppcoin: two types of block: proof-of-work or proof-of-stake
+ bool IsProofOfStake() const
+ {
+ return (vtx.size() > 1 && vtx[1].IsCoinStake());
+ }
+ bool IsProofOfWork() const
+ {
+ return !IsProofOfStake();
+ }
+
+ std::pair<COutPoint, unsigned int> GetProofOfStake() const
+ {
+ 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
+ int64 GetMaxTransactionTime() const
+ {
+ int64 maxTransactionTime = 0;
+ BOOST_FOREACH(const CTransaction& tx, vtx)
+ maxTransactionTime = std::max(maxTransactionTime, (int64)tx.nTime);
+ return maxTransactionTime;
+ }
uint256 BuildMerkleTree() const
{
return hash;
}
-
- bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet)
+ bool WriteToDisk(CDiskBlockPos &pos)
{
// Open history file to append
- CAutoFile fileout = AppendBlockFile(nFileRet);
+ 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)
- {
-#ifdef __WXMSW__
- _commit(_fileno(fileout));
-#else
- fsync(fileno(fileout));
-#endif
- }
+ 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 = OpenBlockFile(nFile, nBlockPos, "rb");
+ 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
- filein >> *this;
+ try {
+ filein >> *this;
+ }
+ catch (std::exception &e) {
+ return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
+ }
// Check the header
- if (!CheckProofOfWork(GetHash(), nBits))
+ if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return error("CBlock::ReadFromDisk() : errors in block header");
return true;
}
-
-
void print() const
{
- printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
- GetHash().ToString().substr(0,20).c_str(),
+ printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu", vchBlockSig=%s)\n",
+ GetHash().ToString().c_str(),
nVersion,
- hashPrevBlock.ToString().substr(0,20).c_str(),
- hashMerkleRoot.ToString().substr(0,10).c_str(),
+ hashPrevBlock.ToString().c_str(),
+ hashMerkleRoot.ToString().c_str(),
nTime, nBits, nNonce,
- vtx.size());
+ vtx.size(),
+ HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str());
for (unsigned int i = 0; i < vtx.size(); i++)
{
printf(" ");
printf("\n");
}
+ // Undo the effects of this block (with given index) on the UTXO set represented by coins
+ bool DisconnectBlock(CBlockIndex *pindex, CCoinsView &coins);
- bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
- bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex);
+ // Apply the effects of this block (with given index) on the UTXO set represented by coins
+ bool ConnectBlock(CBlockIndex *pindex, CCoinsView &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() const;
+
+ // Make this block (with given index) the new tip of the active block chain
+ bool SetBestChain(CBlockIndex* pindexNew);
+
+ // 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();
-};
+ // 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;
-//
-// The block chain is a tree shaped structure starting with the
-// genesis block at the root, with each block potentially having multiple
-// candidates to be the next block. pprev and pnext link a path through the
-// main/longest chain. A blockindex may have multiple pprev pointing back
-// to it, but pnext will only point forward to the longest branch, or will
-// be null if the block is not part of the longest chain.
-//
+/** The block chain is a tree shaped structure starting with the
+ * genesis block at the root, with each block potentially having multiple
+ * candidates to be the next block. pprev and pnext link a path through the
+ * main/longest chain. A blockindex may have multiple pprev pointing back
+ * to it, but pnext will only point forward to the longest branch, or will
+ * be null if the block is not part of the longest chain.
+ */
class CBlockIndex
{
public:
const uint256* phashBlock;
CBlockIndex* pprev;
CBlockIndex* pnext;
- unsigned int nFile;
- unsigned int nBlockPos;
int nHeight;
- CBigNum bnChainWork;
+ CDiskBlockPos pos;
+ unsigned int nUndoPos;
+ uint256 nChainTrust; // trust score of block chain
+
+ int64 nMint;
+ int64 nMoneySupply;
+
+ 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
+ };
+
+ uint64 nStakeModifier; // hash modifier for proof-of-stake
+ unsigned int nStakeModifierChecksum; // checksum of index; in-memeory only
+
+ // proof-of-stake specific fields
+ COutPoint prevoutStake;
+ unsigned int nStakeTime;
+ uint256 hashProofOfStake;
// block header
int nVersion;
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
- nFile = 0;
- nBlockPos = 0;
nHeight = 0;
- bnChainWork = 0;
+ pos.SetNull();
+ nUndoPos = 0;
+ nChainTrust = 0;
+ nMint = 0;
+ nMoneySupply = 0;
+ nFlags = 0;
+ nStakeModifier = 0;
+ nStakeModifierChecksum = 0;
+ hashProofOfStake = 0;
+ prevoutStake.SetNull();
+ nStakeTime = 0;
nVersion = 0;
hashMerkleRoot = 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;
- bnChainWork = 0;
+ pos.SetNull();
+ nUndoPos = 0;
+ nChainTrust = 0;
+ nMint = 0;
+ nMoneySupply = 0;
+ nFlags = 0;
+ nStakeModifier = 0;
+ nStakeModifierChecksum = 0;
+ hashProofOfStake = 0;
+ if (block.IsProofOfStake())
+ {
+ SetProofOfStake();
+ prevoutStake = block.vtx[1].vin[0].prevout;
+ nStakeTime = block.vtx[1].nTime;
+ }
+ else
+ {
+ prevoutStake.SetNull();
+ nStakeTime = 0;
+ }
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
nNonce = block.nNonce;
}
+ CDiskBlockPos GetBlockPos() const {
+ return pos;
+ }
+
+ CDiskBlockPos GetUndoPos() const {
+ CDiskBlockPos ret = pos;
+ if (nUndoPos == 0)
+ ret.SetNull();
+ else
+ ret.nPos = nUndoPos - 1;
+ return ret;
+ }
+
CBlock GetBlockHeader() const
{
CBlock block;
return (int64)nTime;
}
- CBigNum GetBlockWork() const
- {
- CBigNum bnTarget;
- bnTarget.SetCompact(nBits);
- if (bnTarget <= 0)
- return 0;
- return (CBigNum(1)<<256) / (bnTarget+1);
- }
+ uint256 GetBlockTrust() const;
bool IsInMainChain() const
{
bool CheckIndex() const
{
- return CheckProofOfWork(GetBlockHash(), nBits);
- }
-
- bool EraseBlockFromDisk()
- {
- // Open history file
- CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+");
- if (!fileout)
- return false;
-
- // Overwrite with empty null block
- CBlock block;
- block.SetNull();
- fileout << block;
-
return true;
}
return pindex->GetMedianTimePast();
}
+ /**
+ * Returns true if there are nRequired or more blocks of minVersion or above
+ * in the last nToCheck blocks, starting at pstart and going backwards.
+ */
+ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
+ unsigned int nRequired, unsigned int nToCheck);
+
+ bool IsProofOfWork() const
+ {
+ return !(nFlags & BLOCK_PROOF_OF_STAKE);
+ }
+ bool IsProofOfStake() const
+ {
+ return (nFlags & BLOCK_PROOF_OF_STAKE);
+ }
+
+ void SetProofOfStake()
+ {
+ nFlags |= BLOCK_PROOF_OF_STAKE;
+ }
+
+ unsigned int GetStakeEntropyBit() const
+ {
+ return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
+ }
+
+ bool SetStakeEntropyBit(unsigned int nEntropyBit)
+ {
+ if (nEntropyBit > 1)
+ return false;
+ nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
+ return true;
+ }
+
+ bool GeneratedStakeModifier() const
+ {
+ return (nFlags & BLOCK_STAKE_MODIFIER);
+ }
+
+ void SetStakeModifier(uint64 nModifier, bool fGeneratedStakeModifier)
+ {
+ nStakeModifier = nModifier;
+ if (fGeneratedStakeModifier)
+ nFlags |= BLOCK_STAKE_MODIFIER;
+ }
std::string ToString() const
{
- return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
- pprev, pnext, nFile, nBlockPos, nHeight,
- hashMerkleRoot.ToString().substr(0,10).c_str(),
- GetBlockHash().ToString().substr(0,20).c_str());
+ 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,
+ hashProofOfStake.ToString().c_str(),
+ prevoutStake.ToString().c_str(), nStakeTime,
+ hashMerkleRoot.ToString().c_str(),
+ GetBlockHash().ToString().c_str());
}
+
void print() const
{
printf("%s\n", ToString().c_str());
}
};
-
-
-//
-// Used to marshal pointers into hashes for db storage.
-//
+/** 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(pos);
+ READWRITE(nUndoPos);
+ READWRITE(nMint);
+ READWRITE(nMoneySupply);
+ READWRITE(nFlags);
+ READWRITE(nStakeModifier);
+ if (IsProofOfStake())
+ {
+ READWRITE(prevoutStake);
+ READWRITE(nStakeTime);
+ READWRITE(hashProofOfStake);
+ }
+ else if (fRead)
+ {
+ const_cast<CDiskBlockIndex*>(this)->prevoutStake.SetNull();
+ 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().substr(0,20).c_str(),
- hashNext.ToString().substr(0,20).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.
-//
+/** Describes a place in the block chain to another node such that if the
+ * other node doesn't have the same branch, it can find a recent common trunk.
+ * The further back it is, the further before the fork it may be.
+ */
class CBlockLocator
{
protected:
Set((*mi).second);
}
+ CBlockLocator(const std::vector<uint256>& vHaveIn)
+ {
+ vHave = vHaveIn;
+ }
+
IMPLEMENT_SERIALIZE
(
if (!(nType & SER_GETHASH))
if (vHave.size() > 10)
nStep *= 2;
}
- vHave.push_back(hashGenesisBlock);
+ vHave.push_back((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
}
int GetDistanceBack()
return hash;
}
}
- return hashGenesisBlock;
+ return (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet);
}
int GetHeight()
-
-
-
-
-//
-// Alerts are for notifying old versions if they become too obsolete and
-// need to upgrade. The message is displayed in the status bar.
-// Alert messages are broadcast as a vector of signed data. Unserializing may
-// not read the entire buffer if the alert is for a newer version, but older
-// versions can still relay the original data.
-//
-class CUnsignedAlert
+class CTxMemPool
{
public:
- int nVersion;
- int64 nRelayUntil; // when newer nodes stop relaying to newer nodes
- int64 nExpiration;
- int nID;
- int nCancel;
- std::set<int> setCancel;
- int nMinVer; // lowest version inclusive
- int nMaxVer; // highest version inclusive
- std::set<std::string> setSubVer; // empty matches all
- int nPriority;
-
- // Actions
- std::string strComment;
- std::string strStatusBar;
- std::string strReserved;
+ mutable CCriticalSection cs;
+ std::map<uint256, CTransaction> mapTx;
+ std::map<COutPoint, CInPoint> mapNextTx;
- IMPLEMENT_SERIALIZE
- (
- READWRITE(this->nVersion);
- nVersion = this->nVersion;
- READWRITE(nRelayUntil);
- READWRITE(nExpiration);
- READWRITE(nID);
- READWRITE(nCancel);
- READWRITE(setCancel);
- READWRITE(nMinVer);
- READWRITE(nMaxVer);
- READWRITE(setSubVer);
- READWRITE(nPriority);
-
- READWRITE(strComment);
- READWRITE(strStatusBar);
- READWRITE(strReserved);
- )
+ 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);
- void SetNull()
+ unsigned long size()
{
- nVersion = 1;
- nRelayUntil = 0;
- nExpiration = 0;
- nID = 0;
- nCancel = 0;
- setCancel.clear();
- nMinVer = 0;
- nMaxVer = 0;
- setSubVer.clear();
- nPriority = 0;
-
- strComment.clear();
- strStatusBar.clear();
- strReserved.clear();
+ LOCK(cs);
+ return mapTx.size();
}
- std::string ToString() const
+ bool exists(uint256 hash)
{
- std::string strSetCancel;
- BOOST_FOREACH(int n, setCancel)
- strSetCancel += strprintf("%d ", n);
- std::string strSetSubVer;
- BOOST_FOREACH(std::string str, setSubVer)
- strSetSubVer += "\"" + str + "\" ";
- return strprintf(
- "CAlert(\n"
- " nVersion = %d\n"
- " nRelayUntil = %"PRI64d"\n"
- " nExpiration = %"PRI64d"\n"
- " nID = %d\n"
- " nCancel = %d\n"
- " setCancel = %s\n"
- " nMinVer = %d\n"
- " nMaxVer = %d\n"
- " setSubVer = %s\n"
- " nPriority = %d\n"
- " strComment = \"%s\"\n"
- " strStatusBar = \"%s\"\n"
- ")\n",
- nVersion,
- nRelayUntil,
- nExpiration,
- nID,
- nCancel,
- strSetCancel.c_str(),
- nMinVer,
- nMaxVer,
- strSetSubVer.c_str(),
- nPriority,
- strComment.c_str(),
- strStatusBar.c_str());
+ return (mapTx.count(hash) != 0);
}
- void print() const
+ CTransaction& lookup(uint256 hash)
{
- printf("%s", ToString().c_str());
+ return mapTx[hash];
}
};
-class CAlert : public CUnsignedAlert
+extern CTxMemPool mempool;
+
+/** Abstract view on the open txout dataset. */
+class CCoinsView
{
public:
- std::vector<unsigned char> vchMsg;
- std::vector<unsigned char> vchSig;
-
- CAlert()
- {
- SetNull();
- }
-
- IMPLEMENT_SERIALIZE
- (
- READWRITE(vchMsg);
- READWRITE(vchSig);
- )
+ // Retrieve the CCoins (unspent transaction outputs) for a given txid
+ virtual bool GetCoins(uint256 txid, CCoins &coins);
- void SetNull()
- {
- CUnsignedAlert::SetNull();
- vchMsg.clear();
- vchSig.clear();
- }
-
- bool IsNull() const
- {
- return (nExpiration == 0);
- }
+ // Modify the CCoins for a given txid
+ virtual bool SetCoins(uint256 txid, const CCoins &coins);
- uint256 GetHash() const
- {
- return SerializeHash(*this);
- }
+ // 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);
- bool IsInEffect() const
- {
- return (GetAdjustedTime() < nExpiration);
- }
+ // Retrieve the block index whose state this CCoinsView currently represents
+ virtual CBlockIndex *GetBestBlock();
- bool Cancels(const CAlert& alert) const
- {
- if (!IsInEffect())
- return false; // this was a no-op before 31403
- return (alert.nID <= nCancel || setCancel.count(alert.nID));
- }
+ // Modify the currently active block index
+ virtual bool SetBestBlock(CBlockIndex *pindex);
+ virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
+};
- bool AppliesTo(int nVersion, std::string strSubVerIn) const
- {
- return (IsInEffect() &&
- nMinVer <= nVersion && nVersion <= nMaxVer &&
- (setSubVer.empty() || setSubVer.count(strSubVerIn)));
- }
+/** CCoinsView backed by another CCoinsView */
+class CCoinsViewBacked : public CCoinsView
+{
+protected:
+ CCoinsView *base;
- bool AppliesToMe() const
- {
- return AppliesTo(VERSION, ::pszSubVer);
- }
+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 RelayTo(CNode* pnode) const
- {
- if (!IsInEffect())
- return false;
- // returns true if wasn't already contained in the set
- if (pnode->setKnown.insert(GetHash()).second)
- {
- if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
- AppliesToMe() ||
- GetAdjustedTime() < nRelayUntil)
- {
- pnode->PushMessage("alert", *this);
- return true;
- }
- }
- return false;
- }
+/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
+class CCoinsViewCache : public CCoinsViewBacked
+{
+protected:
+ CBlockIndex *pindexTip;
+ std::map<uint256,CCoins> cacheCoins;
- bool CheckSignature()
- {
- CKey key;
- if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284")))
- return error("CAlert::CheckSignature() : SetPubKey failed");
- if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
- return error("CAlert::CheckSignature() : verify signature failed");
+public:
+ CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false);
+ 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);
+ bool Flush();
+ unsigned int GetCacheSize();
+};
- // Now unserialize the data
- CDataStream sMsg(vchMsg);
- sMsg >> *(CUnsignedAlert*)this;
- return true;
- }
+/** 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;
- bool ProcessAlert();
+public:
+ CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool HaveCoins(uint256 txid);
};
+extern CCoinsViewCache *pcoinsTip;
+
#endif