#include "checkpoints.h"
#include "db.h"
#include "txdb.h"
-#include "net.h"
#include "init.h"
#include "ui_interface.h"
#include "checkqueue.h"
CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
-unsigned int nStakeMinAge = 60 * 60 * 24 * 30; // 30 days as zero time weight
-unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // 90 days as full weight
+unsigned int nStakeMinAge = 30 * nOneDay; // 30 days as zero time weight
+unsigned int nStakeMaxAge = 90 * nOneDay; // 90 days as full weight
unsigned int nStakeTargetSpacing = 10 * 60; // 10-minute stakes spacing
-unsigned int nModifierInterval = 6 * 60 * 60; // time to elapse before new modifier is computed
+unsigned int nModifierInterval = 6 * nOneHour; // time to elapse before new modifier is computed
int nCoinbaseMaturity = 500;
// Settings
int64_t nTransactionFee = MIN_TX_FEE;
-int64_t nMinimumInputValue = MIN_TX_FEE;
+int64_t nMinimumInputValue = MIN_TXOUT_AMOUNT;
+
+// Ping and address broadcast intervals
+int64_t nPingInterval = 30 * 60;
extern enum Checkpoints::CPMode CheckpointsMode;
// check whether the passed transaction is from us
bool static IsFromMe(CTransaction& tx)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
if (pwallet->IsFromMe(tx))
return true;
return false;
}
-// get the wallet transaction with the given hash (if it exists)
-bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
-{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- if (pwallet->GetTransaction(hashTx,wtx))
- return true;
- return false;
-}
-
-// erases transaction with the given hash from all wallets
-void static EraseFromWallets(uint256 hash)
-{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->EraseFromWallet(hash);
-}
-
// make sure all wallets know about the given transaction, in the given block
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
{
if (!fConnect)
{
- // ppcoin: wallets need to refund inputs when disconnecting coinstake
+ // wallets need to refund inputs when disconnecting coinstake
if (tx.IsCoinStake())
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
if (pwallet->IsFromMe(tx))
pwallet->DisableTransaction(tx);
}
return;
}
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
- // Preloaded coins cache invalidation
- fCoinsDataActual = false;
}
// notify wallets about a new best chain
void static SetBestChain(const CBlockLocator& loc)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
pwallet->SetBestChain(loc);
}
// notify wallets about an updated transaction
void static UpdatedTransaction(const uint256& hashTx)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
}
// dump all wallets
void static PrintWallets(const CBlock& block)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
pwallet->PrintWallet(block);
}
// notify wallets about an incoming inventory (for request counts)
void static Inventory(const uint256& hash)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ for(CWallet* pwallet : setpwalletRegistered)
pwallet->Inventory(hash);
}
// ask wallets to resend their transactions
-void ResendWalletTransactions()
+void ResendWalletTransactions(bool fForceResend)
{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->ResendWalletTransactions();
+ for(CWallet* pwallet : setpwalletRegistered)
+ pwallet->ResendWalletTransactions(fForceResend);
}
bool AddOrphanTx(const CTransaction& tx)
{
- uint256 hash = tx.GetHash();
+ auto hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
}
mapOrphanTransactions[hash] = tx;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for(const CTxIn& txin : tx.vin)
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
printf("stored orphan tx %s (mapsz %" PRIszu ")\n", hash.ToString().substr(0,10).c_str(),
{
if (!mapOrphanTransactions.count(hash))
return;
- const CTransaction& tx = mapOrphanTransactions[hash];
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ const auto& tx = mapOrphanTransactions[hash];
+ for(const auto& txin : tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
- uint256 randomhash = GetRandHash();
- map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ auto randomhash = GetRandHash();
+ auto it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
// CTransaction and CTxIndex
//
+bool CTransaction::IsFinal(int nBlockHeight, int64_t nBlockTime) const
+{
+ // Time based nLockTime implemented in 0.1.6
+ if (nLockTime == 0)
+ return true;
+ if (nBlockHeight == 0)
+ nBlockHeight = nBestHeight;
+ if (nBlockTime == 0)
+ nBlockTime = GetAdjustedTime();
+ if ((int64_t)nLockTime < ((int64_t)nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
+ return true;
+ for(const CTxIn& txin : vin)
+ if (!txin.IsFinal())
+ return false;
+ return true;
+}
+
+bool CTransaction::IsNewerThan(const CTransaction& old) const
+{
+ if (vin.size() != old.vin.size())
+ return false;
+ 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 = numeric_limits<unsigned int>::max();
+ for (unsigned int i = 0; i < vin.size(); i++)
+ {
+ if (vin[i].nSequence != old.vin[i].nSequence)
+ {
+ if (vin[i].nSequence <= nLowest)
+ {
+ fNewer = false;
+ nLowest = vin[i].nSequence;
+ }
+ if (old.vin[i].nSequence < nLowest)
+ {
+ fNewer = true;
+ nLowest = old.vin[i].nSequence;
+ }
+ }
+ }
+ return fNewer;
+}
+
+bool CTransaction::ReadFromDisk(CDiskTxPos pos, FILE** pfileRet)
+{
+ auto filein = CAutoFile(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION);
+ if (!filein)
+ return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
+
+ // Read transaction
+ if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
+ return error("CTransaction::ReadFromDisk() : fseek failed");
+
+ try {
+ filein >> *this;
+ }
+ catch (const std::exception&) {
+ return error("%s() : deserialize or I/O error", BOOST_CURRENT_FUNCTION);
+ }
+
+ // Return file pointer
+ if (pfileRet)
+ {
+ if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
+ return error("CTransaction::ReadFromDisk() : second fseek failed");
+ *pfileRet = filein.release();
+ }
+ return true;
+}
+
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
{
SetNull();
return ReadFromDisk(txdb, prevout, txindex);
}
-bool CTransaction::IsStandard() const
+bool CTransaction::IsStandard(string& strReason) const
{
if (nVersion > CTransaction::CURRENT_VERSION)
+ {
+ strReason = "version";
return false;
+ }
unsigned int nDataOut = 0;
txnouttype whichType;
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for(const CTxIn& txin : vin)
{
- // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
- // pay-to-script-hash, which is 3 ~80-byte signatures, 3
- // ~65-byte public keys, plus a few script ops.
- if (txin.scriptSig.size() > 500)
+ // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
+ // keys. (remember the 520 byte limit on redeemScript size) That works
+ // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624
+ // bytes of scriptSig, which we round off to 1650 bytes for some minor
+ // future-proofing. That's also enough to spend a 20-of-20
+ // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
+ // considered standard)
+ if (txin.scriptSig.size() > 1650)
+ {
+ strReason = "scriptsig-size";
return false;
+ }
if (!txin.scriptSig.IsPushOnly())
+ {
+ strReason = "scriptsig-not-pushonly";
return false;
+ }
if (!txin.scriptSig.HasCanonicalPushes()) {
+ strReason = "txin-scriptsig-not-canonicalpushes";
return false;
}
}
- BOOST_FOREACH(const CTxOut& txout, vout) {
+ for(const CTxOut& txout : vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
+ strReason = "scriptpubkey";
return false;
}
if (whichType == TX_NULL_DATA)
nDataOut++;
else {
if (txout.nValue == 0) {
+ strReason = "txout-value=0";
return false;
}
if (!txout.scriptPubKey.HasCanonicalPushes()) {
+ strReason = "txout-scriptsig-not-canonicalpushes";
return false;
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
+ strReason = "multi-op-return";
return false;
}
if (IsCoinBase())
return true; // Coinbases don't use vin normally
- for (unsigned int i = 0; i < vin.size(); i++)
+ for (uint32_t i = 0; i < vin.size(); i++)
{
const CTxOut& prev = GetOutputFor(vin[i], mapInputs);
return true;
}
-unsigned int
-CTransaction::GetLegacySigOpCount() const
+unsigned int CTransaction::GetLegacySigOpCount() const
{
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTxIn& txin, vin)
+ if (!IsCoinBase())
{
- nSigOps += txin.scriptSig.GetSigOpCount(false);
+ // Coinbase scriptsigs are never executed, so there is
+ // no sense in calculation of sigops.
+ for(const CTxIn& txin : vin)
+ {
+ nSigOps += txin.scriptSig.GetSigOpCount(false);
+ }
}
- BOOST_FOREACH(const CTxOut& txout, vout)
+ for(const CTxOut& txout : vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
return nSigOps;
}
-
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
{
if (fClient)
else
{
CBlock blockTmp;
+
if (pblock == NULL)
{
// Load the block this tx is in
}
// Is the tx in a block that's in the main chain
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
+ auto mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
- CBlockIndex* pindex = (*mi).second;
+ const CBlockIndex* pindex = (*mi).second;
if (!pindex || !pindex->IsInMainChain())
return 0;
return pindexBest->nHeight - pindex->nHeight + 1;
}
-
-
-
-
-
-
bool CTransaction::CheckTransaction() const
{
// Basic checks that don't depend on any context
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
// Check for negative or overflow output values
- int64_t nValueOut = 0;
- for (unsigned int i = 0; i < vout.size(); i++)
+ CBigNum nValueOut = 0;
+ for (uint32_t i = 0; i < vout.size(); i++)
{
- const CTxOut& txout = vout[i];
+ const auto& txout = vout[i];
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
-
- if (txout.nValue < 0)
- return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative"));
- if (txout.nValue > MAX_MONEY)
- return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
+ if (!MoneyRange(txout.nValue))
+ return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is out of range"));
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for(const CTxIn& txin : vin)
{
if (vInOutPoints.count(txin.prevout))
return false;
}
else
{
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for(const CTxIn& txin : vin)
if (txin.prevout.IsNull())
return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
}
int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const
{
- // Use new fees approach if we are on test network or
- // switch date has been reached
- bool fNewApproach = fTestNet || nTime > FEE_SWITCH_TIME;
-
int64_t nMinTxFee = MIN_TX_FEE, nMinRelayTxFee = MIN_RELAY_TX_FEE;
- if(!fNewApproach || IsCoinStake())
+ if(IsCoinStake())
{
- // Enforce 0.01 as minimum fee for old approach or coinstake
+ // Enforce 0.01 as minimum fee for coinstake
nMinTxFee = CENT;
nMinRelayTxFee = CENT;
}
// Base fee is either nMinTxFee or nMinRelayTxFee
- int64_t nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee;
+ auto nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee;
unsigned int nNewBlockSize = nBlockSize + nBytes;
int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee;
- if (fNewApproach)
+ if (fAllowFree)
{
- if (fAllowFree)
+ if (nBlockSize == 1)
{
- if (nBlockSize == 1)
- {
- // Transactions under 1K are free
- if (nBytes < 1000)
- nMinFee = 0;
- }
- else
- {
- // Free transaction area
- if (nNewBlockSize < 27000)
- nMinFee = 0;
- }
+ // Transactions under 1K are free
+ if (nBytes < 1000)
+ nMinFee = 0;
+ }
+ else
+ {
+ // Free transaction area
+ if (nNewBlockSize < 27000)
+ nMinFee = 0;
}
-
- // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for
- // each non empty output which is less than 0.01
- //
- // It's safe to ignore empty outputs here, because these inputs are allowed
- // only for coinbase and coinstake transactions.
- BOOST_FOREACH(const CTxOut& txout, vout)
- if (txout.nValue < CENT && !txout.IsEmpty())
- nMinFee += nBaseFee;
- }
- else if (nMinFee < nBaseFee)
- {
- // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if
- // any output is less than 0.01
- BOOST_FOREACH(const CTxOut& txout, vout)
- if (txout.nValue < CENT)
- nMinFee = nBaseFee;
}
+ // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for
+ // each non empty output which is less than 0.01
+ //
+ // It's safe to ignore empty outputs here, because these inputs are allowed
+ // only for coinbase and coinstake transactions.
+ for(const CTxOut& txout : vout)
+ if (txout.nValue < CENT && !txout.IsEmpty())
+ nMinFee += nBaseFee;
+
// Raise the price as the block approaches full
if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
{
return nMinFee;
}
+string CTransaction::ToStringShort() const
+{
+ string str;
+ str += strprintf("%s %s", GetHash().ToString().c_str(), IsCoinBase()? "base" : (IsCoinStake()? "stake" : "user"));
+ return str;
+}
+
+string CTransaction::ToString() const
+{
+ string str;
+ 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(),
+ nLockTime);
+ for (unsigned int i = 0; i < vin.size(); i++)
+ str += " " + vin[i].ToString() + "\n";
+ for (unsigned int i = 0; i < vout.size(); i++)
+ str += " " + vout[i].ToString() + "\n";
+ return str;
+}
bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
bool* pfMissingInputs)
if (pfMissingInputs)
*pfMissingInputs = false;
+ // Time (prevent mempool memory exhaustion attack)
+ if (tx.nTime > FutureDrift(GetAdjustedTime()))
+ return tx.DoS(10, error("CTxMemPool::accept() : transaction timestamp is too far in the future"));
+
if (!tx.CheckTransaction())
return error("CTxMemPool::accept() : CheckTransaction failed");
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
// Rather not work on nonstandard transactions (unless -testnet)
- if (!fTestNet && !tx.IsStandard())
- return error("CTxMemPool::accept() : nonstandard transaction type");
+ string strNonStd;
+ if (!fTestNet && !tx.IsStandard(strNonStd))
+ return error("CTxMemPool::accept() : nonstandard transaction (%s)", strNonStd.c_str());
// Do we already have it?
- uint256 hash = tx.GetHash();
+ auto hash = tx.GetHash();
{
LOCK(cs);
if (mapTx.count(hash))
return false;
// Check for conflicts with in-memory transactions
- CTransaction* ptxOld = NULL;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
- COutPoint outpoint = tx.vin[i].prevout;
+ auto outpoint = tx.vin[i].prevout;
if (mapNextTx.count(outpoint))
{
- // Disable replacement feature for now
+ // Replacement feature isn't supported by Novacoin.
return false;
-
- // Allow replacing with a newer version of the same transaction
- if (i != 0)
- return false;
- ptxOld = mapNextTx[outpoint].ptx;
- if (ptxOld->IsFinal())
- return false;
- if (!tx.IsNewerThan(*ptxOld))
- return false;
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- COutPoint outpoint = tx.vin[i].prevout;
- if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
- return false;
- }
- break;
}
}
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
- int64_t nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ auto nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
- int64_t txMinFee = tx.GetMinFee(1000, true, GMF_RELAY, nSize);
+ auto txMinFee = tx.GetMinFee(1000, true, GMF_RELAY, nSize);
if (nFees < txMinFee)
return error("CTxMemPool::accept() : not enough fees %s, %" PRId64 " < %" PRId64,
hash.ToString().c_str(),
static CCriticalSection cs;
static double dFreeCount;
static int64_t nLastTime;
- int64_t nNow = GetTime();
+ auto nNow = GetTime();
{
LOCK(cs);
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, SIG_SWITCH_TIME < tx.nTime ? STRICT_FLAGS : SOFT_FLAGS))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, STRICT_FLAGS))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
// Store transaction in memory
{
LOCK(cs);
- if (ptxOld)
- {
- printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
- remove(*ptxOld);
- }
addUnchecked(hash, tx);
}
- ///// are we sure this is ok when loading transactions or restoring block txes
- // If updated, erase old tx from wallet
- if (ptxOld)
- EraseFromWallets(ptxOld->GetHash());
-
printf("CTxMemPool::accept() : accepted %s (poolsz %" PRIszu ")\n",
hash.ToString().substr(0,10).c_str(),
mapTx.size());
// Remove transaction from memory pool
{
LOCK(cs);
- uint256 hash = tx.GetHash();
+ auto hash = tx.GetHash();
if (mapTx.count(hash))
{
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for(const CTxIn& txin : tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
nTransactionsUpdated++;
LOCK(cs);
vtxid.reserve(mapTx.size());
- for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
+ for (auto mi = mapTx.begin(); mi != mapTx.end(); ++mi)
vtxid.push_back((*mi).first);
}
+string CTxIn::ToString() const
+{
+ string str;
+ str += "CTxIn(";
+ str += CTxIn::prevout.ToString();
+ if (CTxIn::prevout.IsNull())
+ str += strprintf(", coinbase %s", HexStr(CTxIn::scriptSig).c_str());
+ else
+ str += strprintf(", scriptSig=%s", CTxIn::scriptSig.ToString().substr(0,24).c_str());
+ if (CTxIn::nSequence != numeric_limits<unsigned int>::max())
+ str += strprintf(", nSequence=%" PRIu32, CTxIn::nSequence);
+ str += ")";
+ return str;
+}
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
return 0;
// Find the block it claims to be in
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
+ auto mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
{
LOCK(mempool.cs);
// Add previous supporting transactions first
- BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
+ for(CMerkleTx& tx : vtxPrev)
{
if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
- uint256 hash = tx.GetHash();
+ auto hash = tx.GetHash();
if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
tx.AcceptToMemoryPool(txdb, fCheckInputs);
}
if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false))
return 0;
// Find the block in the index
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
+ auto mi = mapBlockIndex.find(block.GetHash());
if (mi == mapBlockIndex.end())
return 0;
- CBlockIndex* pindex = (*mi).second;
+ auto pindex = (*mi).second;
if (!pindex || !pindex->IsInMainChain())
return 0;
return 1 + nBestHeight - pindex->nHeight;
}
-
-
+// Closure representing one script verification
+// Note that this stores references to the spending transaction
+class CScriptCheck
+{
+private:
+ CScript scriptPubKey;
+ const CTransaction *ptxTo = nullptr;
+ uint32_t nIn = 0;
+ unsigned int nFlags = 0;
+ int nHashType = 0;
+
+public:
+ CScriptCheck() {}
+ CScriptCheck(const CTransaction& txFromIn, const CTransaction& txToIn, uint32_t nInIn, unsigned int nFlagsIn, int nHashTypeIn) :
+ scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
+ ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { }
+
+ bool operator()() const;
+
+ void swap(CScriptCheck &check) {
+ scriptPubKey.swap(check.scriptPubKey);
+ std::swap(ptxTo, check.ptxTo);
+ std::swap(nIn, check.nIn);
+ std::swap(nFlags, check.nFlags);
+ std::swap(nHashType, check.nHashType);
+ }
+};
// Human readable form:
//
// nSubsidy = 100 / (diff ^ 1/6)
+ //
+ // Please note that we're using bisection to find an approximate solutuion
CBigNum bnLowerBound = CENT;
CBigNum bnUpperBound = bnSubsidyLimit;
while (bnLowerBound + CENT <= bnUpperBound)
{
CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
- if (fDebug && GetBoolArg("-printcreation"))
- printf("GetProofOfWorkReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
bnUpperBound = bnMidValue;
else
}
// miner's coin stake reward based on nBits and coin age spent (coin-days)
-int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly)
+int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTime, bool bCoinYearOnly)
{
int64_t nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * COIN;
- if(fTestNet || nTime > STAKE_SWITCH_TIME)
- {
- // Stage 2 of emission process is PoS-based. It will be active on mainNet since 20 Jun 2013.
+ // Stage 2 of emission process is mostly PoS-based.
- CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest
- CBigNum bnTarget;
- bnTarget.SetCompact(nBits);
- CBigNum bnTargetLimit = GetProofOfStakeLimit(0, nTime);
- bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
+ CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest
+ CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
+ CBigNum bnTargetLimit = GetProofOfStakeLimit(0, nTime);
+ bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
- // NovaCoin: A reasonably continuous curve is used to avoid shock to market
+ // A reasonably continuous curve is used to avoid shock to market
- CBigNum bnLowerBound = 1 * CENT, // Lower interest bound is 1% per year
- bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year
- bnMidPart, bnRewardPart;
+ CBigNum bnLowerBound = 1 * CENT, // Lower interest bound is 1% per year
+ bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year
+ bnMidPart, bnRewardPart;
- while (bnLowerBound + CENT <= bnUpperBound)
- {
- CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
- if (fDebug && GetBoolArg("-printcreation"))
- printf("GetProofOfStakeReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
+ while (bnLowerBound + CENT <= bnUpperBound)
+ {
+ auto bnMidValue = (bnLowerBound + bnUpperBound) / 2;
- if(!fTestNet && nTime < STAKECURVE_SWITCH_TIME)
- {
- //
- // Until 20 Oct 2013: reward for coin-year is cut in half every 64x multiply of PoS difficulty
- //
- // (nRewardCoinYearLimit / nRewardCoinYear) ** 6 == bnProofOfStakeLimit / bnTarget
- //
- // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/6)
- //
-
- bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue;
- bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit;
- }
- else
- {
- //
- // Since 20 Oct 2013: reward for coin-year is cut in half every 8x multiply of PoS difficulty
- //
- // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget
- //
- // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3)
- //
-
- bnMidPart = bnMidValue * bnMidValue * bnMidValue;
- bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit;
- }
+ //
+ // Reward for coin-year is cut in half every 8x multiply of PoS difficulty
+ //
+ // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget
+ //
+ // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3)
+ //
- if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget)
- bnUpperBound = bnMidValue;
- else
- bnLowerBound = bnMidValue;
- }
+ bnMidPart = bnMidValue * bnMidValue * bnMidValue;
+ bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit;
- nRewardCoinYear = bnUpperBound.getuint64();
- nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE);
- }
- else
- {
- // Old creation amount per coin-year, 5% fixed stake mint rate
- nRewardCoinYear = 5 * CENT;
+ if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget)
+ bnUpperBound = bnMidValue;
+ else
+ bnLowerBound = bnMidValue;
}
+ nRewardCoinYear = bnUpperBound.getuint64();
+ nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE);
+
if(bCoinYearOnly)
return nRewardCoinYear;
nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8);
- // Set reasonable reward limit for large inputs since 20 Oct 2013
+ // Set reasonable reward limit for large inputs
//
// This will stimulate large holders to use smaller inputs, that's good for the network protection
- if(fTestNet || STAKECURVE_SWITCH_TIME < nTime)
- {
- if (fDebug && GetBoolArg("-printcreation") && nSubsidyLimit < nSubsidy)
- printf("GetProofOfStakeReward(): %s is greater than %s, coinstake reward will be truncated\n", FormatMoney(nSubsidy).c_str(), FormatMoney(nSubsidyLimit).c_str());
- nSubsidy = min(nSubsidy, nSubsidyLimit);
- }
+ if (fDebug && GetBoolArg("-printcreation") && nSubsidyLimit < nSubsidy)
+ printf("GetProofOfStakeReward(): %s is greater than %s, coinstake reward will be truncated\n", FormatMoney(nSubsidy).c_str(), FormatMoney(nSubsidyLimit).c_str());
+
+ nSubsidy = min(nSubsidy, nSubsidyLimit);
if (fDebug && GetBoolArg("-printcreation"))
printf("GetProofOfStakeReward(): create=%s nCoinAge=%" PRId64 " nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits);
+
return nSubsidy;
}
-static const int64_t nTargetTimespan = 7 * 24 * 60 * 60; // one week
+static const int64_t nTargetTimespan = 7 * nOneDay; // one week
// get proof of work blocks max spacing according to hard-coded conditions
int64_t inline GetTargetSpacingWorkMax(int nHeight, unsigned int nTime)
{
// Maximum 200% adjustment per day...
bnResult *= 2;
- nTime -= 24 * 60 * 60;
+ nTime -= nOneDay;
}
if (bnResult > bnTargetLimit)
bnResult = bnTargetLimit;
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
- CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : GetProofOfStakeLimit(pindexLast->nHeight, pindexLast->nTime);
-
if (pindexLast == NULL)
- return bnTargetLimit.GetCompact(); // genesis block
+ return bnProofOfWorkLimit.GetCompact(); // genesis block
+
+ CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : GetProofOfStakeLimit(pindexLast->nHeight, pindexLast->nTime);
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
if (pindexPrev->pprev == NULL)
return true;
static int64_t nLastUpdate;
static CBlockIndex* pindexLastBest;
+ auto nCurrentTime = GetTime();
if (pindexBest != pindexLastBest)
{
pindexLastBest = pindexBest;
- nLastUpdate = GetTime();
+ nLastUpdate = nCurrentTime;
}
- return (GetTime() - nLastUpdate < 10 &&
- pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60);
+ return (nCurrentTime - nLastUpdate < 10 &&
+ pindexBest->GetBlockTime() < nCurrentTime - nOneDay);
}
void static InvalidChainFound(CBlockIndex* pindexNew)
uiInterface.NotifyBlocksChanged();
}
- uint256 nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust;
- uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
+ auto nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust;
+ auto nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
printf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n",
pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight,
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
}
-
void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
{
nTime = max(GetBlockTime(), GetAdjustedTime());
}
+unsigned int CBlock::GetStakeEntropyBit(unsigned int nHeight) const
+{
+ // Protocol switch to support p2pool at novacoin block #9689
+ if (nHeight >= 9689 || fTestNet)
+ {
+ // Take last bit of block hash as entropy bit
+ auto nEntropyBit = (GetHash().Get32()) & (uint32_t)1;
+ if (fDebug && GetBoolArg("-printstakemodifier"))
+ printf("GetStakeEntropyBit: nTime=%" PRIu32 " hashBlock=%s nEntropyBit=%" PRIu32 "\n", nTime, GetHash().ToString().c_str(), nEntropyBit);
+ return nEntropyBit;
+ }
+ // Before novacoin block #9689 - get from pregenerated table
+ int nBitNum = nHeight & 0xFF;
+ int nItemNum = nHeight / 0xFF;
-
-
-
-
-
-
-
+ auto nEntropyBit = ((entropyStore[nItemNum] & (uint256(1) << nBitNum)) >> nBitNum).Get32();
+ if (fDebug && GetBoolArg("-printstakemodifier"))
+ printf("GetStakeEntropyBit: from pregenerated table, nHeight=%" PRIu32 " nEntropyBit=%" PRIu32 "\n", nHeight, nEntropyBit);
+ return nEntropyBit;
+}
bool CTransaction::DisconnectInputs(CTxDB& txdb)
{
// Relinquish previous transactions' spent pointers
if (!IsCoinBase())
{
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for(const CTxIn& txin : vin)
{
- COutPoint prevout = txin.prevout;
+ auto prevout = txin.prevout;
// Get prev txindex from disk
CTxIndex txindex;
for (unsigned int i = 0; i < vin.size(); i++)
{
- COutPoint prevout = vin[i].prevout;
+ auto prevout = vin[i].prevout;
if (inputsRet.count(prevout.hash))
continue; // Got it already
const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
{
- MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
+ auto mi = inputs.find(input.prevout.hash);
if (mi == inputs.end())
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
- const CTransaction& txPrev = (mi->second).second;
+ const auto& txPrev = (mi->second).second;
if (input.prevout.n >= txPrev.vout.size())
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");
return txPrev.vout[input.prevout.n];
}
+int64_t CTransaction::GetValueOut() const
+{
+ CBigNum nValueOut = 0;
+ for(const auto& txout : vout)
+ {
+ nValueOut += txout.nValue;
+ if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
+ throw runtime_error("CTransaction::GetValueOut() : value out of range");
+ }
+ return nValueOut.getint64();
+}
+
int64_t CTransaction::GetValueIn(const MapPrevTx& inputs) const
{
if (IsCoinBase())
return 0;
- int64_t nResult = 0;
- for (unsigned int i = 0; i < vin.size(); i++)
+ CBigNum nResult = 0;
+ for (uint32_t i = 0; i < vin.size(); i++)
{
- nResult += GetOutputFor(vin[i], inputs).nValue;
+ const auto& txOut = GetOutputFor(vin[i], inputs);
+ nResult += txOut.nValue;
+ if (!MoneyRange(txOut.nValue) || !MoneyRange(nResult))
+ throw runtime_error("CTransaction::GetValueIn() : value out of range");
}
- return nResult;
+ return nResult.getint64();
}
return true;
}
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, uint32_t nIn, unsigned int flags, int nHashType)
{
return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
}
if (!IsCoinBase())
{
int64_t nValueIn = 0;
- int64_t nFees = 0;
- for (unsigned int i = 0; i < vin.size(); i++)
{
- COutPoint prevout = vin[i].prevout;
- assert(inputs.count(prevout.hash) > 0);
- CTxIndex& txindex = inputs[prevout.hash].first;
- CTransaction& txPrev = inputs[prevout.hash].second;
-
- if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
-
- // If prev is coinbase or coinstake, check that it's matured
- if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
- for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev)
- if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
- return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight);
-
- // ppcoin: check transaction timestamp
- if (txPrev.nTime > nTime)
- return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction"));
-
- // Check for negative or overflow input values
- nValueIn += txPrev.vout[prevout.n].nValue;
- if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
- return DoS(100, error("ConnectInputs() : txin values out of range"));
-
+ CBigNum bnValueIn = 0;
+ for (uint32_t i = 0; i < vin.size(); i++)
+ {
+ auto prevout = vin[i].prevout;
+ assert(inputs.count(prevout.hash) > 0);
+ CTxIndex& txindex = inputs[prevout.hash].first;
+ CTransaction& txPrev = inputs[prevout.hash].second;
+
+ if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
+ return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
+
+ // If prev is coinbase or coinstake, check that it's matured
+ if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
+ for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev)
+ if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
+ return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight);
+
+ // ppcoin: check transaction timestamp
+ if (txPrev.nTime > nTime)
+ return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction"));
+
+ // Check for negative or overflow input values
+ bnValueIn += txPrev.vout[prevout.n].nValue;
+ if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(bnValueIn))
+ return DoS(100, error("ConnectInputs() : txin values out of range"));
+ }
+ nValueIn = bnValueIn.getint64();
}
if (pvChecks)
// Helps prevent CPU exhaustion attacks.
for (unsigned int i = 0; i < vin.size(); i++)
{
- COutPoint prevout = vin[i].prevout;
+ auto prevout = vin[i].prevout;
assert(inputs.count(prevout.hash) > 0);
CTxIndex& txindex = inputs[prevout.hash].first;
CTransaction& txPrev = inputs[prevout.hash].second;
if (IsCoinStake())
{
- // ppcoin: coin stake tx earns reward instead of paying fee
- uint64_t nCoinAge;
- if (!GetCoinAge(txdb, nCoinAge))
- return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
+ if (nTime > Checkpoints::GetLastCheckpointTime())
+ {
+ unsigned int nTxSize = GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION);
- unsigned int nTxSize = (nTime > VALIDATION_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0;
+ // coin stake tx earns reward instead of paying fee
+ uint64_t nCoinAge;
+ if (!GetCoinAge(txdb, nCoinAge))
+ return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
- int64_t nReward = GetValueOut() - nValueIn;
- int64_t nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT;
+ auto nReward = GetValueOut() - nValueIn;
+ auto nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT;
- if (nReward > nCalculatedReward)
- return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%" PRId64 " vs calculated=%" PRId64 ")", nReward, nCalculatedReward));
+ if (nReward > nCalculatedReward)
+ return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%" PRId64 " vs calculated=%" PRId64 ")", nReward, nCalculatedReward));
+ }
}
else
{
return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
// Tally transaction fees
- int64_t nTxFee = nValueIn - GetValueOut();
- if (nTxFee < 0)
- return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
-
- nFees += nTxFee;
- if (!MoneyRange(nFees))
+ auto nTxFee = nValueIn - GetValueOut();
+ if (!MoneyRange(nTxFee))
return DoS(100, error("ConnectInputs() : nFees out of range"));
}
}
// Take over previous transactions' spent pointers
{
LOCK(mempool.cs);
- int64_t nValueIn = 0;
- for (unsigned int i = 0; i < vin.size(); i++)
+ CBigNum bnValueIn = 0;
+ for (uint32_t i = 0; i < vin.size(); i++)
{
// Get prev tx from single transactions in memory
- COutPoint prevout = vin[i].prevout;
+ auto prevout = vin[i].prevout;
if (!mempool.exists(prevout.hash))
return false;
CTransaction& txPrev = mempool.lookup(prevout.hash);
// // Flag outpoints as used
// txPrev.vout[prevout.n].posNext = posThisTx;
- nValueIn += txPrev.vout[prevout.n].nValue;
+ bnValueIn += txPrev.vout[prevout.n].nValue;
- if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
+ if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(bnValueIn))
return error("ClientConnectInputs() : txin values out of range");
}
- if (GetValueOut() > nValueIn)
+ if (GetValueOut() > bnValueIn.getint64())
return false;
}
return true;
}
+int64_t CBlock::GetMaxTransactionTime() const
+{
+ int64_t maxTransactionTime = 0;
+ for(const auto& tx : vtx)
+ maxTransactionTime = max(maxTransactionTime, (int64_t)tx.nTime);
+ return maxTransactionTime;
+}
+
+uint256 CBlock::BuildMerkleTree() const
+{
+ vMerkleTree.clear();
+ for(const auto& tx : vtx)
+ vMerkleTree.push_back(tx.GetHash());
+ int j = 0;
+ for (int nSize = (int)vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ for (int i = 0; i < nSize; i += 2)
+ {
+ int i2 = min(i+1, nSize-1);
+ vMerkleTree.push_back(Hash(vMerkleTree[j+i].begin(), vMerkleTree[j+i].end(),
+ vMerkleTree[j+i2].begin(), vMerkleTree[j+i2].end()));
+ }
+ j += nSize;
+ }
+ return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
+}
+
+vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
+{
+ if (vMerkleTree.empty())
+ BuildMerkleTree();
+ vector<uint256> vMerkleBranch;
+ int j = 0;
+ for (int nSize = (int)vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ int i = min(nIndex^1, nSize-1);
+ vMerkleBranch.push_back(vMerkleTree[j+i]);
+ nIndex >>= 1;
+ j += nSize;
+ }
+ return vMerkleBranch;
+}
+
+uint256 CBlock::CheckMerkleBranch(uint256 hash, const vector<uint256>& vMerkleBranch, int nIndex)
+{
+ if (nIndex == -1)
+ return 0;
+ for(const uint256& otherside : vMerkleBranch)
+ {
+ if (nIndex & 1)
+ hash = Hash(otherside.begin(), otherside.end(), hash.begin(), hash.end());
+ else
+ hash = Hash(hash.begin(), hash.end(), otherside.begin(), otherside.end());
+ nIndex >>= 1;
+ }
+ return hash;
+}
+
+bool CBlock::WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet)
+{
+ // Open history file to append
+ auto fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION);
+ if (!fileout)
+ return error("CBlock::WriteToDisk() : AppendBlockFile failed");
+
+ // Write index header
+ unsigned int nSize = fileout.GetSerializeSize(*this);
+ fileout << nNetworkID << nSize;
+
+ // Write block
+ 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
+ fflush(fileout);
+ if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
+ FileCommit(fileout);
+
+ return true;
+}
+
+bool CBlock::ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions)
+{
+ SetNull();
+
+ // Open history file to read
+ auto 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
+ try {
+ filein >> *this;
+ }
+ catch (const std::exception&) {
+ return error("%s() : deserialize or I/O error", BOOST_CURRENT_FUNCTION);
+ }
+
+ // Check the header
+ if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
+ return error("CBlock::ReadFromDisk() : errors in block header");
+ return true;
+}
+void CBlock::print() const
+{
+ printf("CBlock(hash=%s, ver=%" PRId32 ", hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%" PRIu32 ", nBits=%08x, nNonce=%" PRIu32 ", vtx=%" PRIszu ", vchBlockSig=%s)\n",
+ GetHash().ToString().c_str(),
+ nVersion,
+ hashPrevBlock.ToString().c_str(),
+ hashMerkleRoot.ToString().c_str(),
+ nTime, nBits, nNonce,
+ 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 (unsigned int i = 0; i < vMerkleTree.size(); i++)
+ printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str());
+ printf("\n");
+}
bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
{
}
// ppcoin: clean up wallet after disconnecting coinstake
- BOOST_FOREACH(CTransaction& tx, vtx)
+ for(CTransaction& tx : vtx)
SyncWithWallets(tx, this, false, false);
return true;
int64_t nValueIn = 0;
int64_t nValueOut = 0;
unsigned int nSigOps = 0;
- BOOST_FOREACH(CTransaction& tx, vtx)
+ for(auto& tx : vtx)
{
- uint256 hashTx = tx.GetHash();
+ auto hashTx = tx.GetHash();
if (fEnforceBIP30) {
CTxIndex txindexOld;
if (txdb.ReadTxIndex(hashTx, txindexOld)) {
- BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
+ for(CDiskTxPos &pos : txindexOld.vSpent)
if (pos.IsNull())
return false;
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
- int64_t nTxValueIn = tx.GetValueIn(mapInputs);
- int64_t nTxValueOut = tx.GetValueOut();
+ auto nTxValueIn = tx.GetValueIn(mapInputs);
+ auto nTxValueOut = tx.GetValueOut();
nValueIn += nTxValueIn;
nValueOut += nTxValueOut;
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
+ unsigned int nFlags = SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH;
+
+ if (tx.nTime >= CHECKLOCKTIMEVERIFY_SWITCH_TIME) {
+ nFlags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ // OP_CHECKSEQUENCEVERIFY is senseless without BIP68, so we're going disable it for now.
+ // nFlags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ }
+
std::vector<CScriptCheck> vChecks;
- if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fScriptChecks, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, nScriptCheckThreads ? &vChecks : NULL))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fScriptChecks, nFlags, nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}
if (IsProofOfWork())
{
- int64_t nBlockReward = GetProofOfWorkReward(nBits, nFees);
+ auto nBlockReward = GetProofOfWorkReward(nBits, nFees);
// Check coinbase reward
if (vtx[0].GetValueOut() > nBlockReward)
return true;
// Write queued txindex changes
- for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
+ for (auto mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
if (!txdb.UpdateTxIndex((*mi).first, (*mi).second))
return error("ConnectBlock() : UpdateTxIndex failed");
}
// Watch for transactions paying to me
- BOOST_FOREACH(CTransaction& tx, vtx)
+ for(CTransaction& tx : vtx)
SyncWithWallets(tx, this, true);
while (pfork != plonger)
{
while (plonger->nHeight > pfork->nHeight)
- if (!(plonger = plonger->pprev))
+ if ((plonger = plonger->pprev) == NULL)
return error("Reorganize() : plonger->pprev is null");
if (pfork == plonger)
break;
- if (!(pfork = pfork->pprev))
+ if ((pfork = pfork->pprev) == NULL)
return error("Reorganize() : pfork->pprev is null");
}
// Disconnect shorter branch
vector<CTransaction> vResurrect;
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
+ for(CBlockIndex* pindex : vDisconnect)
{
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
// Queue memory transactions to resurrect
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for(const CTransaction& tx : block.vtx)
if (!(tx.IsCoinBase() || tx.IsCoinStake()))
vResurrect.push_back(tx);
}
}
// Queue memory transactions to delete
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for(const CTransaction& tx : block.vtx)
vDelete.push_back(tx);
}
if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
return error("Reorganize() : TxnCommit failed");
// Disconnect shorter branch
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
+ for(CBlockIndex* pindex : vDisconnect)
if (pindex->pprev)
pindex->pprev->pnext = NULL;
// Connect longer branch
- BOOST_FOREACH(CBlockIndex* pindex, vConnect)
+ for(CBlockIndex* pindex : vConnect)
if (pindex->pprev)
pindex->pprev->pnext = pindex;
// Resurrect memory transactions that were in the disconnected branch
- BOOST_FOREACH(CTransaction& tx, vResurrect)
+ for(CTransaction& tx : vResurrect)
tx.AcceptToMemoryPool(txdb, false);
// Delete redundant memory transactions that are in the connected branch
- BOOST_FOREACH(CTransaction& tx, vDelete)
+ for(CTransaction& tx : vDelete)
mempool.remove(tx);
printf("REORGANIZE: done\n");
// Called from inside SetBestChain: attaches a block to the new best chain being built
bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
{
- uint256 hash = GetHash();
+ auto hash = GetHash();
// Adding to current best branch
if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
pindexNew->pprev->pnext = pindexNew;
// Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
+ for(CTransaction& tx : vtx)
mempool.remove(tx);
return true;
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
{
- uint256 hash = GetHash();
+ auto hash = GetHash();
if (!txdb.TxnBegin())
return error("SetBestChain() : TxnBegin failed");
}
// Connect further blocks
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
+ for (std::vector<CBlockIndex*>::reverse_iterator rit = vpindexSecondary.rbegin(); rit != vpindexSecondary.rend(); ++rit)
{
CBlock block;
- if (!block.ReadFromDisk(pindex))
+ if (!block.ReadFromDisk(*rit))
{
printf("SetBestChain() : ReadFromDisk failed\n");
break;
break;
}
// errors now are not fatal, we still did a reorganisation to a new chain in a valid way
- if (!block.SetBestChainInner(txdb, pindex))
+ if (!block.SetBestChainInner(txdb, *rit))
break;
}
}
strMiscWarning = _("Warning: This version is obsolete, upgrade required!");
}
- std::string strCmd = GetArg("-blocknotify", "");
+ auto strCmd = GetArg("-blocknotify", "");
if (!fIsInitialDownload && !strCmd.empty())
{
if (IsCoinBase())
return true;
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for(const CTxIn& txin : vin)
{
// First try finding the previous transaction in database
CTransaction txPrev;
if (block.GetBlockTime() + nStakeMinAge > nTime)
continue; // only count coins meeting min age requirement
- int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ auto nValueIn = txPrev.vout[txin.prevout.n].nValue;
bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT;
if (fDebug && GetBoolArg("-printcoinage"))
printf("coin age nValueIn=%" PRId64 " nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str());
}
- CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
+ auto bnCoinDay = bnCentSecond * CENT / COIN / nOneDay;
if (fDebug && GetBoolArg("-printcoinage"))
printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str());
nCoinAge = bnCoinDay.getuint64();
nCoinAge = 0;
CTxDB txdb("r");
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ for(const CTransaction& tx : vtx)
{
uint64_t nTxCoinAge;
if (tx.GetCoinAge(txdb, nTxCoinAge))
bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
{
// Check for duplicate
- uint256 hash = GetHash();
+ auto hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str());
// Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
+ CBlockIndex* pindexNew = new(nothrow) CBlockIndex(nFile, nBlockPos, *this);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
pindexNew->phashBlock = &hash;
- map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
+ auto miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
// ppcoin: compute stake entropy bit for stake modifier
- if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime)))
+ if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight)))
return error("AddToBlockIndex() : SetStakeEntropyBit() failed");
// ppcoin: record proof-of-stake hash value
return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016" PRIx64, pindexNew->nHeight, nStakeModifier);
// Add to mapBlockIndex
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+ auto mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
if (pindexNew->IsProofOfStake())
setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
pindexNew->phashBlock = &((*mi).first);
hashPrevBestCoinBase = vtx[0].GetHash();
}
- uiInterface.NotifyBlocksChanged();
+ static int8_t counter = 0;
+ if( (++counter & 0x0F) == 0 || !IsInitialBlockDownload()) // repaint every 16 blocks if not in initial block download
+ uiInterface.NotifyBlocksChanged();
return true;
}
// These are checks that are independent of context
// that can be verified before saving an orphan block.
+ set<uint256> uniqueTx; // tx hashes
+ unsigned int nSigOps = 0; // total sigops
+
// Size limits
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CheckBlock() : size limits failed"));
- // Check proof of work matches claimed amount
- if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
- return DoS(50, error("CheckBlock() : proof of work failed"));
-
- // Check timestamp
- if (GetBlockTime() > FutureDrift(GetAdjustedTime()))
- return error("CheckBlock() : block timestamp too far in the future");
+ bool fProofOfStake = IsProofOfStake();
// First transaction must be coinbase, the rest must not be
- if (vtx.empty() || !vtx[0].IsCoinBase())
+ if (!vtx[0].IsCoinBase())
return DoS(100, error("CheckBlock() : first tx is not coinbase"));
- // Check coinbase timestamp
- if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime))
- return DoS(50, error("CheckBlock() : coinbase timestamp is too late"));
+ if (!vtx[0].CheckTransaction())
+ return DoS(vtx[0].nDoS, error("CheckBlock() : CheckTransaction failed on coinbase"));
- for (unsigned int i = 1; i < vtx.size(); i++)
- {
- if (vtx[i].IsCoinBase())
- return DoS(100, error("CheckBlock() : more than one coinbase"));
+ uniqueTx.insert(vtx[0].GetHash());
+ nSigOps += vtx[0].GetLegacySigOpCount();
- // Check transaction timestamp
- if (GetBlockTime() < (int64_t)vtx[i].nTime)
- return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
- }
-
- if (IsProofOfStake())
+ if (fProofOfStake)
{
+ // Proof-of-STake related checkings. Note that we know here that 1st transactions is coinstake. We don't need
+ // check the type of 1st transaction because it's performed earlier by IsProofOfStake()
+
+ // nNonce must be zero for proof-of-stake blocks
if (nNonce != 0)
return DoS(100, error("CheckBlock() : non-zero nonce in proof-of-stake block"));
if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())
return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));
- // Second transaction must be coinstake, the rest must not be
- if (vtx.empty() || !vtx[1].IsCoinStake())
- return DoS(100, error("CheckBlock() : second tx is not coinstake"));
- for (unsigned int i = 2; i < vtx.size(); i++)
- if (vtx[i].IsCoinStake())
- return DoS(100, error("CheckBlock() : more than one coinstake"));
-
// Check coinstake timestamp
if (GetBlockTime() != (int64_t)vtx[1].nTime)
return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%" PRId64 " nTimeTx=%u", GetBlockTime(), vtx[1].nTime));
// NovaCoin: check proof-of-stake block signature
- if (fCheckSig && !CheckBlockSignature(true))
+ if (fCheckSig && !CheckBlockSignature())
return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
+
+ if (!vtx[1].CheckTransaction())
+ return DoS(vtx[1].nDoS, error("CheckBlock() : CheckTransaction failed on coinstake"));
+
+ uniqueTx.insert(vtx[1].GetHash());
+ nSigOps += vtx[1].GetLegacySigOpCount();
}
else
{
- // Should we check proof-of-work block signature or not?
- //
- // * Always skip on TestNet
- // * Perform checking for the first 9689 blocks
- // * Perform checking since last checkpoint until 20 Sep 2013 (will be removed after)
+ // Check proof of work matches claimed amount
+ if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits))
+ return DoS(50, error("CheckBlock() : proof of work failed"));
- if(!fTestNet && fCheckSig)
- {
- bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
+ // Check timestamp
+ if (GetBlockTime() > FutureDrift(GetAdjustedTime()))
+ return error("CheckBlock() : block timestamp too far in the future");
- // NovaCoin: check proof-of-work block signature
- if (checkEntropySig && !CheckBlockSignature(false))
- return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
- }
+ // Check coinbase timestamp
+ if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime))
+ return DoS(50, error("CheckBlock() : coinbase timestamp is too late"));
}
- // Check transactions
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ // Iterate all transactions starting from second for proof-of-stake block
+ // or first for proof-of-work block
+ for (unsigned int i = fProofOfStake ? 2 : 1; i < vtx.size(); i++)
{
+ const CTransaction& tx = vtx[i];
+
+ // Reject coinbase transactions at non-zero index
+ if (tx.IsCoinBase())
+ return DoS(100, error("CheckBlock() : coinbase at wrong index"));
+
+ // Reject coinstake transactions at index != 1
+ if (tx.IsCoinStake())
+ return DoS(100, error("CheckBlock() : coinstake at wrong index"));
+
+ // Check transaction timestamp
+ if (GetBlockTime() < (int64_t)tx.nTime)
+ return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
+
+ // Check transaction consistency
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
+
+ // Add transaction hash into list of unique transaction IDs
+ uniqueTx.insert(tx.GetHash());
+
+ // Calculate sigops count
+ nSigOps += tx.GetLegacySigOpCount();
}
// Check for duplicate txids. This is caught by ConnectInputs(),
// but catching it earlier avoids a potential DoS attack:
- set<uint256> uniqueTx;
- BOOST_FOREACH(const CTransaction& tx, vtx)
- {
- uniqueTx.insert(tx.GetHash());
- }
if (uniqueTx.size() != vtx.size())
return DoS(100, error("CheckBlock() : duplicate transaction"));
- unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTransaction& tx, vtx)
- {
- nSigOps += tx.GetLegacySigOpCount();
- }
+ // Reject block if validation would consume too much resources.
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
-
return true;
}
bool CBlock::AcceptBlock()
{
// Check for duplicate
- uint256 hash = GetHash();
+ auto hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AcceptBlock() : block already in mapBlockIndex");
// Get prev block index
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
+ auto mi = mapBlockIndex.find(hashPrevBlock);
if (mi == mapBlockIndex.end())
return DoS(10, error("AcceptBlock() : prev block not found"));
CBlockIndex* pindexPrev = (*mi).second;
if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
+ int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
+ int nMaxOffset = 12 * nOneHour; // 12 hours
+ if (fTestNet || pindexPrev->nTime < 1450569600)
+ nMaxOffset = 7 * nOneWeek; // One week (permanently on testNet or until 20 Dec, 2015 on mainNet)
+
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
+ if (GetBlockTime() <= nMedianTimePast || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
return error("AcceptBlock() : block's timestamp is too early");
+ // Don't accept blocks with future timestamps
+ if (pindexPrev->nHeight > 1 && nMedianTimePast + nMaxOffset < GetBlockTime())
+ return error("AcceptBlock() : block's timestamp is too far in the future");
+
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ for(const CTransaction& tx : vtx)
if (!tx.IsFinal(nHeight, GetBlockTime()))
return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev);
// Check that the block satisfies synchronized checkpoint
- if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies)
+ if (CheckpointsMode == Checkpoints::CP_STRICT && !cpSatisfies)
return error("AcceptBlock() : rejected by synchronized checkpoint");
- if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies)
+ if (CheckpointsMode == Checkpoints::CP_ADVISORY && !cpSatisfies)
strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!");
// Enforce rule that the coinbase starts with serialized block height
- CScript expect = CScript() << nHeight;
+ auto expect = CScript() << nHeight;
if (vtx[0].vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
return DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
return error("AcceptBlock() : out of disk space");
- unsigned int nFile = -1;
+ unsigned int nFile = std::numeric_limits<unsigned int>::max();
unsigned int nBlockPos = 0;
if (!WriteToDisk(nFile, nBlockPos))
return error("AcceptBlock() : WriteToDisk failed");
if (hashBestChain == hash)
{
LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
+ for(CNode* pnode : vNodes)
if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
}
if (bnTarget <= 0)
return 0;
- /* Old protocol */
- if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)
- return (IsProofOfStake()? ((CBigNum(1)<<256) / (bnTarget+1)).getuint256() : 1);
-
- /* New protocol */
-
- // Calculate work amount for block
- uint256 nPoWTrust = (CBigNum(nPoWBase) / (bnTarget+1)).getuint256();
-
- // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
- nPoWTrust = (IsProofOfStake() || nPoWTrust < 1) ? 1 : nPoWTrust;
-
- // Return nPoWTrust for the first 12 blocks
+ // Return 1 for the first 12 blocks
if (pprev == NULL || pprev->nHeight < 12)
- return nPoWTrust;
+ return 1;
const CBlockIndex* currentIndex = pprev;
}
else
{
+ // Calculate work amount for block
+ CBigNum bnPoWTrust = CBigNum(nPoWBase) / (bnTarget+1);
+
+ // Set nPowTrust to 1 if PoW difficulty is too low
+ if (bnPoWTrust < 1)
+ bnPoWTrust = 1;
+
CBigNum bnLastBlockTrust = CBigNum(pprev->nChainTrust - pprev->pprev->nChainTrust);
// Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
if (!(pprev->IsProofOfStake() && pprev->pprev->IsProofOfStake()))
- return nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256();
+ return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256();
int nPoSCount = 0;
// Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
if (nPoSCount < 7)
- return nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256();
+ return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256();
bnTarget.SetCompact(pprev->nBits);
CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1);
// Return nPoWTrust + full trust score for previous block nBits
- return nPoWTrust + bnNewTrust.getuint256();
+ return (bnPoWTrust + bnNewTrust).getuint256();
+ }
+}
+
+CBlock CBlockIndex::GetBlockHeader() const
+{
+ CBlock block;
+ block.nVersion = nVersion;
+ if (pprev)
+ block.hashPrevBlock = pprev->GetBlockHash();
+ block.hashMerkleRoot = hashMerkleRoot;
+ block.nTime = nTime;
+ block.nBits = nBits;
+ block.nNonce = nNonce;
+ return block;
+}
+
+enum { nMedianTimeSpan=11 };
+
+int64_t CBlockIndex::GetMedianTimePast() const
+{
+ int64_t pmedian[nMedianTimeSpan];
+ int64_t* pbegin = &pmedian[nMedianTimeSpan];
+ int64_t* pend = &pmedian[nMedianTimeSpan];
+ const CBlockIndex* pindex = this;
+ for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
+ *(--pbegin) = pindex->GetBlockTime();
+ sort(pbegin, pend);
+ return pbegin[(pend - pbegin)/2];
+}
+
+int64_t CBlockIndex::GetMedianTime() const
+{
+ const CBlockIndex* pindex = this;
+ for (int i = 0; i < nMedianTimeSpan/2; i++)
+ {
+ if (!pindex->pnext)
+ return GetBlockTime();
+ pindex = pindex->pnext;
}
+ return pindex->GetMedianTimePast();
}
bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
return (nFound >= nRequired);
}
+bool CBlockIndex::SetStakeEntropyBit(unsigned int nEntropyBit)
+{
+ if (nEntropyBit > 1)
+ return false;
+ nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
+ return true;
+}
+
+void CBlockIndex::SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier)
+{
+ nStakeModifier = nModifier;
+ if (fGeneratedStakeModifier)
+ nFlags |= BLOCK_STAKE_MODIFIER;
+}
+
+string CBlockIndex::ToString() const
+{
+ return strprintf("CBlockIndex(nprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6d nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016" PRIx64 ", nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
+ (const void*)pprev, (const void*)pnext, nFile, nBlockPos, 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());
+}
+
+bool static ReserealizeBlockSignature(CBlock* pblock)
+{
+ if (pblock->IsProofOfWork())
+ {
+ pblock->vchBlockSig.clear();
+ return true;
+ }
+
+ return CPubKey::ReserealizeSignature(pblock->vchBlockSig);
+}
+
+bool static IsCanonicalBlockSignature(CBlock* pblock)
+{
+ if (pblock->IsProofOfWork())
+ return pblock->vchBlockSig.empty();
+
+ return IsDERSignature(pblock->vchBlockSig);
+}
+
bool ProcessBlock(CNode* pfrom, CBlock* pblock)
{
// Check for duplicate
- uint256 hash = pblock->GetHash();
+ auto hash = pblock->GetHash();
if (mapBlockIndex.count(hash))
return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str());
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
- // ppcoin: check proof-of-stake
+ // Check that block isn't listed as unconditionally banned.
+ if (!Checkpoints::CheckBanned(hash)) {
+ if (pfrom)
+ pfrom->Misbehaving(100);
+ return error("ProcessBlock() : block %s is rejected by hard-coded banlist", hash.GetHex().substr(0,20).c_str());
+ }
+
+ // Check proof-of-stake
// Limited duplicity on stake: prevents block flood attack
// Duplicate stake allowed only when there is orphan child block
if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+ // Strip the garbage from newly received blocks, if we found some
+ if (!IsCanonicalBlockSignature(pblock)) {
+ if (!ReserealizeBlockSignature(pblock))
+ printf("WARNING: ProcessBlock() : ReserealizeBlockSignature FAILED\n");
+ }
+
// Preliminary checks
- if (!pblock->CheckBlock())
+ if (!pblock->CheckBlock(true, true, (pblock->nTime > Checkpoints::GetLastCheckpointTime())))
return error("ProcessBlock() : CheckBlock FAILED");
// ppcoin: verify hash target and signature of coinstake tx
if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
- int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
+ auto deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
- CBlock* pblock2 = new CBlock(*pblock);
// ppcoin: check proof-of-stake
- if (pblock2->IsProofOfStake())
+ if (pblock->IsProofOfStake())
{
// Limited duplicity on stake: prevents block flood attack
// Duplicate stake allowed only when there is orphan child block
- if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
- return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock2->GetProofOfStake().first.ToString().c_str(), pblock2->GetProofOfStake().second, hash.ToString().c_str());
+ if (setStakeSeenOrphan.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
else
- setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
+ setStakeSeenOrphan.insert(pblock->GetProofOfStake());
}
+ CBlock* pblock2 = new CBlock(*pblock);
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
vWorkQueue.push_back(hash);
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
- uint256 hashPrev = vWorkQueue[i];
- for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
+ auto hashPrev = vWorkQueue[i];
+ for (auto mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
mi != mapOrphanBlocksByPrev.upper_bound(hashPrev);
++mi)
{
return true;
}
-// novacoin: attempt to generate suitable proof-of-stake
-bool CBlock::SignBlock(CWallet& wallet)
+// ppcoin: check block signature
+bool CBlock::CheckBlockSignature() const
{
- // if we are trying to sign
- // something except proof-of-stake block template
- if (!vtx[0].vout[0].IsEmpty())
+ if (vchBlockSig.empty())
return false;
- // if we are trying to sign
- // a complete proof-of-stake block
- if (IsProofOfStake())
- return true;
-
- static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp
-
- CKey key;
- CTransaction txCoinStake;
- int64_t nSearchTime = txCoinStake.nTime; // search to current time
+ txnouttype whichType;
+ vector<valtype> vSolutions;
+ if (!Solver(vtx[1].vout[1].scriptPubKey, whichType, vSolutions))
+ return false;
- if (nSearchTime > nLastCoinStakeSearchTime)
+ if (whichType == TX_PUBKEY)
{
- if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key))
- {
- if (txCoinStake.nTime >= max(pindexBest->GetMedianTimePast()+1, PastDrift(pindexBest->GetBlockTime())))
- {
- // make sure coinstake would meet timestamp protocol
- // as it would be the same as the block timestamp
- vtx[0].nTime = nTime = txCoinStake.nTime;
- nTime = max(pindexBest->GetMedianTimePast()+1, GetMaxTransactionTime());
- nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime()));
-
- // we have to make sure that we have no future timestamps in
- // our transactions set
- for (vector<CTransaction>::iterator it = vtx.begin(); it != vtx.end();)
- if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; }
-
- vtx.insert(vtx.begin() + 1, txCoinStake);
- hashMerkleRoot = BuildMerkleTree();
-
- // append a signature to our block
- return key.Sign(GetHash(), vchBlockSig);
- }
- }
- nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
- nLastCoinStakeSearchTime = nSearchTime;
+ auto& vchPubKey = vSolutions[0];
+ CPubKey key(vchPubKey);
+ if (!key.IsValid())
+ return false;
+ return key.Verify(GetHash(), vchBlockSig);
}
return false;
}
-// ppcoin: check block signature
-bool CBlock::CheckBlockSignature(bool fProofOfStake) const
+
+
+//
+// CDiskBlockIndex
+//
+
+uint256 CDiskBlockIndex::GetBlockHash() const
{
- if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
- return vchBlockSig.empty();
+ if (fUseFastIndex && blockHash != 0)
+ return blockHash;
- vector<valtype> vSolutions;
- txnouttype whichType;
+ CBlock block;
+ block.nVersion = nVersion;
+ block.hashPrevBlock = hashPrev;
+ block.hashMerkleRoot = hashMerkleRoot;
+ block.nTime = nTime;
+ block.nBits = nBits;
+ block.nNonce = nNonce;
+
+ const_cast<CDiskBlockIndex*>(this)->blockHash = block.GetHash();
- if(fProofOfStake)
+ return blockHash;
+}
+
+string CDiskBlockIndex::ToString() const
+{
+ string str = "CDiskBlockIndex(";
+ str += CBlockIndex::ToString();
+ str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
+ GetBlockHash().ToString().c_str(),
+ hashPrev.ToString().c_str(),
+ hashNext.ToString().c_str());
+ return str;
+}
+
+//
+//CBlockLocator
+//
+
+void CBlockLocator::Set(const CBlockIndex* pindex)
+{
+ vHave.clear();
+ int nStep = 1;
+ while (pindex)
{
- const CTxOut& txout = vtx[1].vout[1];
+ vHave.push_back(pindex->GetBlockHash());
+ // Exponentially larger steps back
+ for (int i = 0; pindex && i < nStep; i++)
+ pindex = pindex->pprev;
+ if (vHave.size() > 10)
+ nStep *= 2;
+ }
+ vHave.push_back((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
+}
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
- if (whichType == TX_PUBKEY)
+int CBlockLocator::GetDistanceBack()
+{
+ // Retrace how far back it was in the sender's branch
+ int nDistance = 0;
+ int nStep = 1;
+ for(const auto& hash : vHave)
+ {
+ auto mi = mapBlockIndex.find(hash);
+ if (mi != mapBlockIndex.end())
{
- valtype& vchPubKey = vSolutions[0];
- CKey key;
- if (!key.SetPubKey(vchPubKey))
- return false;
- if (vchBlockSig.empty())
- return false;
- return key.Verify(GetHash(), vchBlockSig);
+ auto pindex = (*mi).second;
+ if (pindex->IsInMainChain())
+ return nDistance;
}
+ nDistance += nStep;
+ if (nDistance > 10)
+ nStep *= 2;
}
- else
+ return nDistance;
+}
+
+CBlockIndex* CBlockLocator::GetBlockIndex()
+{
+ // Find the first block the caller has in the main chain
+ for(const auto& hash : vHave)
{
- for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
+ auto mi = mapBlockIndex.find(hash);
+ if (mi != mapBlockIndex.end())
{
- const CTxOut& txout = vtx[0].vout[i];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_PUBKEY)
- {
- // Verify
- valtype& vchPubKey = vSolutions[0];
- CKey key;
- if (!key.SetPubKey(vchPubKey))
- continue;
- if (vchBlockSig.empty())
- continue;
- if(!key.Verify(GetHash(), vchBlockSig))
- continue;
+ auto pindex = (*mi).second;
+ if (pindex->IsInMainChain())
+ return pindex;
+ }
+ }
+ return pindexGenesisBlock;
+}
- return true;
- }
+uint256 CBlockLocator::GetBlockHash()
+{
+ // Find the first block the caller has in the main chain
+ for(const uint256& hash : vHave)
+ {
+ auto mi = mapBlockIndex.find(hash);
+ if (mi != mapBlockIndex.end())
+ {
+ auto pindex = (*mi).second;
+ if (pindex->IsInMainChain())
+ return hash;
}
}
- return false;
+ return (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet);
+}
+
+int CBlockLocator::GetHeight()
+{
+ CBlockIndex* pindex = GetBlockIndex();
+ if (!pindex)
+ return 0;
+ return pindex->nHeight;
}
bool CheckDiskSpace(uint64_t nAdditionalBytes)
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
- if ((nFile < 1) || (nFile == (unsigned int) -1))
+ if ((nFile < 1) || (nFile == std::numeric_limits<uint32_t>::max()))
return NULL;
FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode);
if (!file)
FILE* AppendBlockFile(unsigned int& nFileRet)
{
nFileRet = 0;
- while (true)
+ for ( ; ; )
{
FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
if (!file)
return NULL;
if (fseek(file, 0, SEEK_END) != 0)
+ {
+ fclose(file);
return NULL;
+ }
// FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
{
}
}
+void UnloadBlockIndex()
+{
+ mapBlockIndex.clear();
+ setStakeSeen.clear();
+ pindexGenesisBlock = NULL;
+ nBestHeight = 0;
+ nBestChainTrust = 0;
+ nBestInvalidTrust = 0;
+ hashBestChain = 0;
+ pindexBest = NULL;
+}
+
bool LoadBlockIndex(bool fAllowNew)
{
if (fTestNet)
{
- pchMessageStart[0] = 0xcd;
- pchMessageStart[1] = 0xf2;
- pchMessageStart[2] = 0xc0;
- pchMessageStart[3] = 0xef;
+ nNetworkID = 0xefc0f2cd;
bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet
- nStakeMinAge = 2 * 60 * 60; // test net min age is 2 hours
+ nStakeMinAge = 2 * nOneHour; // test net min age is 2 hours
nModifierInterval = 20 * 60; // test modifier interval is 20 minutes
nCoinbaseMaturity = 10; // test maturity is 10 blocks
nStakeTargetSpacing = 5 * 60; // test block spacing is 5 minutes
// CTxOut(empty)
// vMerkleTree: 4cb33b3b6a
- const char* pszTimestamp = "https://bitcointalk.org/index.php?topic=134179.msg1502196#msg1502196";
+ const string strTimestamp = "https://bitcointalk.org/index.php?topic=134179.msg1502196#msg1502196";
CTransaction txNew;
txNew.nTime = 1360105017;
txNew.vin.resize(1);
txNew.vout.resize(1);
- txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
+ txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>(strTimestamp.begin(), strTimestamp.end());
txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
{
// pre-compute tree structure
map<CBlockIndex*, vector<CBlockIndex*> > mapNext;
- for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
+ for (auto mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
{
CBlockIndex* pindex = (*mi).second;
mapNext[pindex->pprev].push_back(pindex);
}
}
-bool LoadExternalBlockFile(FILE* fileIn)
+bool LoadExternalBlockFile(FILE* fileIn, CClientUIInterface& uiInterface)
{
- int64_t nStart = GetTimeMillis();
-
- int nLoaded = 0;
+ auto nStart = GetTimeMillis();
+ vector<uint8_t> pchData(10 * (8+MAX_BLOCK_SIZE));
+ int32_t nLoaded = 0;
{
LOCK(cs_main);
try {
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
- unsigned int nPos = 0;
- while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown)
+ uint32_t nPos = 0;
+ while (nPos != std::numeric_limits<uint32_t>::max() && blkdat.good() && !fRequestShutdown)
{
- unsigned char pchData[65536];
- do {
+ do
+ {
fseek(blkdat, nPos, SEEK_SET);
- int nRead = fread(pchData, 1, sizeof(pchData), blkdat);
+ auto nRead = fread(&pchData[0], 1, pchData.size(), blkdat);
if (nRead <= 8)
{
- nPos = (unsigned int)-1;
+ nPos = numeric_limits<uint32_t>::max();
break;
}
- void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart));
- if (nFind)
+ auto it = pchData.begin();
+ while(it != pchData.end() && !fRequestShutdown)
{
- if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0)
+ auto nBlockLength = *reinterpret_cast<const uint32_t*>(&(*(it+4)));
+ auto SeekToNext = [&pchData, &it, &nPos, &nBlockLength]() {
+ auto previt = it;
+ it = search(it+8, pchData.end(), BEGIN(nNetworkID), END(nNetworkID));
+ if (it != pchData.end())
+ nPos += (it - previt);
+ };
+ if (nBlockLength > 0)
{
- nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart);
- break;
+ if (nBlockLength > (uint32_t)distance(it, pchData.end()))
+ {
+ SeekToNext();
+ break; // We've reached the end of buffer
+ }
+ else
+ {
+ CBlock block;
+ try
+ {
+ vector<unsigned char> vchBlockBytes(it+8, it+8+nBlockLength);
+ CDataStream blockData(vchBlockBytes, SER_NETWORK, PROTOCOL_VERSION);
+ blockData >> block;
+ }
+ catch (const std::exception&)
+ {
+ printf("LoadExternalBlockFile() : Deserialize error caught at the position %" PRIu32 ", this block may be truncated.", nPos);
+ SeekToNext();
+ break;
+ }
+ if (ProcessBlock(NULL, &block))
+ nLoaded++;
+ advance(it, 8 + nBlockLength);
+ nPos += (8 + nBlockLength);
+ {
+ static int64_t nLastUpdate = 0;
+ if (GetTimeMillis() - nLastUpdate > 1000)
+ {
+ uiInterface.InitMessage(strprintf(_("%" PRId32 " blocks were read."), nLoaded));
+ nLastUpdate = GetTimeMillis();
+ }
+ }
+ }
+ }
+ else
+ {
+ SeekToNext();
}
- nPos += ((unsigned char*)nFind - pchData) + 1;
- }
- else
- nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1;
- } while(!fRequestShutdown);
- if (nPos == (unsigned int)-1)
- break;
- fseek(blkdat, nPos, SEEK_SET);
- unsigned int nSize;
- blkdat >> nSize;
- if (nSize > 0 && nSize <= MAX_BLOCK_SIZE)
- {
- CBlock block;
- blkdat >> block;
- if (ProcessBlock(NULL,&block))
- {
- nLoaded++;
- nPos += 4 + nSize;
}
}
+ while(!fRequestShutdown);
}
}
- catch (std::exception &e) {
- printf("%s() : Deserialize or I/O error caught during load\n",
+ catch (const std::exception&) {
+ printf("%s() : I/O error caught during load\n",
BOOST_CURRENT_FUNCTION);
}
}
strRPC = "test";
// Misc warnings like out of disk space and clock is wrong
- if (strMiscWarning != "")
+ if (!strMiscWarning.empty())
{
nPriority = 1000;
strStatusBar = strMiscWarning;
// if detected unmet upgrade requirement enter safe mode
// Note: Modifier upgrade requires blockchain redownload if past protocol switch
- if (IsFixedModifierInterval(nModifierUpgradeTime + 60*60*24)) // 1 day margin
+ if (IsFixedModifierInterval(nModifierUpgradeTime + nOneDay)) // 1 day margin
{
nPriority = 5000;
strStatusBar = strRPC = "WARNING: Blockchain redownload required approaching or past v.0.4.4.6u4 upgrade deadline.";
// Alerts
{
LOCK(cs_mapAlerts);
- BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
+ for(auto& item : mapAlerts)
{
const CAlert& alert = item.second;
if (alert.AppliesToMe() && alert.nPriority > nPriority)
bool static AlreadyHave(CTxDB& txdb, const CInv& inv)
{
- switch (inv.type)
+ int nType = inv.GetType();
+ auto nHash = inv.GetHash();
+
+ switch (nType)
{
case MSG_TX:
{
bool txInMap = false;
{
LOCK(mempool.cs);
- txInMap = (mempool.exists(inv.hash));
+ txInMap = (mempool.exists(nHash));
}
return txInMap ||
- mapOrphanTransactions.count(inv.hash) ||
- txdb.ContainsTx(inv.hash);
+ mapOrphanTransactions.count(nHash) ||
+ txdb.ContainsTx(nHash);
}
case MSG_BLOCK:
- return mapBlockIndex.count(inv.hash) ||
- mapOrphanBlocks.count(inv.hash);
+ return mapBlockIndex.count(nHash) ||
+ mapOrphanBlocks.count(nHash);
}
// Don't know what it is, just say we already got one
return true;
}
-
-
-
-// The message start string is designed to be unlikely to occur in normal data.
-// The characters are rarely used upper ASCII, not valid as UTF-8, and produce
-// a large 4-byte int at any alignment.
-unsigned char pchMessageStart[4] = { 0xe4, 0xe8, 0xe9, 0xe5 };
-
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
static map<CService, CPubKey> mapReuseKey;
// Advertise our address
if (!fNoListen && !IsInitialBlockDownload())
{
- CAddress addr = GetLocalAddress(&pfrom->addr);
+ auto addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
pfrom->PushAddress(addr);
}
// Relay alerts
{
LOCK(cs_mapAlerts);
- BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
+ for(auto& item : mapAlerts)
item.second.RelayTo(pfrom);
}
// Store the new addresses
vector<CAddress> vAddrOk;
- int64_t nNow = GetAdjustedTime();
- int64_t nSince = nNow - 10 * 60;
- BOOST_FOREACH(CAddress& addr, vAddr)
+ auto nNow = GetAdjustedTime();
+ auto nSince = nNow - 10 * 60;
+ for(CAddress& addr : vAddr)
{
if (fShutdown)
return true;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
- addr.nTime = nNow - 5 * 24 * 60 * 60;
+ addr.nTime = nNow - 5 * nOneDay;
pfrom->AddAddressKnown(addr);
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
if (hashSalt == 0)
hashSalt = GetRandHash();
uint64_t hashAddr = addr.GetHash();
- uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
- hashRand = Hash(BEGIN(hashRand), END(hashRand));
+ uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/nOneDay);
+ hashRand = Hash(hashRand.begin(), hashRand.end());
multimap<uint256, CNode*> mapMix;
- BOOST_FOREACH(CNode* pnode, vNodes)
+ for(CNode* pnode : vNodes)
{
if (pnode->nVersion < CADDR_TIME_VERSION)
continue;
unsigned int nPointer;
memcpy(&nPointer, &pnode, sizeof(nPointer));
uint256 hashKey = hashRand ^ nPointer;
- hashKey = Hash(BEGIN(hashKey), END(hashKey));
+ hashKey = Hash(hashKey.begin(), hashKey.end());
mapMix.insert(make_pair(hashKey, pnode));
}
int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
- for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
+ for (auto mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
((*mi).second)->PushAddress(addr);
}
}
if (fReachable)
vAddrOk.push_back(addr);
}
- addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ addrman.Add(vAddrOk, pfrom->addr, 2 * nOneHour);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
}
// find last block in inv vector
- unsigned int nLastBlock = (unsigned int)(-1);
- for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) {
- if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) {
- nLastBlock = vInv.size() - 1 - nInv;
+ int nLastBlock = -1;
+ for (size_t nInv = 0; nInv < vInv.size(); nInv++) {
+ if (vInv[vInv.size() - 1 - nInv].GetType() == MSG_BLOCK) {
+ nLastBlock = (int) (vInv.size() - 1 - nInv);
break;
}
}
CTxDB txdb("r");
- for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
+ for (size_t nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
+ int nType = inv.GetType();
+ auto nHash = inv.GetHash();
if (fShutdown)
return true;
if (!fAlreadyHave)
pfrom->AskFor(inv);
- else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) {
- pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
- } else if (nInv == nLastBlock) {
+ else if (nType == MSG_BLOCK && mapOrphanBlocks.count(nHash)) {
+ pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[nHash]));
+ } else if (nType == nLastBlock) {
// In case we are on a very long side-chain, it is possible that we already have
// the last block in an inv bundle sent in response to getblocks. Try to detect
// this situation and push another getblocks to continue.
- pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0));
+ pfrom->PushGetBlocks(mapBlockIndex[nHash], uint256(0));
if (fDebug)
printf("force request: %s\n", inv.ToString().c_str());
}
// Track requests for our stuff
- Inventory(inv.hash);
+ Inventory(nHash);
}
}
if (fDebugNet || (vInv.size() != 1))
printf("received getdata (%" PRIszu " invsz)\n", vInv.size());
- BOOST_FOREACH(const CInv& inv, vInv)
+ for(const CInv& inv : vInv)
{
if (fShutdown)
return true;
if (fDebugNet || (vInv.size() == 1))
printf("received getdata for: %s\n", inv.ToString().c_str());
- if (inv.type == MSG_BLOCK)
+ int nType = inv.GetType();
+ auto nHash = inv.GetHash();
+
+ if (nType == MSG_BLOCK)
{
// Send block from disk
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
+ auto mi = mapBlockIndex.find(nHash);
if (mi != mapBlockIndex.end())
{
CBlock block;
pfrom->PushMessage("block", block);
// Trigger them to send a getblocks request for the next batch of inventory
- if (inv.hash == pfrom->hashContinue)
+ if (nHash == pfrom->hashContinue)
{
// ppcoin: send latest proof-of-work block to allow the
// download node to accept as orphan (proof-of-stake
bool pushed = false;
{
LOCK(cs_mapRelay);
- map<CInv, CDataStream>::iterator mi = mapRelay.find(inv);
+ auto mi = mapRelay.find(inv);
if (mi != mapRelay.end()) {
pfrom->PushMessage(inv.GetCommand(), (*mi).second);
pushed = true;
}
}
- if (!pushed && inv.type == MSG_TX) {
+ if (!pushed && nType == MSG_TX) {
LOCK(mempool.cs);
- if (mempool.exists(inv.hash)) {
- CTransaction tx = mempool.lookup(inv.hash);
+ if (mempool.exists(nHash)) {
+ CTransaction tx = mempool.lookup(nHash);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << tx;
}
// Track requests for our stuff
- Inventory(inv.hash);
+ Inventory(nHash);
}
}
// Relay
pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
+ for(CNode* pnode : vNodes)
checkpoint.RelayTo(pnode);
}
}
if (locator.IsNull())
{
// If locator is null, return the hashStop block
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop);
+ auto mi = mapBlockIndex.find(hashStop);
if (mi == mapBlockIndex.end())
return true;
pindex = (*mi).second;
bool fMissingInputs = false;
if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
{
+ auto nHash = inv.GetHash();
+
SyncWithWallets(tx, NULL, true);
- RelayTransaction(tx, inv.hash);
+ RelayTransaction(tx, nHash);
mapAlreadyAskedFor.erase(inv);
- vWorkQueue.push_back(inv.hash);
- vEraseQueue.push_back(inv.hash);
+ vWorkQueue.push_back(nHash);
+ vEraseQueue.push_back(nHash);
// Recursively process any orphan transactions that depended on this one
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
- uint256 hashPrev = vWorkQueue[i];
- for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ auto hashPrev = vWorkQueue[i];
+ for (auto mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
const uint256& orphanTxHash = *mi;
- CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash];
+ auto& orphanTx = mapOrphanTransactions[orphanTxHash];
bool fMissingInputs2 = false;
if (orphanTx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
}
}
- BOOST_FOREACH(uint256 hash, vEraseQueue)
+ for(uint256 hash : vEraseQueue)
EraseOrphanTx(hash);
}
else if (fMissingInputs)
{
CBlock block;
vRecv >> block;
- uint256 hashBlock = block.GetHash();
+ auto hashBlock = block.GetHash();
printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str());
// block.print();
}
- else if (strCommand == "getaddr")
+ // This asymmetric behavior for inbound and outbound connections was introduced
+ // to prevent a fingerprinting attack: an attacker can send specific fake addresses
+ // to users' AddrMan and later request them by sending getaddr messages.
+ // Making users (which are behind NAT and can only make outgoing connections) ignore
+ // getaddr message mitigates the attack.
+ else if ((strCommand == "getaddr") && (pfrom->fInbound))
{
// Don't return addresses older than nCutOff timestamp
- int64_t nCutOff = GetTime() - (nNodeLifespan * 24 * 60 * 60);
+ int64_t nCutOff = GetTime() - (nNodeLifespan * nOneDay);
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
- BOOST_FOREACH(const CAddress &addr, vAddr)
+ for(const CAddress &addr : vAddr)
if(addr.nTime > nCutOff)
pfrom->PushAddress(addr);
}
if (!GetBoolArg("-allowreceivebyip"))
{
- pfrom->PushMessage("reply", hashReply, (int)2, string(""));
+ pfrom->PushMessage("reply", hashReply, 2, string(""));
return true;
}
// Send back approval of order and pubkey to use
CScript scriptPubKey;
scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG;
- pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey);
+ pfrom->PushMessage("reply", hashReply, 0, scriptPubKey);
}
CRequestTracker tracker;
{
LOCK(pfrom->cs_mapRequests);
- map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply);
+ auto mi = pfrom->mapRequests.find(hashReply);
if (mi != pfrom->mapRequests.end())
{
tracker = (*mi).second;
else if (strCommand == "ping")
{
- if (pfrom->nVersion > BIP0031_VERSION)
- {
- uint64_t nonce = 0;
- vRecv >> nonce;
- // Echo the message back with the nonce. This allows for two useful features:
- //
- // 1) A remote node can quickly check if the connection is operational
- // 2) Remote nodes can measure the latency of the network thread. If this node
- // is overloaded it won't respond to pings quickly and the remote node can
- // avoid sending us more work, like chain download requests.
- //
- // The nonce stops the remote getting confused between different pings: without
- // it, if the remote node sends a ping once per second and this node takes 5
- // seconds to respond to each, the 5th ping the remote sends would appear to
- // return very quickly.
- pfrom->PushMessage("pong", nonce);
- }
+ uint64_t nonce = 0;
+ vRecv >> nonce;
+ // Echo the message back with the nonce. This allows for two useful features:
+ //
+ // 1) A remote node can quickly check if the connection is operational
+ // 2) Remote nodes can measure the latency of the network thread. If this node
+ // is overloaded it won't respond to pings quickly and the remote node can
+ // avoid sending us more work, like chain download requests.
+ //
+ // The nonce stops the remote getting confused between different pings: without
+ // it, if the remote node sends a ping once per second and this node takes 5
+ // seconds to respond to each, the 5th ping the remote sends would appear to
+ // return very quickly.
+ pfrom->PushMessage("pong", nonce);
}
CAlert alert;
vRecv >> alert;
- uint256 alertHash = alert.GetHash();
+ auto alertHash = alert.GetHash();
if (pfrom->setKnown.count(alertHash) == 0)
{
if (alert.ProcessAlert())
pfrom->setKnown.insert(alertHash);
{
LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
+ for(CNode* pnode : vNodes)
alert.RelayTo(pnode);
}
}
// (x) data
//
- while (true)
+ for ( ; ; )
{
// Don't bother if send buffer is too full to respond anyway
if (pfrom->vSend.size() >= SendBufferSize())
break;
// Scan for message start
- CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
+ CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(nNetworkID), END(nNetworkID));
int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
if (vRecv.end() - pstart < nHeaderSize)
{
}
// Checksum
- uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
+ auto hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
unsigned int nChecksum = 0;
memcpy(&nChecksum, &hash, sizeof(nChecksum));
if (nChecksum != hdr.nChecksum)
}
-bool SendMessages(CNode* pto, bool fSendTrickle)
+bool SendMessages(CNode* pto)
{
TRY_LOCK(cs_main, lockMain);
if (lockMain) {
+ // Current time in microseconds
+ auto nNow = GetTimeMicros();
+
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return true;
// Keep-alive ping. We send a nonce of zero because we don't use it anywhere
// right now.
- if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) {
+ if (pto->nLastSend && GetTime() - pto->nLastSend > nPingInterval && pto->vSend.empty()) {
uint64_t nonce = 0;
- if (pto->nVersion > BIP0031_VERSION)
- pto->PushMessage("ping", nonce);
- else
- pto->PushMessage("ping");
+ pto->PushMessage("ping", nonce);
}
// Start block sync
ResendWalletTransactions();
// Address refresh broadcast
- static int64_t nLastRebroadcast;
- if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
- {
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- {
- // Periodically clear setAddrKnown to allow refresh broadcasts
- if (nLastRebroadcast)
- pnode->setAddrKnown.clear();
-
- // Rebroadcast our address
- if (!fNoListen)
- {
- CAddress addr = GetLocalAddress(&pnode->addr);
- if (addr.IsRoutable())
- pnode->PushAddress(addr);
- }
- }
- }
- nLastRebroadcast = GetTime();
+ if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ AdvertiseLocal(pto);
+ pto->nNextLocalAddrSend = PoissonNextSend(nNow, nOneDay);
}
//
// Message: addr
//
- if (fSendTrickle)
- {
+ if (pto->nNextAddrSend < nNow) {
+ pto->nNextAddrSend = PoissonNextSend(nNow, 30);
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
- BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
+ for(const CAddress& addr : pto->vAddrToSend)
{
- // returns true if wasn't already contained in the set
if (pto->setAddrKnown.insert(addr).second)
{
vAddr.push_back(addr);
pto->PushMessage("addr", vAddr);
}
-
//
// Message: inventory
//
vector<CInv> vInv;
vector<CInv> vInvWait;
{
+ bool fSendTrickle = false;
+ if (pto->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ pto->nNextInvSend = PoissonNextSend(nNow, 5);
+ }
LOCK(pto->cs_inventory);
vInv.reserve(pto->vInventoryToSend.size());
vInvWait.reserve(pto->vInventoryToSend.size());
- BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)
+ for(const CInv& inv : pto->vInventoryToSend)
{
if (pto->setInventoryKnown.count(inv))
continue;
// trickle out tx inv to protect privacy
- if (inv.type == MSG_TX && !fSendTrickle)
+ if (inv.GetType() == MSG_TX && !fSendTrickle)
{
// 1/4 of tx invs blast to all immediately
static uint256 hashSalt;
if (hashSalt == 0)
hashSalt = GetRandHash();
- uint256 hashRand = inv.hash ^ hashSalt;
- hashRand = Hash(BEGIN(hashRand), END(hashRand));
+ uint256 hashRand = inv.GetHash() ^ hashSalt;
+ hashRand = Hash(hashRand.begin(), hashRand.end());
bool fTrickleWait = ((hashRand & 3) != 0);
- // always trickle our own transactions
- if (!fTrickleWait)
- {
- CWalletTx wtx;
- if (GetTransaction(inv.hash, wtx))
- if (wtx.fFromMe)
- fTrickleWait = true;
- }
-
if (fTrickleWait)
{
vInvWait.push_back(inv);
// Message: getdata
//
vector<CInv> vGetData;
- int64_t nNow = GetTime() * 1000000;
CTxDB txdb("r");
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
}
return true;
}
+
+
+class CMainCleanup
+{
+public:
+ CMainCleanup() {}
+ ~CMainCleanup() {
+ // block headers
+ auto it1 = mapBlockIndex.begin();
+ for (; it1 != mapBlockIndex.end(); it1++)
+ delete (*it1).second;
+ mapBlockIndex.clear();
+
+ // orphan blocks
+ auto it2 = mapOrphanBlocks.begin();
+ for (; it2 != mapOrphanBlocks.end(); it2++)
+ delete (*it2).second;
+ mapOrphanBlocks.clear();
+
+ // orphan transactions
+ }
+} instance_of_cmaincleanup;