X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.h;h=2cde696683e1915e0d1df2e18e07aa9f12d34c54;hb=4f6b3161b2dc18c9a3d4143d63fbbd0737296eb0;hp=22035989653f0da818fbc3e63e9f733f81a15973;hpb=b3a022069bb4209f07eff94e1b1e075c1182bc79;p=novacoin.git diff --git a/src/main.h b/src/main.h index 2203598..2cde696 100644 --- a/src/main.h +++ b/src/main.h @@ -1,43 +1,44 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2009-2012 The Bitcoin developers // Copyright (c) 2011-2012 The PPCoin 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 "net.h" -#include "key.h" #include "script.h" -#include "db.h" + +#ifdef WIN32 +#include /* for _commit */ +#endif #include +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 int64 COIN = 1000000; -static const int64 CENT = 10000; -static const int64 MIN_TX_FEE = 10000; -static const int64 MIN_RELAY_TX_FEE = 10000; +static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +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 = 9999 * COIN; +static const int64 MIN_TXOUT_AMOUNT = MIN_TX_FEE; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } -static const int COINBASE_MATURITY = 100; +static const int COINBASE_MATURITY_PPC = 500; // 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 #ifdef USE_UPNP @@ -46,6 +47,11 @@ static const int fHaveUPnP = true; static const int fHaveUPnP = false; #endif +static const uint256 hashGenesisBlockOfficial("0x0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3"); +static const uint256 hashGenesisBlockTestNet("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"); + +extern CScript COINBASE_FLAGS; + @@ -53,28 +59,30 @@ static const int fHaveUPnP = false; extern CCriticalSection cs_main; extern std::map mapBlockIndex; +extern std::set > setStakeSeen; extern uint256 hashGenesisBlock; +extern unsigned int nStakeMinAge; +extern int nCoinbaseMaturity; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; -extern uint64 nBestChainTrust; -extern uint64 nBestInvalidTrust; +extern CBigNum bnBestChainTrust; +extern CBigNum bnBestInvalidTrust; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; +extern uint64 nLastBlockTx; +extern uint64 nLastBlockSize; +extern int64 nLastCoinStakeSearchInterval; +extern const std::string strMessageMagic; extern double dHashesPerSec; extern int64 nHPSTimerStart; extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set setpwalletRegistered; +extern std::map mapOrphanBlocks; // Settings -extern int fGenerateBitcoins; extern int64 nTransactionFee; -extern int fLimitProcessors; -extern int nLimitProcessors; -extern int fMinimizeToTray; -extern int fMinimizeOnClose; -extern int fUseUPnP; @@ -95,17 +103,20 @@ void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); -CBlock* CreateNewBlock(CWallet* pwallet); +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 CheckProofOfWork(uint256 hash, unsigned int nBits); +int64 GetProofOfWorkReward(unsigned int nBits); int64 GetProofOfStakeReward(int64 nCoinAge); unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); int GetNumBlocksOfPeers(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); - +uint256 WantedByOrphan(const CBlock* pblockOrphan); +const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake); +void BitcoinMiner(CWallet *pwallet, bool fProofOfStake); @@ -119,21 +130,7 @@ std::string GetWarnings(std::string strFor); bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); -template -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; -} - - +/** Position on disk for a particular transaction. */ class CDiskTxPos { public: @@ -172,7 +169,7 @@ public: std::string ToString() const { if (IsNull()) - return strprintf("null"); + return "null"; else return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); } @@ -185,7 +182,7 @@ public: - +/** An inpoint - a combination of a transaction and an index n into its vin */ class CInPoint { public: @@ -200,7 +197,7 @@ public: - +/** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { public: @@ -242,11 +239,10 @@ public: -// -// 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: @@ -256,17 +252,17 @@ public: CTxIn() { - nSequence = UINT_MAX; + nSequence = std::numeric_limits::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::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::max()) { prevout = COutPoint(hashPrevTx, nOut); scriptSig = scriptSigIn; @@ -282,7 +278,7 @@ public: bool IsFinal() const { - return (nSequence == UINT_MAX); + return (nSequence == std::numeric_limits::max()); } friend bool operator==(const CTxIn& a, const CTxIn& b) @@ -297,16 +293,21 @@ public: 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::max()) str += strprintf(", nSequence=%u", nSequence); str += ")"; return str; @@ -321,10 +322,9 @@ public: -// -// 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: @@ -359,7 +359,13 @@ public: return (nValue == -1); } - bool IsCoinStake() const + void SetEmpty() + { + nValue = 0; + scriptPubKey.clear(); + } + + bool IsEmpty() const { return (nValue == 0 && scriptPubKey.empty()); } @@ -380,12 +386,17 @@ public: return !(a == b); } + std::string ToStringShort() const + { + return strprintf(" out %s %s", FormatMoney(nValue).c_str(), scriptPubKey.ToString(true).c_str()); + } + std::string ToString() const { - if (IsCoinStake()) return "CTxOut(coinstake)"; + if (IsEmpty()) return "CTxOut(empty)"; if (scriptPubKey.size() < 6) return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue).c_str(), 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 @@ -397,10 +408,18 @@ public: -// -// 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, +}; + +typedef std::map > MapPrevTx; + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ class CTransaction { public: @@ -458,7 +477,7 @@ public: 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()) @@ -470,13 +489,13 @@ public: { if (vin.size() != old.vin.size()) return false; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) if (vin[i].prevout != old.vin[i].prevout) return false; bool fNewer = false; - unsigned int nLowest = UINT_MAX; - for (int i = 0; i < vin.size(); i++) + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned int i = 0; i < vin.size(); i++) { if (vin[i].nSequence != old.vin[i].nSequence) { @@ -497,36 +516,44 @@ public: bool IsCoinBase() const { - return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() == 1); + return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1); } bool IsCoinStake() const { // ppcoin: the coin stake transaction is marked with the first output empty - return (vout.size() == 2 && vout[0].IsCoinStake()); - } - - int GetSigOpCount() 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; - } - - 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; - } - + return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); + } + + /** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms + */ + bool IsStandard() const; + + /** 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 + */ + bool AreInputsStandard(const MapPrevTx& 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 + @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(const MapPrevTx& 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; @@ -539,6 +566,16 @@ public: return nValueOut; } + /** Amount of bitcoins coming in to this transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @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; + static bool AllowFree(double dPriority) { // Large (in bytes) low-priority (new, small-coin) transactions @@ -546,12 +583,12 @@ public: return dPriority > COIN * 144 / 250; } - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, bool fForRelay=false) const + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; @@ -574,9 +611,11 @@ public: // 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) @@ -594,14 +633,20 @@ public: bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) { - CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); + 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"); - filein >> *this; + + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } // Return file pointer if (pfileRet) @@ -628,19 +673,27 @@ public: } + 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, nTime=%d, 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=%d, vout.size=%d, nLockTime=%d)\n", GetHash().ToString().substr(0,10).c_str(), nTime, nVersion, vin.size(), vout.size(), nLockTime); - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; - for (int i = 0; i < vout.size(); i++) + for (unsigned int i = 0; i < vout.size(); i++) str += " " + vout[i].ToString() + "\n"; return str; } @@ -655,26 +708,50 @@ public: bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(COutPoint prevout); bool DisconnectInputs(CTxDB& txdb); - bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + + /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + + @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& 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& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); -protected: - bool AddToMemoryPoolUnchecked(); -public: - bool RemoveFromMemoryPool(); bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const; // ppcoin: get transaction coin age + bool CheckProofOfStake(unsigned int nBits) const; + +protected: + const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; }; -// -// 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: @@ -683,7 +760,7 @@ public: int nIndex; // memory only - mutable char fMerkleVerified; + mutable bool fMerkleVerified; CMerkleTx() @@ -715,8 +792,8 @@ public: 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); @@ -726,11 +803,10 @@ public: -// -// 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. -// +/** 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: @@ -778,23 +854,23 @@ public: 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. + * + * Blocks are appended to blk0001.dat files on disk. Their location on disk + * is indexed by CBlockIndex objects in memory. + */ class CBlock { public: @@ -809,6 +885,9 @@ public: // network and disk std::vector vtx; + // ppcoin: block signature - signed by coin base txout[0]'s owner + std::vector vchBlockSig; + // memory only mutable std::vector vMerkleTree; @@ -831,11 +910,17 @@ public: 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(this)->vtx.clear(); + const_cast(this)->vchBlockSig.clear(); + } ) void SetNull() @@ -847,6 +932,7 @@ public: nBits = 0; nNonce = 0; vtx.clear(); + vchBlockSig.clear(); vMerkleTree.clear(); nDoS = 0; } @@ -866,6 +952,24 @@ public: return (int64)nTime; } + 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 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 { @@ -875,15 +979,6 @@ public: return maxTransactionTime; } - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) - n += tx.GetSigOpCount(); - return n; - } - - uint256 BuildMerkleTree() const { vMerkleTree.clear(); @@ -938,18 +1033,21 @@ public: bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) { // Open history file to append - CAutoFile fileout = AppendBlockFile(nFileRet); + CAutoFile fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); if (!fileout) return error("CBlock::WriteToDisk() : AppendBlockFile failed"); // Write index header + unsigned char pchMessageStart[4]; + GetMessageStart(pchMessageStart, true); unsigned int nSize = fileout.GetSerializeSize(*this); fileout << FLATDATA(pchMessageStart) << nSize; // Write block - nBlockPosRet = ftell(fileout); - if (nBlockPosRet == -1) + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) return error("CBlock::WriteToDisk() : ftell failed"); + nBlockPosRet = fileOutPos; fileout << *this; // Flush stdio buffers and commit to disk before returning @@ -971,17 +1069,22 @@ public: SetNull(); // Open history file to read - CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); + CAutoFile filein = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb"), 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; @@ -991,20 +1094,21 @@ public: void print() const { - printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d, vchBlockSig=%s)\n", GetHash().ToString().substr(0,20).c_str(), nVersion, hashPrevBlock.ToString().substr(0,20).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(), nTime, nBits, nNonce, - vtx.size()); - for (int i = 0; i < vtx.size(); i++) + vtx.size(), + HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str()); + for (unsigned int i = 0; i < vtx.size(); i++) { printf(" "); vtx[i].print(); } printf(" vMerkleTree: "); - for (int i = 0; i < vMerkleTree.size(); i++) + for (unsigned int i = 0; i < vMerkleTree.size(); i++) printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str()); printf("\n"); } @@ -1018,6 +1122,11 @@ public: bool CheckBlock() const; bool AcceptBlock(); bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block + bool SignBlock(const CKeyStore& keystore); + bool CheckBlockSignature() const; + +private: + bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); }; @@ -1025,14 +1134,13 @@ public: -// -// 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: @@ -1041,9 +1149,13 @@ public: CBlockIndex* pnext; unsigned int nFile; unsigned int nBlockPos; - uint64 nChainTrust;// ppcoin: trust score of chain, in the unit of coin-days + CBigNum bnChainTrust; // ppcoin: trust score of block chain int nHeight; - int nCheckpoint; // ppcoin: chain auto checkpoint height + int64 nMint; + int64 nMoneySupply; + bool fProofOfStake; // ppcoin: is the block of proof-of-stake type + COutPoint prevoutStake; + unsigned int nStakeTime; // block header int nVersion; @@ -1061,8 +1173,12 @@ public: nFile = 0; nBlockPos = 0; nHeight = 0; - nChainTrust = 0; - nCheckpoint = 0; + bnChainTrust = 0; + nMint = 0; + nMoneySupply = 0; + fProofOfStake = true; + prevoutStake.SetNull(); + nStakeTime = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1079,8 +1195,20 @@ public: nFile = nFileIn; nBlockPos = nBlockPosIn; nHeight = 0; - nChainTrust = 0; - nCheckpoint = 0; + bnChainTrust = 0; + nMint = 0; + nMoneySupply = 0; + fProofOfStake = block.IsProofOfStake(); + if (fProofOfStake) + { + prevoutStake = block.vtx[1].vin[0].prevout; + nStakeTime = block.vtx[1].nTime; + } + else + { + prevoutStake.SetNull(); + nStakeTime = 0; + } nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -1112,9 +1240,13 @@ public: return (int64)nTime; } - int64 GetBlockTrust() const + CBigNum GetBlockTrust() const { - return (nChainTrust - (pprev? pprev->nChainTrust : 0)); + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (fProofOfStake? (CBigNum(1)<<256) / (bnTarget+1) : 1); } bool IsInMainChain() const @@ -1124,13 +1256,13 @@ public: bool CheckIndex() const { - return CheckProofOfWork(GetBlockHash(), nBits); + return IsProofOfWork() ? CheckProofOfWork(GetBlockHash(), nBits) : true; } bool EraseBlockFromDisk() { // Open history file - CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); + CAutoFile fileout = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb+"), SER_DISK, CLIENT_VERSION); if (!fileout) return false; @@ -1170,12 +1302,22 @@ public: return pindex->GetMedianTimePast(); } + bool IsProofOfWork() const + { + return !fProofOfStake; + } + bool IsProofOfStake() const + { + return fProofOfStake; + } std::string ToString() const { - return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nChainTrust=%"PRI64d" nHeight=%d, nCheckpoint=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nFile, nBlockPos, nChainTrust, nHeight, nCheckpoint, + return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, fProofOfStake=%d prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nHeight, + FormatMoney(nMint).c_str(), FormatMoney(nMoneySupply).c_str(), + fProofOfStake, prevoutStake.ToString().c_str(), nStakeTime, hashMerkleRoot.ToString().substr(0,10).c_str(), GetBlockHash().ToString().substr(0,20).c_str()); } @@ -1188,9 +1330,7 @@ public: -// -// Used to marshal pointers into hashes for db storage. -// +/** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex { public: @@ -1217,9 +1357,20 @@ public: READWRITE(hashNext); READWRITE(nFile); READWRITE(nBlockPos); - READWRITE(nChainTrust); READWRITE(nHeight); - READWRITE(nCheckpoint); + READWRITE(nMint); + READWRITE(nMoneySupply); + READWRITE(fProofOfStake); + if (fProofOfStake) + { + READWRITE(prevoutStake); + READWRITE(nStakeTime); + } + else if (fRead) + { + const_cast(this)->prevoutStake.SetNull(); + const_cast(this)->nStakeTime = 0; + } // block header READWRITE(this->nVersion); @@ -1267,11 +1418,10 @@ public: -// -// 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: @@ -1294,6 +1444,11 @@ public: Set((*mi).second); } + CBlockLocator(const std::vector& vHaveIn) + { + vHave = vHaveIn; + } + IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) @@ -1398,13 +1553,12 @@ public: -// -// 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. -// +/** 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 { public: @@ -1504,6 +1658,7 @@ public: } }; +/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ class CAlert : public CUnsignedAlert { public: @@ -1552,6 +1707,7 @@ public: bool AppliesTo(int nVersion, std::string strSubVerIn) const { + // TODO: rework for client-version-embedded-in-strSubVer ? return (IsInEffect() && nMinVer <= nVersion && nVersion <= nMaxVer && (setSubVer.empty() || setSubVer.count(strSubVerIn))); @@ -1559,7 +1715,7 @@ public: bool AppliesToMe() const { - return AppliesTo(VERSION, ::pszSubVer); + return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); } bool RelayTo(CNode* pnode) const @@ -1583,13 +1739,13 @@ public: bool CheckSignature() { CKey key; - if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"))) + if (!key.SetPubKey(ParseHex("04a0a849dd49b113d3179a332dd77715c43be4d0076e2f19e66de23dd707e56630f792f298dfd209bf042bb3561f4af6983f3d81e439737ab0bf7f898fecd21aab"))) return error("CAlert::CheckSignature() : SetPubKey failed"); if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) return error("CAlert::CheckSignature() : verify signature failed"); // Now unserialize the data - CDataStream sMsg(vchMsg); + CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); sMsg >> *(CUnsignedAlert*)this; return true; } @@ -1597,4 +1753,35 @@ public: bool ProcessAlert(); }; +class CTxMemPool +{ +public: + mutable CCriticalSection cs; + std::map mapTx; + std::map mapNextTx; + + bool accept(CTxDB& txdb, CTransaction &tx, + bool fCheckInputs, bool* pfMissingInputs); + bool addUnchecked(CTransaction &tx); + bool remove(CTransaction &tx); + + unsigned long size() + { + LOCK(cs); + return mapTx.size(); + } + + bool exists(uint256 hash) + { + return (mapTx.count(hash) != 0); + } + + CTransaction& lookup(uint256 hash) + { + return mapTx[hash]; + } +}; + +extern CTxMemPool mempool; + #endif