X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.h;h=84f93f7c9fef1413c658fd0caf27976cef1f9593;hb=8d43f1482a14263a76c039c227c4383580dcc1db;hp=48877f976ad6996bcd69f4731250ad0a18b00e69;hpb=d67b0434f24a629372000b0bd573ea9013ca3d90;p=novacoin.git diff --git a/src/main.h b/src/main.h index 48877f9..84f93f7 100644 --- a/src/main.h +++ b/src/main.h @@ -5,84 +5,93 @@ #ifndef BITCOIN_MAIN_H #define BITCOIN_MAIN_H +#include "timestamps.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 +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; -static const int CLIENT_VERSION = 60008; -static const bool VERSION_IS_BETA = true; -extern const std::string CLIENT_NAME; - 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 int64 MIN_TX_FEE = CENT/10; +static const int64 MIN_RELAY_TX_FEE = CENT/50; + +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 = CENT/100; + 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"); -extern CScript COINBASE_FLAGS; - - - - - +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 mapBlockIndex; -extern uint256 hashGenesisBlock; +extern std::set > 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 uint64 nPooledTx; 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 unsigned char pchMessageStart[4]; +extern std::map mapOrphanBlocks; // Settings extern int64 nTransactionFee; +extern int64 nMinimumInputValue; +extern bool fUseFastIndex; +extern unsigned int nDerivationMethodIndex; +extern bool fEnforceCanonical; - - +// Minimum disk space required - used in CheckDiskSpace() +static const uint64 nMinDiskSpace = 52428800; class CReserveKey; class CTxDB; @@ -90,26 +99,32 @@ class CTxIndex; 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); 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 nFees=0); +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); +uint256 WantedByOrphan(const CBlock* pblockOrphan); +const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake); +void StakeMiner(CWallet *pwallet); +void ResendWalletTransactions(); @@ -143,8 +158,8 @@ public: } IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; } - bool IsNull() const { return (nFile == -1); } + void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == (unsigned int) -1); } friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) { @@ -158,12 +173,13 @@ public: return !(a == b); } + std::string ToString() const { if (IsNull()) - return strprintf("null"); + return "null"; else - return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); + return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); } void print() const @@ -183,8 +199,8 @@ 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); } }; @@ -199,8 +215,8 @@ 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) { @@ -219,7 +235,7 @@ public: std::string ToString() const { - return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n); } void print() const @@ -285,10 +301,15 @@ 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()); @@ -346,6 +367,17 @@ public: return (nValue == -1); } + void SetEmpty() + { + nValue = 0; + scriptPubKey.clear(); + } + + bool IsEmpty() const + { + return (nValue == 0 && scriptPubKey.empty()); + } + uint256 GetHash() const { return SerializeHash(*this); @@ -362,11 +394,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 (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 @@ -393,7 +431,9 @@ typedef std::map > MapPrevTx; class CTransaction { public: + static const int CURRENT_VERSION=1; int nVersion; + unsigned int nTime; std::vector vin; std::vector vout; unsigned int nLockTime; @@ -411,6 +451,7 @@ public: ( READWRITE(this->nVersion); nVersion = this->nVersion; + READWRITE(nTime); READWRITE(vin); READWRITE(vout); READWRITE(nLockTime); @@ -418,7 +459,8 @@ public: void SetNull() { - nVersion = 1; + nVersion = CTransaction::CURRENT_VERSION; + nTime = GetAdjustedTime(); vin.clear(); vout.clear(); nLockTime = 0; @@ -444,7 +486,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()) @@ -483,7 +525,13 @@ public: bool IsCoinBase() const { - return (vin.size() == 1 && vin[0].prevout.IsNull()); + 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 (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); } /** Check for standard transaction types @@ -502,7 +550,7 @@ public: @return number of sigops this transaction's outputs will produce when spent @see CTransaction::FetchInputs */ - int GetLegacySigOpCount() const; + unsigned int GetLegacySigOpCount() const; /** Count ECDSA signature operations in pay-to-script-hash inputs. @@ -510,7 +558,7 @@ public: @return maximum number of sigops required to validate this transaction's inputs @see CTransaction::FetchInputs */ - int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; + unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; /** Amount of bitcoins spent by this transaction. @return sum of all outputs (note: does not include fees) @@ -544,57 +592,11 @@ public: return dPriority > COIN * 144 / 250; } - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const - { - // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); - 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; - } - + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const; 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"); @@ -622,6 +624,7 @@ public: 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); @@ -632,12 +635,20 @@ public: 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(), @@ -685,19 +696,16 @@ public: @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions @return Returns true if all checks succeed */ - bool ConnectInputs(MapPrevTx inputs, + 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); + bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const; // ppcoin: get transaction coin age protected: const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; - bool AddToMemoryPoolUnchecked(); -public: - bool RemoveFromMemoryPool(); }; @@ -713,7 +721,7 @@ public: int nIndex; // memory only - mutable char fMerkleVerified; + mutable bool fMerkleVerified; CMerkleTx() @@ -807,7 +815,7 @@ public: return !(a == b); } int GetDepthInMainChain() const; - + }; @@ -828,6 +836,7 @@ class CBlock { public: // header + static const int CURRENT_VERSION=6; int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; @@ -838,6 +847,9 @@ public: // network and disk std::vector vtx; + // ppcoin: block signature - signed by one of the coin base txout[N]'s owner + std::vector vchBlockSig; + // memory only mutable std::vector vMerkleTree; @@ -860,22 +872,29 @@ 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() { - nVersion = 1; + nVersion = CBlock::CURRENT_VERSION; hashPrevBlock = 0; hashMerkleRoot = 0; nTime = 0; nBits = 0; nNonce = 0; vtx.clear(); + vchBlockSig.clear(); vMerkleTree.clear(); nDoS = 0; } @@ -887,7 +906,7 @@ public: uint256 GetHash() const { - return Hash(BEGIN(nVersion), END(nNonce)); + return scrypt_blockhash(CVOIDBEGIN(nVersion)); } int64 GetBlockTime() const @@ -897,6 +916,52 @@ public: void UpdateTime(const CBlockIndex* pindexPrev); + // ppcoin: entropy bit for stake modifier if chosen by modifier + unsigned int GetStakeEntropyBit(unsigned int nTime) const + { + // Protocol switch to support p2pool at novacoin block #9689 + if (nTime >= ENTROPY_SWITCH_TIME || fTestNet) + { + // Take last bit of block hash as entropy bit + unsigned int nEntropyBit = ((GetHash().Get64()) & 1llu); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("GetStakeEntropyBit: nTime=%u hashBlock=%s nEntropyBit=%u\n", nTime, GetHash().ToString().c_str(), nEntropyBit); + return nEntropyBit; + } + // Before novacoin block #9689 - old protocol + uint160 hashSig = Hash160(vchBlockSig); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("GetStakeEntropyBit: hashSig=%s", hashSig.ToString().c_str()); + hashSig >>= 159; // take the first bit of the hash + if (fDebug && GetBoolArg("-printstakemodifier")) + printf(" entropybit=%"PRI64d"\n", hashSig.Get64()); + return hashSig.Get64(); + } + + // 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 + { + int64 maxTransactionTime = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + maxTransactionTime = std::max(maxTransactionTime, (int64)tx.nTime); + return maxTransactionTime; + } uint256 BuildMerkleTree() const { @@ -952,7 +1017,7 @@ 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"); @@ -970,13 +1035,7 @@ public: // Flush stdio buffers and commit to disk before returning fflush(fileout); if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) - { -#ifdef WIN32 - _commit(_fileno(fileout)); -#else - fsync(fileno(fileout)); -#endif - } + FileCommit(fileout); return true; } @@ -986,7 +1045,7 @@ 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) @@ -1001,7 +1060,7 @@ public: } // Check the header - if (!CheckProofOfWork(GetHash(), nBits)) + if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return error("CBlock::ReadFromDisk() : errors in block header"); return true; @@ -1011,13 +1070,14 @@ public: void print() const { - printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", - GetHash().ToString().substr(0,20).c_str(), + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu", 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(" "); @@ -1031,12 +1091,15 @@ public: bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false); bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); - bool CheckBlock() const; + bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const; bool AcceptBlock(); + bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block + bool SignBlock(CWallet& keystore); + bool CheckBlockSignature(bool fProofOfStake) const; private: bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); @@ -1062,8 +1125,27 @@ public: CBlockIndex* pnext; unsigned int nFile; unsigned int nBlockPos; + uint256 nChainTrust; // ppcoin: trust score of block chain int nHeight; - CBigNum bnChainWork; + + int64 nMint; + int64 nMoneySupply; + + unsigned int nFlags; // ppcoin: block index flags + 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; @@ -1072,7 +1154,6 @@ public: unsigned int nBits; unsigned int nNonce; - CBlockIndex() { phashBlock = NULL; @@ -1081,7 +1162,15 @@ public: nFile = 0; nBlockPos = 0; nHeight = 0; - bnChainWork = 0; + nChainTrust = 0; + nMint = 0; + nMoneySupply = 0; + nFlags = 0; + nStakeModifier = 0; + nStakeModifierChecksum = 0; + hashProofOfStake = 0; + prevoutStake.SetNull(); + nStakeTime = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1098,7 +1187,24 @@ public: nFile = nFileIn; nBlockPos = nBlockPosIn; nHeight = 0; - bnChainWork = 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; @@ -1130,14 +1236,7 @@ public: 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 { @@ -1146,21 +1245,6 @@ public: 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; } @@ -1192,14 +1276,65 @@ public: 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)", + return strprintf("CBlockIndex(nprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016"PRI64x", nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)", pprev, pnext, nFile, nBlockPos, nHeight, - hashMerkleRoot.ToString().substr(0,10).c_str(), - GetBlockHash().ToString().substr(0,20).c_str()); + 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 @@ -1213,6 +1348,9 @@ public: /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex { +private: + uint256 blockHash; + public: uint256 hashPrev; uint256 hashNext; @@ -1221,6 +1359,7 @@ public: { hashPrev = 0; hashNext = 0; + blockHash = 0; } explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) @@ -1238,6 +1377,22 @@ public: READWRITE(nFile); READWRITE(nBlockPos); READWRITE(nHeight); + READWRITE(nMint); + READWRITE(nMoneySupply); + READWRITE(nFlags); + READWRITE(nStakeModifier); + if (IsProofOfStake()) + { + READWRITE(prevoutStake); + READWRITE(nStakeTime); + READWRITE(hashProofOfStake); + } + else if (fRead) + { + const_cast(this)->prevoutStake.SetNull(); + const_cast(this)->nStakeTime = 0; + const_cast(this)->hashProofOfStake = 0; + } // block header READWRITE(this->nVersion); @@ -1246,10 +1401,14 @@ public: READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + READWRITE(blockHash); ) uint256 GetBlockHash() const { + if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0) + return blockHash; + CBlock block; block.nVersion = nVersion; block.hashPrevBlock = hashPrev; @@ -1257,9 +1416,11 @@ public: block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; - return block.GetHash(); - } + const_cast(this)->blockHash = block.GetHash(); + + return blockHash; + } std::string ToString() const { @@ -1267,8 +1428,8 @@ public: str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", GetBlockHash().ToString().c_str(), - hashPrev.ToString().substr(0,20).c_str(), - hashNext.ToString().substr(0,20).c_str()); + hashPrev.ToString().c_str(), + hashNext.ToString().c_str()); return str; } @@ -1347,7 +1508,7 @@ public: if (vHave.size() > 10) nStep *= 2; } - vHave.push_back(hashGenesisBlock); + vHave.push_back((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); } int GetDistanceBack() @@ -1400,7 +1561,7 @@ public: return hash; } } - return hashGenesisBlock; + return (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet); } int GetHeight() @@ -1419,205 +1580,37 @@ 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. - */ -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 setCancel; - int nMinVer; // lowest version inclusive - int nMaxVer; // highest version inclusive - std::set setSubVer; // empty matches all - int nPriority; - - // Actions - std::string strComment; - std::string strStatusBar; - std::string strReserved; + mutable CCriticalSection cs; + std::map mapTx; + std::map 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(CTxDB& txdb, CTransaction &tx, + bool fCheckInputs, bool* pfMissingInputs); + bool addUnchecked(const uint256& hash, CTransaction &tx); + bool remove(CTransaction &tx); + void clear(); + void queryHashes(std::vector& vtxid); - 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]; } }; -/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ -class CAlert : public CUnsignedAlert -{ -public: - std::vector vchMsg; - std::vector vchSig; - - CAlert() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(vchMsg); - READWRITE(vchSig); - ) - - void SetNull() - { - CUnsignedAlert::SetNull(); - vchMsg.clear(); - vchSig.clear(); - } - - bool IsNull() const - { - return (nExpiration == 0); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsInEffect() const - { - return (GetAdjustedTime() < nExpiration); - } - - 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)); - } - - 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))); - } - - bool AppliesToMe() const - { - return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); - } - - 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; - } - - 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"); - - // Now unserialize the data - CDataStream sMsg(vchMsg); - sMsg >> *(CUnsignedAlert*)this; - return true; - } - - bool ProcessAlert(); -}; +extern CTxMemPool mempool; #endif