// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
-// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "alert.h"
#include "checkpoints.h"
#include "db.h"
-#include "net.h"
+#include "txdb.h"
#include "init.h"
#include "ui_interface.h"
+#include "checkqueue.h"
+#include "kernel.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
+#include "main.h"
+
using namespace std;
using namespace boost;
-//
-// Global state
-//
+
CCriticalSection cs_setpwalletRegistered;
set<CWallet*> setpwalletRegistered;
map<uint256, CBlockIndex*> mapBlockIndex;
set<pair<COutPoint, unsigned int> > setStakeSeen;
-uint256 hashGenesisBlock = hashGenesisBlockOfficial;
-static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
+
+CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); // "standard" scrypt target limit for proof of work, results with 0,000244140625 proof-of-work difficulty
+CBigNum bnProofOfStakeLegacyLimit(~uint256(0) >> 24); // proof of stake target limit from block #15000 and until 20 June 2013, results with 0,00390625 proof of stake difficulty
+CBigNum bnProofOfStakeLimit(~uint256(0) >> 27); // proof of stake target limit since 20 June 2013, equal to 0.03125 proof of stake difficulty
+CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 30); // disabled temporarily, will be used in the future to fix minimal proof of stake difficulty at 0.25
+uint256 nPoWBase = uint256("0x00000000ffff0000000000000000000000000000000000000000000000000000"); // difficulty-1 target
+
+CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
+
+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 * nOneHour; // time to elapse before new modifier is computed
+
+int nCoinbaseMaturity = 500;
+
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
-uint64 nBestChainTrust = 0;
-uint64 nBestInvalidTrust = 0;
+
+uint256 nBestChainTrust = 0;
+uint256 nBestInvalidTrust = 0;
+
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
-int64 nTimeBestReceived = 0;
+int64_t nTimeBestReceived = 0;
+int nScriptCheckThreads = 0;
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
+map<uint256, uint256> mapProofOfStake;
-map<uint256, CDataStream*> mapOrphanTransactions;
-map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+map<uint256, CTransaction> mapOrphanTransactions;
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
-const string strMessageMagic = "PPCoin Signed Message:\n";
-
-double dHashesPerSec;
-int64 nHPSTimerStart;
+const string strMessageMagic = "NovaCoin Signed Message:\n";
// Settings
-int64 nTransactionFee = MIN_TX_FEE;
+int64_t nTransactionFee = MIN_TX_FEE;
+int64_t nMinimumInputValue = MIN_TXOUT_AMOUNT;
+// Ping and address broadcast intervals
+int64_t nPingInterval = 30 * 60;
+int64_t nBroadcastInterval = nOneDay;
+extern enum Checkpoints::CPMode CheckpointsMode;
//////////////////////////////////////////////////////////////////////////////
//
}
// make sure all wallets know about the given transaction, in the given block
-void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true)
+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)
}
// ask wallets to resend their transactions
-void static ResendWalletTransactions()
+void ResendWalletTransactions()
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ResendWalletTransactions();
// mapOrphanTransactions
//
-bool AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CTransaction& tx)
{
- CTransaction tx;
- CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
- CDataStream* pvMsg = new CDataStream(vMsg);
-
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
- if (pvMsg->size() > 5000)
+
+ size_t nSize = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
+
+ if (nSize > 5000)
{
- printf("ignoring large orphan tx (size: %u, hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
- delete pvMsg;
+ printf("ignoring large orphan tx (size: %" PRIszu ", hash: %s)\n", nSize, hash.ToString().substr(0,10).c_str());
return false;
}
- mapOrphanTransactions[hash] = pvMsg;
+ mapOrphanTransactions[hash] = tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
- printf("stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10).c_str(),
+ printf("stored orphan tx %s (mapsz %" PRIszu ")\n", hash.ToString().substr(0,10).c_str(),
mapOrphanTransactions.size());
return true;
}
{
if (!mapOrphanTransactions.count(hash))
return;
- const CDataStream* pvMsg = mapOrphanTransactions[hash];
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
+ const CTransaction& tx = mapOrphanTransactions[hash];
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
- delete pvMsg;
mapOrphanTransactions.erase(hash);
}
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
- map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
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)
{
- // 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)
- if (!::IsStandard(txout.scriptPubKey))
+ BOOST_FOREACH(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;
+ }
+
return true;
}
// beside "push data" in the scriptSig the
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0))
return false;
if (whichType == TX_SCRIPTHASH)
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.
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ {
+ nSigOps += txin.scriptSig.GetSigOpCount(false);
+ }
}
BOOST_FOREACH(const CTxOut& txout, vout)
{
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
// Check for negative or overflow output values
- int64 nValueOut = 0;
- for (int i = 0; i < vout.size(); i++)
+ int64_t nValueOut = 0;
+ for (unsigned int i = 0; i < vout.size(); i++)
{
const CTxOut& txout = vout[i];
- if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake()))
+ 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 negative"));
+ 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"));
nValueOut += txout.nValue;
if (IsCoinBase())
{
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
- return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
+ return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size is invalid"));
}
else
{
return true;
}
+int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const
+{
+ int64_t nMinTxFee = MIN_TX_FEE, nMinRelayTxFee = MIN_RELAY_TX_FEE;
+
+ if(IsCoinStake())
+ {
+ // 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;
+
+ unsigned int nNewBlockSize = nBlockSize + nBytes;
+ int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee;
+
+ if (fAllowFree)
+ {
+ if (nBlockSize == 1)
+ {
+ // 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;
+
+ // Raise the price as the block approaches full
+ if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
+ {
+ if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
+ return MAX_MONEY;
+ nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
+ }
+
+ if (!MoneyRange(nMinFee))
+ nMinFee = MAX_MONEY;
+
+ return nMinFee;
+}
+
+
bool 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");
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
+
// ppcoin: coinstake is also only valid in a block, not as a loose transaction
if (tx.IsCoinStake())
return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx"));
// To help v0.1.5 clients who would see it as a negative number
- if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
+ if ((int64_t)tx.nLockTime > std::numeric_limits<int>::max())
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();
return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
if (pfMissingInputs)
*pfMissingInputs = true;
- return error("CTxMemPool::accept() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
+ return false;
}
// Check for non-standard pay-to-script-hash in inputs
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
- int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ int64_t 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
- if (nFees < tx.GetMinFee(1000, false, GMF_RELAY))
- return error("CTxMemPool::accept() : not enough fees");
+ int64_t 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(),
+ nFees, txMinFee);
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
- // be annoying or make other's transactions take longer to confirm.
+ // be annoying or make others' transactions take longer to confirm.
if (nFees < MIN_RELAY_TX_FEE)
{
static CCriticalSection cs;
static double dFreeCount;
- static int64 nLastTime;
- int64 nNow = GetTime();
+ static int64_t nLastTime;
+ int64_t 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))
+ 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());
}
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
remove(*ptxOld);
}
- addUnchecked(tx);
+ addUnchecked(hash, tx);
}
///// are we sure this is ok when loading transactions or restoring block txes
if (ptxOld)
EraseFromWallets(ptxOld->GetHash());
- printf("CTxMemPool::accept() : accepted %s\n", hash.ToString().substr(0,10).c_str());
+ printf("CTxMemPool::accept() : accepted %s (poolsz %" PRIszu ")\n",
+ hash.ToString().substr(0,10).c_str(),
+ mapTx.size());
return true;
}
return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
}
-bool CTxMemPool::addUnchecked(CTransaction &tx)
+bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
{
- printf("addUnchecked(): size %lu\n", mapTx.size());
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
{
- LOCK(cs);
- uint256 hash = tx.GetHash();
mapTx[hash] = tx;
for (unsigned int i = 0; i < tx.vin.size(); i++)
mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i);
return true;
}
+void CTxMemPool::clear()
+{
+ LOCK(cs);
+ mapTx.clear();
+ mapNextTx.clear();
+ ++nTransactionsUpdated;
+}
+
+void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
+{
+ vtxid.clear();
+ LOCK(cs);
+ vtxid.reserve(mapTx.size());
+ for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
+ vtxid.push_back((*mi).first);
+}
{
if (!(IsCoinBase() || IsCoinStake()))
return 0;
- return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain());
+ return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain());
}
return 1 + nBestHeight - pindex->nHeight;
}
-
+// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
+bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
+{
+ {
+ LOCK(cs_main);
+ {
+ LOCK(mempool.cs);
+ if (mempool.exists(hash))
+ {
+ tx = mempool.lookup(hash);
+ return true;
+ }
+ }
+ CTxDB txdb("r");
+ CTxIndex txindex;
+ if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
+ {
+ CBlock block;
+ if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ hashBlock = block.GetHash();
+ return true;
+ }
+ }
+ return false;
+}
// CBlock and CBlockIndex
//
+static CBlockIndex* pblockindexFBBHLast;
+CBlockIndex* FindBlockByHeight(int nHeight)
+{
+ CBlockIndex *pblockindex;
+ if (nHeight < nBestHeight / 2)
+ pblockindex = pindexGenesisBlock;
+ else
+ pblockindex = pindexBest;
+ if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight))
+ pblockindex = pblockindexFBBHLast;
+ while (pblockindex->nHeight > nHeight)
+ pblockindex = pblockindex->pprev;
+ while (pblockindex->nHeight < nHeight)
+ pblockindex = pblockindex->pnext;
+ pblockindexFBBHLast = pblockindex;
+ return pblockindex;
+}
+
bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions)
{
if (!fReadTransactions)
return pblockOrphan->hashPrevBlock;
}
-int64 static GetProofOfWorkReward(unsigned int nBits)
+// select stake target limit according to hard-coded conditions
+CBigNum inline GetProofOfStakeLimit(int nHeight, unsigned int nTime)
{
- CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
+ if(fTestNet) // separate proof of stake target limit for testnet
+ return bnProofOfStakeLimit;
+ if(nTime > TARGETS_SWITCH_TIME) // 27 bits since 20 July 2013
+ return bnProofOfStakeLimit;
+ if(nHeight + 1 > 15000) // 24 bits since block 15000
+ return bnProofOfStakeLegacyLimit;
+ if(nHeight + 1 > 14060) // 31 bits since block 14060 until 15000
+ return bnProofOfStakeHardLimit;
+
+ return bnProofOfWorkLimit; // return bnProofOfWorkLimit of none matched
+}
+
+// miner's coin base reward based on nBits
+int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees)
+{
+ CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK;
+
CBigNum bnTarget;
bnTarget.SetCompact(nBits);
CBigNum bnTargetLimit = bnProofOfWorkLimit;
bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
- // ppcoin: subsidy is cut in half every 16x multiply of difficulty
+ // NovaCoin: subsidy is cut in half every 64x multiply of PoW difficulty
// A reasonably continuous curve is used to avoid shock to market
- // (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
+ // (nSubsidyLimit / nSubsidy) ** 6 == bnProofOfWorkLimit / bnTarget
+ //
+ // 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=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
- if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
+ if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
bnUpperBound = bnMidValue;
else
bnLowerBound = bnMidValue;
}
- int64 nSubsidy = bnUpperBound.getuint64();
+ int64_t nSubsidy = bnUpperBound.getuint64();
+
nSubsidy = (nSubsidy / CENT) * CENT;
if (fDebug && GetBoolArg("-printcreation"))
- printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
+ printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%" PRId64 "\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
- return nSubsidy;
+ return min(nSubsidy, MAX_MINT_PROOF_OF_WORK) + nFees;
}
-// ppcoin: miner's coin stake is rewarded based on coin age spent (coin-days)
-int64 GetProofOfStakeReward(int64 nCoinAge)
+// miner's coin stake reward based on nBits and coin age spent (coin-days)
+int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTime, bool bCoinYearOnly)
{
- static int64 nRewardCoinYear = CENT; // creation amount per coin-year
- int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+ int64_t nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * COIN;
+
+ // 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());
+
+ // 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;
+
+ while (bnLowerBound + CENT <= bnUpperBound)
+ {
+ CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
+
+ //
+ // 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;
+
+ 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
+ //
+ // This will stimulate large holders to use smaller inputs, that's good for the network protection
+
+ 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=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nCoinAge);
+ printf("GetProofOfStakeReward(): create=%s nCoinAge=%" PRId64 " nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits);
+
return nSubsidy;
}
-static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
-static const int64 nTargetSpacingStake = 10 * 60; // ten minutes
-static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
-static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
+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)
+{
+ if(nTime > TARGETS_SWITCH_TIME)
+ return 3 * nStakeTargetSpacing; // 30 minutes on mainNet since 20 Jul 2013 00:00:00
+
+ if(fTestNet)
+ return 3 * nStakeTargetSpacing; // 15 minutes on testNet
+
+ return 12 * nStakeTargetSpacing; // 2 hours otherwise
+}
//
-// minimum amount of work that could possibly be required nTime after
-// minimum work required was nBase
+// maximum nBits value could possible be required nTime after
//
-unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
+unsigned int ComputeMaxBits(CBigNum bnTargetLimit, unsigned int nBase, int64_t nTime)
{
CBigNum bnResult;
bnResult.SetCompact(nBase);
bnResult *= 2;
- while (nTime > 0 && bnResult < bnProofOfWorkLimit)
+ while (nTime > 0 && bnResult < bnTargetLimit)
{
// Maximum 200% adjustment per day...
bnResult *= 2;
- nTime -= 24 * 60 * 60;
+ nTime -= nOneDay;
}
- if (bnResult > bnProofOfWorkLimit)
- bnResult = bnProofOfWorkLimit;
+ if (bnResult > bnTargetLimit)
+ bnResult = bnTargetLimit;
return bnResult.GetCompact();
}
+//
+// minimum amount of work that could possibly be required nTime after
+// minimum proof-of-work required was nBase
+//
+unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime)
+{
+ return ComputeMaxBits(bnProofOfWorkLimit, nBase, nTime);
+}
+
+//
+// minimum amount of stake that could possibly be required nTime after
+// minimum proof-of-stake required was nBase
+//
+unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime)
+{
+ return ComputeMaxBits(GetProofOfStakeLimit(0, nBlockTime), nBase, nTime);
+}
+
+
// ppcoin: find last block index up to pindex
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
{
- while (pindex && (pindex->IsProofOfStake() != fProofOfStake))
+ while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake))
pindex = pindex->pprev;
return pindex;
}
-unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
+unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
- // Genesis block and first block
- if (pindexLast == NULL || pindexLast->pprev == NULL)
- return bnProofOfWorkLimit.GetCompact();
+ if (pindexLast == NULL)
+ return bnProofOfWorkLimit.GetCompact(); // genesis block
+
+ CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : GetProofOfStakeLimit(pindexLast->nHeight, pindexLast->nTime);
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
- if (pindexPrev == NULL)
- return bnProofOfWorkLimit.GetCompact();
+ if (pindexPrev->pprev == NULL)
+ return bnTargetLimit.GetCompact(); // first block
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
- if (pindexPrevPrev == NULL)
- return bnProofOfWorkLimit.GetCompact();
- int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
+ if (pindexPrevPrev->pprev == NULL)
+ return bnTargetLimit.GetCompact(); // second block
+
+ int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
// ppcoin: target change every block
// ppcoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
bnNew.SetCompact(pindexPrev->nBits);
- int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight));
- int64 nInterval = nTargetTimespan / nTargetSpacing;
+ int64_t nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(GetTargetSpacingWorkMax(pindexLast->nHeight, pindexLast->nTime), (int64_t) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight));
+ int64_t nInterval = nTargetTimespan / nTargetSpacing;
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
bnNew /= ((nInterval + 1) * nTargetSpacing);
- if (bnNew > bnProofOfWorkLimit)
- bnNew = bnProofOfWorkLimit;
+ if (bnNew > bnTargetLimit)
+ bnNew = bnTargetLimit;
return bnNew.GetCompact();
}
{
if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
return true;
- static int64 nLastUpdate;
+ static int64_t nLastUpdate;
static CBlockIndex* pindexLastBest;
+ int64_t 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)
if (pindexNew->nChainTrust > nBestInvalidTrust)
{
nBestInvalidTrust = pindexNew->nChainTrust;
- CTxDB().WriteBestInvalidTrust(nBestInvalidTrust);
- MainFrameRepaint();
+ CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
+ uiInterface.NotifyBlocksChanged();
}
- printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString().c_str());
- printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
- if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
- printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
+
+ uint256 nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust;
+ uint256 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,
+ CBigNum(pindexNew->nChainTrust).ToString().c_str(), nBestInvalidBlockTrust.Get64(),
+ DateTimeStrFormat("%x %H:%M:%S", pindexNew->GetBlockTime()).c_str());
+ printf("InvalidChainFound: current best=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ CBigNum(pindexBest->nChainTrust).ToString().c_str(),
+ nBestBlockTrust.Get64(),
+ DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
}
+
void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
{
nTime = max(GetBlockTime(), GetAdjustedTime());
// Remove transaction from index
// This can fail if a duplicate of this transaction was in a chain that got
// reorganized away. This is only possible if this transaction was completely
- // spent, so erasing it would be a no-op anway.
+ // spent, so erasing it would be a no-op anyway.
txdb.EraseTxIndex(*this);
return true;
}
}
- // Make sure all prevout.n's are valid:
+ // Make sure all prevout.n indexes are valid:
for (unsigned int i = 0; i < vin.size(); i++)
{
const COutPoint prevout = vin[i].prevout;
// Revisit this if/when transaction replacement is implemented and allows
// adding inputs:
fInvalid = true;
- return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %d %d 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()));
+ return DoS(100, error("FetchInputs() : %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()));
}
}
return txPrev.vout[input.prevout.n];
}
-int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const
+int64_t CTransaction::GetValueIn(const MapPrevTx& inputs) const
{
if (IsCoinBase())
return 0;
- int64 nResult = 0;
+ int64_t nResult = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
nResult += GetOutputFor(vin[i], inputs).nValue;
return nSigOps;
}
-bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
- map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+bool CScriptCheck::operator()() const {
+ const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
+ if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType))
+ return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str());
+ return true;
+}
+
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
+{
+ return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
+}
+
+bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
+ const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
+
if (!IsCoinBase())
{
- int64 nValueIn = 0;
- int64 nFees = 0;
+ int64_t nValueIn = 0;
+ int64_t nFees = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
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 %d %d 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()));
+ 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/coinstake, check that it's matured
+ // 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 < COINBASE_MATURITY; pindex = pindex->pprev)
+ 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 coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight);
+ 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() : txin values out of range"));
}
+
+ if (pvChecks)
+ pvChecks->reserve(vin.size());
+
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
// before the last blockchain checkpoint. This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint.
- if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
+ if (fScriptChecks)
{
// Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ CScriptCheck check(txPrev, *this, i, flags, 0);
+ if (pvChecks)
{
- // only during transition phase for P2SH: do not invoke anti-DoS code for
- // potentially old clients relaying bad P2SH transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
-
+ pvChecks->push_back(CScriptCheck());
+ check.swap(pvChecks->back());
+ }
+ else if (!check())
+ {
+ if (flags & STRICT_FLAGS)
+ {
+ // Don't trigger DoS code in case of STRICT_FLAGS caused failure.
+ CScriptCheck check(txPrev, *this, i, flags & ~STRICT_FLAGS, 0);
+ if (check())
+ return error("ConnectInputs() : %s strict VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ }
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
}
}
if (IsCoinStake())
{
- // ppcoin: coin stake tx earns reward instead of paying fee
- uint64 nCoinAge;
- if (!GetCoinAge(txdb, nCoinAge))
- return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
- int64 nStakeReward = GetValueOut() - nValueIn;
- if (nStakeReward > GetProofOfStakeReward(nCoinAge))
- return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str()));
+ if (nTime > Checkpoints::GetLastCheckpointTime())
+ {
+ unsigned int nTxSize = GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION);
+
+ // 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;
+
+ 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 nTxFee = nValueIn - GetValueOut();
+ int64_t nTxFee = nValueIn - GetValueOut();
if (nTxFee < 0)
return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
- // ppcoin: enforce transaction fees for every block
- if (nTxFee < GetMinFee())
- return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
+
nFees += nTxFee;
if (!MoneyRange(nFees))
return DoS(100, error("ConnectInputs() : nFees out of range"));
// Take over previous transactions' spent pointers
{
LOCK(mempool.cs);
- int64 nValueIn = 0;
+ int64_t nValueIn = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
// Get prev tx from single transactions in memory
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i, true, 0))
- return error("ConnectInputs() : VerifySignature failed");
+ if (!VerifySignature(txPrev, *this, i, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, 0))
+ return error("ClientConnectInputs() : VerifySignature failed");
///// this is redundant with the mempool.mapNextTx stuff,
///// not sure which I want to get rid of
return true;
}
-bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
+static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
+
+void ThreadScriptCheck(void*) {
+ vnThreadsRunning[THREAD_SCRIPTCHECK]++;
+ RenameThread("novacoin-scriptch");
+ scriptcheckqueue.Thread();
+ vnThreadsRunning[THREAD_SCRIPTCHECK]--;
+}
+
+void ThreadScriptCheckQuit() {
+ scriptcheckqueue.Quit();
+}
+
+bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{
- // Check it again in case a previous version let a bad block in
- if (!CheckBlock())
+ // Check it again in case a previous version let a bad block in, but skip BlockSig checking
+ if (!CheckBlock(!fJustCheck, !fJustCheck, false))
return false;
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// being sent to another address.
// See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
- // already refuses previously-known transaction id's entirely.
- // This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC.
- // On testnet it is enabled as of februari 20, 2012, 0:00 UTC.
- if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000))
+ // already refuses previously-known transaction ids entirely.
+ // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC.
+ // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
+ // two in the chain that violate it. This prevents exploiting the issue against nodes in their
+ // initial block download.
+ bool fEnforceBIP30 = true; // Always active in NovaCoin
+ bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
+
+ //// issue here: it doesn't know the version
+ unsigned int nTxPos;
+ if (fJustCheck)
+ // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator
+ // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from
+ nTxPos = 1;
+ else
+ nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
+
+ map<uint256, CTxIndex> mapQueuedChanges;
+ CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
+
+ int64_t nFees = 0;
+ int64_t nValueIn = 0;
+ int64_t nValueOut = 0;
+ unsigned int nSigOps = 0;
+ BOOST_FOREACH(CTransaction& tx, vtx)
{
- BOOST_FOREACH(CTransaction& tx, vtx)
- {
+ uint256 hashTx = tx.GetHash();
+
+ if (fEnforceBIP30) {
CTxIndex txindexOld;
- if (txdb.ReadTxIndex(tx.GetHash(), txindexOld))
- {
+ if (txdb.ReadTxIndex(hashTx, txindexOld)) {
BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
if (pos.IsNull())
return false;
}
}
- }
-
- // BIP16 didn't become active until Apr 1 2012 (Feb 15 on testnet)
- int64 nBIP16SwitchTime = fTestNet ? 1329264000 : 1333238400;
- bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
-
- //// issue here: it doesn't know the version
- unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
- map<uint256, CTxIndex> mapQueuedChanges;
- int64 nFees = 0;
- unsigned int nSigOps = 0;
- BOOST_FOREACH(CTransaction& tx, vtx)
- {
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
- nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
+ if (!fJustCheck)
+ nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
- if (!tx.IsCoinBase())
+ if (tx.IsCoinBase())
+ nValueOut += tx.GetValueOut();
+ else
{
bool fInvalid;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
return false;
- if (fStrictPayToScriptHash)
- {
- // Add in sigops done by pay-to-script-hash inputs;
- // this is to prevent a "rogue miner" from creating
- // an incredibly-expensive-to-validate block.
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
- if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
- }
-
+ // Add in sigops done by pay-to-script-hash inputs;
+ // this is to prevent a "rogue miner" from creating
+ // an incredibly-expensive-to-validate block.
+ nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ if (nSigOps > MAX_BLOCK_SIGOPS)
+ return DoS(100, error("ConnectBlock() : too many sigops"));
+
+ int64_t nTxValueIn = tx.GetValueIn(mapInputs);
+ int64_t nTxValueOut = tx.GetValueOut();
+ nValueIn += nTxValueIn;
+ nValueOut += nTxValueOut;
if (!tx.IsCoinStake())
- nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ nFees += nTxValueIn - nTxValueOut;
+
+ unsigned int nFlags = SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH;
- if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ 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, nFlags, nScriptCheckThreads ? &vChecks : NULL))
return false;
+ control.Add(vChecks);
}
- mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size());
+ mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
+ }
+
+ if (!control.Wait())
+ return DoS(100, false);
+
+ if (IsProofOfWork())
+ {
+ int64_t nBlockReward = GetProofOfWorkReward(nBits, nFees);
+
+ // Check coinbase reward
+ if (vtx[0].GetValueOut() > nBlockReward)
+ return error("CheckBlock() : coinbase reward exceeded (actual=%" PRId64 " vs calculated=%" PRId64 ")",
+ vtx[0].GetValueOut(),
+ nBlockReward);
}
+ // track money supply and mint amount info
+ pindex->nMint = nValueOut - nValueIn + nFees;
+ pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn;
+ if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex)))
+ return error("Connect() : WriteBlockIndex for pindex failed");
+
+ // fees are not collected by proof-of-stake miners
+ // fees are destroyed to compensate the entire network
+ if (fDebug && IsProofOfStake() && GetBoolArg("-printcreation"))
+ printf("ConnectBlock() : destroy=%s nFees=%" PRId64 "\n", FormatMoney(nFees).c_str(), nFees);
+
+ if (fJustCheck)
+ return true;
+
// Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
return error("ConnectBlock() : UpdateTxIndex failed");
}
- // ppcoin: fees are not collected by miners as in bitcoin
- // ppcoin: fees are destroyed to compensate the entire network
- if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
- return false;
- if (fDebug && GetBoolArg("-printcreation"))
- printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
-
// Update block index on disk without changing it in memory.
// The memory index structure will be changed after the db commits.
if (pindex->pprev)
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
+
return true;
}
-bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
printf("REORGANIZE\n");
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
- printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
- printf("REORGANIZE: Connect %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Disconnect %" PRIszu " blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %" PRIszu " blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
// Disconnect shorter branch
vector<CTransaction> vResurrect;
if (!block.ConnectBlock(txdb, pindex))
{
// Invalid block
- txdb.TxnAbort();
return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
}
-static void
-runCommand(std::string strCommand)
-{
- int nErr = ::system(strCommand.c_str());
- if (nErr)
- printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
-}
-
// Called from inside SetBestChain: attaches a block to the new best chain being built
bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
{
if (!txdb.TxnBegin())
return error("SetBestChain() : TxnBegin failed");
- if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
+ if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
{
txdb.WriteHashBestChain(hash);
if (!txdb.TxnCommit())
}
if (!vpindexSecondary.empty())
- printf("Postponing %i reconnects\n", vpindexSecondary.size());
+ printf("Postponing %" PRIszu " reconnects\n", vpindexSecondary.size());
// Switch to new best branch
if (!Reorganize(txdb, pindexIntermediate))
return error("SetBestChain() : Reorganize failed");
}
- // Connect futher blocks
+ // Connect further blocks
BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
{
CBlock block;
// New best block
hashBestChain = hash;
pindexBest = pindexNew;
+ pblockindexFBBHLast = NULL;
nBestHeight = pindexBest->nHeight;
nBestChainTrust = pindexNew->nChainTrust;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
+
+ uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
+
+ printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%" PRId64 " date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ CBigNum(nBestChainTrust).ToString().c_str(),
+ nBestBlockTrust.Get64(),
+ DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
+
+ // Check the version of the last 100 blocks to see if we need to upgrade:
+ if (!fIsInitialDownload)
+ {
+ int nUpgraded = 0;
+ const CBlockIndex* pindex = pindexBest;
+ for (int i = 0; i < 100 && pindex != NULL; i++)
+ {
+ if (pindex->nVersion > CBlock::CURRENT_VERSION)
+ ++nUpgraded;
+ pindex = pindex->pprev;
+ }
+ if (nUpgraded > 0)
+ printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION);
+ if (nUpgraded > 100/2)
+ // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
+ strMiscWarning = _("Warning: This version is obsolete, upgrade required!");
+ }
std::string strCmd = GetArg("-blocknotify", "");
return true;
}
-
-// ppcoin: coinstake must meet hash target according to the protocol:
-// input 0 must meet the formula
-// hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
-// this ensures that the chance of getting a coinstake is proportional to the
-// amount of coin age one owns.
-// The reason this hash is chosen is the following:
-// nBits: encodes all past block timestamps, making computing hash in advance
-// more difficult
-// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
-// generate transaction for future advantage
-// txPrev.offset: offset of txPrev inside block, to reduce the chance of
-// nodes generating coinstake at the same time
-// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
-// time
-// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
-// generating coinstake at the same time
-// block/tx hash should not be used here as they can be generated in vast
-// quantities so as to generate blocks faster, degrading the system back into
-// a proof-of-work situation.
-//
-bool CTransaction::CheckProofOfStake(unsigned int nBits) const
-{
- CBigNum bnTargetPerCoinDay;
- bnTargetPerCoinDay.SetCompact(nBits);
-
- if (!IsCoinStake())
- return true;
-
- // Input 0 must match the stake hash target per coin age (nBits)
- const CTxIn& txin = vin[0];
-
- // First try finding the previous transaction in database
- CTxDB txdb("r");
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
- return false; // previous transaction not in main chain
- txdb.Close();
- if (nTime < txPrev.nTime)
- return false; // Transaction timestamp violation
-
- // Verify signature
- if (!VerifySignature(txPrev, *this, 0, true, 0))
- return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str()));
-
- // Read block header
- CBlock block;
- if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- return false; // unable to read block of previous transaction
- if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
- return false; // only count coins meeting min age requirement
-
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
- CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
- // Calculate hash
- CDataStream ss(SER_GETHASH, 0);
- ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
- if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
- return true;
- else
- return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str()));
-}
-
// ppcoin: total coin age spent in transaction, in the unit of coin-days.
// Only those coins meeting minimum age requirement counts. As those
// transactions not in main chain are not currently indexed so we
// guaranteed to be in main chain by sync-checkpoint. This rule is
// introduced to help nodes establish a consistent view of the coin
// age (trust score) of competing branches.
-bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const
+bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const
{
CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds
nCoinAge = 0;
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
return false; // unable to read block of previous transaction
- if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+ if (block.GetBlockTime() + nStakeMinAge > nTime)
continue; // only count coins meeting min age requirement
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue;
bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT;
if (fDebug && GetBoolArg("-printcoinage"))
- printf("coin age nValueIn=%-12I64d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str());
+ 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);
+ CBigNum bnCoinDay = bnCentSecond * CENT / COIN / nOneDay;
if (fDebug && GetBoolArg("-printcoinage"))
printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str());
nCoinAge = bnCoinDay.getuint64();
}
// ppcoin: total coin age spent in block, in the unit of coin-days.
-bool CBlock::GetCoinAge(uint64& nCoinAge) const
+bool CBlock::GetCoinAge(uint64_t& nCoinAge) const
{
nCoinAge = 0;
CTxDB txdb("r");
BOOST_FOREACH(const CTransaction& tx, vtx)
{
- uint64 nTxCoinAge;
+ uint64_t nTxCoinAge;
if (tx.GetCoinAge(txdb, nTxCoinAge))
nCoinAge += nTxCoinAge;
else
if (nCoinAge == 0) // block coin age minimum 1 coin-day
nCoinAge = 1;
if (fDebug && GetBoolArg("-printcoinage"))
- printf("block coin age total nCoinDays=%"PRI64d"\n", nCoinAge);
+ printf("block coin age total nCoinDays=%" PRId64 "\n", nCoinAge);
return true;
}
-
bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
{
// Check for duplicate
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");
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
- if (pindexNew->fProofOfStake)
- setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
-
- pindexNew->phashBlock = &((*mi).first);
+ pindexNew->phashBlock = &hash;
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
}
// ppcoin: compute chain trust score
- uint64 nCoinAge;
- if (!GetCoinAge(nCoinAge))
- return error("AddToBlockIndex() : invalid transaction in block");
- pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge;
+ pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
+
+ // ppcoin: compute stake entropy bit for stake modifier
+ if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight)))
+ return error("AddToBlockIndex() : SetStakeEntropyBit() failed");
+
+ // ppcoin: record proof-of-stake hash value
+ if (pindexNew->IsProofOfStake())
+ {
+ if (!mapProofOfStake.count(hash))
+ return error("AddToBlockIndex() : hashProofOfStake not found in map");
+ pindexNew->hashProofOfStake = mapProofOfStake[hash];
+ }
+
+ // ppcoin: compute stake modifier
+ uint64_t nStakeModifier = 0;
+ bool fGeneratedStakeModifier = false;
+ if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier))
+ return error("AddToBlockIndex() : ComputeNextStakeModifier() failed");
+ pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
+ pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
+ if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum))
+ 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;
+ if (pindexNew->IsProofOfStake())
+ setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
+ pindexNew->phashBlock = &((*mi).first);
+
+ // Write to disk block index
CTxDB txdb;
if (!txdb.TxnBegin())
return false;
if (!SetBestChain(txdb, pindexNew))
return false;
- txdb.Close();
-
if (pindexNew == pindexBest)
{
// Notify UI to display prev block's coinbase if it was ours
hashPrevBestCoinBase = vtx[0].GetHash();
}
- MainFrameRepaint();
+ static int8_t counter = 0;
+ if( (++counter & 0x0F) == 0 || !IsInitialBlockDownload()) // repaint every 16 blocks if not in initial block download
+ uiInterface.NotifyBlocksChanged();
return true;
}
-bool CBlock::CheckBlock() const
+bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const
{
// 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 (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
- return DoS(50, error("CheckBlock() : proof of work failed"));
-
- // Check timestamp
- if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift)
- 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"));
- for (unsigned int i = 1; i < vtx.size(); i++)
- if (vtx[i].IsCoinBase())
- return DoS(100, error("CheckBlock() : more than one coinbase"));
- // ppcoin: only the second transaction can be the optional coinstake
- for (int i = 2; i < vtx.size(); i++)
- if (vtx[i].IsCoinStake())
- return DoS(100, error("CheckBlock() : coinstake in wrong position"));
+ if (!vtx[0].CheckTransaction())
+ return DoS(vtx[0].nDoS, error("CheckBlock() : CheckTransaction failed on coinbase"));
- // ppcoin: coinbase output should be empty if proof-of-stake block
- if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty())
- return error("CheckBlock() : coinbase output not empty for proof-of-stake block");
+ uniqueTx.insert(vtx[0].GetHash());
+ nSigOps += vtx[0].GetLegacySigOpCount();
- // Check coinbase timestamp
- if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
- return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
+ 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()
- // Check coinstake timestamp
- if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift)
- return DoS(50, error("CheckBlock() : coinstake timestamp is too early"));
+ // 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"));
- // Check transactions
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ // Coinbase output should be empty if 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"));
+
+ // 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())
+ 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
{
+ // Check proof of work matches claimed amount
+ if (fCheckPOW && !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");
+
+ // Check coinbase timestamp
+ if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime))
+ return DoS(50, error("CheckBlock() : coinbase timestamp is too late"));
+ }
+
+ // 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"));
- // ppcoin: check transaction timestamp
- if (GetBlockTime() < (int64)tx.nTime)
- return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
+
+ // 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"));
- // Check merkleroot
- if (hashMerkleRoot != BuildMerkleTree())
+ // Check merkle root
+ if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
- // ppcoin: check block signature
- if (!CheckBlockSignature())
- return DoS(100, error("CheckBlock() : bad block signature"));
-
return true;
}
// Check proof-of-work or proof-of-stake
if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
- return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
+ 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() || GetBlockTime() + nMaxClockDrift < 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)
if (!tx.IsFinal(nHeight, GetBlockTime()))
return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
- // Check that the block chain matches the known block chain up to a hardened checkpoint
+ // Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckHardened(nHeight, hash))
- return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight));
+ return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight));
- // ppcoin: check that the block satisfies synchronized checkpoint
- if (!Checkpoints::CheckSync(hash, pindexPrev))
+ bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev);
+
+ // Check that the block satisfies synchronized checkpoint
+ if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies)
return error("AcceptBlock() : rejected by synchronized checkpoint");
+ if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies)
+ strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!");
+
+ // Enforce rule that the coinbase starts with serialized block height
+ CScript 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");
return true;
}
+uint256 CBlockIndex::GetBlockTrust() const
+{
+ CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
+
+ if (bnTarget <= 0)
+ return 0;
+
+ // Return 1 for the first 12 blocks
+ if (pprev == NULL || pprev->nHeight < 12)
+ return 1;
+
+ const CBlockIndex* currentIndex = pprev;
+
+ if(IsProofOfStake())
+ {
+ CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1);
+
+ // Return 1/3 of score if parent block is not the PoW block
+ if (!pprev->IsProofOfWork())
+ return (bnNewTrust / 3).getuint256();
+
+ int nPoWCount = 0;
+
+ // Check last 12 blocks type
+ while (pprev->nHeight - currentIndex->nHeight < 12)
+ {
+ if (currentIndex->IsProofOfWork())
+ nPoWCount++;
+ currentIndex = currentIndex->pprev;
+ }
+
+ // Return 1/3 of score if less than 3 PoW blocks found
+ if (nPoWCount < 3)
+ return (bnNewTrust / 3).getuint256();
+
+ return bnNewTrust.getuint256();
+ }
+ 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 (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256();
+
+ int nPoSCount = 0;
+
+ // Check last 12 blocks type
+ while (pprev->nHeight - currentIndex->nHeight < 12)
+ {
+ if (currentIndex->IsProofOfStake())
+ nPoSCount++;
+ currentIndex = currentIndex->pprev;
+ }
+
+ // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
+ if (nPoSCount < 7)
+ return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256();
+
+ bnTarget.SetCompact(pprev->nBits);
+
+ if (bnTarget <= 0)
+ return 0;
+
+ CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1);
+
+ // Return nPoWTrust + full trust score for previous block nBits
+ return (bnPoWTrust + bnNewTrust).getuint256();
+ }
+}
+
+bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
+{
+ unsigned int nFound = 0;
+ for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++)
+ {
+ if (pstart->nVersion >= minVersion)
+ ++nFound;
+ pstart = pstart->pprev;
+ }
+ return (nFound >= nRequired);
+}
+
+bool static ReserealizeBlockSignature(CBlock* pblock)
+{
+ if (pblock->IsProofOfWork())
+ {
+ pblock->vchBlockSig.clear();
+ return true;
+ }
+
+ return CKey::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
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 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 (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
- return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
+ if (pblock->IsProofOfStake())
+ {
+ uint256 hashProofOfStake = 0, targetProofOfStake = 0;
+ if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake))
+ {
+ printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
+ return false; // do not error here as we expect this during initial block download
+ }
+ if (!mapProofOfStake.count(hash)) // add to mapProofOfStake
+ mapProofOfStake.insert(make_pair(hash, hashProofOfStake));
+ }
CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
- int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
+ int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
- bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime));
+
+ if (pblock->IsProofOfStake())
+ bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime));
+ else
+ bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime));
if (bnNewBlock > 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));
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
// ppcoin: getblocks may not obtain the ancestor block rejected
// earlier by duplicate-stake check so we ask for it again directly
- pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+ if (!IsInitialBlockDownload())
+ pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
}
return true;
}
return true;
}
-// ppcoin: sign block
-bool CBlock::SignBlock(const CKeyStore& keystore)
-{
- vector<valtype> vSolutions;
- txnouttype whichType;
- const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
- if (whichType == TX_PUBKEY)
- {
- // Sign
- const valtype& vchPubKey = vSolutions[0];
- CKey key;
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- return false;
- if (key.GetPubKey() != vchPubKey)
- return false;
- return key.Sign(GetHash(), vchBlockSig);
- }
- return false;
-}
-
// ppcoin: check block signature
bool CBlock::CheckBlockSignature() const
{
- if (GetHash() == hashGenesisBlock)
- return vchBlockSig.empty();
+ if (vchBlockSig.empty())
+ return false;
- vector<valtype> vSolutions;
txnouttype whichType;
- const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+ vector<valtype> vSolutions;
+ if (!Solver(vtx[1].vout[1].scriptPubKey, whichType, vSolutions))
return false;
+
if (whichType == TX_PUBKEY)
{
- const valtype& vchPubKey = vSolutions[0];
+ valtype& vchPubKey = vSolutions[0];
CKey key;
if (!key.SetPubKey(vchPubKey))
return false;
- if (vchBlockSig.empty())
- return false;
return key.Verify(GetHash(), vchBlockSig);
}
+
return false;
}
-
-
-
-
-
-bool CheckDiskSpace(uint64 nAdditionalBytes)
+bool CheckDiskSpace(uint64_t nAdditionalBytes)
{
- uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
+ uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
- // Check for 15MB because database could create another 10MB log file at any time
- if (nFreeBytesAvailable < (uint64)15000000 + nAdditionalBytes)
+ // Check for nMinDiskSpace bytes (currently 50MB)
+ if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
{
fShutdown = true;
- string strMessage = _("Warning: Disk space is low");
+ string strMessage = _("Warning: Disk space is low!");
strMiscWarning = strMessage;
printf("*** %s\n", strMessage.c_str());
- ThreadSafeMessageBox(strMessage, "PPCoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
+ uiInterface.ThreadSafeMessageBox(strMessage, "NovaCoin", CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
StartShutdown();
return false;
}
return true;
}
+static filesystem::path BlockFilePath(unsigned int nFile)
+{
+ string strBlockFn = strprintf("blk%04u.dat", nFile);
+ return GetDataDir() / strBlockFn;
+}
+
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
- if (nFile == -1)
+ if ((nFile < 1) || (nFile == std::numeric_limits<uint32_t>::max()))
return NULL;
- FILE* file = fopen((GetDataDir() / strprintf("blk%04d.dat", nFile)).string().c_str(), pszMode);
+ FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode);
if (!file)
return NULL;
if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
FILE* AppendBlockFile(unsigned int& nFileRet)
{
nFileRet = 0;
- loop
+ while (true)
{
FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
if (!file)
return NULL;
if (fseek(file, 0, SEEK_END) != 0)
return NULL;
- // FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
- if (ftell(file) < 0x7F000000 - MAX_SIZE)
+ // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
+ if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
{
nFileRet = nCurrentBlockFile;
return file;
}
}
+void UnloadBlockIndex()
+{
+ mapBlockIndex.clear();
+ setStakeSeen.clear();
+ pindexGenesisBlock = NULL;
+ nBestHeight = 0;
+ nBestChainTrust = 0;
+ nBestInvalidTrust = 0;
+ hashBestChain = 0;
+ pindexBest = NULL;
+}
+
bool LoadBlockIndex(bool fAllowNew)
{
if (fTestNet)
{
- hashGenesisBlock = hashGenesisBlockTestNet;
- bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
- pchMessageStart[0] = 0xfa;
- pchMessageStart[1] = 0xbf;
- pchMessageStart[2] = 0xb5;
- pchMessageStart[3] = 0xda;
+ pchMessageStart[0] = 0xcd;
+ pchMessageStart[1] = 0xf2;
+ pchMessageStart[2] = 0xc0;
+ pchMessageStart[3] = 0xef;
+
+ bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet
+ 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
}
//
// Load block index
//
- CTxDB txdb("cr");
+ CTxDB txdb("cr+");
if (!txdb.LoadBlockIndex())
return false;
- txdb.Close();
//
// Init with genesis block
if (!fAllowNew)
return false;
- // Genesis Block:
- // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
- // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
- // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
- // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
- // vMerkleTree: 4a5e1e
-
// Genesis block
- const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
+
+ // MainNet:
+
+ //CBlock(hash=00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b, nTime=1360105017, nBits=1e0fffff, nNonce=1575379, vtx=1, vchBlockSig=)
+ // Coinbase(hash=4cb33b3b6a, nTime=1360105017, ver=1, vin.size=1, vout.size=1, nLockTime=0)
+ // CTxIn(COutPoint(0000000000, 4294967295), coinbase 04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936)
+ // CTxOut(empty)
+ // vMerkleTree: 4cb33b3b6a
+
+ // TestNet:
+
+ //CBlock(hash=0000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b, nTime=1360105017, nBits=1f00ffff, nNonce=46534, vtx=1, vchBlockSig=)
+ // Coinbase(hash=4cb33b3b6a, nTime=1360105017, ver=1, vin.size=1, vout.size=1, nLockTime=0)
+ // CTxIn(COutPoint(0000000000, 4294967295), coinbase 04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936)
+ // CTxOut(empty)
+ // vMerkleTree: 4cb33b3b6a
+
+ const char* pszTimestamp = "https://bitcointalk.org/index.php?topic=134179.msg1502196#msg1502196";
CTransaction txNew;
- txNew.nTime = 1339538219;
+ txNew.nTime = 1360105017;
txNew.vin.resize(1);
txNew.vout.resize(1);
- txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
+ txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
- block.nTime = 1339540307;
+ block.nTime = 1360105017;
block.nBits = bnProofOfWorkLimit.GetCompact();
- block.nNonce = 1281822831;
-
- if (fTestNet)
- {
- block.nTime = 1296688602;
- block.nBits = 0x1d07fff8;
- block.nNonce = 384568319;
- }
+ block.nNonce = !fTestNet ? 1575379 : 46534;
//// debug print
- printf("%s\n", block.GetHash().ToString().c_str());
- printf("%s\n", hashGenesisBlock.ToString().c_str());
- printf("%s\n", block.hashMerkleRoot.ToString().c_str());
- assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d"));
+ assert(block.hashMerkleRoot == uint256("0x4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b"));
block.print();
- assert(block.GetHash() == hashGenesisBlock);
+ assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
assert(block.CheckBlock());
// Start new block file
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
- // ppcoin: initialize synchronized checkpoint
- if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock))
+ // initialize synchronized checkpoint
+ if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)))
return error("LoadBlockIndex() : failed to init sync checkpoint");
+
+ // upgrade time set to zero if txdb initialized
+ {
+ if (!txdb.WriteModifierUpgradeTime(0))
+ return error("LoadBlockIndex() : failed to init upgrade info");
+ printf(" Upgrade Info: ModifierUpgradeTime txdb initialization\n");
+ }
}
- // ppcoin: if checkpoint master key changed must reset sync-checkpoint
{
- CTxDB txdb;
+ CTxDB txdb("r+");
string strPubKey = "";
if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
{
return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
if (!txdb.TxnCommit())
return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
- if (!Checkpoints::ResetSyncCheckpoint())
+ if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
return error("LoadBlockIndex() : failed to reset sync-checkpoint");
}
+
+ // upgrade time set to zero if blocktreedb initialized
+ if (txdb.ReadModifierUpgradeTime(nModifierUpgradeTime))
+ {
+ if (nModifierUpgradeTime)
+ printf(" Upgrade Info: blocktreedb upgrade detected at timestamp %d\n", nModifierUpgradeTime);
+ else
+ printf(" Upgrade Info: no blocktreedb upgrade detected.\n");
+ }
+ else
+ {
+ nModifierUpgradeTime = GetTime();
+ printf(" Upgrade Info: upgrading blocktreedb at timestamp %u\n", nModifierUpgradeTime);
+ if (!txdb.WriteModifierUpgradeTime(nModifierUpgradeTime))
+ return error("LoadBlockIndex() : failed to write upgrade info");
+ }
+
+#ifndef USE_LEVELDB
txdb.Close();
+#endif
}
return true;
void PrintBlockTree()
{
- // precompute tree structure
+ // pre-compute tree structure
map<CBlockIndex*, vector<CBlockIndex*> > mapNext;
for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
{
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (%u,%u) %s %08lx %s tx %d",
+ printf("%d (%u,%u) %s %08x %s mint %7s tx %" PRIszu "",
pindex->nHeight,
pindex->nFile,
pindex->nBlockPos,
- block.GetHash().ToString().substr(0,20).c_str(),
+ block.GetHash().ToString().c_str(),
block.nBits,
- DateTimeStrFormat(block.GetBlockTime()).c_str(),
+ DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
+ FormatMoney(pindex->nMint).c_str(),
block.vtx.size());
PrintWallets(block);
- // put the main timechain first
+ // put the main time-chain first
vector<CBlockIndex*>& vNext = mapNext[pindex];
for (unsigned int i = 0; i < vNext.size(); i++)
{
}
}
+bool LoadExternalBlockFile(FILE* fileIn)
+{
+ int64_t nStart = GetTimeMillis();
-
-
-
-
-
-
-
+ int nLoaded = 0;
+ {
+ LOCK(cs_main);
+ try {
+ CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
+ unsigned int nPos = 0;
+ while (nPos != std::numeric_limits<uint32_t>::max() && blkdat.good() && !fRequestShutdown)
+ {
+ unsigned char pchData[65536];
+ do {
+ fseek(blkdat, nPos, SEEK_SET);
+ int nRead = fread(pchData, 1, sizeof(pchData), blkdat);
+ if (nRead <= 8)
+ {
+ nPos = std::numeric_limits<uint32_t>::max();
+ break;
+ }
+ void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart));
+ if (nFind)
+ {
+ if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0)
+ {
+ nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart);
+ break;
+ }
+ nPos += ((unsigned char*)nFind - pchData) + 1;
+ }
+ else
+ nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1;
+ } while(!fRequestShutdown);
+ if (nPos == std::numeric_limits<uint32_t>::max())
+ 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;
+ }
+ }
+ }
+ }
+ catch (const std::exception&) {
+ printf("%s() : Deserialize or I/O error caught during load\n",
+ BOOST_CURRENT_FUNCTION);
+ }
+ }
+ printf("Loaded %i blocks from external file in %" PRId64 "ms\n", nLoaded, GetTimeMillis() - nStart);
+ return nLoaded > 0;
+}
//////////////////////////////////////////////////////////////////////////////
//
// CAlert
//
-map<uint256, CAlert> mapAlerts;
-CCriticalSection cs_mapAlerts;
+extern map<uint256, CAlert> mapAlerts;
+extern CCriticalSection cs_mapAlerts;
string GetWarnings(string strFor)
{
int nPriority = 0;
string strStatusBar;
string strRPC;
+
if (GetBoolArg("-testsafemode"))
strRPC = "test";
strStatusBar = strMiscWarning;
}
- // Longer invalid proof-of-work chain
- if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
+ // if detected unmet upgrade requirement enter safe mode
+ // Note: Modifier upgrade requires blockchain redownload if past protocol switch
+ if (IsFixedModifierInterval(nModifierUpgradeTime + nOneDay)) // 1 day margin
{
- nPriority = 2000;
- strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
+ nPriority = 5000;
+ strStatusBar = strRPC = "WARNING: Blockchain redownload required approaching or past v.0.4.4.6u4 upgrade deadline.";
}
+ // if detected invalid checkpoint enter safe mode
if (Checkpoints::hashInvalidCheckpoint != 0)
{
nPriority = 3000;
- strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
+ strStatusBar = strRPC = _("WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers.");
}
// Alerts
{
nPriority = alert.nPriority;
strStatusBar = alert.strStatusBar;
+ if (nPriority > 1000)
+ strRPC = strStatusBar;
}
}
}
return "error";
}
-bool CAlert::ProcessAlert()
-{
- if (!CheckSignature())
- return false;
- if (!IsInEffect())
- return false;
-
- {
- LOCK(cs_mapAlerts);
- // Cancel previous alerts
- for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
- {
- const CAlert& alert = (*mi).second;
- if (Cancels(alert))
- {
- printf("cancelling alert %d\n", alert.nID);
- mapAlerts.erase(mi++);
- }
- else if (!alert.IsInEffect())
- {
- printf("expiring alert %d\n", alert.nID);
- mapAlerts.erase(mi++);
- }
- else
- mi++;
- }
-
- // Check if this alert has been cancelled
- BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
- {
- const CAlert& alert = item.second;
- if (alert.Cancels(*this))
- {
- printf("alert already cancelled by %d\n", alert.nID);
- return false;
- }
- }
-
- // Add to mapAlerts
- mapAlerts.insert(make_pair(GetHash(), *this));
- }
-
- printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
- MainFrameRepaint();
- 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
+// 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] = { 0xf9, 0xbe, 0xb4, 0xd9 };
-
+unsigned char pchMessageStart[4] = { 0xe4, 0xe8, 0xe9, 0xe5 };
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
- static map<CService, vector<unsigned char> > mapReuseKey;
+ static map<CService, CPubKey> mapReuseKey;
RandAddSeedPerfmon();
- if (fDebug) {
- printf("%s ", DateTimeStrFormat(GetTime()).c_str());
- printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size());
- }
+ if (fDebug)
+ printf("received: %s (%" PRIszu " bytes)\n", strCommand.c_str(), vRecv.size());
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
printf("dropmessagestest DROPPING RECV MESSAGE\n");
return true;
}
-
-
-
-
if (strCommand == "version")
{
// Each connection can only send one version message
return false;
}
- int64 nTime;
+ int64_t nTime;
CAddress addrMe;
CAddress addrFrom;
- uint64 nNonce = 1;
+ uint64_t nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
if (pfrom->nVersion < MIN_PROTO_VERSION)
{
if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
+ if (pfrom->fInbound && addrMe.IsRoutable())
+ {
+ pfrom->addrLocal = addrMe;
+ SeenLocal(addrMe);
+ }
+
// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
return true;
}
- // ppcoin: record my external IP reported by peer
+ if (pfrom->nVersion < 60010)
+ {
+ printf("partner %s using a buggy client %d, disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
+ // record my external IP reported by peer
if (addrFrom.IsRoutable() && addrMe.IsRoutable())
addrSeenByPeer = addrMe;
if (!pfrom->fInbound)
{
// Advertise our address
- if (!fNoListen && !fUseProxy && addrLocalHost.IsRoutable() &&
- !IsInitialBlockDownload())
+ if (!fNoListen && !IsInitialBlockDownload())
{
- CAddress addr(addrLocalHost);
- addr.nTime = GetAdjustedTime();
- pfrom->PushAddress(addr);
+ CAddress addr = GetLocalAddress(&pfrom->addr);
+ if (addr.IsRoutable())
+ pfrom->PushAddress(addr);
}
// Get recent addresses
- if (pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
+ if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
{
pfrom->PushMessage("getaddr");
pfrom->fGetAddr = true;
// Ask the first connected node for block updates
static int nAskedForBlocks = 0;
- if (!pfrom->fClient &&
+ if (!pfrom->fClient && !pfrom->fOneShot &&
+ (pfrom->nStartingHeight > (nBestHeight - 144)) &&
(pfrom->nVersion < NOBLKS_VERSION_START ||
pfrom->nVersion >= NOBLKS_VERSION_END) &&
(nAskedForBlocks < 1 || vNodes.size() <= 1))
item.second.RelayTo(pfrom);
}
- // ppcoin: relay sync-checkpoint
+ // Relay sync-checkpoint
{
LOCK(Checkpoints::cs_hashSyncCheckpoint);
if (!Checkpoints::checkpointMessage.IsNull())
pfrom->fSuccessfullyConnected = true;
- printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
+ printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str());
cPeerBlockCounts.input(pfrom->nStartingHeight);
if (vAddr.size() > 1000)
{
pfrom->Misbehaving(20);
- return error("message addr size() = %d", vAddr.size());
+ return error("message addr size() = %" PRIszu "", vAddr.size());
}
// Store the new addresses
- int64 nNow = GetAdjustedTime();
- int64 nSince = nNow - 10 * 60;
+ vector<CAddress> vAddrOk;
+ int64_t nNow = GetAdjustedTime();
+ int64_t nSince = nNow - 10 * 60;
BOOST_FOREACH(CAddress& addr, vAddr)
{
if (fShutdown)
return true;
- // ignore IPv6 for now, since it isn't implemented anyway
- if (!addr.IsIPv4())
- continue;
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())
{
// Relay to a limited number of other nodes
static uint256 hashSalt;
if (hashSalt == 0)
hashSalt = GetRandHash();
- int64 hashAddr = addr.GetHash();
- uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
+ uint64_t hashAddr = addr.GetHash();
+ uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/nOneDay);
hashRand = Hash(BEGIN(hashRand), END(hashRand));
multimap<uint256, CNode*> mapMix;
BOOST_FOREACH(CNode* pnode, vNodes)
hashKey = Hash(BEGIN(hashKey), END(hashKey));
mapMix.insert(make_pair(hashKey, pnode));
}
- int nRelayNodes = 2;
+ 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)
((*mi).second)->PushAddress(addr);
}
}
+ // Do not store addresses outside our network
+ if (fReachable)
+ vAddrOk.push_back(addr);
}
- addrman.Add(vAddr, pfrom->addr, 2 * 60 * 60);
+ addrman.Add(vAddrOk, pfrom->addr, 2 * nOneHour);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
+ if (pfrom->fOneShot)
+ pfrom->fDisconnect = true;
}
-
else if (strCommand == "inv")
{
vector<CInv> vInv;
vRecv >> vInv;
- if (vInv.size() > 50000)
+ if (vInv.size() > MAX_INV_SZ)
{
pfrom->Misbehaving(20);
- return error("message inv size() = %d", vInv.size());
+ return error("message inv size() = %" PRIszu "", vInv.size());
}
// find last block in inv vector
- unsigned int nLastBlock = (unsigned int)(-1);
+ unsigned int nLastBlock = std::numeric_limits<uint32_t>::max();
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) {
if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) {
nLastBlock = vInv.size() - 1 - nInv;
// 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.
- std::vector<CInv> vGetData(1,inv);
pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0));
if (fDebug)
printf("force request: %s\n", inv.ToString().c_str());
{
vector<CInv> vInv;
vRecv >> vInv;
- if (vInv.size() > 50000)
+ if (vInv.size() > MAX_INV_SZ)
{
pfrom->Misbehaving(20);
- return error("message getdata size() = %d", vInv.size());
+ return error("message getdata size() = %" PRIszu "", vInv.size());
}
+ if (fDebugNet || (vInv.size() != 1))
+ printf("received getdata (%" PRIszu " invsz)\n", vInv.size());
+
BOOST_FOREACH(const CInv& inv, vInv)
{
if (fShutdown)
return true;
- printf("received getdata for: %s\n", inv.ToString().c_str());
+ if (fDebugNet || (vInv.size() == 1))
+ printf("received getdata for: %s\n", inv.ToString().c_str());
if (inv.type == MSG_BLOCK)
{
// Trigger them to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom->hashContinue)
{
- // Bypass PushInventory, this must send even if redundant,
- // and we want it right after the last block so they don't
- // wait for other stuff first.
// ppcoin: send latest proof-of-work block to allow the
// download node to accept as orphan (proof-of-stake
// block might be rejected by stake connection check)
else if (inv.IsKnownType())
{
// Send stream from relay memory
+ bool pushed = false;
{
LOCK(cs_mapRelay);
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv);
- if (mi != mapRelay.end())
+ if (mi != mapRelay.end()) {
pfrom->PushMessage(inv.GetCommand(), (*mi).second);
+ pushed = true;
+ }
+ }
+ if (!pushed && inv.type == MSG_TX) {
+ LOCK(mempool.cs);
+ if (mempool.exists(inv.hash)) {
+ CTransaction tx = mempool.lookup(inv.hash);
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ ss.reserve(1000);
+ ss << tx;
+ pfrom->PushMessage("tx", ss);
+ }
}
}
// Send the rest of the chain
if (pindex)
pindex = pindex->pnext;
- int nLimit = 500 + locator.GetDistanceBack();
- unsigned int nBytes = 0;
+ int nLimit = 500;
printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit);
for (; pindex; pindex = pindex->pnext)
{
if (pindex->GetBlockHash() == hashStop)
{
- printf(" getblocks stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes);
+ printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ // ppcoin: tell downloading node about the latest block if it's
+ // without risk being rejected due to stake connection check
+ if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime())
+ pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain));
break;
}
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
- CBlock block;
- block.ReadFromDisk(pindex, true);
- nBytes += block.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION);
- if (--nLimit <= 0 || nBytes >= SendBufferSize()/2)
+ if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll make them
// getblocks the next batch of inventory.
- printf(" getblocks stopping at limit %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes);
+ printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str());
pfrom->hashContinue = pindex->GetBlockHash();
break;
}
}
}
+ else if (strCommand == "checkpoint")
+ {
+ CSyncCheckpoint checkpoint;
+ vRecv >> checkpoint;
+ if (checkpoint.ProcessSyncCheckpoint(pfrom))
+ {
+ // Relay
+ pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ checkpoint.RelayTo(pnode);
+ }
+ }
else if (strCommand == "getheaders")
{
if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
{
SyncWithWallets(tx, NULL, true);
- RelayMessage(inv, vMsg);
+ RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
- for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
- const CDataStream& vMsg = *((*mi).second);
- CTransaction tx;
- CDataStream(vMsg) >> tx;
- CInv inv(MSG_TX, tx.GetHash());
+ const uint256& orphanTxHash = *mi;
+ CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash];
bool fMissingInputs2 = false;
- if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
+ if (orphanTx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
{
- printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+ printf(" accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
SyncWithWallets(tx, NULL, true);
- RelayMessage(inv, vMsg);
- mapAlreadyAskedFor.erase(inv);
- vWorkQueue.push_back(inv.hash);
- vEraseQueue.push_back(inv.hash);
+ RelayTransaction(orphanTx, orphanTxHash);
+ mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash));
+ vWorkQueue.push_back(orphanTxHash);
+ vEraseQueue.push_back(orphanTxHash);
}
else if (!fMissingInputs2)
{
// invalid orphan
- vEraseQueue.push_back(inv.hash);
- printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+ vEraseQueue.push_back(orphanTxHash);
+ printf(" removed invalid orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
}
}
}
}
else if (fMissingInputs)
{
- AddOrphanTx(vMsg);
+ AddOrphanTx(tx);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
{
CBlock block;
vRecv >> block;
+ uint256 hashBlock = block.GetHash();
- printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str());
+ printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str());
// block.print();
- CInv inv(MSG_BLOCK, block.GetHash());
+ CInv inv(MSG_BLOCK, hashBlock);
pfrom->AddInventoryKnown(inv);
if (ProcessBlock(pfrom, &block))
}
- 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 * nOneDay);
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
BOOST_FOREACH(const CAddress &addr, vAddr)
- pfrom->PushAddress(addr);
+ if(addr.nTime > nCutOff)
+ pfrom->PushAddress(addr);
+ }
+
+
+ else if (strCommand == "mempool")
+ {
+ std::vector<uint256> vtxid;
+ mempool.queryHashes(vtxid);
+ vector<CInv> vInv;
+ for (unsigned int i = 0; i < vtxid.size(); i++) {
+ CInv inv(MSG_TX, vtxid[i]);
+ vInv.push_back(inv);
+ if (i == (MAX_INV_SZ - 1))
+ break;
+ }
+ if (vInv.size() > 0)
+ pfrom->PushMessage("inv", vInv);
}
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);
}
else if (strCommand == "ping")
{
- if (pfrom->nVersion > BIP0031_VERSION)
- {
- uint64 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;
- if (alert.ProcessAlert())
+ uint256 alertHash = alert.GetHash();
+ if (pfrom->setKnown.count(alertHash) == 0)
{
- // Relay
- pfrom->setKnown.insert(alert.GetHash());
+ if (alert.ProcessAlert())
{
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- alert.RelayTo(pnode);
+ // Relay
+ pfrom->setKnown.insert(alertHash);
+ {
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ alert.RelayTo(pnode);
+ }
+ }
+ else {
+ // Small DoS penalty so peers that send us lots of
+ // duplicate/expired/invalid-signature/whatever alerts
+ // eventually get banned.
+ // This isn't a Misbehaving(100) (immediate ban) because the
+ // peer might be an older or different implementation with
+ // a different signature key, etc.
+ pfrom->Misbehaving(10);
}
}
}
- else if (strCommand == "checkpoint")
- {
- CSyncCheckpoint checkpoint;
- vRecv >> checkpoint;
-
- if (checkpoint.ProcessSyncCheckpoint(pfrom))
- {
- // Relay
- pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- checkpoint.RelayTo(pnode);
- }
- }
else
{
// (x) data
//
- loop
+ while (true)
{
+ // 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));
int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
break;
}
if (pstart - vRecv.begin() > 0)
- printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin());
+ printf("\n\nPROCESSMESSAGE SKIPPED %" PRIpdd " BYTES\n\n", pstart - vRecv.begin());
vRecv.erase(vRecv.begin(), pstart);
// Read header
{
if (strstr(e.what(), "end of data"))
{
- // Allow exceptions from underlength message on vRecv
+ // Allow exceptions from under-length message on vRecv
printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what());
}
else if (strstr(e.what(), "size too large"))
{
- // Allow exceptions from overlong size
+ // Allow exceptions from over-long size
printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what());
}
else
// 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()) {
- uint64 nonce = 0;
- if (pto->nVersion > BIP0031_VERSION)
- pto->PushMessage("ping", nonce);
- else
- pto->PushMessage("ping");
+ if (pto->nLastSend && GetTime() - pto->nLastSend > nPingInterval && pto->vSend.empty()) {
+ uint64_t nonce = 0;
+ pto->PushMessage("ping", nonce);
+ }
+
+ // Start block sync
+ if (pto->fStartSync) {
+ pto->fStartSync = false;
+ pto->PushGetBlocks(pindexBest, uint256(0));
}
// Resend wallet transactions that haven't gotten in a block yet
ResendWalletTransactions();
// Address refresh broadcast
- static int64 nLastRebroadcast;
- if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
+ static int64_t nLastRebroadcast;
+ if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > nBroadcastInterval))
{
{
LOCK(cs_vNodes);
pnode->setAddrKnown.clear();
// Rebroadcast our address
- if (!fNoListen && !fUseProxy && addrLocalHost.IsRoutable())
+ if (!fNoListen)
{
- CAddress addr(addrLocalHost);
- addr.nTime = GetAdjustedTime();
- pnode->PushAddress(addr);
+ CAddress addr = GetLocalAddress(&pnode->addr);
+ if (addr.IsRoutable())
+ pnode->PushAddress(addr);
}
}
}
// Message: getdata
//
vector<CInv> vGetData;
- int64 nNow = GetTime() * 1000000;
+ int64_t nNow = GetTime() * 1000000;
CTxDB txdb("r");
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
if (!AlreadyHave(txdb, inv))
{
- printf("sending getdata: %s\n", inv.ToString().c_str());
+ if (fDebugNet)
+ printf("sending getdata: %s\n", inv.ToString().c_str());
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
vGetData.clear();
}
+ mapAlreadyAskedFor[inv] = nNow;
}
- mapAlreadyAskedFor[inv] = nNow;
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
}
-
-
-
-
-
-
-
-
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// BitcoinMiner
-//
-
-int static FormatHashBlocks(void* pbuffer, unsigned int len)
-{
- unsigned char* pdata = (unsigned char*)pbuffer;
- unsigned int blocks = 1 + ((len + 8) / 64);
- unsigned char* pend = pdata + 64 * blocks;
- memset(pdata + len, 0, 64 * blocks - len);
- pdata[len] = 0x80;
- unsigned int bits = len * 8;
- pend[-1] = (bits >> 0) & 0xff;
- pend[-2] = (bits >> 8) & 0xff;
- pend[-3] = (bits >> 16) & 0xff;
- pend[-4] = (bits >> 24) & 0xff;
- return blocks;
-}
-
-static const unsigned int pSHA256InitState[8] =
-{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
-
-void SHA256Transform(void* pstate, void* pinput, const void* pinit)
-{
- SHA256_CTX ctx;
- unsigned char data[64];
-
- SHA256_Init(&ctx);
-
- for (int i = 0; i < 16; i++)
- ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
-
- for (int i = 0; i < 8; i++)
- ctx.h[i] = ((uint32_t*)pinit)[i];
-
- SHA256_Update(&ctx, data, sizeof(data));
- for (int i = 0; i < 8; i++)
- ((uint32_t*)pstate)[i] = ctx.h[i];
-}
-
-//
-// ScanHash scans nonces looking for a hash with at least some zero bits.
-// It operates on big endian data. Caller does the byte reversing.
-// All input buffers are 16-byte aligned. nNonce is usually preserved
-// between calls, but periodically or if nNonce is 0xffff0000 or above,
-// the block is rebuilt and nNonce starts over at zero.
-//
-unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
-{
- unsigned int& nNonce = *(unsigned int*)(pdata + 12);
- for (;;)
- {
- // Crypto++ SHA-256
- // Hash pdata using pmidstate as the starting state into
- // preformatted buffer phash1, then hash phash1 into phash
- nNonce++;
- SHA256Transform(phash1, pdata, pmidstate);
- SHA256Transform(phash, phash1, pSHA256InitState);
-
- // Return the nonce if the hash has at least some zero bits,
- // caller will check if it has enough to reach the target
- if (((unsigned short*)phash)[14] == 0)
- return nNonce;
-
- // If nothing found after trying for a while, return -1
- if ((nNonce & 0xffff) == 0)
- {
- nHashesDone = 0xffff+1;
- return (unsigned int) -1;
- }
- }
-}
-
-// Some explaining would be appreciated
-class COrphan
+class CMainCleanup
{
public:
- CTransaction* ptx;
- set<uint256> setDependsOn;
- double dPriority;
-
- COrphan(CTransaction* ptxIn)
- {
- ptx = ptxIn;
- dPriority = 0;
- }
-
- void print() const
- {
- printf("COrphan(hash=%s, dPriority=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority);
- BOOST_FOREACH(uint256 hash, setDependsOn)
- printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str());
- }
-};
-
-
-uint64 nLastBlockTx = 0;
-uint64 nLastBlockSize = 0;
-
-CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
-{
- CReserveKey reservekey(pwallet);
-
- // Create new block
- auto_ptr<CBlock> pblock(new CBlock());
- if (!pblock.get())
- return NULL;
-
- // Create coinbase tx
- CTransaction txNew;
- txNew.vin.resize(1);
- txNew.vin[0].prevout.SetNull();
- txNew.vout.resize(1);
- txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
-
- // Add our coinbase tx as first transaction
- pblock->vtx.push_back(txNew);
-
- // ppcoin: if coinstake available add coinstake tx
- static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60; // only initialized at startup
- CBlockIndex* pindexPrev = pindexBest;
-
- if (!fProofOfWorkOnly)
- {
- while (nLastCoinStakeCheckTime < GetAdjustedTime())
- {
- pindexPrev = pindexBest; // get best block again to avoid getting stale
- pblock->nBits = GetNextTargetRequired(pindexPrev, true);
- CTransaction txCoinStake;
- {
- static CCriticalSection cs;
- LOCK(cs);
- // mining may have been suspended for a while so
- // need to take max to satisfy the timestamp protocol
- nLastCoinStakeCheckTime++;
- nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
- txCoinStake.nTime = nLastCoinStakeCheckTime;
- }
- if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
- {
- pblock->vtx.push_back(txCoinStake);
- pblock->vtx[0].vout[0].SetEmpty();
- break;
- }
- }
- }
-
- pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
-
- // Collect memory pool transactions into the block
- int64 nFees = 0;
- {
- LOCK2(cs_main, mempool.cs);
- CTxDB txdb("r");
-
- // Priority order to process transactions
- list<COrphan> vOrphan; // list memory doesn't move
- map<uint256, vector<COrphan*> > mapDependers;
- multimap<double, CTransaction*> mapPriority;
- for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
- {
- CTransaction& tx = (*mi).second;
- if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
- continue;
-
- COrphan* porphan = NULL;
- double dPriority = 0;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- // Read prev transaction
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
- {
- // Has to wait for dependencies
- if (!porphan)
- {
- // Use list for automatic deletion
- vOrphan.push_back(COrphan(&tx));
- porphan = &vOrphan.back();
- }
- mapDependers[txin.prevout.hash].push_back(porphan);
- porphan->setDependsOn.insert(txin.prevout.hash);
- continue;
- }
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
-
- // Read block header
- int nConf = txindex.GetDepthInMainChain();
-
- dPriority += (double)nValueIn * nConf;
-
- if (fDebug && GetBoolArg("-printpriority"))
- printf("priority nValueIn=%-12"PRI64d" nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
- }
-
- // Priority is sum(valuein * age) / txsize
- dPriority /= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
-
- if (porphan)
- porphan->dPriority = dPriority;
- else
- mapPriority.insert(make_pair(-dPriority, &(*mi).second));
-
- if (fDebug && GetBoolArg("-printpriority"))
- {
- printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
- if (porphan)
- porphan->print();
- printf("\n");
- }
- }
-
- // Collect transactions into block
- map<uint256, CTxIndex> mapTestPool;
- uint64 nBlockSize = 1000;
- uint64 nBlockTx = 0;
- int nBlockSigOps = 100;
- while (!mapPriority.empty())
- {
- // Take highest priority transaction off priority queue
- CTransaction& tx = *(*mapPriority.begin()).second;
- mapPriority.erase(mapPriority.begin());
-
- // Size limits
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
- continue;
-
- // Legacy limits on sigOps:
- unsigned int nTxSigOps = tx.GetLegacySigOpCount();
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- // Timestamp limit
- if (tx.nTime > GetAdjustedTime())
- continue;
-
- // ppcoin: simplify transaction fee - allow free = false
- int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
-
- // Connecting shouldn't fail due to dependency on other memory pool transactions
- // because we're already processing them in order of dependency
- map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
- MapPrevTx mapInputs;
- bool fInvalid;
- if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
- continue;
-
- int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
- if (nTxFees < nMinFee)
- continue;
-
- nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
- continue;
- mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
- swap(mapTestPool, mapTestPoolTmp);
-
- // Added
- pblock->vtx.push_back(tx);
- nBlockSize += nTxSize;
- ++nBlockTx;
- nBlockSigOps += nTxSigOps;
- nFees += nTxFees;
-
- // Add transactions that depend on this one to the priority queue
- uint256 hash = tx.GetHash();
- if (mapDependers.count(hash))
- {
- BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
- {
- if (!porphan->setDependsOn.empty())
- {
- porphan->setDependsOn.erase(hash);
- if (porphan->setDependsOn.empty())
- mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
- }
- }
- }
- }
-
- nLastBlockTx = nBlockTx;
- nLastBlockSize = nBlockSize;
- printf("CreateNewBlock(): total size %lu\n", nBlockSize);
-
- }
- if (pblock->IsProofOfWork())
- pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
-
- // Fill in header
- pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- pblock->hashMerkleRoot = pblock->BuildMerkleTree();
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
- pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
- pblock->UpdateTime(pindexPrev);
- pblock->nNonce = 0;
-
- return pblock.release();
-}
-
-
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
-{
- // Update nExtraNonce
- static uint256 hashPrevBlock;
- if (hashPrevBlock != pblock->hashPrevBlock)
- {
- nExtraNonce = 0;
- hashPrevBlock = pblock->hashPrevBlock;
- }
- ++nExtraNonce;
- pblock->vtx[0].vin[0].scriptSig = (CScript() << pblock->nTime << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
- assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
-
- pblock->hashMerkleRoot = pblock->BuildMerkleTree();
-}
-
-
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
-{
- //
- // Prebuild hash buffers
- //
- struct
- {
- struct unnamed2
- {
- int nVersion;
- uint256 hashPrevBlock;
- uint256 hashMerkleRoot;
- unsigned int nTime;
- unsigned int nBits;
- unsigned int nNonce;
- }
- block;
- unsigned char pchPadding0[64];
- uint256 hash1;
- unsigned char pchPadding1[64];
- }
- tmp;
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.block.nVersion = pblock->nVersion;
- tmp.block.hashPrevBlock = pblock->hashPrevBlock;
- tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
- tmp.block.nTime = pblock->nTime;
- tmp.block.nBits = pblock->nBits;
- tmp.block.nNonce = pblock->nNonce;
-
- FormatHashBlocks(&tmp.block, sizeof(tmp.block));
- FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
-
- // Byte swap all the input buffer
- for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
- ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
-
- // Precalc the first half of the first hash, which stays constant
- SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
-
- memcpy(pdata, &tmp.block, 128);
- memcpy(phash1, &tmp.hash1, 64);
-}
-
-
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
-{
- uint256 hash = pblock->GetHash();
- uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
-
- if (hash > hashTarget && pblock->IsProofOfWork())
- return error("BitcoinMiner : proof-of-work not meeting target");
-
- //// debug print
- printf("BitcoinMiner:\n");
- printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
- pblock->print();
- printf("%s ", DateTimeStrFormat(GetTime()).c_str());
- printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
-
- // Found a solution
- {
- LOCK(cs_main);
- if (pblock->hashPrevBlock != hashBestChain)
- return error("BitcoinMiner : generated block is stale");
-
- // Remove key from key pool
- reservekey.KeepKey();
-
- // Track how many getdata requests this block gets
- {
- LOCK(wallet.cs_wallet);
- wallet.mapRequestCount[pblock->GetHash()] = 0;
- }
-
- // Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock))
- return error("BitcoinMiner : ProcessBlock, block not accepted");
- }
-
- return true;
-}
-
-void static ThreadBitcoinMiner(void* parg);
-
-static bool fGenerateBitcoins = false;
-static bool fLimitProcessors = false;
-static int nLimitProcessors = -1;
-
-void static BitcoinMiner(CWallet *pwallet)
-{
- printf("BitcoinMiner started\n");
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
-
- // Each thread has its own key and counter
- CReserveKey reservekey(pwallet);
- unsigned int nExtraNonce = 0;
-
- while (fGenerateBitcoins)
- {
- if (fShutdown)
- return;
- while (vNodes.empty() || IsInitialBlockDownload())
- {
- Sleep(1000);
- if (fShutdown)
- return;
- if (!fGenerateBitcoins)
- return;
- }
-
-
- //
- // Create new block
- //
- unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
- CBlockIndex* pindexPrev = pindexBest;
-
- auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
- if (!pblock.get())
- return;
-
- IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
-
- // ppcoin: if proof-of-stake block found then process block
- if (pblock->IsProofOfStake())
- {
- if (!pblock->SignBlock(*pwalletMain))
- {
- error("BitcoinMiner: Unable to sign new proof-of-stake block");
- return;
- }
- printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CheckWork(pblock.get(), *pwalletMain, reservekey);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- continue;
- }
-
- printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
-
-
- //
- // Prebuild hash buffers
- //
- char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf);
- char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
- char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
-
- FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1);
-
- unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
- unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
-
-
- //
- // Search
- //
- int64 nStart = GetTime();
- uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- uint256 hashbuf[2];
- uint256& hash = *alignup<16>(hashbuf);
- loop
- {
- unsigned int nHashesDone = 0;
- unsigned int nNonceFound;
-
- // Crypto++ SHA-256
- nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1,
- (char*)&hash, nHashesDone);
-
- // Check if something found
- if (nNonceFound != (unsigned int) -1)
- {
- for (unsigned int i = 0; i < sizeof(hash)/4; i++)
- ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);
-
- if (hash <= hashTarget)
- {
- // Found a solution
- pblock->nNonce = ByteReverse(nNonceFound);
- assert(hash == pblock->GetHash());
- if (!pblock->SignBlock(*pwalletMain))
- {
- error("BitcoinMiner: Unable to sign new proof-of-work block");
- return;
- }
-
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CheckWork(pblock.get(), *pwalletMain, reservekey);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- break;
- }
- }
-
- // Meter hashes/sec
- static int64 nHashCounter;
- if (nHPSTimerStart == 0)
- {
- nHPSTimerStart = GetTimeMillis();
- nHashCounter = 0;
- }
- else
- nHashCounter += nHashesDone;
- if (GetTimeMillis() - nHPSTimerStart > 4000)
- {
- static CCriticalSection cs;
- {
- LOCK(cs);
- if (GetTimeMillis() - nHPSTimerStart > 4000)
- {
- dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
- nHPSTimerStart = GetTimeMillis();
- nHashCounter = 0;
- static int64 nLogTime;
- if (GetTime() - nLogTime > 30 * 60)
- {
- nLogTime = GetTime();
- printf("%s ", DateTimeStrFormat(GetTime()).c_str());
- printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0);
- }
- }
- }
- }
-
- // Check for stop or if block needs to be rebuilt
- if (fShutdown)
- return;
- if (!fGenerateBitcoins)
- return;
- if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors)
- return;
- if (vNodes.empty())
- break;
- if (nBlockNonce >= 0xffff0000)
- break;
- if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
- break;
- if (pindexPrev != pindexBest)
- break;
-
- // Update nTime every few seconds
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
- pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
- pblock->UpdateTime(pindexPrev);
- nBlockTime = ByteReverse(pblock->nTime);
- if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift)
- break; // need to update coinbase timestamp
- }
- }
-}
-
-void static ThreadBitcoinMiner(void* parg)
-{
- CWallet* pwallet = (CWallet*)parg;
- try
- {
- vnThreadsRunning[THREAD_MINER]++;
- BitcoinMiner(pwallet);
- vnThreadsRunning[THREAD_MINER]--;
- }
- catch (std::exception& e) {
- vnThreadsRunning[THREAD_MINER]--;
- PrintException(&e, "ThreadBitcoinMiner()");
- } catch (...) {
- vnThreadsRunning[THREAD_MINER]--;
- PrintException(NULL, "ThreadBitcoinMiner()");
- }
- nHPSTimerStart = 0;
- if (vnThreadsRunning[THREAD_MINER] == 0)
- dHashesPerSec = 0;
- printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]);
-}
-
-
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
-{
- fGenerateBitcoins = fGenerate;
- nLimitProcessors = GetArg("-genproclimit", -1);
- if (nLimitProcessors == 0)
- fGenerateBitcoins = false;
- fLimitProcessors = (nLimitProcessors != -1);
-
- if (fGenerate)
- {
- int nProcessors = boost::thread::hardware_concurrency();
- printf("%d processors\n", nProcessors);
- if (nProcessors < 1)
- nProcessors = 1;
- if (fLimitProcessors && nProcessors > nLimitProcessors)
- nProcessors = nLimitProcessors;
- int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER];
- printf("Starting %d BitcoinMiner threads\n", nAddThreads);
- for (int i = 0; i < nAddThreads; i++)
- {
- if (!CreateThread(ThreadBitcoinMiner, pwallet))
- printf("Error: CreateThread(ThreadBitcoinMiner) failed\n");
- Sleep(10);
- }
- }
-}
+ CMainCleanup() {}
+ ~CMainCleanup() {
+ // block headers
+ std::map<uint256, CBlockIndex*>::iterator it1 = mapBlockIndex.begin();
+ for (; it1 != mapBlockIndex.end(); it1++)
+ delete (*it1).second;
+ mapBlockIndex.clear();
+
+ // orphan blocks
+ std::map<uint256, CBlock*>::iterator it2 = mapOrphanBlocks.begin();
+ for (; it2 != mapOrphanBlocks.end(); it2++)
+ delete (*it2).second;
+ mapOrphanBlocks.clear();
+
+ // orphan transactions
+ }
+} instance_of_cmaincleanup;