aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorLuke Dashjr <[email protected]>2016-01-15 05:17:15 +0000
committerLuke Dashjr <[email protected]>2016-01-15 05:17:15 +0000
commit5bc4fb7b602c420be1c746442edad6b2d8e333ab (patch)
tree32ce1458f7fafdf4f9a1dcb09091f1a6de416625 /src/main.cpp
parentbanlist (bugfix): allow CNode::SweepBanned() to run on interval (diff)
parentMerge pull request #7327 (diff)
downloaddiscoin-5bc4fb7b602c420be1c746442edad6b2d8e333ab.tar.xz
discoin-5bc4fb7b602c420be1c746442edad6b2d8e333ab.zip
Merge branch 'master' into 20150703_banlist_updates
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp1246
1 files changed, 902 insertions, 344 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 5cfb05b0d..06374cc1b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin Core developers
+// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,6 +12,7 @@
#include "checkpoints.h"
#include "checkqueue.h"
#include "consensus/consensus.h"
+#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
#include "init.h"
@@ -66,16 +67,17 @@ bool fReindex = false;
bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
-bool fIsBareMultisigStd = true;
+bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
+unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
bool fCheckBlockIndex = false;
-bool fCheckpointsEnabled = true;
+bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
bool fAlerts = DEFAULT_ALERTS;
-/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
-CFeeRate minRelayTxFee = CFeeRate(1000);
+/** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */
+CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CTxMemPool mempool(::minRelayTxFee);
@@ -83,8 +85,8 @@ struct COrphanTx {
CTransaction tx;
NodeId fromPeer;
};
-map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);;
-map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);;
+map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
@@ -92,7 +94,7 @@ void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
* in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards.
*/
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams);
-static void CheckBlockIndex();
+static void CheckBlockIndex(const Consensus::Params& consensusParams);
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
@@ -133,9 +135,9 @@ namespace {
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** Number of nodes with fSyncStarted. */
int nSyncStarted = 0;
- /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions.
- * Pruned nodes may have entries where B is missing data.
- */
+ /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
+ * Pruned nodes may have entries where B is missing data.
+ */
multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
@@ -180,7 +182,7 @@ namespace {
* million to make it highly unlikely for users to have issues with this
* filter.
*
- * Memory used: 1.7MB
+ * Memory used: 1.3 MB
*/
boost::scoped_ptr<CRollingBloomFilter> recentRejects;
uint256 hashRecentRejectsChainTip;
@@ -246,6 +248,8 @@ struct CNodeState {
uint256 hashLastUnknownBlock;
//! The last full block we both have.
CBlockIndex *pindexLastCommonBlock;
+ //! The best header we have sent our peer.
+ CBlockIndex *pindexBestHeaderSent;
//! Whether we've started headers synchronization with this peer.
bool fSyncStarted;
//! Since when we're stalling block download progress (in microseconds), or 0.
@@ -255,6 +259,8 @@ struct CNodeState {
int nBlocksInFlightValidHeaders;
//! Whether we consider this a preferred download peer.
bool fPreferredDownload;
+ //! Whether this peer wants invs or headers (when possible) for block announcements.
+ bool fPreferHeaders;
CNodeState() {
fCurrentlyConnected = false;
@@ -263,11 +269,13 @@ struct CNodeState {
pindexBestKnownBlock = NULL;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = NULL;
+ pindexBestHeaderSent = NULL;
fSyncStarted = false;
nStallingSince = 0;
nBlocksInFlight = 0;
nBlocksInFlightValidHeaders = 0;
fPreferredDownload = false;
+ fPreferHeaders = false;
}
};
@@ -397,6 +405,22 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
+// Requires cs_main
+bool CanDirectFetch(const Consensus::Params &consensusParams)
+{
+ return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+}
+
+// Requires cs_main
+bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
+{
+ if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
+ return true;
+ if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
+ return true;
+ return false;
+}
+
/** Find the last common ancestor two blocks have.
* Both pa and pb must be non-NULL. */
CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
@@ -650,10 +674,36 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}
-bool CheckFinalTx(const CTransaction &tx)
+bool CheckFinalTx(const CTransaction &tx, int flags)
{
AssertLockHeld(cs_main);
- return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime());
+
+ // By convention a negative value for flags indicates that the
+ // current network-enforced consensus rules should be used. In
+ // a future soft-fork scenario that would mean checking which
+ // rules would be enforced for the next block and setting the
+ // appropriate flags. At the present time no soft-forks are
+ // scheduled, so no flags are set.
+ flags = std::max(flags, 0);
+
+ // CheckFinalTx() uses chainActive.Height()+1 to evaluate
+ // nLockTime because when IsFinalTx() is called within
+ // CBlock::AcceptBlock(), the height of the block *being*
+ // evaluated is what is used. Thus if we want to know if a
+ // transaction can be part of the *next* block, we need to call
+ // IsFinalTx() with one more than chainActive.Height().
+ const int nBlockHeight = chainActive.Height() + 1;
+
+ // BIP113 will require that time-locked transactions have nLockTime set to
+ // less than the median time of the previous block they're contained in.
+ // When the next block is created its previous block will be the current
+ // chain tip, so we use that to calculate the median time passed to
+ // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
+ const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
+ ? chainActive.Tip()->GetMedianTimePast()
+ : GetAdjustedTime();
+
+ return IsFinalTx(tx, nBlockHeight, nBlockTime);
}
unsigned int GetLegacySigOpCount(const CTransaction& tx)
@@ -740,37 +790,19 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return true;
}
-CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
-{
- {
- LOCK(mempool.cs);
- uint256 hash = tx.GetHash();
- double dPriorityDelta = 0;
- CAmount nFeeDelta = 0;
- mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
- if (dPriorityDelta > 0 || nFeeDelta > 0)
- return 0;
- }
-
- CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
+void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
+ int expired = pool.Expire(GetTime() - age);
+ if (expired != 0)
+ LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
- if (fAllowFree)
- {
- // There is a free transaction area in blocks created by most miners,
- // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
- // to be considered to fall into this category. We don't want to encourage sending
- // multiple transactions instead of one big transaction to avoid fees.
- if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))
- nMinFee = 0;
- }
-
- if (!MoneyRange(nMinFee))
- nMinFee = MAX_MONEY;
- return nMinFee;
+ std::vector<uint256> vNoSpendsRemaining;
+ pool.TrimToSize(limit, &vNoSpendsRemaining);
+ BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
+ pcoinsTip->Uncache(removed);
}
/** Convert CValidationState to a human-readable message for logging */
-static std::string FormatStateMessage(const CValidationState &state)
+std::string FormatStateMessage(const CValidationState &state)
{
return strprintf("%s%s (code %i)",
state.GetRejectReason(),
@@ -778,8 +810,9 @@ static std::string FormatStateMessage(const CValidationState &state)
state.GetRejectCode());
}
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fRejectAbsurdFee)
+bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee,
+ std::vector<uint256>& vHashTxnToUncache)
{
AssertLockHeld(cs_main);
if (pfMissingInputs)
@@ -800,7 +833,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- if (!CheckFinalTx(tx))
+ if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
// is it already in the memory pool?
@@ -809,15 +842,42 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
// Check for conflicts with in-memory transactions
+ set<uint256> setConflicts;
{
LOCK(pool.cs); // protect pool.mapNextTx
- for (unsigned int i = 0; i < tx.vin.size(); i++)
+ BOOST_FOREACH(const CTxIn &txin, tx.vin)
{
- COutPoint outpoint = tx.vin[i].prevout;
- if (pool.mapNextTx.count(outpoint))
+ if (pool.mapNextTx.count(txin.prevout))
{
- // Disable replacement feature for now
- return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict");
+ const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx;
+ if (!setConflicts.count(ptxConflicting->GetHash()))
+ {
+ // Allow opt-out of transaction replacement by setting
+ // nSequence >= maxint-1 on all inputs.
+ //
+ // maxint-1 is picked to still allow use of nLockTime by
+ // non-replacable transactions. All inputs rather than just one
+ // is for the sake of multi-party protocols, where we don't
+ // want a single party to be able to disable replacement.
+ //
+ // The opt-out ignores descendants as anyone relying on
+ // first-seen mempool behavior should be checking all
+ // unconfirmed ancestors anyway; doing otherwise is hopelessly
+ // insecure.
+ bool fReplacementOptOut = true;
+ BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin)
+ {
+ if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1)
+ {
+ fReplacementOptOut = false;
+ break;
+ }
+ }
+ if (fReplacementOptOut)
+ return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict");
+
+ setConflicts.insert(ptxConflicting->GetHash());
+ }
}
}
}
@@ -833,13 +893,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
view.SetBackend(viewMemPool);
// do we already have it?
- if (view.HaveCoins(hash))
+ bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash);
+ if (view.HaveCoins(hash)) {
+ if (!fHadTxInCache)
+ vHashTxnToUncache.push_back(hash);
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
+ }
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
BOOST_FOREACH(const CTxIn txin, tx.vin) {
+ if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash))
+ vHashTxnToUncache.push_back(txin.prevout.hash);
if (!view.HaveCoins(txin.prevout.hash)) {
if (pfMissingInputs)
*pfMissingInputs = true;
@@ -864,39 +930,54 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if (fRequireStandard && !AreInputsStandard(tx, view))
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
- // Check that the transaction doesn't have an excessive number of
- // sigops, making it impossible to mine. Since the coinbase transaction
- // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
- // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
- // merely non-standard transaction.
unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view);
- if (nSigOps > MAX_STANDARD_TX_SIGOPS)
- return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
- strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS));
CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut;
- double dPriority = view.GetPriority(tx, chainActive.Height());
+ // nModifiedFees includes any fee deltas from PrioritiseTransaction
+ CAmount nModifiedFees = nFees;
+ double nPriorityDummy = 0;
+ pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees);
+
+ CAmount inChainInputValue;
+ double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
- CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx));
+ // Keep track of transactions that spend a coinbase, which we re-scan
+ // during reorgs to ensure COINBASE_MATURITY is still met.
+ bool fSpendsCoinbase = false;
+ BOOST_FOREACH(const CTxIn &txin, tx.vin) {
+ const CCoins *coins = view.AccessCoins(txin.prevout.hash);
+ if (coins->IsCoinBase()) {
+ fSpendsCoinbase = true;
+ break;
+ }
+ }
+
+ CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps);
unsigned int nSize = entry.GetTxSize();
- // Don't accept it if it can't get into a block
- CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
- if (fLimitFree && nFees < txMinFee)
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
- strprintf("%d < %d", nFees, txMinFee));
+ // Check that the transaction doesn't have an excessive number of
+ // sigops, making it impossible to mine. Since the coinbase transaction
+ // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
+ // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
+ // merely non-standard transaction.
+ if ((nSigOps > MAX_STANDARD_TX_SIGOPS) || (nBytesPerSigOp && nSigOps > nSize / nBytesPerSigOp))
+ return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
+ strprintf("%d", nSigOps));
- // Require that free transactions have sufficient priority to be mined in the next block.
- if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
+ CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
+ if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
+ } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
+ // Require that free transactions have sufficient priority to be mined in the next block.
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}
// Continuously rate-limit free (really, very-low-fee) transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
- if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
+ if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize))
{
static CCriticalSection csFreeLimiter;
static double dFreeCount;
@@ -910,7 +991,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
- if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
+ if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000)
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
@@ -932,6 +1013,160 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
}
+ // A transaction that spends outputs that would be replaced by it is invalid. Now
+ // that we have the set of all ancestors we can detect this
+ // pathological case by making sure setConflicts and setAncestors don't
+ // intersect.
+ BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors)
+ {
+ const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ if (setConflicts.count(hashAncestor))
+ {
+ return state.DoS(10, error("AcceptToMemoryPool: %s spends conflicting transaction %s",
+ hash.ToString(),
+ hashAncestor.ToString()),
+ REJECT_INVALID, "bad-txns-spends-conflicting-tx");
+ }
+ }
+
+ // Check if it's economically rational to mine this transaction rather
+ // than the ones it replaces.
+ CAmount nConflictingFees = 0;
+ size_t nConflictingSize = 0;
+ uint64_t nConflictingCount = 0;
+ CTxMemPool::setEntries allConflicting;
+
+ // If we don't hold the lock allConflicting might be incomplete; the
+ // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
+ // mempool consistency for us.
+ LOCK(pool.cs);
+ if (setConflicts.size())
+ {
+ CFeeRate newFeeRate(nModifiedFees, nSize);
+ set<uint256> setConflictsParents;
+ const int maxDescendantsToVisit = 100;
+ CTxMemPool::setEntries setIterConflicting;
+ BOOST_FOREACH(const uint256 &hashConflicting, setConflicts)
+ {
+ CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting);
+ if (mi == pool.mapTx.end())
+ continue;
+
+ // Save these to avoid repeated lookups
+ setIterConflicting.insert(mi);
+
+ // If this entry is "dirty", then we don't have descendant
+ // state for this transaction, which means we probably have
+ // lots of in-mempool descendants.
+ // Don't allow replacements of dirty transactions, to ensure
+ // that we don't spend too much time walking descendants.
+ // This should be rare.
+ if (mi->IsDirty()) {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; cannot replace tx %s with untracked descendants",
+ hash.ToString(),
+ mi->GetTx().GetHash().ToString()),
+ REJECT_NONSTANDARD, "too many potential replacements");
+ }
+
+ // Don't allow the replacement to reduce the feerate of the
+ // mempool.
+ //
+ // We usually don't want to accept replacements with lower
+ // feerates than what they replaced as that would lower the
+ // feerate of the next block. Requiring that the feerate always
+ // be increased is also an easy-to-reason about way to prevent
+ // DoS attacks via replacements.
+ //
+ // The mining code doesn't (currently) take children into
+ // account (CPFP) so we only consider the feerates of
+ // transactions being directly replaced, not their indirect
+ // descendants. While that does mean high feerate children are
+ // ignored when deciding whether or not to replace, we do
+ // require the replacement to pay more overall fees too,
+ // mitigating most cases.
+ CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
+ if (newFeeRate <= oldFeeRate)
+ {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; new feerate %s <= old feerate %s",
+ hash.ToString(),
+ newFeeRate.ToString(),
+ oldFeeRate.ToString()),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+
+ BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin)
+ {
+ setConflictsParents.insert(txin.prevout.hash);
+ }
+
+ nConflictingCount += mi->GetCountWithDescendants();
+ }
+ // This potentially overestimates the number of actual descendants
+ // but we just want to be conservative to avoid doing too much
+ // work.
+ if (nConflictingCount <= maxDescendantsToVisit) {
+ // If not too many to replace, then calculate the set of
+ // transactions that would have to be evicted
+ BOOST_FOREACH(CTxMemPool::txiter it, setIterConflicting) {
+ pool.CalculateDescendants(it, allConflicting);
+ }
+ BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) {
+ nConflictingFees += it->GetModifiedFee();
+ nConflictingSize += it->GetTxSize();
+ }
+ } else {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; too many potential replacements (%d > %d)\n",
+ hash.ToString(),
+ nConflictingCount,
+ maxDescendantsToVisit),
+ REJECT_NONSTANDARD, "too many potential replacements");
+ }
+
+ for (unsigned int j = 0; j < tx.vin.size(); j++)
+ {
+ // We don't want to accept replacements that require low
+ // feerate junk to be mined first. Ideally we'd keep track of
+ // the ancestor feerates and make the decision based on that,
+ // but for now requiring all new inputs to be confirmed works.
+ if (!setConflictsParents.count(tx.vin[j].prevout.hash))
+ {
+ // Rather than check the UTXO set - potentially expensive -
+ // it's cheaper to just check if the new input refers to a
+ // tx that's in the mempool.
+ if (pool.mapTx.find(tx.vin[j].prevout.hash) != pool.mapTx.end())
+ return state.DoS(0, error("AcceptToMemoryPool: replacement %s adds unconfirmed input, idx %d",
+ hash.ToString(), j),
+ REJECT_NONSTANDARD, "replacement-adds-unconfirmed");
+ }
+ }
+
+ // The replacement must pay greater fees than the transactions it
+ // replaces - if we did the bandwidth used by those conflicting
+ // transactions would not be paid for.
+ if (nModifiedFees < nConflictingFees)
+ {
+ return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s",
+ hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+
+ // Finally in addition to paying more fees than the conflicts the
+ // new transaction must pay for its own bandwidth.
+ CAmount nDeltaFees = nModifiedFees - nConflictingFees;
+ if (nDeltaFees < ::minRelayTxFee.GetFee(nSize))
+ {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s, not enough additional fees to relay; %s < %s",
+ hash.ToString(),
+ FormatMoney(nDeltaFees),
+ FormatMoney(::minRelayTxFee.GetFee(nSize))),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+ }
+
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true))
@@ -952,8 +1187,26 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
__func__, hash.ToString(), FormatStateMessage(state));
}
+ // Remove conflicting transactions from the mempool
+ BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting)
+ {
+ LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
+ it->GetTx().GetHash().ToString(),
+ hash.ToString(),
+ FormatMoney(nModifiedFees - nConflictingFees),
+ (int)nSize - (int)nConflictingSize);
+ }
+ pool.RemoveStaged(allConflicting);
+
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
+
+ // trim mempool and check if tx was trimmed
+ if (!fOverrideMempoolLimit) {
+ LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
+ if (!pool.exists(hash))
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
+ }
}
SyncWithWallets(tx, NULL);
@@ -961,8 +1214,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true;
}
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
+{
+ std::vector<uint256> vHashTxToUncache;
+ bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache);
+ if (!res) {
+ BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
+ pcoinsTip->Uncache(hashTx);
+ }
+ return res;
+}
+
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
-bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
+bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
@@ -1008,7 +1273,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
if (pindexSlow) {
CBlock block;
- if (ReadBlockFromDisk(block, pindexSlow)) {
+ if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
if (tx.GetHash() == hash) {
txOut = tx;
@@ -1053,7 +1318,7 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
return true;
}
-bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
+bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@@ -1071,15 +1336,15 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
}
// Check the header
- if (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus()))
+ if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
return true;
}
-bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
+bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
- if (!ReadBlockFromDisk(block, pindex->GetBlockPos()))
+ if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
@@ -1207,7 +1472,7 @@ void Misbehaving(NodeId pnode, int howmuch)
return;
state->nMisbehavior += howmuch;
- int banscore = GetArg("-banscore", 100);
+ int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
@@ -1274,10 +1539,17 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
undo.nVersion = coins->nVersion;
}
}
+ // add outputs
+ inputs.ModifyNewCoins(tx.GetHash())->FromTx(tx, nHeight);
+ }
+ else {
+ // add outputs for coinbase tx
+ // In this case call the full ModifyCoins which will do a database
+ // lookup to be sure the coins do not already exist otherwise we do not
+ // know whether to mark them fresh or not. We want the duplicate coinbases
+ // before BIP30 to still be properly overwritten.
+ inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
}
-
- // add outputs
- inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
}
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight)
@@ -1383,9 +1655,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
- CScriptCheck check(*coins, tx, i,
+ CScriptCheck check2(*coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
- if (check())
+ if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
// Failures of other flags indicate a transaction that is
@@ -1648,8 +1920,8 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
// How likely is it to find that many by chance?
double p = boost::math::pdf(poisson, nBlocks);
- LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS);
- LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p);
+ LogPrint("partitioncheck", "%s: Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS);
+ LogPrint("partitioncheck", "%s: likelihood: %g\n", __func__, p);
// Aim for one false-positive about every fifty years of normal running:
const int FIFTY_YEARS = 50*365*24*60*60;
@@ -1675,6 +1947,8 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
}
}
+static int64_t nTimeCheck = 0;
+static int64_t nTimeForks = 0;
static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
@@ -1685,6 +1959,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
{
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
+
+ int64_t nTimeStart = GetTimeMicros();
+
// Check it again in case a previous version let a bad block in
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
return false;
@@ -1710,6 +1987,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
}
}
+ int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart;
+ LogPrint("bench", " - Sanity checks: %.2fms [%.2fs]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001);
+
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
@@ -1725,6 +2005,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash.
!((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
+
+ // Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting
+ // with the 2 existing duplicate coinbase pairs, not possible to create overwriting txs. But by the
+ // time BIP34 activated, in each of the existing pairs the duplicate coinbase had overwritten the first
+ // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further
+ // duplicate transactions descending from the known pairs either.
+ // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check.
+ CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height);
+ //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
+ fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
+
if (fEnforceBIP30) {
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
const CCoins* coins = view.AccessCoins(tx.GetHash());
@@ -1740,16 +2031,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
- // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded:
+ // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks,
+ // when 75% of the network has upgraded:
if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
flags |= SCRIPT_VERIFY_DERSIG;
}
+ // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4
+ // blocks, when 75% of the network has upgraded:
+ if (block.nVersion >= 4 && IsSuperMajority(4, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
+ flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ }
+
+ int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
+ LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001);
+
CBlockUndo blockundo;
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
- int64_t nTimeStart = GetTimeMicros();
CAmount nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
@@ -1787,7 +2087,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
nFees += view.GetValueIn(tx)-tx.GetValueOut();
std::vector<CScriptCheck> vChecks;
- if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL))
+ bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
+ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, nScriptCheckThreads ? &vChecks : NULL))
return error("ConnectBlock(): CheckInputs on %s failed with %s",
tx.GetHash().ToString(), FormatStateMessage(state));
control.Add(vChecks);
@@ -1802,8 +2103,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
- int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
- LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
+ int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
+ LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001);
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
if (block.vtx[0].GetValueOut() > blockReward)
@@ -1814,8 +2115,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!control.Wait())
return state.DoS(100, false);
- int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart;
- LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001);
+ int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
+ LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001);
if (fJustCheck)
return true;
@@ -1846,16 +2147,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
- int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2;
- LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001);
+ int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
+ LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001);
// Watch for changes to the previous coinbase transaction.
static uint256 hashPrevBestCoinBase;
GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.vtx[0].GetHash();
- int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3;
- LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001);
+ int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
+ LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001);
return true;
}
@@ -1874,6 +2175,7 @@ enum FlushStateMode {
* or always and in all cases if we're in prune mode and are deleting files.
*/
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
+ const CChainParams& chainparams = Params();
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
@@ -1881,8 +2183,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
std::set<int> setFilesToPrune;
bool fFlushForPrune = false;
try {
- if (fPruneMode && fCheckForPruning) {
- FindFilesToPrune(setFilesToPrune);
+ if (fPruneMode && fCheckForPruning && !fReindex) {
+ FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
fCheckForPruning = false;
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
@@ -2020,14 +2322,14 @@ void static UpdateTip(CBlockIndex *pindexNew) {
}
}
-/** Disconnect chainActive's tip. */
-bool static DisconnectTip(CValidationState &state) {
+/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
+bool static DisconnectTip(CValidationState& state, const Consensus::Params& consensusParams)
+{
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
- mempool.check(pcoinsTip);
// Read block from disk.
CBlock block;
- if (!ReadBlockFromDisk(block, pindexDelete))
+ if (!ReadBlockFromDisk(block, pindexDelete, consensusParams))
return AbortNode(state, "Failed to read block");
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
@@ -2047,7 +2349,7 @@ bool static DisconnectTip(CValidationState &state) {
// ignore validation errors in resurrected transactions
list<CTransaction> removed;
CValidationState stateDummy;
- if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) {
+ if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
mempool.remove(tx, removed, true);
} else if (mempool.exists(tx.GetHash())) {
vHashUpdate.push_back(tx.GetHash());
@@ -2059,8 +2361,6 @@ bool static DisconnectTip(CValidationState &state) {
// UpdateTransactionsFromBlock finds descendants of any transactions in this
// block that were added back and cleans up the mempool state.
mempool.UpdateTransactionsFromBlock(vHashUpdate);
- mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight);
- mempool.check(pcoinsTip);
// Update chainActive and related variables.
UpdateTip(pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
@@ -2081,14 +2381,14 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
-bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) {
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock)
+{
assert(pindexNew->pprev == chainActive.Tip());
- mempool.check(pcoinsTip);
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
CBlock block;
if (!pblock) {
- if (!ReadBlockFromDisk(block, pindexNew))
+ if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
pblock = &block;
}
@@ -2120,7 +2420,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CB
// Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted;
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
- mempool.check(pcoinsTip);
// Update chainActive & related variables.
UpdateTip(pindexNew);
// Tell wallet about transactions that went from mempool
@@ -2213,16 +2512,19 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) {
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock)
+{
AssertLockHeld(cs_main);
bool fInvalidFound = false;
const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
+ bool fBlocksDisconnected = false;
while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
- if (!DisconnectTip(state))
+ if (!DisconnectTip(state, chainparams.GetConsensus()))
return false;
+ fBlocksDisconnected = true;
}
// Build list of new blocks to connect.
@@ -2230,43 +2532,49 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
bool fContinue = true;
int nHeight = pindexFork ? pindexFork->nHeight : -1;
while (fContinue && nHeight != pindexMostWork->nHeight) {
- // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need
- // a few blocks along the way.
- int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight);
- vpindexToConnect.clear();
- vpindexToConnect.reserve(nTargetHeight - nHeight);
- CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
- while (pindexIter && pindexIter->nHeight != nHeight) {
- vpindexToConnect.push_back(pindexIter);
- pindexIter = pindexIter->pprev;
- }
- nHeight = nTargetHeight;
-
- // Connect new blocks.
- BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
- if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
- if (state.IsInvalid()) {
- // The block violates a consensus rule.
- if (!state.CorruptionPossible())
- InvalidChainFound(vpindexToConnect.back());
- state = CValidationState();
- fInvalidFound = true;
- fContinue = false;
- break;
+ // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need
+ // a few blocks along the way.
+ int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight);
+ vpindexToConnect.clear();
+ vpindexToConnect.reserve(nTargetHeight - nHeight);
+ CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
+ while (pindexIter && pindexIter->nHeight != nHeight) {
+ vpindexToConnect.push_back(pindexIter);
+ pindexIter = pindexIter->pprev;
+ }
+ nHeight = nTargetHeight;
+
+ // Connect new blocks.
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
+ if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
+ if (state.IsInvalid()) {
+ // The block violates a consensus rule.
+ if (!state.CorruptionPossible())
+ InvalidChainFound(vpindexToConnect.back());
+ state = CValidationState();
+ fInvalidFound = true;
+ fContinue = false;
+ break;
+ } else {
+ // A system error occurred (disk space, database error, ...).
+ return false;
+ }
} else {
- // A system error occurred (disk space, database error, ...).
- return false;
- }
- } else {
- PruneBlockIndexCandidates();
- if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
- // We're in a better position than we were. Return temporarily to release the lock.
- fContinue = false;
- break;
+ PruneBlockIndexCandidates();
+ if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
+ // We're in a better position than we were. Return temporarily to release the lock.
+ fContinue = false;
+ break;
+ }
}
}
}
+
+ if (fBlocksDisconnected) {
+ mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
+ mempool.check(pcoinsTip);
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2282,49 +2590,72 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CBlock *pblock) {
- CBlockIndex *pindexNewTip = NULL;
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
CBlockIndex *pindexMostWork = NULL;
- const CChainParams& chainParams = Params();
do {
boost::this_thread::interruption_point();
+ CBlockIndex *pindexNewTip = NULL;
+ const CBlockIndex *pindexFork;
bool fInitialDownload;
{
LOCK(cs_main);
+ CBlockIndex *pindexOldTip = chainActive.Tip();
pindexMostWork = FindMostWorkChain();
// Whether we have anything to do at all.
if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip())
return true;
- if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL))
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL))
return false;
pindexNewTip = chainActive.Tip();
+ pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
- if (!fInitialDownload) {
- uint256 hashNewTip = pindexNewTip->GetBlockHash();
- // Relay inventory, but don't relay old inventory during initial block download.
- int nBlockEstimate = 0;
- if (fCheckpointsEnabled)
- nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints());
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
- pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
+ // Always notify the UI if a new block tip was connected
+ if (pindexFork != pindexNewTip) {
+ uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+
+ if (!fInitialDownload) {
+ // Find the hashes of all blocks that weren't previously in the best chain.
+ std::vector<uint256> vHashes;
+ CBlockIndex *pindexToAnnounce = pindexNewTip;
+ while (pindexToAnnounce != pindexFork) {
+ vHashes.push_back(pindexToAnnounce->GetBlockHash());
+ pindexToAnnounce = pindexToAnnounce->pprev;
+ if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
+ // Limit announcements in case of a huge reorganization.
+ // Rely on the peer's synchronization mechanism in that case.
+ break;
+ }
+ }
+ // Relay inventory, but don't relay old inventory during initial block download.
+ int nBlockEstimate = 0;
+ if (fCheckpointsEnabled)
+ nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
+ {
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) {
+ BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
+ pnode->PushBlockHash(hash);
+ }
+ }
+ }
+ }
+ // Notify external listeners about the new tip.
+ if (!vHashes.empty()) {
+ GetMainSignals().UpdatedBlockTip(pindexNewTip);
+ }
}
- // Notify external listeners about the new tip.
- GetMainSignals().UpdatedBlockTip(pindexNewTip);
- uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
- CheckBlockIndex();
+ CheckBlockIndex(chainparams.GetConsensus());
// Write changes periodically to disk, after relay.
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
@@ -2334,7 +2665,8 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) {
return true;
}
-bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
+bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex *pindex)
+{
AssertLockHeld(cs_main);
// Mark the block itself as invalid.
@@ -2349,11 +2681,14 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
setBlockIndexCandidates.erase(pindexWalk);
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
- if (!DisconnectTip(state)) {
+ if (!DisconnectTip(state, consensusParams)) {
+ mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
return false;
}
}
+ LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
+
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
@@ -2365,6 +2700,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
}
InvalidChainFound(pindex);
+ mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
return true;
}
@@ -2492,8 +2828,6 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
- LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString());
- FlushBlockFile(true);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -2503,7 +2837,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
pos.nPos = vinfoBlockFile[nFile].nSize;
}
- nLastBlockFile = nFile;
+ if ((int)nFile != nLastBlockFile) {
+ if (!fKnown) {
+ LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ }
+ FlushBlockFile(!fKnown);
+ nLastBlockFile = nFile;
+ }
+
vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
if (fKnown)
vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize);
@@ -2594,7 +2935,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
- uint256 hashMerkleRoot2 = block.ComputeMerkleRoot(&mutated);
+ uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"),
REJECT_INVALID, "bad-txnmrklroot", true);
@@ -2639,7 +2980,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"),
- REJECT_INVALID, "bad-blk-sigops", true);
+ REJECT_INVALID, "bad-blk-sigops");
if (fCheckPOW && fCheckMerkleRoot)
block.fChecked = true;
@@ -2681,7 +3022,12 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
// Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded:
if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
- return state.Invalid(error("%s : rejected nVersion=2 block", __func__),
+ return state.Invalid(error("%s: rejected nVersion=2 block", __func__),
+ REJECT_OBSOLETE, "bad-version");
+
+ // Reject block.nVersion=3 blocks when 95% (75% on testnet) of the network has upgraded:
+ if (block.nVersion < 4 && IsSuperMajority(4, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
+ return state.Invalid(error("%s : rejected nVersion=3 block", __func__),
REJECT_OBSOLETE, "bad-version");
return true;
@@ -2693,10 +3039,15 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
const Consensus::Params& consensusParams = Params().GetConsensus();
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
- if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
+ BOOST_FOREACH(const CTransaction& tx, block.vtx) {
+ int nLockTimeFlags = 0;
+ int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
+ ? pindexPrev->GetMedianTimePast()
+ : block.GetBlockTime();
+ if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
}
+ }
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
@@ -2712,9 +3063,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
return true;
}
-bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex)
+static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL)
{
- const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
@@ -2760,14 +3110,14 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
return true;
}
-bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
+/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
+static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
{
- const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
CBlockIndex *&pindex = *ppindex;
- if (!AcceptBlockHeader(block, state, &pindex))
+ if (!AcceptBlockHeader(block, state, chainparams, &pindex))
return false;
// Try to process all requested blocks that we don't have, but only
@@ -2837,7 +3187,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
}
-bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp)
+bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp)
{
// Preliminary checks
bool checked = CheckBlock(*pblock, state);
@@ -2852,24 +3202,23 @@ bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock*
// Store to disk
CBlockIndex *pindex = NULL;
- bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp);
+ bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
- CheckBlockIndex();
+ CheckBlockIndex(chainparams.GetConsensus());
if (!ret)
return error("%s: AcceptBlock FAILED", __func__);
}
- if (!ActivateBestChain(state, pblock))
+ if (!ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
}
-bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
+bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
- const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == chainActive.Tip());
if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash()))
@@ -2952,13 +3301,13 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune)
}
/* Calculate the block/rev files that should be deleted to remain under target*/
-void FindFilesToPrune(std::set<int>& setFilesToPrune)
+void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
if (chainActive.Tip() == NULL || nPruneTarget == 0) {
return;
}
- if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) {
+ if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) {
return;
}
@@ -3186,7 +3535,7 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100);
}
-bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
LOCK(cs_main);
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
@@ -3212,7 +3561,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
break;
CBlock block;
// check level 0: read from disk
- if (!ReadBlockFromDisk(block, pindex))
+ if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state))
@@ -3252,7 +3601,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))));
pindex = chainActive.Next(pindex);
CBlock block;
- if (!ReadBlockFromDisk(block, pindex))
+ if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
@@ -3303,9 +3652,8 @@ bool LoadBlockIndex()
return true;
}
-
-bool InitBlockIndex() {
- const CChainParams& chainparams = Params();
+bool InitBlockIndex(const CChainParams& chainparams)
+{
LOCK(cs_main);
// Initialize global variables that cannot be constructed at startup.
@@ -3316,14 +3664,14 @@ bool InitBlockIndex() {
return true;
// Use the provided setting for -txindex in the new database
- fTxIndex = GetBoolArg("-txindex", false);
+ fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
pblocktree->WriteFlag("txindex", fTxIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
if (!fReindex) {
try {
- CBlock &block = const_cast<CBlock&>(Params().GenesisBlock());
+ CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
@@ -3335,7 +3683,7 @@ bool InitBlockIndex() {
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex(): genesis block not accepted");
- if (!ActivateBestChain(state, &block))
+ if (!ActivateBestChain(state, chainparams, &block))
return error("LoadBlockIndex(): genesis block cannot be activated");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
@@ -3347,11 +3695,8 @@ bool InitBlockIndex() {
return true;
}
-
-
-bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp)
{
- const CChainParams& chainparams = Params();
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
@@ -3371,10 +3716,10 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
try {
// locate a header
unsigned char buf[MESSAGE_START_SIZE];
- blkdat.FindByte(Params().MessageStart()[0]);
+ blkdat.FindByte(chainparams.MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> FLATDATA(buf);
- if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE))
+ if (memcmp(buf, chainparams.MessageStart(), MESSAGE_START_SIZE))
continue;
// read size
blkdat >> nSize;
@@ -3408,7 +3753,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
// process in case the block isn't known yet
if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
- if (ProcessNewBlock(state, NULL, &block, true, dbp))
+ if (ProcessNewBlock(state, chainparams, NULL, &block, true, dbp))
nLoaded++;
if (state.IsError())
break;
@@ -3425,12 +3770,12 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
- if (ReadBlockFromDisk(block, it->second))
+ if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()))
{
LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(),
head.ToString());
CValidationState dummy;
- if (ProcessNewBlock(dummy, NULL, &block, true, &it->second))
+ if (ProcessNewBlock(dummy, chainparams, NULL, &block, true, &it->second))
{
nLoaded++;
queue.push_back(block.GetHash());
@@ -3452,9 +3797,8 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
return nLoaded > 0;
}
-void static CheckBlockIndex()
+void static CheckBlockIndex(const Consensus::Params& consensusParams)
{
- const Consensus::Params& consensusParams = Params().GetConsensus();
if (!fCheckBlockIndex) {
return;
}
@@ -3647,29 +3991,34 @@ std::string GetWarnings(const std::string& strFor)
int nPriority = 0;
string strStatusBar;
string strRPC;
+ string strGUI;
- if (!CLIENT_VERSION_IS_RELEASE)
- strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ if (!CLIENT_VERSION_IS_RELEASE) {
+ strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications";
+ strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ }
- if (GetBoolArg("-testsafemode", false))
- strStatusBar = strRPC = "testsafemode enabled";
+ if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE))
+ strStatusBar = strRPC = strGUI = "testsafemode enabled";
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
nPriority = 1000;
- strStatusBar = strMiscWarning;
+ strStatusBar = strGUI = strMiscWarning;
}
if (fLargeWorkForkFound)
{
nPriority = 2000;
- strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
+ strGUI = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
else if (fLargeWorkInvalidChainFound)
{
nPriority = 2000;
- strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
+ strGUI = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
@@ -3681,12 +4030,14 @@ std::string GetWarnings(const std::string& strFor)
if (alert.AppliesToMe() && alert.nPriority > nPriority)
{
nPriority = alert.nPriority;
- strStatusBar = alert.strStatusBar;
+ strStatusBar = strGUI = alert.strStatusBar;
}
}
}
- if (strFor == "statusbar")
+ if (strFor == "gui")
+ return strGUI;
+ else if (strFor == "statusbar")
return strStatusBar;
else if (strFor == "rpc")
return strRPC;
@@ -3736,7 +4087,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return true;
}
-void static ProcessGetData(CNode* pfrom)
+void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
@@ -3769,29 +4120,40 @@ void static ProcessGetData(CNode* pfrom)
// best equivalent proof of work) than the best header chain we know about.
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
(pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth);
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
if (!send) {
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
}
}
}
+ // disconnect node in case we have reached the outbound limit for serving historical blocks
+ // never disconnect whitelisted nodes
+ static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
+ if (send && CNode::OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+
+ //disconnect node
+ pfrom->fDisconnect = true;
+ send = false;
+ }
// Pruned nodes may have deleted the block, so check whether
// it's available before trying to send.
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
{
// Send block from disk
CBlock block;
- if (!ReadBlockFromDisk(block, (*mi).second))
+ if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
assert(!"cannot load block from disk");
if (inv.type == MSG_BLOCK)
- pfrom->PushMessage("block", block);
+ pfrom->PushMessage(NetMsgType::BLOCK, block);
else // MSG_FILTERED_BLOCK)
{
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
{
CMerkleBlock merkleBlock(block, *pfrom->pfilter);
- pfrom->PushMessage("merkleblock", merkleBlock);
+ pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didn't send here -
@@ -3800,8 +4162,7 @@ void static ProcessGetData(CNode* pfrom)
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
- if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second)))
- pfrom->PushMessage("tx", block.vtx[pair.first]);
+ pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]);
}
// else
// no response
@@ -3815,7 +4176,7 @@ void static ProcessGetData(CNode* pfrom)
// wait for other stuff first.
vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
- pfrom->PushMessage("inv", vInv);
+ pfrom->PushMessage(NetMsgType::INV, vInv);
pfrom->hashContinue.SetNull();
}
}
@@ -3838,7 +4199,7 @@ void static ProcessGetData(CNode* pfrom)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << tx;
- pfrom->PushMessage("tx", ss);
+ pfrom->PushMessage(NetMsgType::TX, ss);
pushed = true;
}
}
@@ -3865,7 +4226,7 @@ void static ProcessGetData(CNode* pfrom)
// do that because they want to know about (and store and rebroadcast and
// risk analyze) the dependencies of transactions relevant to them, without
// having to download the entire memory pool.
- pfrom->PushMessage("notfound", vNotFound);
+ pfrom->PushMessage(NetMsgType::NOTFOUND, vNotFound);
}
}
@@ -3881,14 +4242,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
+ if (!(nLocalServices & NODE_BLOOM) &&
+ (strCommand == NetMsgType::FILTERLOAD ||
+ strCommand == NetMsgType::FILTERADD ||
+ strCommand == NetMsgType::FILTERCLEAR))
+ {
+ if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+ Misbehaving(pfrom->GetId(), 100);
+ return false;
+ } else if (GetBoolArg("-enforcenodebloom", false)) {
+ pfrom->fDisconnect = true;
+ return false;
+ }
+ }
- if (strCommand == "version")
+ if (strCommand == NetMsgType::VERSION)
{
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
- pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
Misbehaving(pfrom->GetId(), 1);
return false;
}
@@ -3902,7 +4276,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
// disconnect from peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
- pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
@@ -3947,7 +4321,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
// Change version
- pfrom->PushMessage("verack");
+ pfrom->PushMessage(NetMsgType::VERACK);
pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
if (!pfrom->fInbound)
@@ -3958,9 +4332,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
{
+ LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
} else if (IsPeerAddrLocalGood(pfrom)) {
addr.SetIP(pfrom->addrLocal);
+ LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
}
}
@@ -3968,7 +4344,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Get recent addresses
if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
{
- pfrom->PushMessage("getaddr");
+ pfrom->PushMessage(NetMsgType::GETADDR);
pfrom->fGetAddr = true;
}
addrman.Good(pfrom->addr);
@@ -4012,7 +4388,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "verack")
+ else if (strCommand == NetMsgType::VERACK)
{
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
@@ -4021,10 +4397,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
}
+
+ if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ // Tell our peer we prefer to receive headers rather than inv's
+ // We send this to non-NODE NETWORK peers as well, because even
+ // non-NODE NETWORK peers can announce blocks (such as pruning
+ // nodes)
+ pfrom->PushMessage(NetMsgType::SENDHEADERS);
+ }
}
- else if (strCommand == "addr")
+ else if (strCommand == NetMsgType::ADDR)
{
vector<CAddress> vAddr;
vRecv >> vAddr;
@@ -4090,8 +4474,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fDisconnect = true;
}
+ else if (strCommand == NetMsgType::SENDHEADERS)
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fPreferHeaders = true;
+ }
+
- else if (strCommand == "inv")
+ else if (strCommand == NetMsgType::INV)
{
vector<CInv> vInv;
vRecv >> vInv;
@@ -4101,6 +4491,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
return error("message inv size() = %u", vInv.size());
}
+ bool fBlocksOnly = GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
+
+ // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistalwaysrelay is true
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistalwaysrelay", DEFAULT_WHITELISTALWAYSRELAY))
+ fBlocksOnly = false;
+
LOCK(cs_main);
std::vector<CInv> vToFetch;
@@ -4115,9 +4511,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
bool fAlreadyHave = AlreadyHave(inv);
LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
- if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK)
- pfrom->AskFor(inv);
-
if (inv.type == MSG_BLOCK) {
UpdateBlockAvailability(pfrom->GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
@@ -4129,9 +4522,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// time the block arrives, the header chain leading up to it is already validated. Not
// doing this will result in the received block being rejected as an orphan in case it is
// not a direct successor.
- pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash);
+ pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash);
CNodeState *nodestate = State(pfrom->GetId());
- if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 &&
+ if (CanDirectFetch(chainparams.GetConsensus()) &&
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
vToFetch.push_back(inv);
// Mark block as in flight already, even though the actual "getdata" message only goes out
@@ -4141,6 +4534,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
}
}
+ else
+ {
+ if (fBlocksOnly)
+ LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
+ else if (!fAlreadyHave && !fImporting && !fReindex)
+ pfrom->AskFor(inv);
+ }
// Track requests for our stuff
GetMainSignals().Inventory(inv.hash);
@@ -4152,11 +4552,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
if (!vToFetch.empty())
- pfrom->PushMessage("getdata", vToFetch);
+ pfrom->PushMessage(NetMsgType::GETDATA, vToFetch);
}
- else if (strCommand == "getdata")
+ else if (strCommand == NetMsgType::GETDATA)
{
vector<CInv> vInv;
vRecv >> vInv;
@@ -4173,11 +4573,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom);
+ ProcessGetData(pfrom, chainparams.GetConsensus());
}
- else if (strCommand == "getblocks")
+ else if (strCommand == NetMsgType::GETBLOCKS)
{
CBlockLocator locator;
uint256 hashStop;
@@ -4221,17 +4621,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "getheaders")
+ else if (strCommand == NetMsgType::GETHEADERS)
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
-
- if (IsInitialBlockDownload())
+ if (IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
return true;
+ }
+ CNodeState *nodestate = State(pfrom->GetId());
CBlockIndex* pindex = NULL;
if (locator.IsNull())
{
@@ -4259,12 +4661,25 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
- pfrom->PushMessage("headers", vHeaders);
+ // pindex can be NULL either if we sent chainActive.Tip() OR
+ // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // headers message). In both cases it's safe to update
+ // pindexBestHeaderSent to be our tip.
+ nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
+ pfrom->PushMessage(NetMsgType::HEADERS, vHeaders);
}
- else if (strCommand == "tx")
+ else if (strCommand == NetMsgType::TX)
{
+ // Stop processing the transaction early if
+ // We are in blocks only mode and peer is either not whitelisted or whitelistalwaysrelay is off
+ if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistalwaysrelay", DEFAULT_WHITELISTALWAYSRELAY)))
+ {
+ LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
+ return true;
+ }
+
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CTransaction tx;
@@ -4278,22 +4693,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
bool fMissingInputs = false;
CValidationState state;
+ pfrom->setAskFor.erase(inv.hash);
mapAlreadyAskedFor.erase(inv);
- // Check for recently rejected (and do other quick existence checks)
- if (AlreadyHave(inv))
- return true;
-
- if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
+ if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
{
mempool.check(pcoinsTip);
RelayTransaction(tx);
vWorkQueue.push_back(inv.hash);
- LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u)\n",
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
pfrom->id,
tx.GetHash().ToString(),
- mempool.size());
+ mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
set<NodeId> setMisbehaving;
@@ -4362,15 +4774,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
assert(recentRejects);
recentRejects->insert(tx.GetHash());
- if (pfrom->fWhitelisted) {
+ if (pfrom->fWhitelisted && GetBoolArg("-whitelistalwaysrelay", DEFAULT_WHITELISTALWAYSRELAY)) {
// Always relay transactions received from whitelisted peers, even
- // if they were rejected from the mempool, allowing the node to
- // function as a gateway for nodes hidden behind it.
+ // if they were already in the mempool or rejected from it due
+ // to policy, allowing the node to function as a gateway for
+ // nodes hidden behind it.
//
- // FIXME: This includes invalid transactions, which means a
- // whitelisted peer could get us banned! We may want to change
- // that.
- RelayTransaction(tx);
+ // Never relay transactions that we would assign a non-zero DoS
+ // score for, as we expect peers to do the same with us in that
+ // case.
+ int nDoS = 0;
+ if (!state.IsInvalid(nDoS) || nDoS == 0) {
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
+ RelayTransaction(tx);
+ } else {
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
+ }
}
}
int nDoS = 0;
@@ -4380,15 +4799,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->id,
FormatStateMessage(state));
if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
- pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
+ FlushStateToDisk(state, FLUSH_STATE_PERIODIC);
}
- else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing
+ else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
{
std::vector<CBlockHeader> headers;
@@ -4418,7 +4838,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
Misbehaving(pfrom->GetId(), 20);
return error("non-continuous headers sequence");
}
- if (!AcceptBlockHeader(header, state, &pindexLast)) {
+ if (!AcceptBlockHeader(header, state, chainparams, &pindexLast)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
if (nDoS > 0)
@@ -4436,13 +4856,60 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
// from there instead.
LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
- pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256());
+ pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256());
+ }
+
+ bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ CNodeState *nodestate = State(pfrom->GetId());
+ // If this set of headers is valid and ends in a block with at least as
+ // much work as our tip, download as much as possible.
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
+ vector<CBlockIndex *> vToFetch;
+ CBlockIndex *pindexWalk = pindexLast;
+ // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
+ !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) {
+ // We don't have this block, and it's not yet in flight.
+ vToFetch.push_back(pindexWalk);
+ }
+ pindexWalk = pindexWalk->pprev;
+ }
+ // If pindexWalk still isn't on our main chain, we're looking at a
+ // very large reorg at a time we think we're close to caught up to
+ // the main chain -- this shouldn't really happen. Bail out on the
+ // direct fetch and rely on parallel download instead.
+ if (!chainActive.Contains(pindexWalk)) {
+ LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
+ LogPrint("net", "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom->id);
+ }
+ if (vGetData.size() > 1) {
+ LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
+ pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ }
+ if (vGetData.size() > 0) {
+ pfrom->PushMessage(NetMsgType::GETDATA, vGetData);
+ }
+ }
}
- CheckBlockIndex();
+ CheckBlockIndex(chainparams.GetConsensus());
}
- else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing
+ else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
{
CBlock block;
vRecv >> block;
@@ -4458,11 +4925,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Such an unrequested block may still be processed, subject to the
// conditions in AcceptBlock().
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
- ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL);
+ ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
- pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) {
LOCK(cs_main);
@@ -4478,7 +4945,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
- else if ((strCommand == "getaddr") && (pfrom->fInbound))
+ else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound))
{
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
@@ -4487,8 +4954,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "mempool")
+ else if (strCommand == NetMsgType::MEMPOOL)
{
+ if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ {
+ LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
+ }
LOCK2(cs_main, pfrom->cs_filter);
std::vector<uint256> vtxid;
@@ -4496,23 +4969,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
vector<CInv> vInv;
BOOST_FOREACH(uint256& hash, vtxid) {
CInv inv(MSG_TX, hash);
- CTransaction tx;
- bool fInMemPool = mempool.lookup(hash, tx);
- if (!fInMemPool) continue; // another thread removed since queryHashes, maybe...
- if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) ||
- (!pfrom->pfilter))
- vInv.push_back(inv);
+ if (pfrom->pfilter) {
+ CTransaction tx;
+ bool fInMemPool = mempool.lookup(hash, tx);
+ if (!fInMemPool) continue; // another thread removed since queryHashes, maybe...
+ if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue;
+ }
+ vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
- pfrom->PushMessage("inv", vInv);
+ pfrom->PushMessage(NetMsgType::INV, vInv);
vInv.clear();
}
}
if (vInv.size() > 0)
- pfrom->PushMessage("inv", vInv);
+ pfrom->PushMessage(NetMsgType::INV, vInv);
}
- else if (strCommand == "ping")
+ else if (strCommand == NetMsgType::PING)
{
if (pfrom->nVersion > BIP0031_VERSION)
{
@@ -4529,12 +5003,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// 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);
+ pfrom->PushMessage(NetMsgType::PONG, nonce);
}
}
- else if (strCommand == "pong")
+ else if (strCommand == NetMsgType::PONG)
{
int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
@@ -4591,7 +5065,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (fAlerts && strCommand == "alert")
+ else if (fAlerts && strCommand == NetMsgType::ALERT)
{
CAlert alert;
vRecv >> alert;
@@ -4599,7 +5073,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
uint256 alertHash = alert.GetHash();
if (pfrom->setKnown.count(alertHash) == 0)
{
- if (alert.ProcessAlert(Params().AlertKey()))
+ if (alert.ProcessAlert(chainparams.AlertKey()))
{
// Relay
pfrom->setKnown.insert(alertHash);
@@ -4622,22 +5096,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (!(nLocalServices & NODE_BLOOM) &&
- (strCommand == "filterload" ||
- strCommand == "filteradd" ||
- strCommand == "filterclear") &&
- //TODO: Remove this line after reasonable network upgrade
- pfrom->nVersion >= NO_BLOOM_VERSION)
- {
- if (pfrom->nVersion >= NO_BLOOM_VERSION)
- Misbehaving(pfrom->GetId(), 100);
- //TODO: Enable this after reasonable network upgrade
- //else
- // pfrom->fDisconnect = true;
- }
-
-
- else if (strCommand == "filterload")
+ else if (strCommand == NetMsgType::FILTERLOAD)
{
CBloomFilter filter;
vRecv >> filter;
@@ -4656,7 +5115,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "filteradd")
+ else if (strCommand == NetMsgType::FILTERADD)
{
vector<unsigned char> vData;
vRecv >> vData;
@@ -4676,7 +5135,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "filterclear")
+ else if (strCommand == NetMsgType::FILTERCLEAR)
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
@@ -4685,7 +5144,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (strCommand == "reject")
+ else if (strCommand == NetMsgType::REJECT)
{
if (fDebug) {
try {
@@ -4695,7 +5154,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
ostringstream ss;
ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
- if (strMsg == "block" || strMsg == "tx")
+ if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
{
uint256 hash;
vRecv >> hash;
@@ -4723,6 +5182,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// requires LOCK(cs_vRecvMsg)
bool ProcessMessages(CNode* pfrom)
{
+ const CChainParams& chainparams = Params();
//if (fDebug)
// LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size());
@@ -4737,7 +5197,7 @@ bool ProcessMessages(CNode* pfrom)
bool fOk = true;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(pfrom);
+ ProcessGetData(pfrom, chainparams.GetConsensus());
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return fOk;
@@ -4764,7 +5224,7 @@ bool ProcessMessages(CNode* pfrom)
it++;
// Scan for message start
- if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) {
+ if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), MESSAGE_START_SIZE) != 0) {
LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
fOk = false;
break;
@@ -4772,7 +5232,7 @@ bool ProcessMessages(CNode* pfrom)
// Read header
CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(Params().MessageStart()))
+ if (!hdr.IsValid(chainparams.MessageStart()))
{
LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
continue;
@@ -4802,7 +5262,7 @@ bool ProcessMessages(CNode* pfrom)
}
catch (const std::ios_base::failure& e)
{
- pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message"));
+ pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message"));
if (strstr(e.what(), "end of data"))
{
// Allow exceptions from under-length message on vRecv
@@ -4841,7 +5301,7 @@ bool ProcessMessages(CNode* pfrom)
}
-bool SendMessages(CNode* pto, bool fSendTrickle)
+bool SendMessages(CNode* pto)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
{
@@ -4870,11 +5330,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->nPingUsecStart = GetTimeMicros();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
- pto->PushMessage("ping", nonce);
+ pto->PushMessage(NetMsgType::PING, nonce);
} else {
// Peer is too old to support ping command with nonce, pong will never arrive.
pto->nPingNonceSent = 0;
- pto->PushMessage("ping");
+ pto->PushMessage(NetMsgType::PING);
}
}
@@ -4883,28 +5343,17 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
return true;
// Address refresh broadcast
- static int64_t nLastRebroadcast;
- if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- {
- // Periodically clear addrKnown to allow refresh broadcasts
- if (nLastRebroadcast)
- pnode->addrKnown.reset();
-
- // Rebroadcast our address
- AdvertizeLocal(pnode);
- }
- if (!vNodes.empty())
- nLastRebroadcast = GetTime();
+ int64_t nNow = GetTimeMicros();
+ if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ AdvertizeLocal(pto);
+ pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
//
// Message: addr
//
- if (fSendTrickle)
- {
+ if (pto->nNextAddrSend < nNow) {
+ pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
@@ -4916,14 +5365,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
{
- pto->PushMessage("addr", vAddr);
+ pto->PushMessage(NetMsgType::ADDR, vAddr);
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
- pto->PushMessage("addr", vAddr);
+ pto->PushMessage(NetMsgType::ADDR, vAddr);
}
CNodeState &state = *State(pto->GetId());
@@ -4943,7 +5392,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
}
BOOST_FOREACH(const CBlockReject& reject, state.rejects)
- pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
+ pto->PushMessage(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
state.rejects.clear();
// Start block sync
@@ -4955,9 +5404,18 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
nSyncStarted++;
- CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader;
+ const CBlockIndex *pindexStart = pindexBestHeader;
+ /* If possible, start at the block preceding the currently
+ best known header. This ensures that we always get a
+ non-empty list of headers back as long as the peer
+ is up-to-date. With a non-empty response, we can initialise
+ the peer's known best block. This wouldn't be possible
+ if we requested starting at pindexBestHeader and
+ got back an empty response. */
+ if (pindexStart->pprev)
+ pindexStart = pindexStart->pprev;
LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
- pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256());
+ pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256());
}
}
@@ -4970,17 +5428,116 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
}
//
+ // Try sending block announcements via headers
+ //
+ {
+ // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
+ // list of block hashes we're relaying, and our peer wants
+ // headers announcements, then find the first header
+ // not yet known to our peer but would connect, and send.
+ // If no header would connect, or if we have too many
+ // blocks, or if the peer doesn't want headers, just
+ // add all to the inv queue.
+ LOCK(pto->cs_inventory);
+ vector<CBlock> vHeaders;
+ bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
+ CBlockIndex *pBestIndex = NULL; // last header queued for delivery
+ ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
+
+ if (!fRevertToInv) {
+ bool fFoundStartingHeader = false;
+ // Try to find first header that our peer doesn't have, and
+ // then send all headers past that one. If we come across any
+ // headers that aren't on chainActive, give up.
+ BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
+ BlockMap::iterator mi = mapBlockIndex.find(hash);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // Bail out if we reorged away from this block
+ fRevertToInv = true;
+ break;
+ }
+ assert(pBestIndex == NULL || pindex->pprev == pBestIndex);
+ pBestIndex = pindex;
+ if (fFoundStartingHeader) {
+ // add this to the headers message
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else if (PeerHasHeader(&state, pindex)) {
+ continue; // keep looking for the first new block
+ } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
+ // Peer doesn't have this header but they do have the prior one.
+ // Start sending headers.
+ fFoundStartingHeader = true;
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else {
+ // Peer doesn't have this header or the prior one -- nothing will
+ // connect, so bail out.
+ fRevertToInv = true;
+ break;
+ }
+ }
+ }
+ if (fRevertToInv) {
+ // If falling back to using an inv, just try to inv the tip.
+ // The last entry in vBlockHashesToAnnounce was our tip at some point
+ // in the past.
+ if (!pto->vBlockHashesToAnnounce.empty()) {
+ const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
+ BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+
+ // Warn if we're announcing a block that is not on the main chain.
+ // This should be very rare and could be optimized out.
+ // Just log for now.
+ if (chainActive[pindex->nHeight] != pindex) {
+ LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
+ hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ }
+
+ // If the peer announced this block to us, don't inv it back.
+ // (Since block announcements may not be via inv's, we can't solely rely on
+ // setInventoryKnown to track this.)
+ if (!PeerHasHeader(&state, pindex)) {
+ pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
+ pto->id, hashToAnnounce.ToString());
+ }
+ }
+ } else if (!vHeaders.empty()) {
+ if (vHeaders.size() > 1) {
+ LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
+ vHeaders.size(),
+ vHeaders.front().GetHash().ToString(),
+ vHeaders.back().GetHash().ToString(), pto->id);
+ } else {
+ LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ }
+ pto->PushMessage(NetMsgType::HEADERS, vHeaders);
+ state.pindexBestHeaderSent = pBestIndex;
+ }
+ pto->vBlockHashesToAnnounce.clear();
+ }
+
+ //
// Message: inventory
//
vector<CInv> vInv;
vector<CInv> vInvWait;
{
+ bool fSendTrickle = pto->fWhitelisted;
+ if (pto->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL);
+ }
LOCK(pto->cs_inventory);
- vInv.reserve(pto->vInventoryToSend.size());
+ vInv.reserve(std::min<size_t>(1000, pto->vInventoryToSend.size()));
vInvWait.reserve(pto->vInventoryToSend.size());
BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)
{
- if (pto->setInventoryKnown.count(inv))
+ if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash))
continue;
// trickle out tx inv to protect privacy
@@ -5001,24 +5558,22 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
}
}
- // returns true if wasn't already contained in the set
- if (pto->setInventoryKnown.insert(inv).second)
+ pto->filterInventoryKnown.insert(inv.hash);
+
+ vInv.push_back(inv);
+ if (vInv.size() >= 1000)
{
- vInv.push_back(inv);
- if (vInv.size() >= 1000)
- {
- pto->PushMessage("inv", vInv);
- vInv.clear();
- }
+ pto->PushMessage(NetMsgType::INV, vInv);
+ vInv.clear();
}
}
pto->vInventoryToSend = vInvWait;
}
if (!vInv.empty())
- pto->PushMessage("inv", vInv);
+ pto->PushMessage(NetMsgType::INV, vInv);
// Detect whether we're stalling
- int64_t nNow = GetTimeMicros();
+ nNow = GetTimeMicros();
if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
@@ -5084,14 +5639,17 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
- pto->PushMessage("getdata", vGetData);
+ pto->PushMessage(NetMsgType::GETDATA, vGetData);
vGetData.clear();
}
+ } else {
+ //If we're not going to ask, don't expect a response.
+ pto->setAskFor.erase(inv.hash);
}
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
- pto->PushMessage("getdata", vGetData);
+ pto->PushMessage(NetMsgType::GETDATA, vGetData);
}
return true;