From 7c041b3b91aa08a8f5863382b865a5174281ad03 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 10 Nov 2014 02:52:28 -0500 Subject: Check against MANDATORY flags prior to accepting to mempool Previously transactions were only tested again the STANDARD_SCRIPT_VERIFY_FLAGS prior to mempool acceptance, so any bugs in those flags that allowed actually-invalid transactions to pass would result in allowing invalid transactions into the mempool. Fortunately there is a second check in CreateNewBlock() that would prevent those transactions from being mined, resulting in an invalid block, however this could still be exploited as a DoS attack. --- src/main.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4aa49531b3..8d4a9e59f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1039,6 +1039,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } + + // Check again against just the consensus-critical mandatory script + // verification flags, in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks, however allowing such transactions into the mempool + // can be exploited as a DoS attack. + if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) + { + return error("AcceptToMemoryPool: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + } + // Store transaction in memory pool.addUnchecked(hash, entry); } -- cgit v1.2.3 From 63d1ae5556ea40dde0cca20addda4bba40005496 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 25 Nov 2014 16:26:20 +0100 Subject: Do all block index writes in a batch --- src/main.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 88fb31980f..832d7747f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1784,24 +1784,23 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { // First make sure all block and undo data is flushed to disk. FlushBlockFile(); // Then update all block file information (which may refer to block and undo files). - bool fileschanged = false; - for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - if (!pblocktree->WriteBlockFileInfo(*it, vinfoBlockFile[*it])) { - return state.Abort("Failed to write to block index"); + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return state.Abort("Files to write to block index database"); } - fileschanged = true; - setDirtyFileInfo.erase(it++); - } - if (fileschanged && !pblocktree->WriteLastBlockFile(nLastBlockFile)) { - return state.Abort("Failed to write to block index"); - } - for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(*it))) { - return state.Abort("Failed to write to block index"); - } - setDirtyBlockIndex.erase(it++); } - pblocktree->Sync(); // Finally flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) return state.Abort("Failed to write to coin database"); -- cgit v1.2.3 From 9dcd524f3248cb4fca5613a3c11dddcc7a0efbd7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 24 Nov 2014 14:51:10 +0100 Subject: Make IsSuperMajority a standalone function --- src/main.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 50e63e93e4..bc08c3ea29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,6 +67,13 @@ map mapOrphanTransactions; map > mapOrphanTransactionsByPrev; void EraseOrphansFor(NodeId peer); +/** + * Returns true if there are nRequired or more blocks of minVersion or above + * in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart + * and going backwards. + */ +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired); + /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; @@ -2479,8 +2486,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && - CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) + if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) { return state.Invalid(error("%s : rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); @@ -2501,8 +2507,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // 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): - if (block.nVersion >= 2 && - CBlockIndex::IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority())) + if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority())) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || @@ -2600,7 +2605,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return true; } -bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired) +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired) { unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority(); unsigned int nFound = 0; -- cgit v1.2.3 From 5ea3bc06d501594a36c97e20df2d446e87956aed Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 23 Nov 2014 14:07:38 +0100 Subject: Move remaining CBlockIndex methods to chain.cpp --- src/main.cpp | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bc08c3ea29..7533c32614 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2618,54 +2618,6 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned return (nFound >= nRequired); } -/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ -int static inline InvertLowestOne(int n) { return n & (n - 1); } - -/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */ -int static inline GetSkipHeight(int height) { - if (height < 2) - return 0; - - // Determine which height to jump back to. Any number strictly lower than height is acceptable, - // but the following expression seems to perform well in simulations (max 110 steps to go back - // up to 2**18 blocks). - return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); -} - -CBlockIndex* CBlockIndex::GetAncestor(int height) -{ - if (height > nHeight || height < 0) - return NULL; - - CBlockIndex* pindexWalk = this; - int heightWalk = nHeight; - while (heightWalk > height) { - int heightSkip = GetSkipHeight(heightWalk); - int heightSkipPrev = GetSkipHeight(heightWalk - 1); - if (heightSkip == height || - (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && - heightSkipPrev >= height))) { - // Only follow pskip if pprev->pskip isn't better than pskip->pprev. - pindexWalk = pindexWalk->pskip; - heightWalk = heightSkip; - } else { - pindexWalk = pindexWalk->pprev; - heightWalk--; - } - } - return pindexWalk; -} - -const CBlockIndex* CBlockIndex::GetAncestor(int height) const -{ - return const_cast(this)->GetAncestor(height); -} - -void CBlockIndex::BuildSkip() -{ - if (pprev) - pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); -} bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { -- cgit v1.2.3 From 26a6bae7537c73cb4b713293ce29b7c6d71a542d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Mon, 15 Dec 2014 11:06:15 +0100 Subject: Add time offset to getpeerinfo output --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7533c32614..d917c6e0ea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3450,7 +3450,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - AddTimeData(pfrom->addr, nTime); + int64_t nTimeOffset = nTime - GetTime(); + pfrom->nTimeOffset = nTimeOffset; + AddTimeData(pfrom->addr, nTimeOffset); } -- cgit v1.2.3 From bf7835c2713e9cc8356cd1a32bf99c40e222773b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Tue, 25 Nov 2014 18:54:36 +0100 Subject: Fix small typos in comments and error messages Rebased-From: 67b2d819cdf6181e7f016e5366ce7479830893bd Github-Pull: #5404 --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7533c32614..4160e4b788 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1025,7 +1025,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa hash.ToString(), nFees, txMinFee), REJECT_INSUFFICIENTFEE, "insufficient fee"); - // Continuously rate-limit free (really, very-low-fee)transactions + // 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)) @@ -1050,7 +1050,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: : insane fees %s, %d > %d", + return error("AcceptToMemoryPool: insane fees %s, %d > %d", hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); @@ -1058,7 +1058,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); + return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); } // Store transaction in memory pool.addUnchecked(hash, entry); @@ -2988,7 +2988,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex() : genesis block not accepted"); if (!ActivateBestChain(state, &block)) return error("LoadBlockIndex() : genesis block cannot be activated"); - // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data + // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } catch(std::runtime_error &e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); -- cgit v1.2.3 From 27df4123c433e5ad4e5592f0a8fbc40ca933865b Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Sun, 7 Dec 2014 13:29:06 +0100 Subject: make all catch() arguments const - I saw this on http://en.cppreference.com/w/cpp/language/try_catch and thought it would be a good idea - also unify used format to better be able to search for exception uses in our codebase --- src/main.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4160e4b788..9c038f90fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1091,7 +1091,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock file >> header; fseek(file.Get(), postx.nTxOffset, SEEK_CUR); file >> txOut; - } catch (std::exception &e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } hashBlock = header.GetHash(); @@ -1174,7 +1174,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) try { filein >> block; } - catch (std::exception &e) { + catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } @@ -2598,7 +2598,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Abort("Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock() : ReceivedBlockTransactions failed"); - } catch(std::runtime_error &e) { + } catch (const std::runtime_error& e) { return state.Abort(std::string("System error: ") + e.what()); } @@ -2990,7 +2990,7 @@ bool InitBlockIndex() { 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); - } catch(std::runtime_error &e) { + } catch (const std::runtime_error& e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); } } @@ -3030,7 +3030,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) blkdat >> nSize; if (nSize < 80 || nSize > MAX_BLOCK_SIZE) continue; - } catch (const std::exception &) { + } catch (const std::exception&) { // no valid block header found; don't complain break; } @@ -3090,11 +3090,11 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) mapBlocksUnknownParent.erase(it); } } - } catch (std::exception &e) { + } catch (const std::exception& e) { LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); } } - } catch(std::runtime_error &e) { + } catch (const std::runtime_error& e) { AbortNode(std::string("System error: ") + e.what()); } if (nLoaded > 0) @@ -4088,7 +4088,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ss << ": hash " << hash.ToString(); } LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); - } catch (std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { // Avoid feedback loops by preventing reject messages from triggering a new reject message. LogPrint("net", "Unparseable reject message received\n"); } @@ -4192,7 +4192,7 @@ bool ProcessMessages(CNode* pfrom) fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); boost::this_thread::interruption_point(); } - catch (std::ios_base::failure& e) + catch (const std::ios_base::failure& e) { pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); if (strstr(e.what(), "end of data")) @@ -4210,10 +4210,10 @@ bool ProcessMessages(CNode* pfrom) PrintExceptionContinue(&e, "ProcessMessages()"); } } - catch (boost::thread_interrupted) { + catch (const boost::thread_interrupted&) { throw; } - catch (std::exception& e) { + catch (const std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); @@ -4507,7 +4507,7 @@ bool CBlockUndo::ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock filein >> *this; filein >> hashChecksum; } - catch (std::exception &e) { + catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } -- cgit v1.2.3 From 84857e87e42e412336ea60d0f8544c1679bab827 Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Sun, 7 Dec 2014 12:57:11 +0100 Subject: add missing CAutoFile::IsNull() check in main Rebased-From: ccd056a30db0ba5e04b858a682907b378e464d34 Github-Pull: #5437 --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9c038f90fa..3e0741ae7b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1086,6 +1086,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); CBlockHeader header; try { file >> header; -- cgit v1.2.3 From f914f1a746d7f91951c1da262a4a749dd3ebfa71 Mon Sep 17 00:00:00 2001 From: sandakersmann Date: Wed, 17 Dec 2014 02:47:57 +0100 Subject: Added "Core" to copyright headers Github-Pull: #5494 Rebased-From: 15de949bb9277e442302bdd8dee299a8d6deee60 --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3e0741ae7b..dd71c53d9d 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 developers +// Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From 34970223472c9e83689a1c710eebc7c16f152b02 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 11 Dec 2014 13:35:14 +0100 Subject: Reject headers that build on an invalid parent --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ad3f70eed9..762a404032 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -449,6 +449,10 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorIsValid(BLOCK_VALID_TREE)) { + // We consider the chain that this peer is on invalid. + return; + } if (pindex->nStatus & BLOCK_HAVE_DATA) { if (pindex->nChainTx) state->pindexLastCommonBlock = pindex; @@ -2563,6 +2567,8 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (mi == mapBlockIndex.end()) return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk"); pindexPrev = (*mi).second; + if (pindexPrev->nStatus & BLOCK_FAILED_MASK) + return state.DoS(100, error("%s : prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } if (!ContextualCheckBlockHeader(block, state, pindexPrev)) -- cgit v1.2.3 From c90770430d7c1eb7ece2d4ddb987b0f2210fd86f Mon Sep 17 00:00:00 2001 From: Adam Weiss Date: Thu, 18 Dec 2014 18:28:29 -0500 Subject: DOS: Respect max per-peer blocks in flight limit Don't allow immediate inv driven block downloads if a peer already has MAX_BLOCKS_IN_TRANSIT_PER_PEER active downloads. Prevents bogus inv spam from blowing up block transfer tracking data structures. --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9c038f90fa..accdfd10b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3574,7 +3574,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // 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); - if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - Params().TargetSpacing() * 20) { + CNodeState *nodestate = State(pfrom->GetId()); + if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - Params().TargetSpacing() * 20 && + 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 // later (within the same cs_main lock, though). -- cgit v1.2.3 From c444c620c62c51d65f7de0b2a3c351e61ab1e388 Mon Sep 17 00:00:00 2001 From: jtimon Date: Sun, 19 Oct 2014 02:57:02 +0200 Subject: Decouple CCoins from CTxInUndo --- src/main.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2410230ef4..3f775e3d33 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1383,9 +1383,20 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach if (!tx.IsCoinBase()) { txundo.vprevout.reserve(tx.vin.size()); BOOST_FOREACH(const CTxIn &txin, tx.vin) { - txundo.vprevout.push_back(CTxInUndo()); - bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout, txundo.vprevout.back()); - assert(ret); + CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); + unsigned nPos = txin.prevout.n; + + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) + assert(false); + // mark an outpoint spent, and construct undo information + txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos])); + coins->Spend(nPos); + if (coins->vout.size() == 0) { + CTxInUndo& undo = txundo.vprevout.back(); + undo.nHeight = coins->nHeight; + undo.fCoinBase = coins->fCoinBase; + undo.nVersion = coins->nVersion; + } } } -- cgit v1.2.3 From d7621ccf9d064db19de742ed1a0ac59fffa06c0d Mon Sep 17 00:00:00 2001 From: jtimon Date: Mon, 27 Oct 2014 16:07:50 +0100 Subject: Decouple miner.o and txmempool.o from CTxUndo --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3f775e3d33..bcd3a5ff05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1404,6 +1404,12 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); } +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) +{ + CTxUndo txundo; + UpdateCoins(tx, state, inputs, txundo, nHeight); +} + bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { -- cgit v1.2.3 From e035c6a7371027fe33c460ee2d0946a7f1e4e592 Mon Sep 17 00:00:00 2001 From: jtimon Date: Mon, 27 Oct 2014 14:35:52 +0100 Subject: Decouple CBlockUndo from CDiskBlockPos --- src/main.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bcd3a5ff05..ec24e55ce1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1520,7 +1520,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi return true; } - +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock); +bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock); bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { @@ -1535,7 +1536,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) return error("DisconnectBlock() : no undo data available"); - if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock() : failure reading undo data"); if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) @@ -1777,7 +1778,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) + if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash())) return state.Abort("Failed to write undo data"); // update nUndoPos in block index @@ -2943,7 +2944,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlockUndo undo; CDiskBlockPos pos = pindex->GetUndoPos(); if (!pos.IsNull()) { - if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash())) return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -4509,44 +4510,44 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } -bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock) { // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) - return error("CBlockUndo::WriteToDisk : OpenUndoFile failed"); + return error("%s : OpenUndoFile failed", __func__); // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); + unsigned int nSize = fileout.GetSerializeSize(blockundo); fileout << FLATDATA(Params().MessageStart()) << nSize; // Write undo data long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) - return error("CBlockUndo::WriteToDisk : ftell failed"); + return error("%s : ftell failed", __func__); pos.nPos = (unsigned int)fileOutPos; - fileout << *this; + fileout << blockundo; // calculate & write checksum CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); hasher << hashBlock; - hasher << *this; + hasher << blockundo; fileout << hasher.GetHash(); return true; } -bool CBlockUndo::ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) +bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) { // Open history file to read CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("CBlockUndo::ReadFromDisk : OpenBlockFile failed"); + return error("%s : OpenBlockFile failed", __func__); // Read block uint256 hashChecksum; try { - filein >> *this; + filein >> blockundo; filein >> hashChecksum; } catch (const std::exception& e) { @@ -4556,9 +4557,9 @@ bool CBlockUndo::ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock // Verify checksum CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); hasher << hashBlock; - hasher << *this; + hasher << blockundo; if (hashChecksum != hasher.GetHash()) - return error("CBlockUndo::ReadFromDisk : Checksum mismatch"); + return error("%s : Checksum mismatch", __func__); return true; } -- cgit v1.2.3 From 937ba572d0758891f43c658d7d98d7bd747c3ce4 Mon Sep 17 00:00:00 2001 From: jtimon Date: Mon, 27 Oct 2014 14:42:49 +0100 Subject: MOVEONLY: CBlockUndo from main.h to undo.h --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ec24e55ce1..24ed487af0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" +#include "undo.h" #include "util.h" #include "utilmoneystr.h" -- cgit v1.2.3 From 87fb31086d70e4902170976ce261a01c8c380c00 Mon Sep 17 00:00:00 2001 From: jtimon Date: Wed, 3 Dec 2014 00:39:03 +0100 Subject: Move UndoWriteToDisk() and UndoReadFromDisk() to anon namespace --- src/main.cpp | 114 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 57 insertions(+), 57 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 24ed487af0..f6033566ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1521,8 +1521,63 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi return true; } -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock); -bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock); +namespace { + +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock) +{ + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s : OpenUndoFile failed", __func__); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(blockundo); + fileout << FLATDATA(Params().MessageStart()) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s : ftell failed", __func__); + pos.nPos = (unsigned int)fileOutPos; + fileout << blockundo; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + fileout << hasher.GetHash(); + + return true; +} + +bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) +{ + // Open history file to read + CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s : OpenBlockFile failed", __func__); + + // Read block + uint256 hashChecksum; + try { + filein >> blockundo; + filein >> hashChecksum; + } + catch (const std::exception& e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + if (hashChecksum != hasher.GetHash()) + return error("%s : Checksum mismatch", __func__); + + return true; +} + +} // anon namespace bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { @@ -4510,61 +4565,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) return true; } - -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock) -{ - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s : OpenUndoFile failed", __func__); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(Params().MessageStart()) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("%s : ftell failed", __func__); - pos.nPos = (unsigned int)fileOutPos; - fileout << blockundo; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - fileout << hasher.GetHash(); - - return true; -} - -bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) -{ - // Open history file to read - CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s : OpenBlockFile failed", __func__); - - // Read block - uint256 hashChecksum; - try { - filein >> blockundo; - filein >> hashChecksum; - } - catch (const std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); - } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - if (hashChecksum != hasher.GetHash()) - return error("%s : Checksum mismatch", __func__); - - return true; -} - std::string CBlockFileInfo::ToString() const { return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } -- cgit v1.2.3 From 1c52aad540ec1370db60fd68fc3485413e3cb8e1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 23 Dec 2014 23:50:21 +0100 Subject: Require sufficent priority for relay of free transactions --- src/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2410230ef4..011189544c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1029,6 +1029,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa hash.ToString(), nFees, txMinFee), REJECT_INSUFFICIENTFEE, "insufficient fee"); + // 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))) { + 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. @@ -1048,7 +1053,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "insufficient priority"); + REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } -- cgit v1.2.3 From de236f57c68c2c29d148a2df9e019f431cee3cb7 Mon Sep 17 00:00:00 2001 From: Pavel Vasin Date: Mon, 29 Dec 2014 18:48:19 +0300 Subject: clarify obscure uses of EvalScript() The 3rd argument of EvalScript() is the unsigned int flags, not a bool. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2410230ef4..3b9a0d4cf0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -762,7 +762,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // IsStandard() will have already returned false // and this method isn't called. vector > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker())) + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) return false; if (whichType == TX_SCRIPTHASH) -- cgit v1.2.3 From 4f1ee565b5669fece4178d590f37a577c4062420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Tue, 30 Dec 2014 23:02:46 +0100 Subject: Clean AcceptToMemoryPool error messages --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2410230ef4..0e199fe603 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -926,11 +926,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("AcceptToMemoryPool: : CheckTransaction failed"); + return error("AcceptToMemoryPool: CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), + return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) @@ -1000,7 +1000,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check for non-standard pay-to-script-hash in inputs if (Params().RequireStandard() && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: : nonstandard transaction input"); + return error("AcceptToMemoryPool: nonstandard transaction input"); // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1076,7 +1076,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // can be exploited as a DoS attack. if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: : BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } // Store transaction in memory -- cgit v1.2.3 From 1b178a7f966cd3649b7f0a52ffc4b8ef4c227900 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 31 Dec 2014 03:28:05 +0000 Subject: Bugfix: ConnectBlock: In case the genesis block gets in with fJustCheck, behave correctly --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 70e3973e6c..7c5b7401ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1616,7 +1616,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { - view.SetBestBlock(pindex->GetBlockHash()); + if (!fJustCheck) + view.SetBestBlock(pindex->GetBlockHash()); return true; } -- cgit v1.2.3 From 23f3435962f0cfa57a95b293094fe9d0fe9d968c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 1 Jan 2015 11:35:29 +0000 Subject: Rename MAX_TX_SIGOPS to MAX_STANDARD_TX_SIGOPS to match similar policy constant MAX_STANDARD_TX_SIZE --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5c0f0116d6..7a75ca2cf6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1005,15 +1005,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // 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_TX_SIGOPS is less than + // 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_TX_SIGOPS) + if (nSigOps > MAX_STANDARD_TX_SIGOPS) return state.DoS(0, error("AcceptToMemoryPool : too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_TX_SIGOPS), + hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); CAmount nValueOut = tx.GetValueOut(); -- cgit v1.2.3 From a3241998e1e300e87e7c57d92c2478f67ec91db6 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 19 Dec 2014 15:21:29 -0500 Subject: namespace: remove boost namespace pollution --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f02a6a12ef..f768f1e29f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,6 @@ #include #include -using namespace boost; using namespace std; #if defined(NDEBUG) @@ -2788,7 +2787,7 @@ bool AbortNode(const std::string &strMessage, const std::string &userMessage) { bool CheckDiskSpace(uint64_t nAdditionalBytes) { - uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) -- cgit v1.2.3 From 70477a0bdf6eb6d123ce256f064bbd3bc356c82a Mon Sep 17 00:00:00 2001 From: Thomas Zander Date: Sun, 28 Dec 2014 22:39:53 +0100 Subject: On close of splashscreen interrupt verifyDB With the splashscreen being able to be closed it is possible to shutdown during the lengthy verifyDB method. (Takes about a minute on my machine). This change allows us to shutdown much sooner. Github-Pull: #5557 --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f02a6a12ef..4fcb4f093a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3017,6 +3017,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } else nGoodTransactions += block.vtx.size(); } + if (ShutdownRequested()) + return true; } if (pindexFailure) return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); -- cgit v1.2.3 From e41345790f1041f5c5e5605d73a0af174769aa55 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 4 Jan 2015 19:11:44 +0100 Subject: Catch LevelDB errors during flush --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2410230ef4..6bc1cefede 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1807,6 +1807,7 @@ enum FlushStateMode { bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { LOCK(cs_main); static int64_t nLastWrite = 0; + try { if ((mode == FLUSH_STATE_ALWAYS) || ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) { @@ -1846,6 +1847,9 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } nLastWrite = GetTimeMicros(); } + } catch (const std::runtime_error& e) { + return state.Abort(std::string("System error while flushing: ") + e.what()); + } return true; } -- cgit v1.2.3 From 0ea28baeb8e83a1cc7870090ac20eed9d7ca8539 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Sat, 20 Dec 2014 17:04:21 -0500 Subject: Reject non-final txs even in testnet/regtest Previous behavior with IsFinalTx() being an IsStandard() rule was rather confusing and interferred with testing of protocols that depended on nLockTime. --- src/main.cpp | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index dd71c53d9d..1ac1e8d534 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -620,34 +620,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) bool IsStandardTx(const CTransaction& tx, string& reason) { - AssertLockHeld(cs_main); if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { reason = "version"; return false; } - // Treat non-final transactions as non-standard to prevent a specific type - // of double-spend attack, as well as DoS attacks. (if the transaction - // can't be mined, the attacker isn't expending resources broadcasting it) - // Basically we don't want to propagate transactions that can't be included in - // the next block. - // - // However, IsFinalTx() is confusing... Without arguments, it uses - // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() - // is set to the value of nHeight in the block. However, 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(). - // - // Timestamps on the other hand don't get any special treatment, because we - // can't know what timestamp the next block will have, and there aren't - // timestamp applications where it matters. - if (!IsFinalTx(tx, chainActive.Height() + 1)) { - reason = "non-final"; - return false; - } - // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions @@ -936,6 +913,26 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa error("AcceptToMemoryPool : nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); + // 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. + // + // However, IsFinalTx() is confusing... Without arguments, it uses + // chainActive.Height() to evaluate nLockTime; when a block is accepted, + // chainActive.Height() is set to the value of nHeight in the block. + // However, 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(). + // + // Timestamps on the other hand don't get any special treatment, because we + // can't know what timestamp the next block will have, and there aren't + // timestamp applications where it matters. + if (!IsFinalTx(tx, chainActive.Height() + 1)) + return state.DoS(0, + error("AcceptToMemoryPool : non-final"), + REJECT_NONSTANDARD, "non-final"); + // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) -- cgit v1.2.3 From 4f1524966a99c83db8c0c9f1ff74789253a1171e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 15 Dec 2014 09:11:16 +0100 Subject: Replace direct use of 0 with SetNull and IsNull Replace x=0 with .SetNull(), x==0 with IsNull(), x!=0 with !IsNull(). Replace uses of uint256(0) with uint256(). --- src/main.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4fcb4f093a..c392138ff3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -261,7 +261,7 @@ struct CNodeState { nMisbehavior = 0; fShouldBan = false; pindexBestKnownBlock = NULL; - hashLastUnknownBlock = uint256(0); + hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = NULL; fSyncStarted = false; nStallingSince = 0; @@ -349,12 +349,12 @@ void ProcessBlockAvailability(NodeId nodeid) { CNodeState *state = State(nodeid); assert(state != NULL); - if (state->hashLastUnknownBlock != 0) { + if (!state->hashLastUnknownBlock.IsNull()) { BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = itOld->second; - state->hashLastUnknownBlock = uint256(0); + state->hashLastUnknownBlock.SetNull(); } } } @@ -1712,7 +1712,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return false; // verify that the view's current state corresponds to the previous block - uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its transactions @@ -2835,7 +2835,7 @@ boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char CBlockIndex * InsertBlockIndex(uint256 hash) { - if (hash == 0) + if (hash.IsNull()) return NULL; // Return existing @@ -3369,7 +3369,7 @@ void static ProcessGetData(CNode* pfrom) vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); pfrom->PushMessage("inv", vInv); - pfrom->hashContinue = 0; + pfrom->hashContinue.SetNull(); } } } @@ -3604,7 +3604,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the setAddrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; - if (hashSalt == 0) + if (hashSalt.IsNull()) hashSalt = GetRandHash(); uint64_t hashAddr = addr.GetHash(); uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); @@ -3738,7 +3738,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; - LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); + LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) @@ -3954,7 +3954,7 @@ 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(0)); + pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } } @@ -4452,7 +4452,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) nSyncStarted++; CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); + pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } } @@ -4483,7 +4483,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { // 1/4 of tx invs blast to all immediately static uint256 hashSalt; - if (hashSalt == 0) + if (hashSalt.IsNull()) hashSalt = GetRandHash(); uint256 hashRand = inv.hash ^ hashSalt; hashRand = Hash(BEGIN(hashRand), END(hashRand)); -- cgit v1.2.3 From 34cdc41128eee5da0be9c5e17b3c24b1f91a1957 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 16 Dec 2014 14:50:05 +0100 Subject: String conversions uint256 -> uint256S If uint256() constructor takes a string, uint256(0) will become dangerous when uint256 does not take integers anymore (it will go through std::string(const char*) making a NULL string, and the explicit keyword is no help). --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c392138ff3..77d2dcb204 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1738,8 +1738,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. - !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || - (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); + !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || + (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { BOOST_FOREACH(const CTransaction& tx, block.vtx) { const CCoins* coins = view.AccessCoins(tx.GetHash()); -- cgit v1.2.3 From 734f85c4f0b40efd3f6c0367683c1bab1a2a7b19 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 16 Dec 2014 15:43:03 +0100 Subject: Use arith_uint256 where necessary Also add conversion from/to uint256 where needed. --- src/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 77d2dcb204..d115f763f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "main.h" +#include "arith_uint256.h" #include "addrman.h" #include "alert.h" #include "chainparams.h" @@ -3607,7 +3608,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (hashSalt.IsNull()) hashSalt = GetRandHash(); uint64_t hashAddr = addr.GetHash(); - uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); + uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap mapMix; BOOST_FOREACH(CNode* pnode, vNodes) @@ -3616,7 +3617,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, continue; unsigned int nPointer; memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = hashRand ^ nPointer; + uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); hashKey = Hash(BEGIN(hashKey), END(hashKey)); mapMix.insert(make_pair(hashKey, pnode)); } @@ -4485,9 +4486,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) static uint256 hashSalt; if (hashSalt.IsNull()) hashSalt = GetRandHash(); - uint256 hashRand = inv.hash ^ hashSalt; + uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((hashRand & 3) != 0); + bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); if (fTrickleWait) { -- cgit v1.2.3 From 916130348ca803d762db912307b247f60f9aacd6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 6 Jan 2015 17:05:46 +0100 Subject: Introduce 10 minute block download timeout This will disconnect peers that do not transfer a block in 10 minutes, plus 5 minutes for every previously queued block with validated headers (accomodating downstream bandwidth down to a few kilobytes per second - below that the node would have trouble staying synchronized anyway). --- src/main.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e1a0973352..e7a3392966 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,9 +139,14 @@ namespace { uint256 hash; CBlockIndex *pindex; //! Optional. int64_t nTime; //! Time of "getdata" request in microseconds. + int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested. + bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. }; map::iterator> > mapBlocksInFlight; + /** Number of blocks in flight with validated headers. */ + int nQueuedValidatedHeaders = 0; + /** Number of preferable block download peers. */ int nPreferredDownload = 0; @@ -323,6 +328,7 @@ void MarkBlockAsReceived(const uint256& hash) { map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); + nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; @@ -338,7 +344,8 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - QueuedBlock newentry = {hash, pindex, GetTimeMicros()}; + QueuedBlock newentry = {hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL}; + nQueuedValidatedHeaders += newentry.fValidatedHeaders; list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; mapBlocksInFlight[hash] = std::make_pair(nodeid, it); @@ -4527,6 +4534,15 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } + // In case there is a block that has been in flight from this peer for (1 + 0.5 * N) times the block interval + // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to + // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link + // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes + // to unreasonably increase our timeout. + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (2 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); + pto->fDisconnect = true; + } // // Message: getdata (blocks) -- cgit v1.2.3 From 3ff735c99ae75c21397079f49859b81e89a2f5f8 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 12 Jan 2015 09:53:10 -0800 Subject: Increase block download timeout base from 10 to 20 minutes. This harmonizes the block fetch timeout with the existing ping timeout and eliminates a guaranteed eventual failure from congestion collapse for a network operating right at its limit. It's unlikely that we wouldn't suffer other failures if we were really anywhere near the network's limit, and a complete avoidance of congestion collapse risk requires (I think) an exponential back-off. So this isn't a major concern, but I think it's also useful for reducing the complexity of understanding out timeouts. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7cc69c318b..0f536cbc6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4534,12 +4534,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } - // In case there is a block that has been in flight from this peer for (1 + 0.5 * N) times the block interval + // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (2 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); pto->fDisconnect = true; } -- cgit v1.2.3 From 5262fde0ecd19f1febbfcd488f2be41c5dffd047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Thu, 8 Jan 2015 11:44:25 +0100 Subject: Remove whitespaces before double colon in errors and logs --- src/main.cpp | 190 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 95 insertions(+), 95 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7cc69c318b..629e0dcbc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -820,14 +820,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction() : vin empty"), + return state.DoS(10, error("CheckTransaction(): vin empty"), REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction() : vout empty"), + return state.DoS(10, error("CheckTransaction(): vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckTransaction() : size limits failed"), + return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values @@ -835,14 +835,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), + return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), + return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction() : txout total out of range"), + return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } @@ -851,7 +851,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction() : duplicate inputs"), + return state.DoS(100, error("CheckTransaction(): duplicate inputs"), REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } @@ -859,14 +859,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction() : coinbase script size"), + return state.DoS(100, error("CheckTransaction(): coinbase script size"), REJECT_INVALID, "bad-cb-length"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction() : prevout is null"), + return state.DoS(10, error("CheckTransaction(): prevout is null"), REJECT_INVALID, "bad-txns-prevout-null"); } @@ -922,7 +922,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason)) return state.DoS(0, - error("AcceptToMemoryPool : nonstandard transaction: %s", reason), + error("AcceptToMemoryPool: nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); // Only accept nLockTime-using transactions that can be mined in the next @@ -942,7 +942,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // timestamp applications where it matters. if (!IsFinalTx(tx, chainActive.Height() + 1)) return state.DoS(0, - error("AcceptToMemoryPool : non-final"), + error("AcceptToMemoryPool: non-final"), REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? @@ -991,7 +991,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), + return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), REJECT_DUPLICATE, "bad-txns-inputs-spent"); // Bring the best block into scope @@ -1016,7 +1016,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) return state.DoS(0, - error("AcceptToMemoryPool : too many sigops %s, %d > %d", + error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); @@ -1030,7 +1030,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // 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, error("AcceptToMemoryPool : not enough fees %s, %d < %d", + return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee), REJECT_INSUFFICIENTFEE, "insufficient fee"); @@ -1057,7 +1057,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // -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) - return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; @@ -1123,11 +1123,11 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock fseek(file.Get(), postx.nTxOffset, SEEK_CUR); file >> txOut; } catch (const std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } hashBlock = header.GetHash(); if (txOut.GetHash() != hash) - return error("%s : txid mismatch", __func__); + return error("%s: txid mismatch", __func__); return true; } } @@ -1176,7 +1176,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) - return error("WriteBlockToDisk : OpenBlockFile failed"); + return error("WriteBlockToDisk: OpenBlockFile failed"); // Write index header unsigned int nSize = fileout.GetSerializeSize(block); @@ -1185,7 +1185,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) - return error("WriteBlockToDisk : ftell failed"); + return error("WriteBlockToDisk: ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; @@ -1199,19 +1199,19 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("ReadBlockFromDisk : OpenBlockFile failed"); + return error("ReadBlockFromDisk: OpenBlockFile failed"); // Read block try { filein >> block; } catch (const std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } // Check the header if (!CheckProofOfWork(block.GetHash(), block.nBits)) - return error("ReadBlockFromDisk : Errors in block header"); + return error("ReadBlockFromDisk: Errors in block header"); return true; } @@ -1221,7 +1221,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) return false; if (block.GetHash() != pindex->GetBlockHash()) - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index"); return true; } @@ -1423,7 +1423,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { - return ::error("CScriptCheck() : %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; } @@ -1438,7 +1438,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString())); + return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. @@ -1456,31 +1456,31 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi if (coins->IsCoinBase()) { if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) return state.Invalid( - error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs() : txin values out of range"), + return state.DoS(100, error("CheckInputs(): txin values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in (%s) < value out (%s)", + return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), REJECT_INVALID, "bad-txns-in-belowout"); // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString()), + return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs() : nFees out of range"), + return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); // The first loop above does all the inexpensive checks. @@ -1537,7 +1537,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) - return error("%s : OpenUndoFile failed", __func__); + return error("%s: OpenUndoFile failed", __func__); // Write index header unsigned int nSize = fileout.GetSerializeSize(blockundo); @@ -1546,7 +1546,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint // Write undo data long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) - return error("%s : ftell failed", __func__); + return error("%s: ftell failed", __func__); pos.nPos = (unsigned int)fileOutPos; fileout << blockundo; @@ -1564,7 +1564,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin // Open history file to read CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("%s : OpenBlockFile failed", __func__); + return error("%s: OpenBlockFile failed", __func__); // Read block uint256 hashChecksum; @@ -1573,7 +1573,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin filein >> hashChecksum; } catch (const std::exception& e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } // Verify checksum @@ -1581,7 +1581,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin hasher << hashBlock; hasher << blockundo; if (hashChecksum != hasher.GetHash()) - return error("%s : Checksum mismatch", __func__); + return error("%s: Checksum mismatch", __func__); return true; } @@ -1600,12 +1600,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex CBlockUndo blockUndo; CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) - return error("DisconnectBlock() : no undo data available"); + return error("DisconnectBlock(): no undo data available"); if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) - return error("DisconnectBlock() : failure reading undo data"); + return error("DisconnectBlock(): failure reading undo data"); if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) - return error("DisconnectBlock() : block and undo data inconsistent"); + return error("DisconnectBlock(): block and undo data inconsistent"); // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { @@ -1628,7 +1628,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (outsBlock.nVersion < 0) outs->nVersion = outsBlock.nVersion; if (*outs != outsBlock) - fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); + fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); // remove outputs outs->Clear(); @@ -1638,7 +1638,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; if (txundo.vprevout.size() != tx.vin.size()) - return error("DisconnectBlock() : transaction and undo data inconsistent"); + return error("DisconnectBlock(): transaction and undo data inconsistent"); for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; @@ -1646,17 +1646,17 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (undo.nHeight != 0) { // undo data contains height: this is the last output of the prevout tx being spent if (!coins->IsPruned()) - fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); + fClean = fClean && error("DisconnectBlock(): undo data overwriting existing transaction"); coins->Clear(); coins->fCoinBase = undo.fCoinBase; coins->nHeight = undo.nHeight; coins->nVersion = undo.nVersion; } else { if (coins->IsPruned()) - fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); + fClean = fClean && error("DisconnectBlock(): undo data adding output to missing transaction"); } if (coins->IsAvailable(out.n)) - fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); + fClean = fClean && error("DisconnectBlock(): undo data overwriting existing output"); if (coins->vout.size() < out.n+1) coins->vout.resize(out.n+1); coins->vout[out.n] = undo.txout; @@ -1753,7 +1753,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin BOOST_FOREACH(const CTransaction& tx, block.vtx) { const CCoins* coins = view.AccessCoins(tx.GetHash()); if (coins && !coins->IsPruned()) - return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), + return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } } @@ -1783,13 +1783,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops"), + return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), + return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); if (fStrictPayToScriptHash) @@ -1799,7 +1799,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // an incredibly-expensive-to-validate block. nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops"), + return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } @@ -1825,7 +1825,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) return state.DoS(100, - error("ConnectBlock() : coinbase pays too much (actual=%d vs limit=%d)", + error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), REJECT_INVALID, "bad-cb-amount"); @@ -1843,7 +1843,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) - return error("ConnectBlock() : FindUndoPos failed"); + return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash())) return state.Abort("Failed to write undo data"); @@ -1995,7 +1995,7 @@ bool static DisconnectTip(CValidationState &state) { { CCoinsViewCache view(pcoinsTip); if (!DisconnectBlock(block, state, pindexDelete, view)) - return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); + return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); assert(view.Flush()); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -2055,7 +2055,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * if (!rv) { if (state.IsInvalid()) InvalidBlockFound(pindexNew, state); - return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); + return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } mapBlockSource.erase(inv.hash); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; @@ -2492,12 +2492,12 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f { // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) - return state.DoS(50, error("CheckBlockHeader() : proof of work failed"), + return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"), + return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), REJECT_INVALID, "time-too-new"); return true; @@ -2517,14 +2517,14 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo bool mutated; uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); if (block.hashMerkleRoot != hashMerkleRoot2) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), + return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. if (mutated) - return state.DoS(100, error("CheckBlock() : duplicate transaction"), + return state.DoS(100, error("CheckBlock(): duplicate transaction"), REJECT_INVALID, "bad-txns-duplicate", true); } @@ -2534,22 +2534,22 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock() : size limits failed"), + return state.DoS(100, error("CheckBlock(): size limits failed"), REJECT_INVALID, "bad-blk-length"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), + return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), REJECT_INVALID, "bad-cb-missing"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : more than one coinbase"), + return state.DoS(100, error("CheckBlock(): more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) - return error("CheckBlock() : CheckTransaction failed"); + return error("CheckBlock(): CheckTransaction failed"); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2557,7 +2557,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), + return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); return true; @@ -2576,28 +2576,28 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta // Check proof of work if ((!Params().SkipProofOfWorkCheck()) && (block.nBits != GetNextWorkRequired(pindexPrev, &block))) - return state.DoS(100, error("%s : incorrect proof of work", __func__), + return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("%s : block's timestamp is too early", __func__), + return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), + return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), REJECT_CHECKPOINT, "checkpoint mismatch"); // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) { - return state.Invalid(error("%s : rejected nVersion=1 block", __func__), + return state.Invalid(error("%s: rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); } @@ -2611,7 +2611,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { - return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); + 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 @@ -2621,7 +2621,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - return state.DoS(100, error("%s : block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); + return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); } } @@ -2641,7 +2641,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (ppindex) *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate"); + return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); return true; } @@ -2653,10 +2653,10 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (hash != Params().HashGenesisBlock()) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) - return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk"); + return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); pindexPrev = (*mi).second; if (pindexPrev->nStatus & BLOCK_FAILED_MASK) - return state.DoS(100, error("%s : prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } if (!ContextualCheckBlockHeader(block, state, pindexPrev)) @@ -2682,7 +2682,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (pindex->nStatus & BLOCK_HAVE_DATA) { // TODO: deal better with duplicate blocks. - // return state.DoS(20, error("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); + // return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); return true; } @@ -2703,12 +2703,12 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (dbp != NULL) blockPos = *dbp; if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); + return error("AcceptBlock(): FindBlockPos failed"); if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos)) return state.Abort("Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("AcceptBlock() : ReceivedBlockTransactions failed"); + return error("AcceptBlock(): ReceivedBlockTransactions failed"); } catch (const std::runtime_error& e) { return state.Abort(std::string("System error: ") + e.what()); } @@ -2739,7 +2739,7 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis LOCK(cs_main); MarkBlockAsReceived(pblock->GetHash()); if (!checked) { - return error("%s : CheckBlock FAILED", __func__); + return error("%s: CheckBlock FAILED", __func__); } // Store to disk @@ -2749,11 +2749,11 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } if (!ret) - return error("%s : AcceptBlock FAILED", __func__); + return error("%s: AcceptBlock FAILED", __func__); } if (!ActivateBestChain(state, pblock)) - return error("%s : ActivateBestChain failed", __func__); + return error("%s: ActivateBestChain failed", __func__); return true; } @@ -2859,7 +2859,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) - throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + throw runtime_error("LoadBlockIndex(): new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); @@ -3005,24 +3005,24 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlock block; // check level 0: read from disk if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + 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)) - return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { CBlockUndo undo; CDiskBlockPos pos = pindex->GetUndoPos(); if (!pos.IsNull()) { if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash())) - return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) { bool fClean = true; if (!DisconnectBlock(block, state, pindex, coins, &fClean)) - return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); pindexState = pindex->pprev; if (!fClean) { nGoodTransactions = 0; @@ -3034,7 +3034,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth return true; } if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); + return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { @@ -3045,9 +3045,9 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + 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()); + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -3093,18 +3093,18 @@ bool InitBlockIndex() { CDiskBlockPos blockPos; CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) - return error("LoadBlockIndex() : FindBlockPos failed"); + return error("LoadBlockIndex(): FindBlockPos failed"); if (!WriteBlockToDisk(block, blockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); + return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); + return error("LoadBlockIndex(): genesis block not accepted"); if (!ActivateBestChain(state, &block)) - return error("LoadBlockIndex() : genesis block cannot be activated"); + 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); } catch (const std::runtime_error& e) { - return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } } @@ -3204,7 +3204,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } } } catch (const std::exception& e) { - LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); + LogPrintf("%s: Deserialize or I/O error - %s", __func__, e.what()); } } } catch (const std::runtime_error& e) { @@ -3268,7 +3268,7 @@ string GetWarnings(string strFor) return strStatusBar; else if (strFor == "rpc") return strRPC; - assert(!"GetWarnings() : invalid parameter"); + assert(!"GetWarnings(): invalid parameter"); return "error"; } @@ -3835,7 +3835,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); - LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n", + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), mempool.mapTx.size()); @@ -4297,7 +4297,7 @@ bool ProcessMessages(CNode* pfrom) memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) { - LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", strCommand, nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4315,12 +4315,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", strCommand, nMessageSize, e.what()); } else { -- cgit v1.2.3 From bd2b73bb9b416139a0fbaf6eb2131356a59184a6 Mon Sep 17 00:00:00 2001 From: Vitalii Demianets Date: Wed, 14 Jan 2015 10:25:06 +0100 Subject: TRIVIAL: fix misleading comment --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 629e0dcbc0..f43d8c58b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -744,7 +744,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations // beside "push data" in the scriptSig - // IsStandard() will have already returned false + // IsStandardTx() will have already returned false // and this method isn't called. vector > stack; if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) -- cgit v1.2.3 From 1371e6f5db88941c3b3c70d7a13b0cbf150ebf66 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sat, 31 Jan 2015 02:54:55 +0000 Subject: Change "insane" to "absurd" (referring to high fees) in text strings and identifiers. Note that this will also require translation changes in Transifex for the key "A fee higher than %1 is considered an insanely high fee." which is now "A fee higher than %1 is considered an absurdly high fee." Signed-off-by: Daira Hopwood --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7cc69c318b..35118cebe5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -904,7 +904,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee) + bool* pfMissingInputs, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1063,8 +1063,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa dFreeCount += nSize; } - if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: insane fees %s, %d > %d", + if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) + return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); -- cgit v1.2.3 From 5a47811da5158df763aa2fca09ce646ee0c51e7b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 19 Jan 2015 18:37:21 -0500 Subject: BIP66 changeover logic --- src/main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7cc69c318b..f20575eaf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1764,6 +1764,11 @@ 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: + if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + flags |= SCRIPT_VERIFY_DERSIG; + } + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2601,6 +2606,13 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta REJECT_OBSOLETE, "bad-version"); } + // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, Params().RejectBlockOutdatedMajority())) + { + return state.Invalid(error("%s : rejected nVersion=2 block", __func__), + REJECT_OBSOLETE, "bad-version"); + } + return true; } -- cgit v1.2.3 From 858809a33e4f690c4ad213f44a6c4465fc2ef025 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 27 Jan 2015 09:28:45 -0400 Subject: Use separate SignatureChecker for CMutableTransaction --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f43d8c58b6..702358cf94 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1422,7 +1422,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; -- cgit v1.2.3 From 9fddceda44fb5592be179d783f0e5ac616c51c0d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 27 Jan 2015 10:01:31 -0400 Subject: Avoid storing a reference passed to SignatureChecker constructors --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 702358cf94..57b44baac5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1422,7 +1422,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; -- cgit v1.2.3 From eb1c2cd37f75cf4d0e85970932b9774b2d073225 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 3 Feb 2015 15:44:39 +0100 Subject: Split logic to undo txin's off DisconnectBlock. Instead, create a separate function that applies the undo operation of a CTxInUndo object onto a CCoinsViewCache. This method is used from DisconnectBlock. --- src/main.cpp | 62 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27c427f7cd..a0ad5e51f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1588,6 +1588,39 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin } // anon namespace +/** + * Apply the undo operation of a CTxInUndo to the given chain state. + * @param undo The undo object. + * @param view The coins view to which to apply the changes. + * @param out The out point that corresponds to the tx input. + * @return True on success. + */ +static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) +{ + bool fClean = true; + + CCoinsModifier coins = view.ModifyCoins(out.hash); + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins->IsPruned()) + fClean = fClean && error("%s: undo data overwriting existing transaction", __func__); + coins->Clear(); + coins->fCoinBase = undo.fCoinBase; + coins->nHeight = undo.nHeight; + coins->nVersion = undo.nVersion; + } else { + if (coins->IsPruned()) + fClean = fClean && error("%s: undo data adding output to missing transaction", __func__); + } + if (coins->IsAvailable(out.n)) + fClean = fClean && error("%s: undo data overwriting existing output", __func__); + if (coins->vout.size() < out.n+1) + coins->vout.resize(out.n+1); + coins->vout[out.n] = undo.txout; + + return fClean; +} + bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -1613,11 +1646,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex uint256 hash = tx.GetHash(); // Check that all outputs are available and match the outputs in the block itself - // exactly. Note that transactions with only provably unspendable outputs won't - // have outputs available even in the block itself, so we handle that case - // specially with outsEmpty. + // exactly. { - CCoins outsEmpty; CCoinsModifier outs = view.ModifyCoins(hash); outs->ClearUnspendable(); @@ -1642,24 +1672,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; - CCoinsModifier coins = view.ModifyCoins(out.hash); - if (undo.nHeight != 0) { - // undo data contains height: this is the last output of the prevout tx being spent - if (!coins->IsPruned()) - fClean = fClean && error("DisconnectBlock(): undo data overwriting existing transaction"); - coins->Clear(); - coins->fCoinBase = undo.fCoinBase; - coins->nHeight = undo.nHeight; - coins->nVersion = undo.nVersion; - } else { - if (coins->IsPruned()) - fClean = fClean && error("DisconnectBlock(): undo data adding output to missing transaction"); - } - if (coins->IsAvailable(out.n)) - fClean = fClean && error("DisconnectBlock(): undo data overwriting existing output"); - if (coins->vout.size() < out.n+1) - coins->vout.resize(out.n+1); - coins->vout[out.n] = undo.txout; + if (!ApplyTxInUndo(undo, view, out)) + fClean = false; } } } @@ -1670,9 +1684,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (pfClean) { *pfClean = fClean; return true; - } else { - return fClean; } + + return fClean; } void static FlushBlockFile(bool fFinalize = false) -- cgit v1.2.3 From f5791c6ae3b59ef4c8d04e6873310a022e1211b6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 26 Jan 2015 09:47:59 +0100 Subject: Add more information to errors in ReadBlockFromDisk A lot of times, disk corruption problems appear here. To facilitate debugging and troubleshooting, add position information to the error messages. --- src/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27c427f7cd..32baf5508a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1199,19 +1199,19 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("ReadBlockFromDisk: OpenBlockFile failed"); + return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); // Read block try { filein >> block; } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); } // Check the header if (!CheckProofOfWork(block.GetHash(), block.nBits)) - return error("ReadBlockFromDisk: Errors in block header"); + return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; } @@ -1221,7 +1221,8 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) return false; if (block.GetHash() != pindex->GetBlockHash()) - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index"); + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", + pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } -- cgit v1.2.3 From dca799e1db6e319fdd47e0bfdb038eab0efabb85 Mon Sep 17 00:00:00 2001 From: Ivan Pustogarov Date: Sun, 7 Dec 2014 17:30:57 +0100 Subject: Ignore getaddr messages on Outbound connections. The only time when a client sends a "getaddr" message is when he esatblishes an Outbound connection (see ProcessMessage() in src/main.cpp). Another bitcoin client is expected to receive a "getaddr" message only on Inbound connection. Ignoring "gettaddr" requests on Outbound connections can resolve potential privacy issues (and as was said such request normally do not happen anyway). --- src/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27c427f7cd..a30084a75b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4008,7 +4008,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getaddr") + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making users (which are behind NAT and can only make outgoing connections) ignore + // getaddr message mitigates the attack. + else if ((strCommand == "getaddr") && (pfrom->fInbound)) { pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); -- cgit v1.2.3 From 28d4cff0ed2d4438da4bbf2d4ca0465715603af5 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 8 Feb 2015 00:59:58 +0000 Subject: Sanitize command strings before logging them. Normally bitcoin core does not display any network originated strings without sanitizing or hex encoding. This wasn't done for strcommand in many places. This could be used to play havoc with a terminal displaying the logs, especially with printtoconsole in use. Thanks to Evil-Knievel for reporting this issue. --- src/main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27c427f7cd..43e5a2d024 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3450,7 +3450,7 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); - LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id); + LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -4285,7 +4285,7 @@ bool ProcessMessages(CNode* pfrom) // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", msg.hdr.GetCommand(), pfrom->id); + LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; } @@ -4294,7 +4294,7 @@ bool ProcessMessages(CNode* pfrom) CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { - LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", hdr.GetCommand(), pfrom->id); + LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; } string strCommand = hdr.GetCommand(); @@ -4310,7 +4310,7 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand, nMessageSize, nChecksum, hdr.nChecksum); + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4327,12 +4327,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), nMessageSize, e.what()); } else { @@ -4349,7 +4349,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id); + LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); break; } -- cgit v1.2.3 From 85da07a5a001a563488382435202b74a3e3e964a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 24 Feb 2015 04:27:25 -0800 Subject: Better fingerprinting protection for non-main-chain getdatas. With headers-first we can compare against the best header timestamp, rather than using checkpoints which require code updates to maintain. --- src/main.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 34b4c51d5f..b6a61f7da1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3355,19 +3355,17 @@ void static ProcessGetData(CNode* pfrom) BlockMap::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { - // If the requested block is at a height below our last - // checkpoint, only serve it if it's in the checkpointed chain - int nHeight = mi->second->nHeight; - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) { - if (!chainActive.Contains(mi->second)) - { - LogPrintf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n"); - } else { - send = true; - } - } else { + if (chainActive.Contains(mi->second)) { send = true; + } else { + // To prevent fingerprinting attacks, only send blocks outside of the active + // chain if they are valid, and no more than a month older than the best header + // chain we know about. + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && + (mi->second->GetBlockTime() > pindexBestHeader->GetBlockTime() - 30 * 24 * 60 * 60); + if (!send) { + LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); + } } } if (send) -- cgit v1.2.3 From eec37136fd0f6ab80772380d8f047d98743f31f5 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Mon, 27 Oct 2014 20:24:31 -0400 Subject: make CMessageHeader a dumb storage class It shouldn't know or care about bitcoind's chain param selection --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 34b4c51d5f..c9a2f34a4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4307,7 +4307,7 @@ bool ProcessMessages(CNode* pfrom) // Read header CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid()) + if (!hdr.IsValid(Params().MessageStart())) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; -- cgit v1.2.3 From 51598b26319bf1ee98b399dee8152b902c62891a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 3 Mar 2015 07:49:12 -0800 Subject: Reinitialize state in between individual unit tests. This changes the TestingSetup fixture to be per-unit-test rather than global. Most tests don't need it, so it's only invoked in a few. --- src/main.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b6a61f7da1..d945ece417 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,6 @@ bool fTxIndex = false; bool fIsBareMultisigStd = true; unsigned int nCoinCacheSize = 5000; - /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); @@ -3085,10 +3084,31 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth void UnloadBlockIndex() { - mapBlockIndex.clear(); + LOCK(cs_main); setBlockIndexCandidates.clear(); chainActive.SetTip(NULL); pindexBestInvalid = NULL; + pindexBestHeader = NULL; + mempool.clear(); + mapOrphanTransactions.clear(); + mapOrphanTransactionsByPrev.clear(); + nSyncStarted = 0; + mapBlocksUnlinked.clear(); + vinfoBlockFile.clear(); + nLastBlockFile = 0; + nBlockSequenceId = 1; + mapBlockSource.clear(); + mapBlocksInFlight.clear(); + nQueuedValidatedHeaders = 0; + nPreferredDownload = 0; + setDirtyBlockIndex.clear(); + setDirtyFileInfo.clear(); + mapNodeState.clear(); + + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { + delete entry.second; + } + mapBlockIndex.clear(); } bool LoadBlockIndex() -- cgit v1.2.3 From 556814ec4ec7e2c5a1b441f8b9a49dbf715fec6b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 19 Dec 2014 11:38:30 +0100 Subject: src/main.cpp: endian compatibility in packet checksum check --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b6a61f7da1..9927ac7598 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4318,8 +4318,7 @@ bool ProcessMessages(CNode* pfrom) // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); + unsigned int nChecksum = ReadLE32((unsigned char*)&hash); if (nChecksum != hdr.nChecksum) { LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", -- cgit v1.2.3 From 3aa0130b963087025b64108225bbadea36d8c6df Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 9 Mar 2015 16:09:54 +0100 Subject: test: remove fSkipProofOfWork Not used, and REGTEST already allows creating blocks at the lowerst possible difficulty. --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0b36b5f219..f47155ca90 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2593,8 +2593,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; // Check proof of work - if ((!Params().SkipProofOfWorkCheck()) && - (block.nBits != GetNextWorkRequired(pindexPrev, &block))) + if ((block.nBits != GetNextWorkRequired(pindexPrev, &block))) return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); -- cgit v1.2.3 From bb6acff07982dda68b5c2ac81c99dbd7255bb9cc Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 6 Mar 2015 20:26:09 -0500 Subject: fix possible block db breakage during re-index When re-indexing, there are a few cases where garbage data may be skipped in the block files. In these cases, the indices are correctly written to the index db, however the pointer to the next position for writing in the current block file is calculated by adding the sizes of the valid blocks found. As a result, when the re-index is finished, the index db is correct for all existing blocks, but the next block will be written to an incorrect offset, likely overwriting existing blocks. Rather than using the sum of all valid blocks to determine the next write position, use the end of the last block written to the file. Don't assume that the current block is the last one in the file, since they may be read out-of-order. --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9927ac7598..183dff6d39 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2455,8 +2455,11 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } nLastBlockFile = nFile; - vinfoBlockFile[nFile].nSize += nAddSize; vinfoBlockFile[nFile].AddBlock(nHeight, nTime); + if (fKnown) + vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); + else + vinfoBlockFile[nFile].nSize += nAddSize; if (!fKnown) { unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; -- cgit v1.2.3 From a9af415887f03cb2565895bc55be72748549e527 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 11 Mar 2015 11:56:44 -0400 Subject: fix InvalidateBlock to repopulate setBlockIndexCandidates --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6bbb3bc135..9b4bb43128 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2311,7 +2311,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { - setBlockIndexCandidates.insert(pindex); + setBlockIndexCandidates.insert(it->second); } it++; } -- cgit v1.2.3 From cd3d67cf3b0d573d2c387c2ec35e8b52129863d9 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 12 Mar 2015 16:03:23 -0400 Subject: Fix InvalidateBlock to add chainActive.Tip to setBlockIndexCandidates --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9b4bb43128..5207f60896 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2310,7 +2310,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { // add them again. BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { - if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { setBlockIndexCandidates.insert(it->second); } it++; -- cgit v1.2.3 From 9c2737901b5203f267d21d728019d64b46f1d9f3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 5 Mar 2015 04:01:22 -0800 Subject: Reduce fingerprinting through timestamps in 'addr' messages. Suggested by Jonas Nick. --- src/main.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e16887fa4a..4eeb913d09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -238,6 +238,10 @@ struct CBlockReject { * and we're no longer holding the node's locks. */ struct CNodeState { + //! The peer's address + CService address; + //! Whether we have a fully established connection. + bool fCurrentlyConnected; //! Accumulated misbehaviour score for this peer. int nMisbehavior; //! Whether this peer should be disconnected and banned (unless whitelisted). @@ -262,6 +266,7 @@ struct CNodeState { bool fPreferredDownload; CNodeState() { + fCurrentlyConnected = false; nMisbehavior = 0; fShouldBan = false; pindexBestKnownBlock = NULL; @@ -305,6 +310,7 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; state.name = pnode->addrName; + state.address = pnode->addr; } void FinalizeNode(NodeId nodeid) { @@ -314,6 +320,10 @@ void FinalizeNode(NodeId nodeid) { if (state->fSyncStarted) nSyncStarted--; + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + AddressCurrentlyConnected(state->address); + } + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) mapBlocksInFlight.erase(entry.hash); EraseOrphansFor(nodeid); @@ -3628,6 +3638,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + + // Mark this node as currently connected, so we update its timestamp later. + if (pfrom->fNetworkNode) { + LOCK(cs_main); + State(pfrom->GetId())->fCurrentlyConnected = true; + } } @@ -4272,11 +4288,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - // Update the last seen time for this node's address - if (pfrom->fNetworkNode) - if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") - AddressCurrentlyConnected(pfrom->addr); - return true; } -- cgit v1.2.3 From 26c16d9de9874ad2f5fe28377b838a19a558639d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Thu, 5 Feb 2015 01:11:44 +0100 Subject: Includes: Refactor: Move CValidationInterface and CMainSignals out of main --- src/main.cpp | 77 +++++++----------------------------------------------------- 1 file changed, 8 insertions(+), 69 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index aa6f904574..0ffacc338e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,9 +5,9 @@ #include "main.h" -#include "arith_uint256.h" #include "addrman.h" #include "alert.h" +#include "arith_uint256.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -21,6 +21,7 @@ #include "undo.h" #include "util.h" #include "utilmoneystr.h" +#include "validationinterface.h" #include @@ -156,68 +157,6 @@ namespace { set setDirtyFileInfo; } // anon namespace -////////////////////////////////////////////////////////////////////////////// -// -// dispatching functions -// - -// These functions dispatch to one or all registered wallets - -namespace { - -struct CMainSignals { - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ - boost::signals2::signal SyncTransaction; - /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */ - boost::signals2::signal EraseTransaction; - /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ - boost::signals2::signal UpdatedTransaction; - /** Notifies listeners of a new active block chain. */ - boost::signals2::signal SetBestChain; - /** Notifies listeners about an inventory item being seen on the network. */ - boost::signals2::signal Inventory; - /** Tells listeners to broadcast their data. */ - boost::signals2::signal Broadcast; - /** Notifies listeners of a block validation result */ - boost::signals2::signal BlockChecked; -} g_signals; - -} // anon namespace - -void RegisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); - g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); -} - -void UnregisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); -} - -void UnregisterAllValidationInterfaces() { - g_signals.BlockChecked.disconnect_all_slots(); - g_signals.Broadcast.disconnect_all_slots(); - g_signals.Inventory.disconnect_all_slots(); - g_signals.SetBestChain.disconnect_all_slots(); - g_signals.UpdatedTransaction.disconnect_all_slots(); - g_signals.EraseTransaction.disconnect_all_slots(); - g_signals.SyncTransaction.disconnect_all_slots(); -} - -void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) { - g_signals.SyncTransaction(tx, pblock); -} - ////////////////////////////////////////////////////////////////////////////// // // Registration of network node signals. @@ -1897,7 +1836,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; - g_signals.UpdatedTransaction(hashPrevBestCoinBase); + GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.vtx[0].GetHash(); int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; @@ -1956,7 +1895,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return state.Abort("Failed to write to coin database"); // Update best block in wallet (so we can detect restored wallets). if (mode != FLUSH_STATE_IF_NEEDED) { - g_signals.SetBestChain(chainActive.GetLocator()); + GetMainSignals().SetBestChain(chainActive.GetLocator()); } nLastWrite = GetTimeMicros(); } @@ -2080,7 +2019,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * CCoinsViewCache view(pcoinsTip); CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); bool rv = ConnectBlock(*pblock, state, pindexNew, view); - g_signals.BlockChecked(*pblock, state); + GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) InvalidBlockFound(pindexNew, state); @@ -3471,7 +3410,7 @@ void static ProcessGetData(CNode* pfrom) } // Track requests for our stuff. - g_signals.Inventory(inv.hash); + GetMainSignals().Inventory(inv.hash); if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) break; @@ -3765,7 +3704,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Track requests for our stuff - g_signals.Inventory(inv.hash); + GetMainSignals().Inventory(inv.hash); if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); @@ -4536,7 +4475,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - g_signals.Broadcast(); + GetMainSignals().Broadcast(); } // -- cgit v1.2.3 From 0f5954c434fdd04b9abca6ddc6f1bbf895b6c6be Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 23 Mar 2015 13:47:18 -0400 Subject: Regression test for ResendWalletTransactions Adds a regression test for the wallet's ResendWalletTransactions function, which uses a new, hidden RPC command "resendwallettransactions." I refactored main's Broadcast signal so it is passed the best-block time, which let me remove a global variable shared between main.cpp and the wallet (nTimeBestReceived). I also manually tested the "rebroadcast unconfirmed every half hour or so" functionality by: 1. Running bitcoind -connect=0.0.0.0:8333 2. Creating a couple of send-to-self transactions 3. Connect to a peer using -addnode 4. Waited a while, monitoring debug.log, until I see: ```2015-03-23 18:48:10 ResendWalletTransactions: rebroadcast 2 unconfirmed transactions``` One last change: don't bother putting ResendWalletTransactions messages in debug.log unless unconfirmed transactions were actually rebroadcast. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0ffacc338e..8f50e7fc16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4475,7 +4475,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - GetMainSignals().Broadcast(); + GetMainSignals().Broadcast(nTimeBestReceived); } // -- cgit v1.2.3 From d698ef690f62c08676dcf9ddc0988e23aa81cbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Sun, 15 Feb 2015 02:21:42 +0100 Subject: Consensus: Refactor: Decouple pow.o from chainparams.o --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0ffacc338e..1d78eedc1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1158,7 +1158,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) } // Check the header - if (!CheckProofOfWork(block.GetHash(), block.nBits)) + if (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; @@ -2462,7 +2462,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) { // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); @@ -2545,7 +2545,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; // Check proof of work - if ((block.nBits != GetNextWorkRequired(pindexPrev, &block))) + if (block.nBits != GetNextWorkRequired(pindexPrev, &block, Params().GetConsensus())) return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); -- cgit v1.2.3 From 3fcfbc8ac5fcba474151ceedf61c4e433e433474 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 13 Mar 2015 09:25:34 -0700 Subject: Add a consistency check for the block chain data structures This adds a -checkblockindex (defaulting to true for regtest), which occasionally does a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive, and mapBlocksUnlinked. --- src/main.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 1d78eedc1a..b8f9b949a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ bool fImporting = false; bool fReindex = false; bool fTxIndex = false; bool fIsBareMultisigStd = true; +bool fCheckBlockIndex = false; unsigned int nCoinCacheSize = 5000; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ @@ -74,6 +75,7 @@ void EraseOrphansFor(NodeId peer); * and going backwards. */ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired); +static void CheckBlockIndex(); /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; @@ -85,7 +87,7 @@ namespace { struct CBlockIndexWorkComparator { - bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; @@ -107,8 +109,8 @@ namespace { CBlockIndex *pindexBestInvalid; /** - * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least - * as good as our current tip. Entries may be failed, though. + * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and + * as good as our current tip or better. Entries may be failed, though. */ set setBlockIndexCandidates; /** Number of nodes with fSyncStarted. */ @@ -2226,6 +2228,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { uiInterface.NotifyBlockTip(hashNewTip); } } while(pindexMostWork != chainActive.Tip()); + CheckBlockIndex(); // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { @@ -2362,7 +2365,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl CBlockIndex *pindex = queue.front(); queue.pop_front(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - setBlockIndexCandidates.insert(pindex); + if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { + setBlockIndexCandidates.insert(pindex); + } std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex); while (range.first != range.second) { std::multimap::iterator it = range.first; @@ -2725,6 +2730,7 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } + CheckBlockIndex(); if (!ret) return error("%s: AcceptBlock FAILED", __func__); } @@ -3213,6 +3219,136 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) return nLoaded > 0; } +void static CheckBlockIndex() +{ + if (!fCheckBlockIndex) { + return; + } + + LOCK(cs_main); + + // Build forward-pointing map of the entire block tree. + std::multimap forward; + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + forward.insert(std::make_pair(it->second->pprev, it->second)); + } + + assert(forward.size() == mapBlockIndex.size()); + + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); + CBlockIndex *pindex = rangeGenesis.first->second; + rangeGenesis.first++; + assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. + + // Iterate over the entire block tree, using depth-first search. + // Along the way, remember whether there are blocks on the path from genesis + // block being explored which are the first to have certain properties. + size_t nNodes = 0; + int nHeight = 0; + CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. + CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). + CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). + CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). + while (pindex != NULL) { + nNodes++; + if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; + if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; + + // Begin: actual consistency checks. + if (pindex->pprev == NULL) { + // Genesis block checks. + assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match. + assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. + } + assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. + assert(pindex->nHeight == nHeight); // nHeight must be consistent. + assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. + assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. + assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid + if (pindexFirstInvalid == NULL) { + // Checks for not-invalid blocks. + assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. + } + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) { + if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates. + assert(setBlockIndexCandidates.count(pindex)); + } + } else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates. + assert(setBlockIndexCandidates.count(pindex) == 0); + } + // Check whether this block is in mapBlocksUnlinked. + std::pair::iterator,std::multimap::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); + bool foundInUnlinked = false; + while (rangeUnlinked.first != rangeUnlinked.second) { + assert(rangeUnlinked.first->first == pindex->pprev); + if (rangeUnlinked.first->second == pindex) { + foundInUnlinked = true; + break; + } + rangeUnlinked.first++; + } + if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) { + if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked. + assert(foundInUnlinked); + } + } else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked. + assert(!foundInUnlinked); + } + // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow + // End: actual consistency checks. + + // Try descending into the first subnode. + std::pair::iterator,std::multimap::iterator> range = forward.equal_range(pindex); + if (range.first != range.second) { + // A subnode was found. + pindex = range.first->second; + nHeight++; + continue; + } + // This is a leaf node. + // Move upwards until we reach a node of which we have not yet visited the last child. + while (pindex) { + // We are going to either move to a parent or a sibling of pindex. + // If pindex was the first with a certain property, unset the corresponding variable. + if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; + if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; + if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; + if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; + if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; + // Find our parent. + CBlockIndex* pindexPar = pindex->pprev; + // Find which child we just visited. + std::pair::iterator,std::multimap::iterator> rangePar = forward.equal_range(pindexPar); + while (rangePar.first->second != pindex) { + assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child. + rangePar.first++; + } + // Proceed to the next one. + rangePar.first++; + if (rangePar.first != rangePar.second) { + // Move to the sibling. + pindex = rangePar.first->second; + break; + } else { + // Move up further. + pindex = pindexPar; + nHeight--; + continue; + } + } + } + + // Check that we actually traversed the entire map. + assert(nNodes == forward.size()); +} + ////////////////////////////////////////////////////////////////////////////// // // CAlert @@ -3971,6 +4107,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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()); } + + CheckBlockIndex(); } else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing -- cgit v1.2.3 From ede379f780b8b976be5e44ce0d8019d59e8829d6 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 1 Apr 2015 13:12:50 -0400 Subject: Add additional block index consistency checks This adds more tests to CheckBlockIndex: - HAVE_DATA is true iff nTx > 0 - BLOCK_VALID_TRANSACTIONS is true iff nTx > 0 - BLOCK_VALID_TRANSACTIONS is true for a block and all parents iff nChainTx > 0 --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 011016204e..7da190bdf0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3248,6 +3248,7 @@ void static CheckBlockIndex() CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). + CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). while (pindex != NULL) { @@ -3255,6 +3256,7 @@ void static CheckBlockIndex() if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; @@ -3264,7 +3266,12 @@ void static CheckBlockIndex() assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. } + // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) + assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); + assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); + // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. + assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); assert(pindex->nHeight == nHeight); // nHeight must be consistent. assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. @@ -3320,6 +3327,7 @@ void static CheckBlockIndex() if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; + if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; // Find our parent. -- cgit v1.2.3 From 00dcaf4bebb6e238b5711530a8a5e4f8fdb6b455 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 12 Jan 2015 09:55:48 -0500 Subject: Change download logic to allow calling getheaders/getdata on inbound peers SendMessages will now call getheaders on both inbound and outbound peers, once the headers chain is close to synced. It will also try downloading blocks from inbound peers once we're out of initial block download (so inbound peers will participate in parallel block fetching for the last day or two of blocks being downloaded). --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3ceacf32e7..a741115932 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4459,9 +4459,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. - if (!state.fSyncStarted && !pto->fClient && fFetch && !fImporting && !fReindex) { + if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { // Only actively request headers from a single peer, unless we're close to today. - if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { + if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; nSyncStarted++; CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; @@ -4549,7 +4549,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Message: getdata (blocks) // vector vGetData; - if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vector vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); -- cgit v1.2.3 From f14e687feb554e64bf38715c001da0c0954be694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 3 Apr 2015 17:42:06 +0200 Subject: Chainparams: Decouple CAlert from CChainParams --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 011016204e..bd424b1826 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4262,7 +4262,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { - if (alert.ProcessAlert()) + if (alert.ProcessAlert(Params().AlertKey())) { // Relay pfrom->setKnown.insert(alertHash); -- cgit v1.2.3 From 4e382177eda203e799310350ffb2efc28d010799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Thu, 9 Apr 2015 15:58:34 +0200 Subject: Chainparams: Refactor: Remove redundant HashGenesisBlock() getter --- src/main.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb6766301..247c2c786d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1680,6 +1680,7 @@ static int64_t nTimeTotal = 0; bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); // Check it again in case a previous version let a bad block in if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) @@ -1691,7 +1692,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) - if (block.GetHash() == Params().HashGenesisBlock()) { + if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { if (!fJustCheck) view.SetBestBlock(pindex->GetBlockHash()); return true; @@ -2541,8 +2542,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) { + const Consensus::Params& consensusParams = Params().GetConsensus(); uint256 hash = block.GetHash(); - if (hash == Params().HashGenesisBlock()) + if (hash == consensusParams.hashGenesisBlock) return true; assert(pindexPrev); @@ -2612,6 +2614,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); // Check for duplicate uint256 hash = block.GetHash(); @@ -2632,7 +2635,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc // Get prev block index CBlockIndex* pindexPrev = NULL; - if (hash != Params().HashGenesisBlock()) { + if (hash != chainparams.GetConsensus().hashGenesisBlock) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); @@ -3119,6 +3122,7 @@ bool InitBlockIndex() { bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) { + const CChainParams& chainparams = Params(); // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); @@ -3164,7 +3168,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); - if (hash != Params().HashGenesisBlock() && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { + if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) @@ -3179,7 +3183,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) nLoaded++; if (state.IsError()) break; - } else if (hash != Params().HashGenesisBlock() && mapBlockIndex[hash]->nHeight % 1000 == 0) { + } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } @@ -3221,6 +3225,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) void static CheckBlockIndex() { + const Consensus::Params& consensusParams = Params().GetConsensus(); if (!fCheckBlockIndex) { return; } @@ -3263,7 +3268,7 @@ void static CheckBlockIndex() // Begin: actual consistency checks. if (pindex->pprev == NULL) { // Genesis block checks. - assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match. + assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. } // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) -- cgit v1.2.3 From e8e8904ddae09ef9dc444bcdd2955109c9e9ce64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 10 Apr 2015 18:35:09 +0200 Subject: Chainparams: Cleanup: Delete CChainParams getters to attributes from Consensus::Params --- src/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb6766301..5878d3dc07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3577,6 +3577,7 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { + const CChainParams& chainparams = Params(); RandAddSeedPerfmon(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) @@ -3836,7 +3837,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // not a direct successor. pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); - if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - Params().TargetSpacing() * 20 && + if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 && 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 @@ -4499,6 +4500,7 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { + const Consensus::Params& consensusParams = Params().GetConsensus(); { // Don't send anything until we get their version message if (pto->nVersion == 0) @@ -4686,7 +4688,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * consensusParams.nPowTargetSpacing * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); pto->fDisconnect = true; } -- cgit v1.2.3 From c1ecee8f723c2635fbd51100fa09acdb0cbec8a0 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 13 Apr 2015 12:26:28 -0400 Subject: Set nSequenceId when a block is fully linked Also adds a test to CheckBlockIndex --- src/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb6766301..6feebd24b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2349,10 +2349,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); - { - LOCK(cs_nBlockSequenceId); - pindexNew->nSequenceId = nBlockSequenceId++; - } setDirtyBlockIndex.insert(pindexNew); if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { @@ -2365,6 +2361,10 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl CBlockIndex *pindex = queue.front(); queue.pop_front(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + { + LOCK(cs_nBlockSequenceId); + pindex->nSequenceId = nBlockSequenceId++; + } if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { setBlockIndexCandidates.insert(pindex); } @@ -3269,6 +3269,7 @@ void static CheckBlockIndex() // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); + if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); -- cgit v1.2.3 From 0421c18f3a261f04e83a03f59884e5798af74fd9 Mon Sep 17 00:00:00 2001 From: mrbandrews Date: Thu, 9 Apr 2015 11:08:39 -0400 Subject: Fix CheckBlockIndex for reindex. Some tests in CheckBlockIndex require chainActive.Tip(), but when reindexing, chainActive has not been set on the first call to CheckBlockIndex. reindex.py starts a node, mines 3 blocks, stops, and reindexes with CheckBlockIndex enabled. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb6766301..a9051118a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3227,6 +3227,14 @@ void static CheckBlockIndex() LOCK(cs_main); + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, + // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when + // iterating the block tree require that chainActive has been initialized.) + if (chainActive.Height() < 0) { + assert(mapBlockIndex.size() <= 1); + return; + } + // Build forward-pointing map of the entire block tree. std::multimap forward; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { -- cgit v1.2.3 From 8ba7f842e5a00e43167e53b456c6d8732b8861c5 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 6 Apr 2015 13:10:33 -0400 Subject: Reduce download timeouts as blocks arrive Compare the block download timeout to what the timeout would be if calculated based on current time and current value of nQueuedValidatedHeaders, but ignoring other in-flight blocks from the same peer. If the calculation based on present conditions is shorter, then set that to be the time after which we disconnect the peer for not delivering this block. --- src/main.cpp | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f0aa3022cd..bc2e211843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -141,8 +141,8 @@ namespace { uint256 hash; CBlockIndex *pindex; //! Optional. int64_t nTime; //! Time of "getdata" request in microseconds. - int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested. bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. + int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; @@ -203,6 +203,7 @@ struct CNodeState { int64_t nStallingSince; list vBlocksInFlight; int nBlocksInFlight; + int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; @@ -216,6 +217,7 @@ struct CNodeState { fSyncStarted = false; nStallingSince = 0; nBlocksInFlight = 0; + nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; } }; @@ -247,6 +249,12 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } +// Returns time at which to timeout block request (nTime in microseconds) +int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore) +{ + return nTime + 500000 * Params().GetConsensus().nPowTargetSpacing * (4 + nValidatedQueuedBefore); +} + void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; @@ -279,6 +287,7 @@ void MarkBlockAsReceived(const uint256& hash) { if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; + state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; @@ -294,10 +303,12 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - QueuedBlock newentry = {hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL}; + int64_t nNow = GetTimeMicros(); + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders)}; nQueuedValidatedHeaders += newentry.fValidatedHeaders; list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; + state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } @@ -4693,9 +4704,22 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * consensusParams.nPowTargetSpacing * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { - LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); - pto->fDisconnect = true; + // We also compare the block download timeout originally calculated against the time at which we'd disconnect + // if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're + // only looking at this peer's oldest request). This way a large queue in the past doesn't result in a + // permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing + // more quickly than once every 5 minutes, then we'll shorten the download window for this block). + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { + QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); + int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders); + if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { + LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); + queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; + } + if (queuedBlock.nTimeDisconnect < nNow) { + LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); + pto->fDisconnect = true; + } } // -- cgit v1.2.3 From 30c1db1c612ef2622d4eb72f0b7f12f311cac7cd Mon Sep 17 00:00:00 2001 From: Nicolas Benoit Date: Tue, 24 Feb 2015 18:32:34 +0100 Subject: Replaced current function names with __func__ in LogPrintf() calls. --- src/main.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8dd61a3720..75738dace7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1233,14 +1233,14 @@ void CheckForkWarningConditions() } if (pindexBestForkTip && pindexBestForkBase) { - LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", + LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else { - LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); + LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); fLargeWorkInvalidChainFound = true; } } @@ -1298,10 +1298,10 @@ void Misbehaving(NodeId pnode, int howmuch) int banscore = GetArg("-banscore", 100); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { - LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); } void static InvalidChainFound(CBlockIndex* pindexNew) @@ -1309,11 +1309,11 @@ void static InvalidChainFound(CBlockIndex* pindexNew) if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; - LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); - LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", + LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); CheckForkWarningConditions(); @@ -1921,7 +1921,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", + LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); @@ -1941,7 +1941,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: @@ -2936,7 +2936,7 @@ bool static LoadBlockIndexDB() // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); - LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + LogPrintf("%s(): transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); @@ -2946,7 +2946,7 @@ bool static LoadBlockIndexDB() PruneBlockIndexCandidates(); - LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n", + LogPrintf("%s(): hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); @@ -3492,7 +3492,7 @@ void static ProcessGetData(CNode* pfrom) send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && (mi->second->GetBlockTime() > pindexBestHeader->GetBlockTime() - 30 * 24 * 60 * 60); if (!send) { - LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); + LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } } } @@ -4394,7 +4394,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool ProcessMessages(CNode* pfrom) { //if (fDebug) - // LogPrintf("ProcessMessages(%u messages)\n", pfrom->vRecvMsg.size()); + // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); // // Message format @@ -4422,7 +4422,7 @@ bool ProcessMessages(CNode* pfrom) CNetMessage& msg = *it; //if (fDebug) - // LogPrintf("ProcessMessages(message %u msgsz, %u bytes, complete:%s)\n", + // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); @@ -4458,7 +4458,7 @@ bool ProcessMessages(CNode* pfrom) unsigned int nChecksum = ReadLE32((unsigned char*)&hash); if (nChecksum != hdr.nChecksum) { - LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4476,12 +4476,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else { @@ -4498,7 +4498,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); + LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); break; } -- cgit v1.2.3 From 52070c87fda663e1f074998fd95fa1dafff667f9 Mon Sep 17 00:00:00 2001 From: Nicolas Benoit Date: Wed, 25 Feb 2015 18:40:32 +0100 Subject: Removed '()' where used without contents inside This additional patch removes '()' from current function name in LogPrintf output. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 75738dace7..c6a936c5cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2936,7 +2936,7 @@ bool static LoadBlockIndexDB() // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); - LogPrintf("%s(): transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); @@ -2946,7 +2946,7 @@ bool static LoadBlockIndexDB() PruneBlockIndexCandidates(); - LogPrintf("%s(): hashBestChain=%s height=%d date=%s progress=%f\n", __func__, + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); -- cgit v1.2.3 From f7303f97933be33e34d482cf8348d180c8da2a26 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 17 Mar 2015 06:35:59 -0700 Subject: Use equivalent PoW for non-main-chain requests --- src/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4352c719a1..c5168ec8c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3459,7 +3459,6 @@ bool static AlreadyHave(const CInv& inv) return true; } - void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); @@ -3487,11 +3486,13 @@ void static ProcessGetData(CNode* pfrom) if (chainActive.Contains(mi->second)) { send = true; } else { + static const int nOneMonth = 30 * 24 * 60 * 60; // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a month older than the best header - // chain we know about. + // chain if they are valid, and no more than a month older (both in time, and in + // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (mi->second->GetBlockTime() > pindexBestHeader->GetBlockTime() - 30 * 24 * 60 * 60); + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } -- cgit v1.2.3 From f9ec3f0fadb11ee9889af977e16915f5d6e01944 Mon Sep 17 00:00:00 2001 From: mrbandrews Date: Mon, 23 Feb 2015 14:27:44 -0500 Subject: Add block pruning functionality This adds a -prune=N option to bitcoind, which if set to N>0 will enable block file pruning. When pruning is enabled, block and undo files will be deleted to try to keep total space used by those files to below the prune target (N, in MB) specified by the user, subject to some constraints: - The last 288 blocks on the main chain are always kept (MIN_BLOCKS_TO_KEEP), - N must be at least 550MB (chosen as a value for the target that could reasonably be met, with some assumptions about block sizes, orphan rates, etc; see comment in main.h), - No blocks are pruned until chainActive is at least 100,000 blocks long (on mainnet; defined separately for mainnet, testnet, and regtest in chainparams as nPruneAfterHeight). This unsets NODE_NETWORK if pruning is enabled. Also included is an RPC test for pruning (pruning.py). Thanks to @rdponticelli for earlier work on this feature; this is based in part off that work. --- src/main.cpp | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 248 insertions(+), 30 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4352c719a1..bf32ac91e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,9 +52,12 @@ int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; +bool fHavePruned = false; +bool fPruneMode = false; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; unsigned int nCoinCacheSize = 5000; +uint64_t nPruneTarget = 0; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); @@ -110,17 +113,25 @@ namespace { /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and - * as good as our current tip or better. Entries may be failed, though. + * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be + * missing the data for the block. */ set 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. */ + /** 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. + */ multimap mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; + /** Global flag to indicate we should check to see if there are + * block/undo files that should be deleted. Set on startup + * or if we allocate more file space when we're in prune mode + */ + bool fCheckForPruning = false; /** * Every received block is assigned a unique and increasing identifier, so we @@ -1849,6 +1860,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } enum FlushStateMode { + FLUSH_STATE_NONE, FLUSH_STATE_IF_NEEDED, FLUSH_STATE_PERIODIC, FLUSH_STATE_ALWAYS @@ -1856,16 +1868,30 @@ enum FlushStateMode { /** * Update the on-disk chain state. - * The caches and indexes are flushed if either they're too large, forceWrite is set, or - * fast is not set and it's been a while since the last write. + * The caches and indexes are flushed depending on the mode we're called with + * if they're too large, if it's been a while since the last write, + * or always and in all cases if we're in prune mode and are deleting files. */ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { - LOCK(cs_main); + LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; + std::set setFilesToPrune; + bool fFlushForPrune = false; try { + if (fPruneMode && fCheckForPruning) { + FindFilesToPrune(setFilesToPrune); + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } + } + } if ((mode == FLUSH_STATE_ALWAYS) || ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || - (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) { + (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) || + fFlushForPrune) { // Typical CCoins structures on disk are around 100 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is already @@ -1893,9 +1919,16 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return state.Abort("Files to write to block index database"); } } - // Finally flush the chainstate (which may refer to block index entries). + // Flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) return state.Abort("Failed to write to coin database"); + + // Finally remove any pruned files + if (fFlushForPrune) { + UnlinkPrunedFiles(setFilesToPrune); + fCheckForPruning = false; + } + // Update best block in wallet (so we can detect restored wallets). if (mode != FLUSH_STATE_IF_NEEDED) { GetMainSignals().SetBestChain(chainActive.GetLocator()); @@ -1913,6 +1946,12 @@ void FlushStateToDisk() { FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } +void PruneAndFlush() { + CValidationState state; + fCheckForPruning = true; + FlushStateToDisk(state, FLUSH_STATE_NONE); +} + /** Update chainActive and related internal data structures. */ void static UpdateTip(CBlockIndex *pindexNew) { chainActive.SetTip(pindexNew); @@ -2083,15 +2122,29 @@ static CBlockIndex* FindMostWorkChain() { CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !chainActive.Contains(pindexTest)) { - assert(pindexTest->nStatus & BLOCK_HAVE_DATA); assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - if (pindexTest->nStatus & BLOCK_FAILED_MASK) { - // Candidate has an invalid ancestor, remove entire chain from the set. - if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork) + + // Pruned nodes may have entries in setBlockIndexCandidates for + // which block files have been deleted. Remove those as candidates + // for the most work chain if we come across them; we can't switch + // to a chain unless we have all the non-active-chain parent blocks. + bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; + bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); + if (fFailedChain || fMissingData) { + // Candidate chain is not usable (either invalid or missing data) + if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; + // Remove the entire chain from the set. while (pindexTest != pindexFailed) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + if (fFailedChain) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + } else if (fMissingData) { + // If we're missing data, then add back to mapBlocksUnlinked, + // so that if the block arrives in the future we can try adding + // to setBlockIndexCandidates again. + mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); + } setBlockIndexCandidates.erase(pindexFailed); pindexFailed = pindexFailed->pprev; } @@ -2219,7 +2272,9 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - { + // Don't relay blocks if pruning -- could cause a peer to try to download, resulting + // in a stalled download if the block file is pruned before the request. + if (nLocalServices & NODE_NETWORK) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) @@ -2419,6 +2474,8 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenBlockFile(pos); if (file) { @@ -2450,6 +2507,8 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenUndoFile(pos); if (file) { @@ -2665,7 +2724,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!AcceptBlockHeader(block, state, &pindex)) return false; - if (pindex->nStatus & BLOCK_HAVE_DATA) { + // If we're pruning, ensure that we don't allow a peer to dump a copy + // of old blocks. But we might need blocks that are not on the main chain + // to handle a reorg, even if we've processed once. + if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { // TODO: deal better with duplicate blocks. // return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); return true; @@ -2698,6 +2760,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Abort(std::string("System error: ") + e.what()); } + if (fCheckForPruning) + FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files + return true; } @@ -2785,6 +2850,112 @@ bool AbortNode(const std::string &strMessage, const std::string &userMessage) { return false; } + +/** + * BLOCK PRUNING CODE + */ + +/* Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage() +{ + uint64_t retval = 0; + BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) { + retval += file.nSize + file.nUndoSize; + } + return retval; +} + +/* Prune a block file (modify associated database entries)*/ +void PruneOneBlockFile(const int fileNumber) +{ + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { + CBlockIndex* pindex = it->second; + if (pindex->nFile == fileNumber) { + pindex->nStatus &= ~BLOCK_HAVE_DATA; + pindex->nStatus &= ~BLOCK_HAVE_UNDO; + pindex->nFile = 0; + pindex->nDataPos = 0; + pindex->nUndoPos = 0; + setDirtyBlockIndex.insert(pindex); + + // Prune from mapBlocksUnlinked -- any block we prune would have + // to be downloaded again in order to consider its chain, at which + // point it would be considered as a candidate for + // mapBlocksUnlinked or setBlockIndexCandidates. + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); + while (range.first != range.second) { + std::multimap::iterator it = range.first; + range.first++; + if (it->second == pindex) { + mapBlocksUnlinked.erase(it); + } + } + } + } + + vinfoBlockFile[fileNumber].SetNull(); + setDirtyFileInfo.insert(fileNumber); +} + + +void UnlinkPrunedFiles(std::set& setFilesToPrune) +{ + for (set::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { + CDiskBlockPos pos(*it, 0); + boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); + boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); + LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it); + } +} + +/* Calculate the block/rev files that should be deleted to remain under target*/ +void FindFilesToPrune(std::set& setFilesToPrune) +{ + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL || nPruneTarget == 0) { + return; + } + if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { + return; + } + + unsigned int nLastBlockWeMustKeep = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + uint64_t nCurrentUsage = CalculateCurrentUsage(); + // We don't check to prune until after we've allocated new space for files + // So we should leave a buffer under our target to account for another allocation + // before the next pruning. + uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; + uint64_t nBytesToPrune; + int count=0; + + if (nCurrentUsage + nBuffer >= nPruneTarget) { + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; + + if (vinfoBlockFile[fileNumber].nSize == 0) + continue; + + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? + break; + + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeMustKeep) + break; + + PruneOneBlockFile(fileNumber); + // Queue up the files for removal + setFilesToPrune.insert(fileNumber); + nCurrentUsage -= nBytesToPrune; + count++; + } + } + + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB min_must_keep=%d removed %d blk/rev pairs\n", + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeMustKeep, count); +} + bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; @@ -2872,7 +3043,9 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); - if (pindex->nStatus & BLOCK_HAVE_DATA) { + // We can link the chain of blocks for which we've received transactions at some point. + // Pruned nodes may have deleted the block. + if (pindex->nTx > 0) { if (pindex->pprev) { if (pindex->pprev->nChainTx) { pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; @@ -2929,6 +3102,11 @@ bool static LoadBlockIndexDB() } } + // Check whether we have ever pruned block & undo files + pblocktree->ReadFlag("prunedblockfiles", fHavePruned); + if (fHavePruned) + LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); + // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); @@ -3069,6 +3247,7 @@ void UnloadBlockIndex() delete entry.second; } mapBlockIndex.clear(); + fHavePruned = false; } bool LoadBlockIndex() @@ -3260,6 +3439,7 @@ void static CheckBlockIndex() int nHeight = 0; CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNeverProcessed = NULL; // Oldest ancestor of pindex for which nTx == 0. CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). @@ -3268,6 +3448,7 @@ void static CheckBlockIndex() nNodes++; if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindexFirstNeverProcessed == NULL && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; @@ -3279,12 +3460,21 @@ void static CheckBlockIndex() assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. } - // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) - assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); - assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked - // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. - assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. + // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). + // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. + if (!fHavePruned) { + // If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0 + assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); + assert(pindexFirstMissing == pindexFirstNeverProcessed); + } else { + // If we have pruned, then we can only say that HAVE_DATA implies nTx > 0 + if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); + } + if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA); + assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. + // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. + assert((pindexFirstNeverProcessed != NULL) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); assert(pindex->nHeight == nHeight); // nHeight must be consistent. assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. @@ -3297,11 +3487,20 @@ void static CheckBlockIndex() // Checks for not-invalid blocks. assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. } - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) { - if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates. - assert(setBlockIndexCandidates.count(pindex)); + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == NULL) { + if (pindexFirstInvalid == NULL) { + // If this block sorts at least as good as the current tip and + // is valid and we have all data for its parents, it must be in + // setBlockIndexCandidates. chainActive.Tip() must also be there + // even if some data has been pruned. + if (pindexFirstMissing == NULL || pindex == chainActive.Tip()) { + assert(setBlockIndexCandidates.count(pindex)); + } + // If some parent is missing, then it could be that this block was in + // setBlockIndexCandidates but had to be removed because of the missing data. + // In this case it must be in mapBlocksUnlinked -- see test below. } - } else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates. + } else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates. assert(setBlockIndexCandidates.count(pindex) == 0); } // Check whether this block is in mapBlocksUnlinked. @@ -3315,12 +3514,28 @@ void static CheckBlockIndex() } rangeUnlinked.first++; } - if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) { - if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked. - assert(foundInUnlinked); + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != NULL && pindexFirstInvalid == NULL) { + // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. + assert(foundInUnlinked); + } + if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA + if (pindexFirstMissing == NULL) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == NULL && pindexFirstMissing != NULL) { + // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. + assert(fHavePruned); // We must have pruned. + // This block may have entered mapBlocksUnlinked if: + // - it has a descendant that at some point had more work than the + // tip, and + // - we tried switching to that descendant but were missing + // data for some intermediate block between chainActive and the + // tip. + // So if this block is itself better than chainActive.Tip() and it wasn't in + // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { + if (pindexFirstInvalid == NULL) { + assert(foundInUnlinked); + } } - } else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked. - assert(!foundInUnlinked); } // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow // End: actual consistency checks. @@ -3340,6 +3555,7 @@ void static CheckBlockIndex() // If pindex was the first with a certain property, unset the corresponding variable. if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; + if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = NULL; if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; @@ -3497,7 +3713,9 @@ void static ProcessGetData(CNode* pfrom) } } } - if (send) + // 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; -- cgit v1.2.3 From d81cff32e50fe5f686f985d0af2e74219f328ed0 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sat, 25 Apr 2015 16:25:44 -0400 Subject: Replace mruset setAddrKnown with CRollingBloomFilter addrKnown Use a probabilistic bloom filter to keep track of which addresses we think we have given our peers, instead of a list. This uses much less memory, at the cost of sometimes failing to relay an address to a peer-- worst case if the bloom filter happens to be as full as it gets, 1-in-1,000. Measured memory usage of a full mruset setAddrKnown: 650Kbytes Constant memory usage of CRollingBloomFilter addrKnown: 37Kbytes. This will also help heap fragmentation, because the 37K of storage is allocated when a CNode is created (when a connection to a peer is established) and then there is no per-item-remembered memory allocation. I plan on testing by restarting a full node with an empty peers.dat, running a while with -debug=addrman and -debug=net, and making sure that the 'addr' message traffic out is reasonable. (suggestions for better tests welcome) --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e6248c6617..a6b717d57f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3995,7 +3995,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours - // at a time so the setAddrKnowns of the chosen nodes prevent repeats + // at a time so the addrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; if (hashSalt.IsNull()) hashSalt = GetRandHash(); @@ -4779,9 +4779,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { - // Periodically clear setAddrKnown to allow refresh broadcasts + // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) - pnode->setAddrKnown.clear(); + pnode->addrKnown.clear(); // Rebroadcast our address AdvertizeLocal(pnode); @@ -4799,9 +4799,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vAddr.reserve(pto->vAddrToSend.size()); BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) { - // returns true if wasn't already contained in the set - if (pto->setAddrKnown.insert(addr).second) + if (!pto->addrKnown.contains(addr.GetKey())) { + pto->addrKnown.insert(addr.GetKey()); vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) -- cgit v1.2.3 From 11982d366df0301b8ceb6e9ec5bdc5a713be9ff0 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 22 Apr 2015 18:19:11 -0400 Subject: checkpoints: Decouple checkpoints from Params Pass checkpoint data in as necessary --- src/main.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e6248c6617..4e26cae52e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1204,8 +1204,9 @@ CAmount GetBlockValue(int nHeight, const CAmount& nFees) bool IsInitialBlockDownload() { + const CChainParams& chainParams = Params(); LOCK(cs_main); - if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) + if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) return true; static bool lockIBDState = false; if (lockIBDState) @@ -1709,7 +1710,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; } - bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -1954,6 +1955,7 @@ void PruneAndFlush() { /** Update chainActive and related internal data structures. */ void static UpdateTip(CBlockIndex *pindexNew) { + const CChainParams& chainParams = Params(); chainActive.SetTip(pindexNew); // New best block @@ -1963,7 +1965,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); cvBlockChange.notify_all(); @@ -2247,6 +2249,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool ActivateBestChain(CValidationState &state, CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; + const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); @@ -2271,7 +2274,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (!fInitialDownload) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); // Relay inventory, but don't relay old inventory during initial block download. - int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); // Don't relay blocks if pruning -- could cause a peer to try to download, resulting // in a stalled download if the block file is pruned before the request. if (nLocalServices & NODE_NETWORK) { @@ -2601,7 +2604,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) { - const Consensus::Params& consensusParams = Params().GetConsensus(); + const CChainParams& chainParams = Params(); + const Consensus::Params& consensusParams = chainParams.GetConsensus(); uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) return true; @@ -2611,7 +2615,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; // Check proof of work - if (block.nBits != GetNextWorkRequired(pindexPrev, &block, Params().GetConsensus())) + if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); @@ -2621,24 +2625,24 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta REJECT_INVALID, "time-too-old"); // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) + if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), REJECT_CHECKPOINT, "checkpoint mismatch"); // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); if (pcheckpoint && nHeight < pcheckpoint->nHeight) return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) + if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) { return state.Invalid(error("%s: rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); } // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, Params().RejectBlockOutdatedMajority())) + if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) { return state.Invalid(error("%s : rejected nVersion=2 block", __func__), REJECT_OBSOLETE, "bad-version"); @@ -3025,6 +3029,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) bool static LoadBlockIndexDB() { + const CChainParams& chainparams = Params(); if (!pblocktree->LoadBlockIndexGuts()) return false; @@ -3127,7 +3132,7 @@ bool static LoadBlockIndexDB() LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainActive.Tip())); + Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); return true; } -- cgit v1.2.3 From a8cdaf5c962ff9018e2d8411f532eec7355f0623 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Wed, 22 Apr 2015 23:22:36 -0400 Subject: checkpoints: move the checkpoints enable boolean into main This pertains to app-state, so it doesn't make sense to handle inside the checkpoint functions. --- src/main.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4e26cae52e..df27a7c7af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,6 +56,7 @@ bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; +bool fCheckpointsEnabled = true; unsigned int nCoinCacheSize = 5000; uint64_t nPruneTarget = 0; @@ -1206,7 +1207,9 @@ bool IsInitialBlockDownload() { const CChainParams& chainParams = Params(); LOCK(cs_main); - if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) + if (fImporting || fReindex) + return true; + if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) return true; static bool lockIBDState = false; if (lockIBDState) @@ -1710,7 +1713,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; } - bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); + bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -2274,7 +2277,9 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (!fInitialDownload) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); // Relay inventory, but don't relay old inventory during initial block download. - int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); + int nBlockEstimate = 0; + if (fCheckpointsEnabled) + nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); // Don't relay blocks if pruning -- could cause a peer to try to download, resulting // in a stalled download if the block file is pruned before the request. if (nLocalServices & NODE_NETWORK) { @@ -2624,15 +2629,18 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) - return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); + if(fCheckpointsEnabled) + { + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) + return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + } // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) -- cgit v1.2.3 From 7e6d23b1719191f83af7d3e7b134a90889b5c1ef Mon Sep 17 00:00:00 2001 From: Corinne Dashjr Date: Tue, 28 Apr 2015 14:48:28 +0000 Subject: Bugfix: Grammar fixes --- src/main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2c12e08492..263c2abf6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -941,7 +941,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), - // only helps filling in pfMissingInputs (to determine missing vs spent). + // and only helps with filling in pfMissingInputs (to determine missing vs spent). BOOST_FOREACH(const CTxIn txin, tx.vin) { if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) @@ -1277,8 +1277,8 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) pfork = pfork->pprev; } - // We define a condition which we should warn the user about as a fork of at least 7 blocks - // who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours + // We define a condition where we should warn the user about as a fork of at least 7 blocks + // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network // hash rate operating on the fork. // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) @@ -1719,7 +1719,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool // already refuses previously-known transaction ids entirely. - // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC. + // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. @@ -1984,7 +1984,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); CAlert::Notify(strMiscWarning, true); fWarned = true; } @@ -3732,7 +3732,7 @@ void static ProcessGetData(CNode* pfrom) pfrom->PushMessage("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 didnt send here - + // Note that there is currently no way for a node to request any single transactions we didn't send here - // they must either disconnect and retry or request the full block. // Thus, the protocol spec specified allows for us to provide duplicate txn here, // however we MUST always provide at least what the remote peer needs @@ -4059,7 +4059,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { - // First request the headers preceeding the announced block. In the normal fully-synced + // First request the headers preceding the announced block. In the normal fully-synced // case where a new block is announced that succeeds the current tip (no reorganization), // there are no such headers. // Secondly, and only when we are close to being synced, we request the announced block directly, @@ -4466,7 +4466,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Nonce mismatches are normal when pings are overlapping sProblem = "Nonce mismatch"; if (nonce == 0) { - // This is most likely a bug in another implementation somewhere, cancel this ping + // This is most likely a bug in another implementation somewhere; cancel this ping bPingFinished = true; sProblem = "Nonce zero"; } @@ -4475,7 +4475,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, sProblem = "Unsolicited pong without ping"; } } else { - // This is most likely a bug in another implementation somewhere, cancel this ping + // This is most likely a bug in another implementation somewhere; cancel this ping bPingFinished = true; sProblem = "Short payload"; } @@ -4918,7 +4918,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link - // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes + // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * consensusParams.nPowTargetSpacing * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); -- cgit v1.2.3 From b05a89b2def301704df8f22f05a45ccf891c2b83 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 28 Apr 2015 14:47:17 +0000 Subject: Non-grammatical language improvements --- src/main.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 263c2abf6f..07156a9af2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -142,8 +142,9 @@ namespace { uint32_t nBlockSequenceId = 1; /** - * Sources of received blocks, to be able to send them reject messages or ban - * them, if processing happens afterwards. Protected by cs_main. + * Sources of received blocks, saved to be able to send them reject + * messages or ban them when processing happens afterwards. Protected by + * cs_main. */ map mapBlockSource; @@ -389,7 +390,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorpindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) return; @@ -1721,7 +1722,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // already refuses previously-known transaction ids entirely. // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the - // two in the chain that violate it. This prevents exploiting the issue against nodes in their + // two in the chain that violate it. This prevents exploiting the issue against nodes during their // initial block download. bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || @@ -2315,7 +2316,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } // The resulting new best tip may not be in setBlockIndexCandidates anymore, so - // add them again. + // add it again. BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { @@ -3745,7 +3746,7 @@ void static ProcessGetData(CNode* pfrom) // no response } - // Trigger them to send a getblocks request for the next batch of inventory + // Trigger the peer node to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) { // Bypass PushInventory, this must send even if redundant, @@ -4141,8 +4142,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { - // When this block is requested, we'll send an inv that'll make them - // getblocks the next batch of inventory. + // When this block is requested, we'll send an inv that'll + // trigger the peer to getblocks the next batch of inventory. LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); pfrom->hashContinue = pindex->GetBlockHash(); break; @@ -4379,9 +4380,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses - // to users' AddrMan and later request them by sending getaddr messages. - // Making users (which are behind NAT and can only make outgoing connections) ignore - // getaddr message mitigates the attack. + // 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)) { pfrom->vAddrToSend.clear(); @@ -4734,7 +4735,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { const Consensus::Params& consensusParams = Params().GetConsensus(); { - // Don't send anything until we get their version message + // Don't send anything until we get its version message if (pto->nVersion == 0) return true; -- cgit v1.2.3 From 51aa24927e6be7d5dd9855bf485989483fef13bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 1 Apr 2015 16:10:37 +0200 Subject: Chainparams: Refactor: Decouple IsSuperMajority from Params() --- src/main.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4f4926eb40..7bd2bdd447 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,10 +75,9 @@ void EraseOrphansFor(NodeId peer); /** * Returns true if there are nRequired or more blocks of minVersion or above - * in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart - * and going backwards. + * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. */ -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired); +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); static void CheckBlockIndex(); /** Constant stuff for coinbase transactions we create: */ @@ -1747,7 +1746,7 @@ 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: - if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { flags |= SCRIPT_VERIFY_DERSIG; } @@ -2644,18 +2643,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta } // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) - { + if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s: rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); - } // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) - { + if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s : rejected nVersion=2 block", __func__), REJECT_OBSOLETE, "bad-version"); - } return true; } @@ -2663,6 +2658,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + const Consensus::Params& consensusParams = Params().GetConsensus(); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2672,7 +2668,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // 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): - if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority())) + if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || @@ -2779,11 +2775,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return true; } -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired) +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams) { - unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority(); unsigned int nFound = 0; - for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) + for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++) { if (pstart->nVersion >= minVersion) ++nFound; -- cgit v1.2.3 From fc684ad8afae19c209701230837d338c5a6c1f72 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 4 May 2015 01:38:08 +0200 Subject: Use accurate memory for flushing decisions --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4f4926eb40..916e1a6093 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,7 +57,7 @@ bool fPruneMode = false; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = true; -unsigned int nCoinCacheSize = 5000; +size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ @@ -1894,7 +1894,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } } if ((mode == FLUSH_STATE_ALWAYS) || - ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || + ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->DynamicMemoryUsage() > nCoinCacheUsage) || (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) || fFlushForPrune) { // Typical CCoins structures on disk are around 100 bytes in size. @@ -3197,7 +3197,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) { + if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { bool fClean = true; if (!DisconnectBlock(block, state, pindex, coins, &fClean)) return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); -- cgit v1.2.3 From b3ed4236beb7f68e1720ceb3da15e0c3682ef629 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 4 May 2015 01:56:42 +0200 Subject: Cache tweak and logging improvements --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 916e1a6093..a1b3b81905 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1966,10 +1966,10 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__, + LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); cvBlockChange.notify_all(); -- cgit v1.2.3 From 67708acff9c18e380fa6136ff0ae718959ead4b5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 4 May 2015 22:00:19 +0200 Subject: Write block index more frequently than cache flushes --- src/main.cpp | 55 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 15 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a1b3b81905..28582e0486 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1880,6 +1880,8 @@ enum FlushStateMode { bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; + static int64_t nLastFlush = 0; + static int64_t nLastSetChain = 0; std::set setFilesToPrune; bool fFlushForPrune = false; try { @@ -1893,16 +1895,36 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } } } - if ((mode == FLUSH_STATE_ALWAYS) || - ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->DynamicMemoryUsage() > nCoinCacheUsage) || - (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) || - fFlushForPrune) { - // Typical CCoins structures on disk are around 100 bytes in size. + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Typical CCoins structures on disk are around 128 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is already // an overestimation, as most will delete an existing entry or // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + if (fDoFullFlush && !CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) return state.Error("out of disk space"); // First make sure all block and undo data is flushed to disk. FlushBlockFile(); @@ -1924,21 +1946,24 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return state.Abort("Files to write to block index database"); } } - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return state.Abort("Failed to write to coin database"); - // Finally remove any pruned files if (fFlushForPrune) { UnlinkPrunedFiles(setFilesToPrune); fCheckForPruning = false; } - + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return state.Abort("Failed to write to coin database"); + nLastFlush = nNow; + } + if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { // Update best block in wallet (so we can detect restored wallets). - if (mode != FLUSH_STATE_IF_NEEDED) { - GetMainSignals().SetBestChain(chainActive.GetLocator()); - } - nLastWrite = GetTimeMicros(); + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; } } catch (const std::runtime_error& e) { return state.Abort(std::string("System error while flushing: ") + e.what()); -- cgit v1.2.3 From 14d4eef79931318cb5968f9154cf458d9f8d27fa Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 8 Apr 2015 08:57:19 -0500 Subject: Fix removing of orphan transactions We don't want to erase orphans that still have missing inputs, they should still be tracked as orphans. Also, the transaction thats being accepted can't be an orphan otherwise it would have previously been accepted, so doesn't need to be added to the erase queue. --- src/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb6766301..b04274fa21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3979,7 +3979,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - vEraseQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, @@ -4006,7 +4005,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // anyone relaying LegitTxX banned) CValidationState stateDummy; - vEraseQueue.push_back(orphanHash); if (setMisbehaving.count(fromPeer)) continue; @@ -4015,6 +4013,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx); vWorkQueue.push_back(orphanHash); + vEraseQueue.push_back(orphanHash); } else if (!fMissingInputs2) { @@ -4026,8 +4025,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, setMisbehaving.insert(fromPeer); LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); } - // too-little-fee orphan + // Has inputs but not accepted to mempool + // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); + vEraseQueue.push_back(orphanHash); } mempool.check(pcoinsTip); } -- cgit v1.2.3 From 86a5f4b54ebf5f3251f4c172cf9a5041ae43c082 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 11 May 2015 11:18:39 -0400 Subject: Relocate calls to CheckDiskSpace Make sure we're checking disk space for block index writes and allow for pruning to happen before chainstate writes. --- src/main.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 28582e0486..94ab7ff7bb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1919,12 +1919,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; // Write blocks and block index to disk. if (fDoFullFlush || fPeriodicWrite) { - // Typical CCoins structures on disk are around 128 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (fDoFullFlush && !CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) return state.Error("out of disk space"); // First make sure all block and undo data is flushed to disk. FlushBlockFile(); @@ -1955,6 +1951,13 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } // Flush best chain related state. This can only be done if the blocks / block index write was also done. if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); // Flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) return state.Abort("Failed to write to coin database"); -- cgit v1.2.3 From b649e0395464a659f4b3485ec71d28dc95ba48bd Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Tue, 26 Aug 2014 16:28:32 -0400 Subject: Create new BlockPolicyEstimator for fee estimates This class groups transactions that have been confirmed in blocks into buckets, based on either their fee or their priority. Then for each bucket, the class calculates what percentage of the transactions were confirmed within various numbers of blocks. It does this by keeping an exponentially decaying moving history for each bucket and confirm block count of the percentage of transactions in that bucket that were confirmed within that number of blocks. -Eliminate txs which didn't have all inputs available at entry from fee/pri calcs -Add dynamic breakpoints and tracking of confirmation delays in mempool transactions -Remove old CMinerPolicyEstimator and CBlockAverage code -New smartfees.py -Pass a flag to the estimation code, using IsInitialBlockDownload as a proxy for when we are still catching up and we shouldn't be counting how many blocks it takes for transactions to be included. -Add a policyestimator unit test --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4352c719a1..45e6e4d25d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -974,7 +974,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block @@ -1040,7 +1040,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Store transaction in memory - pool.addUnchecked(hash, entry); + pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); } SyncWithWallets(tx, NULL); @@ -2042,7 +2042,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); // Remove conflicting transactions from the mempool. list txConflicted; - mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted); + mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); mempool.check(pcoinsTip); // Update chainActive & related variables. UpdateTip(pindexNew); -- cgit v1.2.3 From b89f30775d9b8516573b6347b522f3e28f550f8d Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 23 Apr 2015 09:40:21 -0400 Subject: Fix incorrect variable name in FindFilesToPrune --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79ee4e55ec..9a3c272e0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2932,7 +2932,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) return; } - unsigned int nLastBlockWeMustKeep = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files // So we should leave a buffer under our target to account for another allocation @@ -2952,7 +2952,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) break; // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip - if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeMustKeep) + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) break; PruneOneBlockFile(fileNumber); @@ -2963,10 +2963,10 @@ void FindFilesToPrune(std::set& setFilesToPrune) } } - LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB min_must_keep=%d removed %d blk/rev pairs\n", + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", nPruneTarget/1024/1024, nCurrentUsage/1024/1024, ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeMustKeep, count); + nLastBlockWeCanPrune, count); } bool CheckDiskSpace(uint64_t nAdditionalBytes) -- cgit v1.2.3 From dce8360e44d5330cc9f9d09c9b09ac9237237204 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 19 Mar 2015 05:34:06 -0700 Subject: Reduce checkpoints' effect on consensus. Instead of only checking height to decide whether to disable script checks, actually check whether a block is an ancestor of a checkpoint, up to which headers have been validated. This means that we don't have to prevent accepting a side branch anymore - it will be safe, just less fast to do. We still need to prevent being fed a multitude of low-difficulty headers filling up our memory. The mechanism for that is unchanged for now: once a checkpoint is reached with headers, no headers chain branching off before that point are allowed anymore. --- src/main.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4f4926eb40..39da3406a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1714,7 +1714,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; } - bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); + bool fScriptChecks = true; + if (fCheckpointsEnabled) { + CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); + if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) { + // This block is an ancestor of a checkpoint: disable script checks + fScriptChecks = false; + } + } // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -2630,13 +2637,8 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - if(fCheckpointsEnabled) + if (fCheckpointsEnabled) { - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) - return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); - // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); if (pcheckpoint && nHeight < pcheckpoint->nHeight) -- cgit v1.2.3 From 36cba8f1182e8284ec0017e57c5ffb519cd9798f Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 26 Mar 2015 11:20:59 -0400 Subject: Alert if it is very likely we are getting a bad chain Create a monitoring task that counts how many blocks have been found in the last four hours. If very few or too many have been found, an alert is triggered. "Very few" and "too many" are set based on a false positive rate of once every fifty years of constant running with constant hashing power, which works out to getting 5 or fewer or 48 or more blocks in four hours (instead of the average of 24). Only one alert per day is triggered, so if you get disconnected from the network (or are being Sybil'ed) -alertnotify will be triggered after 3.5 hours but you won't get another -alertnotify for 24 hours. Tested with a new unit test and by running on the main network with -debug=partitioncheck Run test/test_bitcoin --log_level=message to see the alert messages: WARNING: check your network connection, 3 blocks received in the last 4 hours (24 expected) WARNING: abnormally high number of blocks generated, 60 blocks received in the last 4 hours (24 expected) The -debug=partitioncheck debug.log messages look like: ThreadPartitionCheck : Found 22 blocks in the last 4 hours ThreadPartitionCheck : likelihood: 0.0777702 --- src/main.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79ee4e55ec..5ebb48477f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include using namespace std; @@ -1688,6 +1689,64 @@ void ThreadScriptCheck() { scriptcheckqueue.Thread(); } +// +// Called periodically asynchronously; alerts if it smells like +// we're being fed a bad chain (blocks being generated much +// too slowly or too quickly). +// +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing) +{ + if (initialDownloadCheck()) return; + + static int64_t lastAlertTime = 0; + int64_t now = GetAdjustedTime(); + if (lastAlertTime > now-60*60*24) return; // Alert at most once per day + + const int SPAN_HOURS=4; + const int SPAN_SECONDS=SPAN_HOURS*60*60; + int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; + + boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); + + std::string strWarning; + int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; + + LOCK(cs); + int h = chain.Height(); + while (h > 0 && chain[h]->GetBlockTime() >= startTime) + --h; + int nBlocks = chain.Height()-h; + + // 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); + + // Aim for one false-positive about every fifty years of normal running: + const int FIFTY_YEARS = 50*365*24*60*60; + double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); + + if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) + { + // Many fewer blocks than expected: alert! + strWarning = strprintf(_("WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)"), + nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); + } + else if (p <= alertThreshold && nBlocks > BLOCKS_EXPECTED) + { + // Many more blocks than expected: alert! + strWarning = strprintf(_("WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)"), + nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); + } + if (!strWarning.empty()) + { + strMiscWarning = strWarning; + CAlert::Notify(strWarning, true); + lastAlertTime = now; + } +} + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; -- cgit v1.2.3 From c208040354c9312c7d23c7f26a4d009e775e00ff Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 13 May 2015 11:13:13 -0400 Subject: Fix for clearing fCheckForPruning Previously this was cleared only after UnlinkPrunedFiles, but it should really be cleared after FindFilesToPrune, regardless of whether there are any files to be pruned. --- src/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5fd9c62486..7d7c97f773 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1886,6 +1886,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { try { if (fPruneMode && fCheckForPruning) { FindFilesToPrune(setFilesToPrune); + fCheckForPruning = false; if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { @@ -1942,10 +1943,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } } // Finally remove any pruned files - if (fFlushForPrune) { + if (fFlushForPrune) UnlinkPrunedFiles(setFilesToPrune); - fCheckForPruning = false; - } nLastWrite = nNow; } // Flush best chain related state. This can only be done if the blocks / block index write was also done. -- cgit v1.2.3 From 27afcd89db302d87168e0f504ee141dbf0e1ea22 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 16 Jan 2015 18:57:14 -0500 Subject: Consensus: Refactor: Decouple CValidationState from main::AbortNode() --- src/main.cpp | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5fd9c62486..0a849c035c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1552,6 +1552,24 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin return true; } +/** Abort with a message */ +bool AbortNode(const std::string& strMessage, const std::string& userMessage="") +{ + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; +} + +bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") +{ + AbortNode(strMessage, userMessage); + return state.Error(strMessage); +} + } // anon namespace /** @@ -1831,7 +1849,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash())) - return state.Abort("Failed to write undo data"); + return AbortNode(state, "Failed to write undo data"); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1844,7 +1862,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort("Failed to write transaction index"); + return AbortNode(state, "Failed to write transaction index"); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -1938,7 +1956,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { setDirtyBlockIndex.erase(it++); } if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return state.Abort("Files to write to block index database"); + return AbortNode(state, "Files to write to block index database"); } } // Finally remove any pruned files @@ -1959,7 +1977,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return state.Error("out of disk space"); // Flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) - return state.Abort("Failed to write to coin database"); + return AbortNode(state, "Failed to write to coin database"); nLastFlush = nNow; } if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { @@ -1968,7 +1986,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { nLastSetChain = nNow; } } catch (const std::runtime_error& e) { - return state.Abort(std::string("System error while flushing: ") + e.what()); + return AbortNode(state, std::string("System error while flushing: ") + e.what()); } return true; } @@ -2032,7 +2050,7 @@ bool static DisconnectTip(CValidationState &state) { // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete)) - return state.Abort("Failed to read block"); + return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { @@ -2083,7 +2101,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * CBlock block; if (!pblock) { if (!ReadBlockFromDisk(block, pindexNew)) - return state.Abort("Failed to read block"); + return AbortNode(state, "Failed to read block"); pblock = █ } // Apply the block atomically to the chain state. @@ -2790,11 +2808,11 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return error("AcceptBlock(): FindBlockPos failed"); if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos)) - return state.Abort("Failed to write block"); + AbortNode(state, "Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); } catch (const std::runtime_error& e) { - return state.Abort(std::string("System error: ") + e.what()); + return AbortNode(state, std::string("System error: ") + e.what()); } if (fCheckForPruning) @@ -2869,24 +2887,6 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex return true; } - - - - - - - -bool AbortNode(const std::string &strMessage, const std::string &userMessage) { - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - - /** * BLOCK PRUNING CODE */ -- cgit v1.2.3 From da29ecbcc6c003a603ee2312309d593708ec6fb2 Mon Sep 17 00:00:00 2001 From: jtimon Date: Sat, 24 Jan 2015 15:57:12 +0100 Subject: Consensus: MOVEONLY: Move CValidationState from main consensus/validation --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0a849c035c..481eeaa9e4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" +#include "consensus/validation.h" #include "init.h" #include "merkleblock.h" #include "net.h" -- cgit v1.2.3 From 935bd0a447834fa48c218ddae2a5d835a67979ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 1 Apr 2015 16:03:11 +0200 Subject: Chainparams: Refactor: Decouple main::GetBlockValue() from Params() [renamed GetBlockSubsidy] Remove redundant getter CChainParams::SubsidyHalvingInterval() --- src/main.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5fd9c62486..462b74b937 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1188,19 +1188,17 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } -CAmount GetBlockValue(int nHeight, const CAmount& nFees) +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { - CAmount nSubsidy = 50 * COIN; - int halvings = nHeight / Params().SubsidyHalvingInterval(); - + int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; // Force block reward to zero when right shift is undefined. if (halvings >= 64) - return nFees; + return 0; + CAmount nSubsidy = 50 * COIN; // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. nSubsidy >>= halvings; - - return nSubsidy + nFees; + return nSubsidy; } bool IsInitialBlockDownload() @@ -1809,10 +1807,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin 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); - if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); + if (block.vtx[0].GetValueOut() > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), + block.vtx[0].GetValueOut(), blockReward), REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) -- cgit v1.2.3 From 28bf06236d3b385e95fe26a7a742395b30efd6ee Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 25 May 2015 00:48:33 -0400 Subject: Fix off-by-one error w/ nLockTime in the wallet Previously due to an off-by-one error the wallet ignored nLockTime-by-height transactions that would be valid in the next block even though they are accepted into the mempool. The transactions wouldn't show up until confirmed, nor would they be included in the unconfirmed balance. Similar to the mempool behavior fix in 665bdd3b, the wallet code was calling IsFinalTx() directly without taking into account the fact that doing so tells you if the transaction could have been mined in the *current* block, rather than the next block. To fix this we strip IsFinalTx() of non-consensus-critical functionality, removing the default arguments, and add CheckFinalTx() to check if a transaction will be final in the next block. --- src/main.cpp | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d3956fafda..9eec21edd9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -658,14 +658,8 @@ bool IsStandardTx(const CTransaction& tx, string& reason) bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { - AssertLockHeld(cs_main); - // Time based nLockTime implemented in 0.1.6 if (tx.nLockTime == 0) return true; - if (nBlockHeight == 0) - nBlockHeight = chainActive.Height(); - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) @@ -674,6 +668,12 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } +bool CheckFinalTx(const CTransaction &tx) +{ + AssertLockHeld(cs_main); + return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime()); +} + /** * Check transaction inputs to mitigate two * potential denial-of-service attacks: @@ -890,21 +890,8 @@ 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. - // - // However, IsFinalTx() is confusing... Without arguments, it uses - // chainActive.Height() to evaluate nLockTime; when a block is accepted, - // chainActive.Height() is set to the value of nHeight in the block. - // However, 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(). - // - // Timestamps on the other hand don't get any special treatment, because we - // can't know what timestamp the next block will have, and there aren't - // timestamp applications where it matters. - if (!IsFinalTx(tx, chainActive.Height() + 1)) - return state.DoS(0, - error("AcceptToMemoryPool: non-final"), + if (!CheckFinalTx(tx)) + return state.DoS(0, error("AcceptToMemoryPool: non-final"), REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? -- cgit v1.2.3 From e69734309c948bed697292a1c3b420d163e07fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Sun, 19 Apr 2015 23:48:25 +0200 Subject: Chainparams: Explicit CMessageHeader::MessageStartChars to functions in main: -UndoWriteToDisk -WriteBlockToDisk --- src/main.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6b2e779db7..651348300e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1146,7 +1146,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -1155,7 +1155,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Write index header unsigned int nSize = fileout.GetSerializeSize(block); - fileout << FLATDATA(Params().MessageStart()) << nSize; + fileout << FLATDATA(messageStart) << nSize; // Write block long fileOutPos = ftell(fileout.Get()); @@ -1509,7 +1509,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi namespace { -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock) +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); @@ -1518,7 +1518,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint // Write index header unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(Params().MessageStart()) << nSize; + fileout << FLATDATA(messageStart) << nSize; // Write undo data long fileOutPos = ftell(fileout.Get()); @@ -1918,7 +1918,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); - if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash())) + if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); // update nUndoPos in block index @@ -2841,6 +2841,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); CBlockIndex *&pindex = *ppindex; @@ -2876,7 +2877,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) return error("AcceptBlock(): FindBlockPos failed"); if (dbp == NULL) - if (!WriteBlockToDisk(block, blockPos)) + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) AbortNode(state, "Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); @@ -3366,6 +3367,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { + const CChainParams& chainparams = Params(); LOCK(cs_main); // Check whether we're already initialized if (chainActive.Genesis() != NULL) @@ -3386,7 +3388,7 @@ bool InitBlockIndex() { CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) return error("LoadBlockIndex(): FindBlockPos failed"); - if (!WriteBlockToDisk(block, blockPos)) + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) -- cgit v1.2.3 From a1ba0778dd3c784046dea334e5d39f37eca264f7 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 21 May 2015 13:29:09 -0400 Subject: Ignore getheaders requests when not synced. --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79ee4e55ec..79a65a73eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4174,6 +4174,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); + if (IsInitialBlockDownload()) + return true; + CBlockIndex* pindex = NULL; if (locator.IsNull()) { -- cgit v1.2.3 From 8273793350e47786edbb2abf5831ebab9b72ca01 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 27 May 2015 10:57:17 -0400 Subject: Eliminate compiler warning due to unused variable --- src/main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6b2e779db7..012495594a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -264,9 +264,9 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) } // Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore) +int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) { - return nTime + 500000 * Params().GetConsensus().nPowTargetSpacing * (4 + nValidatedQueuedBefore); + return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); } void InitializeNode(NodeId nodeid, const CNode *pnode) { @@ -310,7 +310,7 @@ void MarkBlockAsReceived(const uint256& hash) { } // Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex = NULL) { +void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); @@ -318,7 +318,7 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex MarkBlockAsReceived(hash); int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders)}; + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; nQueuedValidatedHeaders += newentry.fValidatedHeaders; list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; @@ -4181,7 +4181,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out // later (within the same cs_main lock, though). - MarkBlockAsInFlight(pfrom->GetId(), inv.hash); + MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); } LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } @@ -5034,7 +5034,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // more quickly than once every 5 minutes, then we'll shorten the download window for this block). if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); - int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders); + int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams); if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; @@ -5055,7 +5055,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->id); } -- cgit v1.2.3 From 9be0e6837b878f72bd087ce32b7a2f2ffb2fd544 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 9 Apr 2015 13:21:11 -0400 Subject: Be stricter in processing unrequested blocks AcceptBlock will no longer process an unrequested block, unless it has not been previously processed and has more work than chainActive.Tip() --- src/main.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index efb7fb3f87..760cda5dee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -296,7 +296,8 @@ void FinalizeNode(NodeId nodeid) { } // Requires cs_main. -void MarkBlockAsReceived(const uint256& hash) { +// Returns a bool indicating whether we requested this block. +bool MarkBlockAsReceived(const uint256& hash) { map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); @@ -306,7 +307,9 @@ void MarkBlockAsReceived(const uint256& hash) { state->nBlocksInFlight--; state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); + return true; } + return false; } // Requires cs_main. @@ -2826,7 +2829,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp) +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); @@ -2836,13 +2839,18 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!AcceptBlockHeader(block, state, &pindex)) return false; - // If we're pruning, ensure that we don't allow a peer to dump a copy - // of old blocks. But we might need blocks that are not on the main chain - // to handle a reorg, even if we've processed once. - if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { - // TODO: deal better with duplicate blocks. - // return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); - return true; + // Try to process all requested blocks that we don't have, but only + // process an unrequested block if it's new and has enough work to + // advance our tip. + bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; + bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + + // TODO: deal better with return value and error conditions for duplicate + // and unrequested blocks. + if (fAlreadyHave) return true; + if (!fRequested) { // If we didn't ask for it: + if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned + if (!fHasMoreWork) return true; // Don't process less-work chains } if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { @@ -2891,21 +2899,22 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks bool checked = CheckBlock(*pblock, state); { LOCK(cs_main); - MarkBlockAsReceived(pblock->GetHash()); + bool fRequested = MarkBlockAsReceived(pblock->GetHash()); + fRequested |= fForceProcessing; if (!checked) { return error("%s: CheckBlock FAILED", __func__); } // Store to disk CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, dbp); + bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } @@ -3453,7 +3462,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, dbp)) + if (ProcessNewBlock(state, NULL, &block, true, dbp)) nLoaded++; if (state.IsError()) break; @@ -3475,7 +3484,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, &it->second)) + if (ProcessNewBlock(dummy, NULL, &block, true, &it->second)) { nLoaded++; queue.push_back(block.GetHash()); @@ -4462,7 +4471,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->AddInventoryKnown(inv); CValidationState state; - ProcessNewBlock(state, pfrom, &block); + // Process all blocks from whitelisted peers, even if not requested. + ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL); int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), -- cgit v1.2.3 From bfc30b34374d71928acee5ff41282f09cedfd5e4 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 2 Jun 2015 15:17:36 -0400 Subject: Ignore unrequested blocks too far ahead of tip --- src/main.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 760cda5dee..e0997515b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2841,9 +2841,15 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to - // advance our tip. + // advance our tip, and isn't too many blocks ahead. bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + // Blocks that are too out-of-order needlessly limit the effectiveness of + // pruning, because pruning will not delete block files that contain any + // blocks which are too close in height to the tip. Apply this test + // regardless of whether pruning is enabled; it should generally be safe to + // not process unrequested blocks. + bool fTooFarAhead = (pindex->nHeight - chainActive.Height()) > MIN_BLOCKS_TO_KEEP; // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. @@ -2851,6 +2857,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!fRequested) { // If we didn't ask for it: if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned if (!fHasMoreWork) return true; // Don't process less-work chains + if (fTooFarAhead) return true; // Block height is too high } if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { -- cgit v1.2.3 From c257a8c9a6397eee40734b235a4fdcb8045aec91 Mon Sep 17 00:00:00 2001 From: Adam Weiss Date: Tue, 2 Jun 2015 15:24:53 -0400 Subject: Prune: Support noncontiguous block files In some corner cases, it may be possible for recent blocks to end up in the same block file as much older blocks. Previously, the pruning code would stop looking for files to remove upon first encountering a file containing a block that cannot be pruned, now it will keep looking for candidate files until the target is met and all other criteria are satisfied. This can result in a noncontiguous set of block files (by number) on disk, which is fine except for during some reindex corner cases, so make reindex preparation smarter such that we keep the data we can actually use and throw away the rest. This allows pruning to work correctly while downloading any blocks needed during the reindex. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index efb7fb3f87..03a4082bba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3031,9 +3031,9 @@ void FindFilesToPrune(std::set& setFilesToPrune) if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) - break; + continue; PruneOneBlockFile(fileNumber); // Queue up the files for removal -- cgit v1.2.3 From 3e9143386a90e508c8d41719294db11264f5f0a0 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 4 Jun 2015 13:00:26 -0400 Subject: Advance pindexLastCommonBlock for blocks in chainActive This prevents an edge case where a block downloaded and pruned in-between successive calls to FindNextBlocksToDownload could cause the block to be unnecessarily re-requested. --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 760cda5dee..dc9585b021 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -433,13 +433,14 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorIsValid(BLOCK_VALID_TREE)) { // We consider the chain that this peer is on invalid. return; } - if (pindex->nStatus & BLOCK_HAVE_DATA) { + if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { if (pindex->nChainTx) state->pindexLastCommonBlock = pindex; } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { -- cgit v1.2.3 From a9ac95c1bc67726a7d6eecb35d7650eed6c89361 Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Sun, 31 May 2015 15:36:44 +0200 Subject: use const references where appropriate --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 760cda5dee..e9a5f7efd9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -693,7 +693,7 @@ bool CheckFinalTx(const CTransaction &tx) /** * Check transaction inputs to mitigate two * potential denial-of-service attacks: - * + * * 1. scriptSigs with extra data stuffed into them, * not consumed by scriptPubKey (or P2SH script) * 2. P2SH scripts with a crazy number of expensive @@ -2148,7 +2148,7 @@ static int64_t nTimeFlush = 0; static int64_t nTimeChainState = 0; 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. */ @@ -3696,7 +3696,7 @@ void static CheckBlockIndex() // CAlert // -string GetWarnings(string strFor) +std::string GetWarnings(const std::string& strFor) { int nPriority = 0; string strStatusBar; -- cgit v1.2.3 From 65b94545036ae6e38e79e9c7166a3ba1ddb83f66 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 8 Jun 2015 16:34:58 -0400 Subject: Use best header chain timestamps to detect partitioning The partition checking code was using chainActive timestamps to detect partitioning; with headers-first syncing, it should use (and with this pull request, does use) pIndexBestHeader timestamps. Fixes issue #6251 --- src/main.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e9a5f7efd9..d74db2b920 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1711,9 +1711,10 @@ void ThreadScriptCheck() { // we're being fed a bad chain (blocks being generated much // too slowly or too quickly). // -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing) +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, + int64_t nPowTargetSpacing) { - if (initialDownloadCheck()) return; + if (bestHeader == NULL || initialDownloadCheck()) return; static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); @@ -1729,10 +1730,13 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; LOCK(cs); - int h = chain.Height(); - while (h > 0 && chain[h]->GetBlockTime() >= startTime) - --h; - int nBlocks = chain.Height()-h; + const CBlockIndex* i = bestHeader; + int nBlocks = 0; + while (i->GetBlockTime() >= startTime) { + ++nBlocks; + i = i->pprev; + if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed + } // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); -- cgit v1.2.3 From 425c3a87ff128bc068949b86e7f764c2c3e3f904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Mon, 30 Mar 2015 13:48:04 +0200 Subject: Consensus: Separate CheckIndexAgainstCheckpoint() from ContextualCheckBlockHeader --- src/main.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b5dd2643d3..ab5649417c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2719,18 +2719,23 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidationState& state, const CChainParams& chainparams, const uint256& hash) { - const CChainParams& chainParams = Params(); - const Consensus::Params& consensusParams = chainParams.GetConsensus(); - uint256 hash = block.GetHash(); - if (hash == consensusParams.hashGenesisBlock) + if (*pindexPrev->phashBlock == chainparams.GetConsensus().hashGenesisBlock) return true; - assert(pindexPrev); - int nHeight = pindexPrev->nHeight+1; + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + + return true; +} +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +{ + const Consensus::Params& consensusParams = Params().GetConsensus(); // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, error("%s: incorrect proof of work", __func__), @@ -2741,14 +2746,6 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - if (fCheckpointsEnabled) - { - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); - } - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s: rejected nVersion=1 block", __func__), @@ -2818,6 +2815,9 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (pindexPrev->nStatus & BLOCK_FAILED_MASK) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } + assert(pindexPrev); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; @@ -2933,8 +2933,11 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - assert(pindexPrev == chainActive.Tip()); + assert(pindexPrev && pindexPrev == chainActive.Tip()); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash())) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); -- cgit v1.2.3 From eb837199a06293ea541171bdc4652591c6506921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 24 Apr 2015 16:45:16 +0200 Subject: Consensus: Refactor: Separate Consensus::CheckTxInputs and GetSpendHeight in CheckInputs --- src/main.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4352c719a1..6629647850 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1379,22 +1379,21 @@ bool CScriptCheck::operator()() { return true; } -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector *pvChecks) +int GetSpendHeight(const CCoinsViewCache& inputs) { - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vin.size()); + LOCK(cs_main); + CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + return pindexPrev->nHeight + 1; +} +namespace Consensus { +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +{ // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1433,6 +1432,19 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); + return true; +} +}// namespace Consensus + +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector *pvChecks) +{ + if (!tx.IsCoinBase()) + { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) + return false; + + if (pvChecks) + pvChecks->reserve(tx.vin.size()); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. -- cgit v1.2.3 From 02a6702a82a5b00e0e0351041dd3267308b7f319 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 12 Jun 2015 12:00:39 +0200 Subject: Add option `-alerts` to opt out of alert system Make it possible to opt-out of the centralized alert system by providing an option `-noalerts` or `-alerts=0`. The default remains unchanged. This is a gentler form of #6260, in which I went a bit overboard by removing the alert system completely. I intend to add this to the GUI options in another pull after this. --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 69c972a79f..49263a13fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,6 +61,7 @@ bool fCheckBlockIndex = false; bool fCheckpointsEnabled = true; 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); @@ -4622,7 +4623,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "alert") + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; -- cgit v1.2.3 From a794284e61988a226ea39327449be9906a1b5abd Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 16 Jun 2015 04:08:26 -0400 Subject: locking: add a quick example of GUARDED_BY This was chosen not because it's necessarily helpful, but because its locking assumptions were already correct. --- src/main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0be54ebd41..f198edffe2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,9 +72,9 @@ struct COrphanTx { CTransaction tx; NodeId fromPeer; }; -map mapOrphanTransactions; -map > mapOrphanTransactionsByPrev; -void EraseOrphansFor(NodeId peer); +map mapOrphanTransactions GUARDED_BY(cs_main);; +map > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);; +void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Returns true if there are nRequired or more blocks of minVersion or above @@ -523,7 +523,7 @@ CBlockTreeDB *pblocktree = NULL; // mapOrphanTransactions // -bool AddOrphanTx(const CTransaction& tx, NodeId peer) +bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) @@ -553,7 +553,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) return true; } -void static EraseOrphanTx(uint256 hash) +void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { map::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) @@ -587,7 +587,7 @@ void EraseOrphansFor(NodeId peer) } -unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) +unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { unsigned int nEvicted = 0; while (mapOrphanTransactions.size() > nMaxOrphans) @@ -3783,7 +3783,7 @@ std::string GetWarnings(const std::string& strFor) // -bool static AlreadyHave(const CInv& inv) +bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { -- cgit v1.2.3 From 0da6ae2dc312ad180dd5118a41810d2ee95f01d4 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 12 May 2015 20:56:50 -0400 Subject: Do not inv old or missing blocks when pruning When responding to a getblocks message, only return inv's as long as we HAVE_DATA for blocks in the chain, and only for blocks that we aren't likely to delete in the near future. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4f4926eb40..49bcce5ce2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4153,6 +4153,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } + // If pruning, don't inv blocks unless we have on disk and are likely to still have + // for some reasonable time window (1 hour) that block relay might require. + const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing; + if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave)) + { + LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + break; + } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { -- cgit v1.2.3 From ae6f957a628efeeae47b8a2deee595c2ac5e7640 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 15 May 2015 17:07:11 -0400 Subject: Enable block relay when pruning --- src/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 49bcce5ce2..0791e3ed00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2281,9 +2281,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { int nBlockEstimate = 0; if (fCheckpointsEnabled) nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); - // Don't relay blocks if pruning -- could cause a peer to try to download, resulting - // in a stalled download if the block file is pruned before the request. - if (nLocalServices & NODE_NETWORK) { + { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) -- cgit v1.2.3 From 36c97b4e5dbf233734bd1882f8b6822a3bdcee97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 17 Jun 2015 21:23:53 +0200 Subject: Bugfix: Don't check the genesis block header before accepting it This fixes an error triggered when running with -reindex after #5975 --- src/main.cpp | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0be54ebd41..a4cbe43d3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2809,36 +2809,37 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc uint256 hash = block.GetHash(); BlockMap::iterator miSelf = mapBlockIndex.find(hash); CBlockIndex *pindex = NULL; - if (miSelf != mapBlockIndex.end()) { - // Block header is already known. - pindex = miSelf->second; - if (ppindex) - *ppindex = pindex; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); - return true; - } + if (hash != chainparams.GetConsensus().hashGenesisBlock) { - if (!CheckBlockHeader(block, state)) - return false; + if (miSelf != mapBlockIndex.end()) { + // Block header is already known. + pindex = miSelf->second; + if (ppindex) + *ppindex = pindex; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + return true; + } - // Get prev block index - CBlockIndex* pindexPrev = NULL; - if (hash != chainparams.GetConsensus().hashGenesisBlock) { + if (!CheckBlockHeader(block, state)) + return false; + + // Get prev block index + CBlockIndex* pindexPrev = NULL; BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); pindexPrev = (*mi).second; if (pindexPrev->nStatus & BLOCK_FAILED_MASK) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); - } - assert(pindexPrev); - if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) - return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; + assert(pindexPrev); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + return false; + } if (pindex == NULL) pindex = AddToBlockIndex(block); -- cgit v1.2.3 From 627b9deff4e8e18fc6d001b6eb9818c13c648820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 24 Jun 2015 07:25:30 +0200 Subject: Policy: MOVEONLY: Create policy/policy.h with some constants --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6c4cfe75aa..05130f3089 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,10 +11,12 @@ #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" +#include "consensus/consensus.h" #include "consensus/validation.h" #include "init.h" #include "merkleblock.h" #include "net.h" +#include "policy/policy.h" #include "pow.h" #include "txdb.h" #include "txmempool.h" -- cgit v1.2.3 From 9238ecb41752e097443d0bc117df35ebd4ac932e Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 11 Oct 2014 22:41:05 +0000 Subject: Policy: MOVEONLY: 3 functions to policy.o: - [script/standard.o] IsStandard - [main.o] IsStandardTx - [main.o] AreInputsStandard Also, don't use namespace std in policy.cpp --- src/main.cpp | 138 ----------------------------------------------------------- 1 file changed, 138 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 05130f3089..f966f68698 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -606,76 +606,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } - - - - - - -bool IsStandardTx(const CTransaction& tx, string& reason) -{ - if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { - reason = "version"; - return false; - } - - // Extremely large transactions with lots of inputs can cost the network - // almost as much to process as they cost the sender in fees, because - // computing signature hashes is O(ninputs*txsize). Limiting transactions - // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) { - reason = "tx-size"; - return false; - } - - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed - // keys. (remember the 520 byte limit on redeemScript size) That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 - // bytes of scriptSig, which we round off to 1650 bytes for some minor - // future-proofing. That's also enough to spend a 20-of-20 - // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not - // considered standard) - if (txin.scriptSig.size() > 1650) { - reason = "scriptsig-size"; - return false; - } - if (!txin.scriptSig.IsPushOnly()) { - reason = "scriptsig-not-pushonly"; - return false; - } - } - - unsigned int nDataOut = 0; - txnouttype whichType; - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) { - reason = "scriptpubkey"; - return false; - } - - if (whichType == TX_NULL_DATA) - nDataOut++; - else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { - reason = "bare-multisig"; - return false; - } else if (txout.IsDust(::minRelayTxFee)) { - reason = "dust"; - return false; - } - } - - // only one OP_RETURN txout is permitted - if (nDataOut > 1) { - reason = "multi-op-return"; - return false; - } - - return true; -} - bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) @@ -694,74 +624,6 @@ bool CheckFinalTx(const CTransaction &tx) return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime()); } -/** - * Check transaction inputs to mitigate two - * potential denial-of-service attacks: - * - * 1. scriptSigs with extra data stuffed into them, - * not consumed by scriptPubKey (or P2SH script) - * 2. P2SH scripts with a crazy number of expensive - * CHECKSIG/CHECKMULTISIG operations - */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) -{ - if (tx.IsCoinBase()) - return true; // Coinbases don't use vin normally - - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - - vector > vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) - return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig - // IsStandardTx() will have already returned false - // and this method isn't called. - vector > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) - return false; - - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - vector > vSolutions2; - txnouttype whichType2; - if (Solver(subscript, whichType2, vSolutions2)) - { - int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - else - { - // Any other Script with less than 15 sigops OK: - unsigned int sigops = subscript.GetSigOpCount(true); - // ... extra data left on the stack after execution is OK, too: - return (sigops <= MAX_P2SH_SIGOPS); - } - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } - - return true; -} - unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; -- cgit v1.2.3 From 6b1066fab41523d25d75b45a10e4b0f61d7c61d0 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 2 Jun 2015 20:46:41 -0400 Subject: Ignore whitelisting during IBD for unrequested blocks. --- src/main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e0997515b7..87750f9ea3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4478,8 +4478,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->AddInventoryKnown(inv); CValidationState state; - // Process all blocks from whitelisted peers, even if not requested. - ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL); + // Process all blocks from whitelisted peers, even if not requested, + // unless we're still syncing with the network. + // 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); int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), -- cgit v1.2.3 From a651403e09ccc9ac0a3322e880d6543dd51a14bd Mon Sep 17 00:00:00 2001 From: Shaul Kfir Date: Mon, 16 Mar 2015 21:36:43 -0400 Subject: Add absurdly high fee message to validation state (for RPC propagation) --- src/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6c4cfe75aa..f67f1fd0d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1031,9 +1031,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + return state.Invalid(error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", + hash.ToString(), + nFees, ::minRelayTxFee.GetFee(nSize) * 10000), + REJECT_HIGHFEE, "absurdly-high-fee"); // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. -- cgit v1.2.3 From 409bccfbf52b531b2a9d60ac2308f56223931a2e Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 26 Jun 2015 21:38:33 +0200 Subject: use CBanEntry as object container for banned nodes - added a reason enum for a ban - added creation time for a ban Using CBanEntry as container will keep banlist.dat extenable. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6c4cfe75aa..a000a81fd9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4959,7 +4959,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); else { - CNode::Ban(pto->addr); + CNode::Ban(pto->addr, BanReasonNodeMisbehaving); } } state.fShouldBan = false; -- cgit v1.2.3 From 0c376340a4efa2842e9ffd1d93eca9895b4a9391 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 24 Jun 2015 03:36:22 +0000 Subject: acceptnonstdtxn option to skip (most) "non-standard transaction" checks, for testnet/regtest only --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e92f64413a..f958fb46cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,6 +57,7 @@ bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = true; +bool fRequireStandard = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = true; size_t nCoinCacheUsage = 5000 * 300; @@ -898,7 +899,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason)) + if (fRequireStandard && !IsStandardTx(tx, reason)) return state.DoS(0, error("AcceptToMemoryPool: nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); @@ -969,7 +970,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Check for non-standard pay-to-script-hash in inputs - if (Params().RequireStandard() && !AreInputsStandard(tx, view)) + if (fRequireStandard && !AreInputsStandard(tx, view)) return error("AcceptToMemoryPool: nonstandard transaction input"); // Check that the transaction doesn't have an excessive number of -- cgit v1.2.3 From 59b49cd07482494d9a99068af502be9ceda41ed2 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Sat, 11 Jul 2015 06:52:35 -0400 Subject: Eliminate signed/unsigned comparison warning --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 87750f9ea3..875b7b7068 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2849,7 +2849,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // blocks which are too close in height to the tip. Apply this test // regardless of whether pruning is enabled; it should generally be safe to // not process unrequested blocks. - bool fTooFarAhead = (pindex->nHeight - chainActive.Height()) > MIN_BLOCKS_TO_KEEP; + bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. -- cgit v1.2.3 From 2cb8ee9ac7fb4d2600305a61400ab5e8a200b722 Mon Sep 17 00:00:00 2001 From: Pavel Vasin Date: Tue, 30 Jun 2015 23:10:27 +0300 Subject: remove unused inv from ConnectTip() --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 03c09f0a27..fb90d7578c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2059,7 +2059,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); bool rv = ConnectBlock(*pblock, state, pindexNew, view); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { @@ -2067,7 +2066,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * InvalidBlockFound(pindexNew, state); return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } - mapBlockSource.erase(inv.hash); + mapBlockSource.erase(pindexNew->GetBlockHash()); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); assert(view.Flush()); -- cgit v1.2.3 From c433828df95eb72f486200c95b2b40de02a6f027 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Sat, 4 Jul 2015 20:14:03 +0100 Subject: Handle no chain tip available in InvalidChainFound() Handle the case where no chain tip is available, in InvalidChainFound(). This fixes a null pointer deference when running unit tests, if the genesis block or block validation code is broken. --- src/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fb90d7578c..a3f7ab8191 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1196,9 +1196,11 @@ void static InvalidChainFound(CBlockIndex* pindexNew) pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); + CBlockIndex *tip = chainActive.Tip(); + assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); CheckForkWarningConditions(); } -- cgit v1.2.3 From 9dd793f499254600efa468938cef9baa28ac81b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Sun, 5 Jul 2015 14:17:46 +0200 Subject: TRIVIAL: Missing includes --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fb90d7578c..58c42cf96e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "checkqueue.h" #include "consensus/consensus.h" #include "consensus/validation.h" +#include "hash.h" #include "init.h" #include "merkleblock.h" #include "net.h" @@ -24,6 +25,7 @@ #include "undo.h" #include "util.h" #include "utilmoneystr.h" +#include "utilstrencodings.h" #include "validationinterface.h" #include -- cgit v1.2.3 From 60c8bac77c6612b84e3496b2227a01058d720ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Sun, 5 Jul 2015 14:30:07 +0200 Subject: Includes: Cleanup around net main and wallet -Move from .h to .cpp: in main, net and wallet -Remove unnecessary #include "main.h" -Cleanup some wallet files includes --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 58c42cf96e..cf0002cc35 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,12 @@ #include "net.h" #include "policy/policy.h" #include "pow.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/script.h" +#include "script/sigcache.h" +#include "script/standard.h" +#include "tinyformat.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" -- cgit v1.2.3 From 17b11428c135203342aff38cabc8047e673f38ac Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 Apr 2015 10:27:16 -0700 Subject: Cache transaction validation successes --- src/main.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fefeabeb64..7477d08b18 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1277,6 +1277,9 @@ int GetSpendHeight(const CCoinsViewCache& inputs) return pindexPrev->nHeight + 1; } +static mrumap cacheCheck(2 * MAX_BLOCK_SIZE / CTransaction().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)); +static boost::mutex cs_cacheCheck; + namespace Consensus { bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) { @@ -1331,6 +1334,17 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi { if (!tx.IsCoinBase()) { + if (fScriptChecks) { + boost::unique_lock lock(cs_cacheCheck); + mrumap::const_iterator iter = cacheCheck.find(tx.GetHash()); + if (iter != cacheCheck.end()) { + // The following test relies on the fact that all script validation flags are softforks (i.e. an extra bit set cannot cause a false result to become true). + if ((iter->second & flags) == flags) { + return true; + } + } + } + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) return false; @@ -1381,6 +1395,11 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi } } + if (cacheStore && fScriptChecks && pvChecks == NULL) { + boost::unique_lock lock(cs_cacheCheck); + cacheCheck.insert(tx.GetHash(), flags); + } + return true; } @@ -2101,6 +2120,13 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { SyncWithWallets(tx, pblock); } + // Erase block's transactions from the validation cache + { + boost::unique_lock lock(cs_cacheCheck); + BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { + cacheCheck.erase(tx.GetHash()); + } + } int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); -- cgit v1.2.3 From d2d7ee0e863b286e1c9f9c54659d494fb0a7712d Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 20 Jul 2015 04:43:34 +0900 Subject: Make CRollingBloomFilter set nTweak for you While CBloomFilter is usually used with an explicitly set nTweak, CRollingBloomFilter is only used internally. Requiring every caller to set nTweak is error-prone and redundant; better to have the class handle that for you with a high-quality randomness source. Additionally when clearing the filter it makes sense to change nTweak as well to recover from a bad setting, e.g. due to insufficient randomness at initialization, so the clear() method is replaced by a reset() method that sets a new, random, nTweak value. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7477d08b18..01b62bdf60 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4812,7 +4812,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) - pnode->addrKnown.clear(); + pnode->addrKnown.reset(); // Rebroadcast our address AdvertizeLocal(pnode); -- cgit v1.2.3 From eddaba7b5692288087a926da5733e86b47274e4e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 Jul 2015 20:14:43 +0200 Subject: Revert "Cache transaction validation successes" This reverts commit 17b11428c135203342aff38cabc8047e673f38ac. --- src/main.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7477d08b18..fefeabeb64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1277,9 +1277,6 @@ int GetSpendHeight(const CCoinsViewCache& inputs) return pindexPrev->nHeight + 1; } -static mrumap cacheCheck(2 * MAX_BLOCK_SIZE / CTransaction().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)); -static boost::mutex cs_cacheCheck; - namespace Consensus { bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) { @@ -1334,17 +1331,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi { if (!tx.IsCoinBase()) { - if (fScriptChecks) { - boost::unique_lock lock(cs_cacheCheck); - mrumap::const_iterator iter = cacheCheck.find(tx.GetHash()); - if (iter != cacheCheck.end()) { - // The following test relies on the fact that all script validation flags are softforks (i.e. an extra bit set cannot cause a false result to become true). - if ((iter->second & flags) == flags) { - return true; - } - } - } - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) return false; @@ -1395,11 +1381,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi } } - if (cacheStore && fScriptChecks && pvChecks == NULL) { - boost::unique_lock lock(cs_cacheCheck); - cacheCheck.insert(tx.GetHash(), flags); - } - return true; } @@ -2120,13 +2101,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { SyncWithWallets(tx, pblock); } - // Erase block's transactions from the validation cache - { - boost::unique_lock lock(cs_cacheCheck); - BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - cacheCheck.erase(tx.GetHash()); - } - } int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); -- cgit v1.2.3 From 0847d9cb5fcd2fdd5a21bde699944d966cf5add9 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 17 Jul 2015 06:46:48 -0400 Subject: Keep track of recently rejected transactions Nodes can have divergent policies on which transactions they will accept and relay. This can cause you to repeatedly request and reject the same tx after its inved to you from various peers which have accepted it. Here we add rolling bloom filter to keep track of such rejections, clearing the filter every time the chain tip changes. Credit goes to Alex Morcos, who created the patch that this code is based on. Original code by Peter Todd. Refactored to not construct the filter at startup time by Pieter Wuille. --- src/main.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 01b62bdf60..0865ecc506 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -162,6 +162,29 @@ namespace { */ map mapBlockSource; + /** + * Filter for transactions that were recently rejected by + * AcceptToMemoryPool. These are not rerequested until the chain tip + * changes, at which point the entire filter is reset. Protected by + * cs_main. + * + * Without this filter we'd be re-requesting txs from each of our peers, + * increasing bandwidth consumption considerably. For instance, with 100 + * peers, half of which relay a tx we don't accept, that might be a 50x + * bandwidth increase. A flooding attacker attempting to roll-over the + * filter using minimum-sized, 60byte, transactions might manage to send + * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a + * two minute window to send invs to us. + * + * Decreasing the false positive rate is fairly cheap, so we pick one in a + * million to make it highly unlikely for users to have issues with this + * filter. + * + * Memory used: 1.7MB + */ + boost::scoped_ptr recentRejects; + uint256 hashRecentRejectsChainTip; + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -3267,6 +3290,7 @@ void UnloadBlockIndex() setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); mapNodeState.clear(); + recentRejects.reset(NULL); BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; @@ -3320,6 +3344,9 @@ bool InitBlockIndex() { } } + // Initialize global variables that cannot be constructed at startup. + recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); + return true; } @@ -3689,10 +3716,20 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { case MSG_TX: { - bool txInMap = false; - txInMap = mempool.exists(inv.hash); - return txInMap || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) + { + // If the chain tip has changed previously rejected transactions + // might be now valid, e.g. due to a nLockTime'd tx becoming valid, + // or a double-spend. Reset the rejects filter and give those + // txs a second chance. + hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); + recentRejects->reset(); + } + + return recentRejects->contains(inv.hash) || + mempool.exists(inv.hash) || + mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash); @@ -4292,6 +4329,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); + recentRejects->insert(orphanHash); } mempool.check(pcoinsTip); } @@ -4309,11 +4347,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); - } else if (pfrom->fWhitelisted) { - // Always relay transactions received from whitelisted peers, even - // if they are already in the mempool (allowing the node to function - // as a gateway for nodes hidden behind it). - RelayTransaction(tx); + } else { + // AcceptToMemoryPool() returned false, possibly because the tx is + // already in the mempool; if the tx isn't in the mempool that + // means it was rejected and we shouldn't ask for it again. + if (!mempool.exists(tx.GetHash())) { + recentRejects->insert(tx.GetHash()); + } + if (pfrom->fWhitelisted) { + // 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. + // + // FIXME: This includes invalid transactions, which means a + // whitelisted peer could get us banned! We may want to change + // that. + RelayTransaction(tx); + } } int nDoS = 0; if (state.IsInvalid(nDoS)) -- cgit v1.2.3 From a8d0407c4fcf7c4e8ed0e8edabd204f7a4efa477 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 31 Jul 2015 17:55:05 +0200 Subject: Move recentRejects initialization to top of InitBlockIndex This avoids that premature return in the condition that a new chain is initialized results in NULL pointer errors due to recentReject not being constructed. Also add assertions where it is used. --- src/main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 0865ecc506..0ad41af955 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3311,6 +3311,10 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); + + // Initialize global variables that cannot be constructed at startup. + recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); + // Check whether we're already initialized if (chainActive.Genesis() != NULL) return true; @@ -3344,9 +3348,6 @@ bool InitBlockIndex() { } } - // Initialize global variables that cannot be constructed at startup. - recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); - return true; } @@ -3716,6 +3717,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { case MSG_TX: { + assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) { // If the chain tip has changed previously rejected transactions @@ -4329,6 +4331,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); + assert(recentRejects); recentRejects->insert(orphanHash); } mempool.check(pcoinsTip); @@ -4352,6 +4355,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // already in the mempool; if the tx isn't in the mempool that // means it was rejected and we shouldn't ask for it again. if (!mempool.exists(tx.GetHash())) { + assert(recentRejects); recentRejects->insert(tx.GetHash()); } if (pfrom->fWhitelisted) { -- cgit v1.2.3 From f18b8ec7cf6ebfff9eef839c6a5630ad2e6e7db6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 31 Jul 2015 16:41:06 +0200 Subject: Make sure LogPrintf strings are line-terminated Fix the cases where LogPrint[f] was accidentally called without line terminator, which resulted in concatenated log lines. (see e.g. #6492) --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d470ba9003..fa12281655 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3398,7 +3398,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } } } catch (const std::exception& e) { - LogPrintf("%s: Deserialize or I/O error - %s", __func__, e.what()); + LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what()); } } } catch (const std::runtime_error& e) { -- cgit v1.2.3 From 7b79cbd722d35b8113d5136b06d4a8e5fd569fc6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 31 Jul 2015 18:05:42 +0200 Subject: limit total length of user agent comments Reworked-By: Wladimir J. van der Laan --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d470ba9003..b1f5e3ffd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3859,7 +3859,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, 256); + vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } if (!vRecv.empty()) -- cgit v1.2.3 From 5922b6774e352e802ca9a347cc224cfebf1a6a7d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 5 Aug 2015 19:02:19 +0200 Subject: Add assertion and cast before sending reject code This gets rid of a warning. Add an assertion to make sure that the reject code is in the correct range for the network protocol (if it is outside the range it must be a bug) --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f67f1fd0d7..4bf5883692 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1343,7 +1343,8 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; + assert(state.GetRejectCode() < 0x100); + CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) Misbehaving(it->second, nDoS); -- cgit v1.2.3 From 69c3bde4480ebe28095f4399a29f9fc73567a1d3 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 8 Aug 2015 18:18:41 +0200 Subject: Add some const declarations where they are appropriate. Declare some arguments of functions as "const" pointers where they are not meant to be modified. --- src/main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 755bec47b8..bfae0d7f8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1035,7 +1035,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -1520,7 +1520,7 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO return fClean; } -bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) +bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -2077,7 +2077,7 @@ 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, CBlock *pblock) { +bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) { assert(pindexNew->pprev == chainActive.Tip()); mempool.check(pcoinsTip); // Read block from disk. @@ -2209,7 +2209,7 @@ 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, CBlock *pblock) { +static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) { AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2278,7 +2278,7 @@ 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, CBlock *pblock) { +bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; const CChainParams& chainParams = Params(); @@ -2749,7 +2749,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) +bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); @@ -2826,7 +2826,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) +bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks bool checked = CheckBlock(*pblock, state); -- cgit v1.2.3 From 9f68ed6b6d1a9c6436ce37913666165f2b180ee3 Mon Sep 17 00:00:00 2001 From: Veres Lajos Date: Sun, 9 Aug 2015 00:17:27 +0100 Subject: typofixes (found by misspell_fixer) --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79cc606856..34e163ff61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1471,7 +1471,7 @@ bool AbortNode(const std::string& strMessage, const std::string& userMessage="") strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; -- cgit v1.2.3 From dc58258adf1fb1e05002c3057045f113aa214cb7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Aug 2015 09:51:36 +0200 Subject: Introduce REJECT_INTERNAL codes for local AcceptToMempool errors Add status codes specific to AcceptToMempool procession of transactions. These can never happen due to block validation, and must never be sent over the P2P network. Add assertions where appropriate. --- src/main.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79cc606856..5949e5285d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -811,7 +811,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) - return false; + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions { @@ -822,7 +822,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now - return false; + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); } } } @@ -839,7 +839,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // do we already have it? if (view.HaveCoins(hash)) - return false; + 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), @@ -848,7 +848,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; - return false; + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } } @@ -868,7 +868,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check for non-standard pay-to-script-hash in inputs if (fRequireStandard && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: nonstandard transaction input"); + 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 @@ -1239,7 +1239,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { - assert(state.GetRejectCode() < 0x100); + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) @@ -4358,8 +4358,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } @@ -4439,6 +4440,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ProcessNewBlock(state, 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(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) { -- cgit v1.2.3 From 9003c7cdd88402986e48ec126258fc10d6742fc7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Aug 2015 09:53:24 +0200 Subject: Add function to convert CValidationState to a human-readable message It is necessary to be able to concisely log a validation state. Convert CValidationState to a human-readable message for logging. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5949e5285d..3d7a620433 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -778,6 +778,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF return nMinFee; } +/** Convert CValidationState to a human-readable message for logging */ +static std::string FormatStateMessage(const CValidationState &state) +{ + return strprintf("%s%s (code %i)", + state.GetRejectReason(), + state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(), + state.GetRejectCode()); +} bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectAbsurdFee) -- cgit v1.2.3 From 6cab80827277a66a339f1035c58a8f26e347e191 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Aug 2015 09:59:09 +0200 Subject: Remove most logging from transaction validation Remove unnecessary direct logging in CheckTransaction, AcceptToMemoryPool, CheckTxInputs, CScriptCheck::operator() All status information should be returned in the CValidationState. Relevant debug information is also added to the CValidationState using the recently introduced debug message. Do keep the "BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags" error as it is meant to appear as bug in the log. --- src/main.cpp | 94 ++++++++++++++++++++++-------------------------------------- 1 file changed, 35 insertions(+), 59 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3d7a620433..947bd8ef1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -696,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction(): vin empty"), - REJECT_INVALID, "bad-txns-vin-empty"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction(): vout empty"), - REJECT_INVALID, "bad-txns-vout-empty"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckTransaction(): size limits failed"), - REJECT_INVALID, "bad-txns-oversize"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction(): txout total out of range"), - REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } // Check for duplicate inputs @@ -727,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction(): duplicate inputs"), - REJECT_INVALID, "bad-txns-inputs-duplicate"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction(): coinbase script size"), - REJECT_INVALID, "bad-cb-length"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } return true; @@ -795,26 +786,22 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("AcceptToMemoryPool: CheckTransaction failed"); + return false; // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); + return state.DoS(100, false, REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (fRequireStandard && !IsStandardTx(tx, reason)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); + return state.DoS(0, false, REJECT_NONSTANDARD, reason); // 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)) - return state.DoS(0, error("AcceptToMemoryPool: non-final"), - REJECT_NONSTANDARD, "non-final"); + return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? uint256 hash = tx.GetHash(); @@ -862,8 +849,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); + return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); // Bring the best block into scope view.GetBestBlock(); @@ -886,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-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; @@ -901,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // 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, error("AcceptToMemoryPool: not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("%d < %d", nFees, txMinFee)); // 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))) { @@ -928,24 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // -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) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return state.Invalid(error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000), - REJECT_HIGHFEE, "absurdly-high-fee"); + return state.Invalid(false, + REJECT_HIGHFEE, "absurdly-high-fee", + strprintf("%d > %d", nFees, ::minRelayTxFee.GetFee(nSize) * 10000)); // 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)) - { - return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); - } + return false; // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -958,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // can be exploited as a DoS attack. if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); } // Store transaction in memory @@ -1298,7 +1278,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { - return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + return false; } return true; } @@ -1316,7 +1296,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); + return state.Invalid(false, 0, "", "Inputs unavailable"); CAmount nValueIn = 0; CAmount nFees = 0; @@ -1329,33 +1309,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // If prev is coinbase, check that it's matured if (coins->IsCoinBase()) { if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight)); } // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs(): txin values out of range"), - REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", - tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), - REJECT_INVALID, "bad-txns-fee-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs(): nFees out of range"), - REJECT_INVALID, "bad-txns-fee-outofrange"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); return true; } }// namespace Consensus -- cgit v1.2.3 From 66daed5e1be7932e08896794ca016ff31c71a1d4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Aug 2015 10:02:12 +0200 Subject: Add information to errors in ConnectBlock, CheckBlock Add detailed state information to the errors, as it is no longer being logged downstream. Also add the state information to mempool rejection debug message in ProcessMessages. --- src/main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 947bd8ef1a..5d9247e4b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1777,7 +1777,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) - return false; + return error("ConnectBlock(): CheckInputs on %s failed with %s", + tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); } @@ -2604,7 +2605,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) - return error("CheckBlock(): CheckTransaction failed"); + return error("CheckBlock(): CheckTransaction of %s failed with %s", + tx.GetHash().ToString(), + FormatStateMessage(state)); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -4341,7 +4344,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); + FormatStateMessage(state)); if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); -- cgit v1.2.3 From 7f1f8f5edf36b0885b0f2b746e28a2f9bde8f4f0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 6 Aug 2015 10:03:11 +0200 Subject: Move mempool rejections to new debug category Move mempool rejections to debug category `mempoolrej`, to make it possible to show them without enabling the entire category `mempool` which is high volume. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5d9247e4b0..bb0ca9db54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4342,7 +4342,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + LogPrint("mempoolrej", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, FormatStateMessage(state)); if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P -- cgit v1.2.3 From 4bac60161029de6d71ef1e51e7af803ce6fb8405 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Thu, 13 Aug 2015 02:31:46 -0700 Subject: Record nMinPingUsecTime --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 33b57a5285..35fbec6665 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4522,6 +4522,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; + pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime); } else { // This should never happen sProblem = "Timing mishap"; -- cgit v1.2.3 From 9524c4d35cb6ec4bd7becf1d938d21c536669d1f Mon Sep 17 00:00:00 2001 From: Tom Harding Date: Mon, 24 Aug 2015 14:02:24 -0700 Subject: In (strCommand == "tx"), return if AlreadyHave() The main effect is to exit processing for recently-rejected hashes, in case they are pushed to us without prior advertisement. This behavior was seen in the wild. An additional effect is to do early checks for mempool or mapOrphan existence. No logging or nDoS tracking is needed for failures of these checks. --- src/main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 33b57a5285..6d07f83ce5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4248,6 +4248,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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)) { mempool.check(pcoinsTip); @@ -4323,13 +4327,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } else { - // AcceptToMemoryPool() returned false, possibly because the tx is - // already in the mempool; if the tx isn't in the mempool that - // means it was rejected and we shouldn't ask for it again. - if (!mempool.exists(tx.GetHash())) { - assert(recentRejects); - recentRejects->insert(tx.GetHash()); - } + assert(recentRejects); + recentRejects->insert(tx.GetHash()); + if (pfrom->fWhitelisted) { // Always relay transactions received from whitelisted peers, even // if they were rejected from the mempool, allowing the node to -- cgit v1.2.3 From afb0ccaf9c9e4e8fac7db3564c4e19c9218c6b03 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 20 Aug 2015 21:15:27 -0700 Subject: Add NODE_BLOOM service bit and bump protocol version Lets nodes advertise that they offer bloom filter support explicitly. The protocol version bump allows SPV nodes to assume that NODE_BLOOM is set if NODE_NETWORK is set for pre-70011 nodes. Also adds an option to turn bloom filter support off for nodes which advertise a version number >= 70011. Nodes attempting to use bloom filters on such protocol versions are banned, and a later upgade should drop nodes of an older version which attempt to use bloom filters. Much code stolen from Peter Todd. Implements BIP 111 --- src/main.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 35fbec6665..a880533e65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4591,6 +4591,21 @@ 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") { CBloomFilter filter; -- cgit v1.2.3 From 6ecaec395cf747349d59396d71a83288cd55c218 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 7 Sep 2015 08:19:57 +0000 Subject: [Trivial] [logging] Rm extraneous cleansubver in serveral debug messages. We log the cleanSubVer as part of connect. It is not uniquely more informative than any of the other data we have about a peer, often less. It's also often long now as well. There is no need to output it as part of mempoolrej, AcceptToMemoryPool, or pong entries. Leaving it out makes our log entries more uniform and consistent. --- src/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 35fbec6665..16522656a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4254,8 +4254,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, + LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u)\n", + pfrom->id, tx.GetHash().ToString(), mempool.mapTx.size()); @@ -4344,8 +4344,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempoolrej", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), - pfrom->id, pfrom->cleanSubVer, + LogPrint("mempoolrej", "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(), + pfrom->id, FormatStateMessage(state)); if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), @@ -4546,9 +4546,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!(sProblem.empty())) { - LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", + LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n", pfrom->id, - pfrom->cleanSubVer, sProblem, pfrom->nPingNonceSent, nonce, -- cgit v1.2.3 From 5624e055b310284e8dfd68833eea7f7246316354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 7 May 2015 15:49:00 +0100 Subject: Add UpdatedBlockTip signal to CMainSignals and CValidationInterface --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a880533e65..27278b977a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2303,6 +2303,7 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. + GetMainSignals().UpdatedBlockTip(hashNewTip); uiInterface.NotifyBlockTip(hashNewTip); } } while(pindexMostWork != chainActive.Tip()); -- cgit v1.2.3 From d76a8acb9b7bcabf43e3e05168a36911f187818d Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 16 Sep 2015 16:42:23 +0200 Subject: use CBlockIndex* insted of uint256 for UpdatedBlockTip signal - removes mapBlockIndex find operation - theoretically allows removing the cs_main lock during zqm notification while introducing a new file position lock --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27278b977a..d0d2476462 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2303,7 +2303,7 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(hashNewTip); + GetMainSignals().UpdatedBlockTip(pindexNewTip); uiInterface.NotifyBlockTip(hashNewTip); } } while(pindexMostWork != chainActive.Tip()); -- cgit v1.2.3 From 131c23d02733c547975844226c1fe0dd4c87211c Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 17 Sep 2015 17:43:34 -0400 Subject: Fix locking in GetTransaction. GetTransaction needs to lock cs_main until ReadBlockFromDisk completes, the data inside CBlockIndex's can change since pruning. This lock was held by all calls to GetTransaction except rest_tx. --- src/main.cpp | 68 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27278b977a..0b5b52b499 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -954,47 +954,45 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; + + LOCK(cs_main); + + if (mempool.lookup(hash, txOut)) { - LOCK(cs_main); - { - if (mempool.lookup(hash, txOut)) - { - return true; - } - } + return true; + } - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s: txid mismatch", __func__); - return true; + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s: txid mismatch", __func__); + return true; } + } - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache &view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); - if (coins) - nHeight = coins->nHeight; - } - if (nHeight > 0) - pindexSlow = chainActive[nHeight]; + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + const CCoins* coins = view.AccessCoins(hash); + if (coins) + nHeight = coins->nHeight; } + if (nHeight > 0) + pindexSlow = chainActive[nHeight]; } if (pindexSlow) { -- cgit v1.2.3 From 5add7a74a672cb12b0a2a630d318d9bc64dd0f77 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 15 Jul 2015 14:47:45 -0400 Subject: Track transaction packages in CTxMemPoolEntry Associate with each CTxMemPoolEntry all the size/fees of descendant mempool transactions. Sort mempool by max(feerate of entry, feerate of descendants). Update statistics on-the-fly as transactions enter or leave the mempool. Also add ancestor and descendant limiting, so that transactions can be rejected if the number or size of unconfirmed ancestors exceeds a target, or if adding a transaction would cause some other mempool entry to have too many (or too large) a set of unconfirmed in- mempool descendants. --- src/main.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 27278b977a..2a24d38e52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -921,6 +921,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, ::minRelayTxFee.GetFee(nSize) * 10000)); + // Calculate in-mempool ancestors, up to a limit. + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + std::string errString; + if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString); + } + // 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)) @@ -942,7 +953,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); } SyncWithWallets(tx, NULL); @@ -2033,13 +2044,23 @@ bool static DisconnectTip(CValidationState &state) { if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; // Resurrect mempool transactions from the disconnected block. + std::vector vHashUpdate; BOOST_FOREACH(const CTransaction &tx, block.vtx) { // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { mempool.remove(tx, removed, true); + } else if (mempool.exists(tx.GetHash())) { + vHashUpdate.push_back(tx.GetHash()); + } } + // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have + // no in-mempool children, which is generally not true when adding + // previously-confirmed transactions back to the mempool. + // 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. @@ -4258,7 +4279,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), - mempool.mapTx.size()); + mempool.size()); // Recursively process any orphan transactions that depended on this one set setMisbehaving; -- cgit v1.2.3 From 391dff16fe9ace90fc0f3308a5c63c453370e713 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 11 Aug 2015 21:03:31 +0200 Subject: Do not store Merkle branches in the wallet. Assume that when a wallet transaction has a valid block hash and transaction position in it, the transaction is actually there. We're already trusting wallet data in a much more fundamental way anyway. To prevent backward compatibility issues, a new record is used for storing the block locator in the wallet. Old wallets will see a wallet file synchronized up to the genesis block, and rescan automatically. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2a24d38e52..1e0194e2cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2595,7 +2595,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); + uint256 hashMerkleRoot2 = block.ComputeMerkleRoot(&mutated); if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); -- cgit v1.2.3 From 3b33ec85ed00ba7e7525858e3701f9f55071c58b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 15 Aug 2015 23:32:38 +0200 Subject: Avoid duplicate CheckBlock checks --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 1e0194e2cd..e8bc68fe64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2587,6 +2587,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo { // These are checks that are independent of context. + if (block.fChecked) + return true; + // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(block, state, fCheckPOW)) @@ -2642,6 +2645,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); + if (fCheckPOW && fCheckMerkleRoot) + block.fChecked = true; + return true; } -- cgit v1.2.3 From e761d7a94ff8b9f638b65b16f5c4ffab6573884a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 9 Apr 2015 11:50:18 +0000 Subject: Bugfix: Allow mining on top of old tip blocks for testnet (fixes testnet-in-a-box use case) --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d470ba9003..8da2a80bb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1091,7 +1091,7 @@ bool IsInitialBlockDownload() if (lockIBDState) return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - 24 * 60 * 60); + pindexBestHeader->GetBlockTime() < GetTime() - chainParams.MaxTipAge()); if (!state) lockIBDState = true; return state; -- cgit v1.2.3 From 287f54fc90c29301faede8d4ac2ea24a91441917 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Sun, 28 Jun 2015 14:30:50 -0400 Subject: Add CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic Based on the earlier BIP66 soft-fork logic implemented by Pieter Wuille's 5a47811da5158df763aa2fca09ce646ee0c51e7b --- src/main.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5cfb05b0d2..61c0d8e1c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1740,11 +1740,18 @@ 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; + } + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2684,6 +2691,11 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta 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; } -- cgit v1.2.3 From 28e3249e53b8ef7516636df0f1406466a513095d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 9 Oct 2015 19:36:32 +0200 Subject: Bump minrelaytxfee default To bridge the time until a dynamic method for determining this fee is merged. This is especially aimed at the stable releases (0.10, 0.11) because full mempool limiting, as will be in 0.12, is too invasive and risky to backport. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5cfb05b0d2..baad7fc050 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ 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); +CFeeRate minRelayTxFee = CFeeRate(5000); CTxMemPool mempool(::minRelayTxFee); -- cgit v1.2.3 From 9c9b66f771ad904cd665f7f5f68e3279ebb2fa7e Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 2 Oct 2015 14:17:27 -0700 Subject: Fix calling mempool directly, instead of pool, in ATMP --- src/main.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index baad7fc050..613eeff227 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -740,17 +740,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } -CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) +CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, 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; - } + uint256 hash = tx.GetHash(); + double dPriorityDelta = 0; + CAmount nFeeDelta = 0; + pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); + if (dPriorityDelta > 0 || nFeeDelta > 0) + return 0; CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); @@ -879,11 +876,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx)); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); + CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); if (fLimitFree && nFees < txMinFee) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("%d < %d", nFees, txMinFee)); -- cgit v1.2.3 From e6c7b362ab8915e2aac167fa519bd29836d482af Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 8 Oct 2015 00:46:57 -0700 Subject: Print mempool size in KB when adding txn --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 613eeff227..f01bb8ec9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4287,10 +4287,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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 setMisbehaving; -- cgit v1.2.3 From 794a8cec5db84fde1cce82ada51740070ec188ac Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 2 Oct 2015 14:19:55 -0700 Subject: Implement on-the-fly mempool size limitation. After each transaction which is added to mempool, we first call Expire() to remove old transactions, then throwing away the lowest-feerate transactions. After throwing away transactions by feerate, we set the minimum relay fee to the maximum fee transaction-and-dependant-set we removed, plus the default minimum relay fee. After the next block is received, the minimum relay fee is allowed to decrease exponentially. Its halflife defaults to 12 hours, but is decreased to 6 hours if the mempool is smaller than half its maximum size, and 3 hours if the mempool is smaller than a quarter its maximum size. The minimum -maxmempool size is 40*-limitdescendantsize, as it is easy for an attacker to play games with the cheapest -limitdescendantsize transactions. -maxmempool defaults to 300MB. This disables high-priority transaction relay when the min relay fee adjustment is >0 (ie when the mempool is full). When the relay fee adjustment drops below the default minimum relay fee / 2 it is set to 0 (re-enabling priority-based free relay). --- src/main.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f01bb8ec9e..c1df9998a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -885,8 +885,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("%d < %d", nFees, txMinFee)); - // 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 && nFees < mempoolRejectFee) { + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); + } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, 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"); } @@ -951,6 +954,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); + + // trim mempool and check if tx was trimmed + int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (!pool.exists(tx.GetHash())) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); } SyncWithWallets(tx, NULL); -- cgit v1.2.3 From d355cf4420043a866e418c97778d999cd1958f61 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 2 Oct 2015 14:20:38 -0700 Subject: Only call TrimToSize once per reorg/blocks disconnect --- src/main.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c1df9998a9..f379ea46e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -776,7 +776,7 @@ static std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee) + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -956,13 +956,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); // trim mempool and check if tx was trimmed - int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + if (!fOverrideMempoolLimit) { + int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); - pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); - if (!pool.exists(tx.GetHash())) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); + pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (!pool.exists(tx.GetHash())) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); + } } SyncWithWallets(tx, NULL); @@ -2029,7 +2031,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } -/** Disconnect chainActive's tip. */ +/** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */ bool static DisconnectTip(CValidationState &state) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); @@ -2056,7 +2058,7 @@ bool static DisconnectTip(CValidationState &state) { // ignore validation errors in resurrected transactions list 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()); @@ -2229,9 +2231,11 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo 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)) return false; + fBlocksDisconnected = true; } // Build list of new blocks to connect. @@ -2277,6 +2281,9 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } } + if (fBlocksDisconnected) + mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + // Callbacks/notifications for a new best chain. if (fInvalidFound) CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); @@ -2363,6 +2370,8 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } } + mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); -- cgit v1.2.3 From 9e93640be6c49fa1505ba5c5df8c89210da5a6e4 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 13 Oct 2015 01:06:59 -0700 Subject: Drop minRelayTxFee to 1000 There is no exact science to setting this parameter, but 5000 (just over 1 US cent at the time of writing) is higher than the cost to relay a transaction around the network (the new benchmark due to mempool limiting). --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f379ea46e2..31b3c21143 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ 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(5000); +CFeeRate minRelayTxFee = CFeeRate(1000); CTxMemPool mempool(::minRelayTxFee); -- cgit v1.2.3 From 53b86d0de5440ffb94f6742e26dd1fd9e11952f2 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 14 Oct 2015 20:42:49 +0200 Subject: doc: add comment explaining initial header request Add a comment that explains why the initial "getheader" requests are made starting from the block preceding the currently best one. Thanks to sdaftuar for the explanation! --- src/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index baad7fc050..e931d40c94 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4955,7 +4955,16 @@ 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()); } -- cgit v1.2.3 From d3b09f6bac738958b6bf5711bcb5291049b7466d Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 19 Oct 2015 14:43:04 -0400 Subject: Do not allow blockfile pruning during reindex. Also clarify startup message. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index baad7fc050..95c71b9cc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1881,7 +1881,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning) { + if (fPruneMode && fCheckForPruning && !fReindex) { FindFilesToPrune(setFilesToPrune); fCheckForPruning = false; if (!setFilesToPrune.empty()) { -- cgit v1.2.3 From 9d55050773d57c0e12005e524f2e54d9e622c6e2 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Wed, 3 Jun 2015 12:55:45 -0700 Subject: Add rules--presently disabled--for using GetMedianTimePast as endpoint for lock-time calculations The lock-time code currently uses CBlock::nTime as the cutoff point for time based locked transactions. This has the unfortunate outcome of creating a perverse incentive for miners to lie about the time of a block in order to collect more fees by including transactions that by wall clock determination have not yet matured. By using CBlockIndex::GetMedianTimePast from the prior block instead, the self-interested miner no longer gains from generating blocks with fraudulent timestamps. Users can compensate for this change by simply adding an hour (3600 seconds) to their time-based lock times. If enforced, this would be a soft-fork change. This commit only adds the functionality on an unexecuted code path, without changing the behaviour of Bitcoin Core. --- src/main.cpp | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 30df2744a3..499f2c3f76 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -650,10 +650,35 @@ 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; + + // Timestamps on the other hand don't get any special treatment, + // because we can't know what timestamp the next block will have, + // and there aren't timestamp applications where it matters. + // However this changes once median past time-locks are enforced: + const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + + return IsFinalTx(tx, nBlockHeight, nBlockTime); } unsigned int GetLegacySigOpCount(const CTransaction& tx) @@ -797,7 +822,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? @@ -2723,10 +2748,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): -- cgit v1.2.3 From 872fee3fccc8b33b9af0a401b5f85ac5504b57eb Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 2 Sep 2015 17:03:27 +0200 Subject: Introduce -maxuploadtarget * -maxuploadtarget can be set in MiB * if - ( time-left-in-24h-cycle / 600 * MAX_BLOCK_SIZE ) has reach, stop serve blocks older than one week and filtered blocks * no action if limit has reached, no guarantee that the target will not be surpassed * add outbound limit informations to rpc getnettotals --- src/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 30df2744a3..26a22ae6fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3805,6 +3805,16 @@ void static ProcessGetData(CNode* pfrom) } } } + // disconnect node in case we have reached the outbound limit for serving historical blocks + 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) ) + { + 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)) -- cgit v1.2.3 From 87cbdb8b41eee4067023cfa0d9d68722da74a5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 17 Apr 2015 14:19:21 +0200 Subject: Globals: Explicit Consensus::Params arg for main: -CheckBlockIndex -DisconnectTip -GetTransaction -InvalidateBlock -ProcessGetData -ReadBlockFromDisk --- src/main.cpp | 64 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 29 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e038fe3663..4470cd7336 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,7 +92,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; @@ -998,7 +998,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } /** 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; @@ -1044,7 +1044,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; @@ -1089,7 +1089,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(); @@ -1107,15 +1107,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", @@ -2064,13 +2064,14 @@ void static UpdateTip(CBlockIndex *pindexNew) { } /** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */ -bool static DisconnectTip(CValidationState &state) { +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(); @@ -2125,13 +2126,14 @@ static int64_t nTimePostConnect = 0; * corresponding to pindexNew, to bypass loading it again from disk. */ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) { + const CChainParams& chainparams = Params(); 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 = █ } @@ -2257,6 +2259,7 @@ static void PruneBlockIndexCandidates() { * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2265,7 +2268,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // 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; } @@ -2333,7 +2336,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; - const CChainParams& chainParams = Params(); + const CChainParams& chainparams = Params(); do { boost::this_thread::interruption_point(); @@ -2360,7 +2363,7 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = 0; if (fCheckpointsEnabled) - nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); + nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -2372,7 +2375,7 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { uiInterface.NotifyBlockTip(hashNewTip); } } while(pindexMostWork != chainActive.Tip()); - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { @@ -2382,7 +2385,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. @@ -2397,7 +2401,7 @@ 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)) { return false; } } @@ -2899,6 +2903,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { + const CChainParams& chainparams = Params(); // Preliminary checks bool checked = CheckBlock(*pblock, state); @@ -2916,7 +2921,7 @@ bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); if (!ret) return error("%s: AcceptBlock FAILED", __func__); } @@ -3248,6 +3253,7 @@ CVerifyDB::~CVerifyDB() bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) { + const CChainParams& chainparams = Params(); LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; @@ -3272,7 +3278,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)) @@ -3312,7 +3318,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()); @@ -3485,7 +3491,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap::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()); @@ -3512,9 +3518,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; } @@ -3796,7 +3801,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::iterator it = pfrom->vRecvGetData.begin(); @@ -3851,7 +3856,7 @@ void static ProcessGetData(CNode* pfrom) { // 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); @@ -4243,7 +4248,7 @@ 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()); } @@ -4509,7 +4514,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); } else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing @@ -4793,6 +4798,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()); @@ -4807,7 +4813,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; @@ -4834,7 +4840,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; @@ -4842,7 +4848,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; -- cgit v1.2.3 From 40cd32e835092c3158175511da5193193ec54939 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 1 Nov 2015 20:05:18 +0000 Subject: Revert "Add rules--presently disabled--for using GetMedianTimePast as endpoint for lock-time calculations" This reverts commit 9d55050773d57c0e12005e524f2e54d9e622c6e2. As noted by Luke-Jr, under some conditions this will accept transactions which are invalid by the network rules. This happens when the current block time is head of the median time past and a transaction's locktime is in the middle. This could be addressed by changing the rule to MAX(this_block_time, MTP+offset) but this solution and the particular offset used deserve some consideration. --- src/main.cpp | 40 +++++----------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e038fe3663..26a22ae6fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -650,35 +650,10 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } -bool CheckFinalTx(const CTransaction &tx, int flags) +bool CheckFinalTx(const CTransaction &tx) { AssertLockHeld(cs_main); - - // 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; - - // Timestamps on the other hand don't get any special treatment, - // because we can't know what timestamp the next block will have, - // and there aren't timestamp applications where it matters. - // However this changes once median past time-locks are enforced: - const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) - ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); - - return IsFinalTx(tx, nBlockHeight, nBlockTime); + return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime()); } unsigned int GetLegacySigOpCount(const CTransaction& tx) @@ -822,7 +797,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, STANDARD_LOCKTIME_VERIFY_FLAGS)) + if (!CheckFinalTx(tx)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? @@ -2748,15 +2723,10 @@ 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) { - int nLockTimeFlags = 0; - int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) - ? pindexPrev->GetMedianTimePast() - : block.GetBlockTime(); - if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { 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): -- cgit v1.2.3 From 69d373ff6693de204bdf58cbc90f8a26d8f711c8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 2 Nov 2015 02:01:45 +0100 Subject: Don't wipe the sigcache in TestBlockValidity --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e038fe3663..ad19b50ce4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1830,7 +1830,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector 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); -- cgit v1.2.3 From 06d81ad516f1d136da9f03ca2ae823211c0f6988 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 2 Nov 2015 16:10:57 -0500 Subject: Skip BIP30 check after BIP34 activation --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 26a22ae6fd..159f6f644c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1736,6 +1736,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 227931 where BIP34 activated, we can save the db accesses needed for the BIP30 check. + CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(227931); + //Only continue to enforce if we're below height 227931 or the block hash at that height doesn't correspond. + fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"))); + if (fEnforceBIP30) { BOOST_FOREACH(const CTransaction& tx, block.vtx) { const CCoins* coins = view.AccessCoins(tx.GetHash()); -- cgit v1.2.3 From 33c90cf197223fb95f858db80d090d570d70498a Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 2 Nov 2015 16:41:55 -0500 Subject: Make skipping BIP30 check chain agnostic --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 159f6f644c..a4c9de185d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1742,10 +1742,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // 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 227931 where BIP34 activated, we can save the db accesses needed for the BIP30 check. - CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(227931); - //Only continue to enforce if we're below height 227931 or the block hash at that height doesn't correspond. - fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"))); + // 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) { -- cgit v1.2.3 From 14470f9aa6baf02ca7162564f397153a2da0c592 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 2 Nov 2015 21:27:15 -0500 Subject: ModifyNewCoins saves database lookups When processing a new transaction, in addition to spending the Coins of its txin's it creates a new Coins for its outputs. The existing ModifyCoins function will first make sure this Coins does not already exist. It can not exist due to BIP 30, but because of that the lookup can't be cached and always has to go to the database. Since we are creating the coins to match the new tx anyway, there is no point in checking if they exist first anyway. However this should not be used for coinbase tx's in order to preserve the historical behavior of overwriting the two existing duplicate tx pairs. --- src/main.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 26a22ae6fd..270b4c063a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1285,10 +1285,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) -- cgit v1.2.3 From 53238ff0b1085352e4aaa796d0e473551e573143 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 25 Oct 2015 03:01:20 +0100 Subject: Clarify what minrelaytxfee does --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 30df2744a3..c340bcd5c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,7 +74,7 @@ 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) */ +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(1000); CTxMemPool mempool(::minRelayTxFee); -- cgit v1.2.3 From e4e5334ef8d923993ef9cf704ea8f3d0b5801350 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Tue, 3 Nov 2015 17:12:36 +0000 Subject: Restore MedianTimePast for locktime. Revert "Revert "Add rules--presently disabled--for using GetMedianTimePast as endpoint for lock-time calculations"" This reverts commit 40cd32e835092c3158175511da5193193ec54939. After careful analysis it was determined that the change was, in fact, safe and several people were suffering momentary confusion about locktime semantics. --- src/main.cpp | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 26a22ae6fd..e038fe3663 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -650,10 +650,35 @@ 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; + + // Timestamps on the other hand don't get any special treatment, + // because we can't know what timestamp the next block will have, + // and there aren't timestamp applications where it matters. + // However this changes once median past time-locks are enforced: + const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + + return IsFinalTx(tx, nBlockHeight, nBlockTime); } unsigned int GetLegacySigOpCount(const CTransaction& tx) @@ -797,7 +822,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? @@ -2723,10 +2748,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): -- cgit v1.2.3 From 22e780737db57bcb18b3824eb8158e19a4775cb6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 5 Nov 2015 00:16:49 +0100 Subject: Always flush block and undo when switching to new file Previously, the undo weren't being flushed during a reindex because fKnown was set to true in FindBlockPos. That is the correct behaviour for block files as they aren't being touched, but undo files are touched. This changes the behaviour to always flush when switching to a new file (even for block files, though that isn't really necessary). --- src/main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 26a22ae6fd..a2a5e0b729 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2517,8 +2517,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); @@ -2528,7 +2526,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nPos = vinfoBlockFile[nFile].nSize; } - nLastBlockFile = nFile; + if (nFile != nLastBlockFile) { + if (!fKnown) { + LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); + } + FlushBlockFile(!fKnown); + nLastBlockFile = nFile; + } + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); if (fKnown) vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); -- cgit v1.2.3 From 77f1f59d12b8ea097e8ccba83bd329011817bdf8 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 Nov 2015 15:12:30 -0800 Subject: Benchmark sanity checks and fork checks in ConnectBlock --- src/main.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9016fe42a0..a93a2d8505 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1711,6 +1711,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; @@ -1721,6 +1723,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; @@ -1746,6 +1751,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 @@ -1788,11 +1796,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin 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 control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; @@ -1845,8 +1855,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) @@ -1857,8 +1867,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; @@ -1889,16 +1899,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; } -- cgit v1.2.3 From 40b77d450dff6879c50a33d4e7f795c385fb0002 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 9 Nov 2015 14:27:08 +0100 Subject: Always allow getheaders from whitelisted peers Process `getheaders` messages from whitelisted peers even if we are in initial block download. Whitelisted peers can always use a node as a block source. Also log a debug message when the request is ignored, for troubleshooting. Fixes #6971. --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9016fe42a0..c5c5e40ee8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4303,10 +4303,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; - + } CBlockIndex* pindex = NULL; if (locator.IsNull()) { -- cgit v1.2.3 From 536766c903c8dc84e6709e4bd387aad5acf8e433 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 27 Oct 2015 17:21:43 +0100 Subject: [trivial] New DEFAULT_MIN_RELAY_TX_FEE = 1000 --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9016fe42a0..9212b309da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ -CFeeRate minRelayTxFee = CFeeRate(1000); +CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CTxMemPool mempool(::minRelayTxFee); -- cgit v1.2.3 From 8f4e67f152a9625a1c66c20de00679286b2c187c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 25 Aug 2015 20:12:08 +0200 Subject: net: Automatically create hidden service, listen on Tor Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket API, to create and destroy 'ephemeral' hidden services programmatically. https://stem.torproject.org/api/control.html#stem.control.Controller.create_ephemeral_hidden_service This means that if Tor is running (and proper authorization is available), bitcoin automatically creates a hidden service to listen on, without user manual configuration. This will positively affect the number of available .onion nodes. - When the node is started, connect to Tor through control socket - Send `ADD_ONION` command - First time: - Make it create a hidden service key - Save the key in the data directory for later usage - Make it redirect port 8333 to the local port 8333 (or whatever port we're listening on). - Keep control socket connection open for as long node is running. The hidden service will (by default) automatically go away when the connection is closed. --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index cd932b5d3a..8afb7ddcdd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4038,9 +4038,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); } } -- cgit v1.2.3 From 5891f870d68d90408aa5ce5b597fb574f2d2cbca Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Thu, 22 Oct 2015 14:13:18 -0400 Subject: Add opt-in full-RBF to mempool Replaces transactions already in the mempool if a new transaction seen with a higher fee, specifically both a higher fee per KB and a higher absolute fee. Children are evaluateed for replacement as well, using the mempool package tracking to calculate replaced fees/size. Transactions can opt-out of transaction replacement by setting nSequence >= maxint-1 on all inputs. (which all wallets do already) --- src/main.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b8abcff596..274a336ee2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -831,15 +831,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 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::max()-1) + { + fReplacementOptOut = false; + break; + } + } + if (fReplacementOptOut) + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); + + setConflicts.insert(ptxConflicting->GetHash()); + } } } } @@ -957,6 +984,82 @@ 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; + if (setConflicts.size()) + { + LOCK(pool.cs); + + // For efficiency we simply sum up the pre-calculated + // fees/size-with-descendants values from the mempool package + // tracking; this does mean the pathological case of diamond tx + // graphs will be overcounted. + BOOST_FOREACH(const uint256 hashConflicting, setConflicts) + { + CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); + if (mi == pool.mapTx.end()) + continue; + nConflictingFees += mi->GetFeesWithDescendants(); + nConflictingSize += mi->GetSizeWithDescendants(); + } + + // First of all we can't allow a replacement unless it pays greater + // fees than the transactions it conflicts with - if we did the + // bandwidth used by those conflicting transactions would not be + // paid for + if (nFees < nConflictingFees) + { + return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s", + hash.ToString(), FormatMoney(nFees), FormatMoney(nConflictingFees)), + REJECT_INSUFFICIENTFEE, "insufficient fee"); + } + + // Secondly in addition to paying more fees than the conflicts the + // new transaction must additionally pay for its own bandwidth. + CAmount nDeltaFees = nFees - 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"); + } + + // Finally replace only if we end up with a larger fees-per-kb than + // the replacements. + CFeeRate oldFeeRate(nConflictingFees, nConflictingSize); + CFeeRate newFeeRate(nFees, nSize); + 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"); + } + } + // 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)) @@ -977,6 +1080,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa __func__, hash.ToString(), FormatStateMessage(state)); } + // Remove conflicting transactions from the mempool + list ltxConflicted; + pool.removeConflicts(tx, ltxConflicted); + + BOOST_FOREACH(const CTransaction &txConflicted, ltxConflicted) + { + LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", + txConflicted.GetHash().ToString(), + hash.ToString(), + FormatMoney(nFees - nConflictingFees), + (int)nSize - (int)nConflictingSize); + } + // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); -- cgit v1.2.3 From fc8c19a07c20ab63f6a69f7494f486204d8f2b7a Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Thu, 29 Oct 2015 22:55:48 -0400 Subject: Prevent low feerate txs from (directly) replacing high feerate txs Previously all conflicting transactions were evaluated as a whole to determine if the feerate was being increased. This meant that low feerate children pulled the feerate down, potentially allowing a high transaction with a high feerate to be replaced by one with a lower feerate. --- src/main.cpp | 62 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 274a336ee2..10d661b2a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1008,23 +1008,51 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { LOCK(pool.cs); - // For efficiency we simply sum up the pre-calculated - // fees/size-with-descendants values from the mempool package - // tracking; this does mean the pathological case of diamond tx - // graphs will be overcounted. + CFeeRate newFeeRate(nFees, nSize); BOOST_FOREACH(const uint256 hashConflicting, setConflicts) { CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); if (mi == pool.mapTx.end()) continue; + + // 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->GetFee(), 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"); + } + + // For efficiency we simply sum up the pre-calculated + // fees/size-with-descendants values from the mempool package + // tracking; this does mean the pathological case of diamond tx + // graphs will be overcounted. nConflictingFees += mi->GetFeesWithDescendants(); nConflictingSize += mi->GetSizeWithDescendants(); } - // First of all we can't allow a replacement unless it pays greater - // fees than the transactions it conflicts with - if we did the - // bandwidth used by those conflicting transactions would not be - // paid for + // 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 (nFees < nConflictingFees) { return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s", @@ -1032,8 +1060,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa REJECT_INSUFFICIENTFEE, "insufficient fee"); } - // Secondly in addition to paying more fees than the conflicts the - // new transaction must additionally pay for its own bandwidth. + // Finally in addition to paying more fees than the conflicts the + // new transaction must pay for its own bandwidth. CAmount nDeltaFees = nFees - nConflictingFees; if (nDeltaFees < ::minRelayTxFee.GetFee(nSize)) { @@ -1044,20 +1072,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa FormatMoney(::minRelayTxFee.GetFee(nSize))), REJECT_INSUFFICIENTFEE, "insufficient fee"); } - - // Finally replace only if we end up with a larger fees-per-kb than - // the replacements. - CFeeRate oldFeeRate(nConflictingFees, nConflictingSize); - CFeeRate newFeeRate(nFees, nSize); - 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"); - } } // Check against previous transactions -- cgit v1.2.3 From b272ecfdb39f976dd61e35bacb22047da02b3416 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 30 Oct 2015 00:04:00 -0400 Subject: Reject replacements that add new unconfirmed inputs --- src/main.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 10d661b2a8..6e238f552e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1009,6 +1009,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LOCK(pool.cs); CFeeRate newFeeRate(nFees, nSize); + set setConflictsParents; BOOST_FOREACH(const uint256 hashConflicting, setConflicts) { CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); @@ -1042,6 +1043,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa REJECT_INSUFFICIENTFEE, "insufficient fee"); } + BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin) + { + setConflictsParents.insert(txin.prevout.hash); + } + // For efficiency we simply sum up the pre-calculated // fees/size-with-descendants values from the mempool package // tracking; this does mean the pathological case of diamond tx @@ -1050,6 +1056,24 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa nConflictingSize += mi->GetSizeWithDescendants(); } + 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. -- cgit v1.2.3 From 73d904009dc25ddfe5d6c4a91a13673c8f5cf87a Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 29 Oct 2015 22:49:00 -0400 Subject: Improve RBF replacement criteria Fix the calculation of conflicting size/conflicting fees. --- src/main.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6e238f552e..79d4c91b77 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1004,18 +1004,39 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // than the ones it replaces. CAmount nConflictingFees = 0; size_t nConflictingSize = 0; + uint64_t nConflictingCount = 0; + CTxMemPool::setEntries allConflicting; if (setConflicts.size()) { LOCK(pool.cs); CFeeRate newFeeRate(nFees, nSize); set setConflictsParents; - BOOST_FOREACH(const uint256 hashConflicting, setConflicts) + 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. // @@ -1048,12 +1069,28 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa setConflictsParents.insert(txin.prevout.hash); } - // For efficiency we simply sum up the pre-calculated - // fees/size-with-descendants values from the mempool package - // tracking; this does mean the pathological case of diamond tx - // graphs will be overcounted. - nConflictingFees += mi->GetFeesWithDescendants(); - nConflictingSize += mi->GetSizeWithDescendants(); + 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->GetFee(); + 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++) @@ -1119,17 +1156,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Remove conflicting transactions from the mempool - list ltxConflicted; - pool.removeConflicts(tx, ltxConflicted); - - BOOST_FOREACH(const CTransaction &txConflicted, ltxConflicted) + BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting) { LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", - txConflicted.GetHash().ToString(), + it->GetTx().GetHash().ToString(), hash.ToString(), FormatMoney(nFees - nConflictingFees), (int)nSize - (int)nConflictingSize); } + pool.RemoveStaged(allConflicting); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); -- cgit v1.2.3 From 16a2f93629f75d182871f288f0396afe6cdc8504 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Tue, 10 Nov 2015 17:58:06 -0500 Subject: Fix incorrect locking of mempool during RBF replacement Previously RemoveStaged() was called without pool.cs held. --- src/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 79d4c91b77..e3527a83d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1006,10 +1006,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa 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()) { - LOCK(pool.cs); - CFeeRate newFeeRate(nFees, nSize); set setConflictsParents; const int maxDescendantsToVisit = 100; -- cgit v1.2.3 From 726784374520347f09372532bd89156cdfd950f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Tue, 10 Nov 2015 19:28:56 +0100 Subject: Globals: Make AcceptBlockHeader static (Fix #6163) ..and at the same time prevent AcceptBlockHeader() from calling global function Params() --- src/main.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bc49d09fe7..cfac2fcc1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2781,9 +2781,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(); @@ -2836,7 +2835,7 @@ bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex** ppi 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 @@ -4498,7 +4497,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) -- cgit v1.2.3 From 598e4945872349ead03ff0cddc87c14b925157ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Mon, 20 Apr 2015 00:17:11 +0200 Subject: Chainparams: Explicit CChainParams arg for main (pre miner): -ProcessNewBlock -TestBlockValidity --- src/main.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8eb8776558..a71fcff17a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2915,9 +2915,8 @@ 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) { - const CChainParams& chainparams = Params(); // Preliminary checks bool checked = CheckBlock(*pblock, state); @@ -2946,9 +2945,8 @@ bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* 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())) @@ -3488,7 +3486,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; @@ -3510,7 +3508,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) 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()); @@ -4547,7 +4545,7 @@ 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 -- cgit v1.2.3 From d61fcff07112411a1e7c28984777480e0c0873aa Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 11 Nov 2015 10:10:48 +0100 Subject: don't enforce maxuploadtargets disconnect for whitelisted peers --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5208fbb031..4647112d4c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3867,8 +3867,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } // 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) ) + 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()); -- cgit v1.2.3 From c277a63ed70d063541e1e939917159129c102fec Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 13 Nov 2015 16:36:54 -0500 Subject: Clarify nLockTime-by-time comment in CheckFinalTx() --- src/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5208fbb031..baef017dc1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -670,10 +670,11 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - // Timestamps on the other hand don't get any special treatment, - // because we can't know what timestamp the next block will have, - // and there aren't timestamp applications where it matters. - // However this changes once median past time-locks are enforced: + // 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(); -- cgit v1.2.3 From 420fa8143a81f60fea79ee05df553e89378f1054 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Sat, 14 Nov 2015 04:44:59 -0800 Subject: Do not process tx inv's in blocksonly mode --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5208fbb031..b35a032733 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4218,7 +4218,7 @@ 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) + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK && !GetBoolArg("-blocksonly", false)) pfrom->AskFor(inv); if (inv.type == MSG_BLOCK) { -- cgit v1.2.3 From 3a964973fe9335f31c418b00e762ea04c3d3f088 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Sat, 14 Nov 2015 04:46:23 -0800 Subject: Add whitelistalwaysrelay option --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b35a032733..9842acb4b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4465,7 +4465,7 @@ 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. -- cgit v1.2.3 From 71a2683f4b526b17adf317733b0aa18ffacecfdc Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Sat, 14 Nov 2015 05:10:59 -0800 Subject: Use DEFAULT_BLOCKSONLY and DEFAULT_WHITELISTALWAYSRELAY constants --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9842acb4b3..fb529eb5b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4218,7 +4218,7 @@ 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 && !GetBoolArg("-blocksonly", false)) + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK && !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) pfrom->AskFor(inv); if (inv.type == MSG_BLOCK) { -- cgit v1.2.3 From 4d29032a6437eaa147a69ce2857fb243bf3a1e49 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Sun, 15 Nov 2015 20:13:30 -0500 Subject: Fixed integer comparison warning. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9fca183bb0..5d053e7813 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2577,7 +2577,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nPos = vinfoBlockFile[nFile].nSize; } - if (nFile != nLastBlockFile) { + if ((int)nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); } -- cgit v1.2.3 From e855b0152fa9d23fd49ccdd1b8e9427826b44a67 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Tue, 17 Nov 2015 22:23:39 -0500 Subject: Fix debug log message for block files --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a1f326fb13..86bf957436 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2579,7 +2579,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd if ((int)nFile != nLastBlockFile) { if (!fKnown) { - LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); + LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString()); } FlushBlockFile(!fKnown); nLastBlockFile = nFile; -- cgit v1.2.3 From 8504867b146014c99c6acb180020a1369069c761 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 12 Nov 2015 16:57:03 -0500 Subject: Save the last unnecessary database read It's possible coins with the same hash exist when you create a duplicate coinbase, so previously we were reading from the database to make sure we had the old coins cached so if we were to spend the new ones, the old ones would also be spent. This pull instead just marks the new coins as not fresh if they are from a coinbase, so if they are spent they will be written all the way down to the database anyway overwriting any duplicates. --- src/main.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb121c00d..3c9c77ef67 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1310,17 +1310,9 @@ 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.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight); } void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) -- cgit v1.2.3 From 012fc91511b153ce3fd3e9fb7bbed8f85fb1690e Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 26 Nov 2015 15:48:26 +0100 Subject: NotifyBlockTip signal: switch from hash (uint256) to CBlockIndex* - also adds a boolean for indication if the tip update was happening during initial sync - emit notification also during initial sync --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 31913956b7..b41c1ad2e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2636,9 +2636,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, // Notify external listeners about the new tip. if (!vHashes.empty()) { GetMainSignals().UpdatedBlockTip(pindexNewTip); - uiInterface.NotifyBlockTip(vHashes.front()); } } + if (!vHashes.empty()) { + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(chainparams.GetConsensus()); -- cgit v1.2.3 From 4082e4660305f7696949c3a13e2e9611210894e8 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 27 Nov 2015 18:22:18 +0100 Subject: [Qt] call GuessVerificationProgress synchronous during core signal, pass double over UI signal --- src/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b41c1ad2e1..385ab71538 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2638,9 +2638,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, GetMainSignals().UpdatedBlockTip(pindexNewTip); } } - if (!vHashes.empty()) { - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); - } + // Always notify the UI if a new block tip was connected + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(chainparams.GetConsensus()); -- cgit v1.2.3 From 9af5f9cb8773da2904aa3819234aaebd2efb5d15 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 30 Nov 2015 11:32:13 +0100 Subject: Move uiInterface.NotifyBlockTip signal above the core/wallet signal - This will keep getbestblockhash more in sync with blocknotify callbacks --- src/main.cpp | 59 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 385ab71538..0ae721af44 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2606,40 +2606,43 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, // When we reach this point, we switched to a new tip (stored in pindexNewTip). // Notifications/callbacks that can run without cs_main - if (!fInitialDownload) { - // Find the hashes of all blocks that weren't previously in the best chain. - std::vector 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; + // 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 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); + // 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. + if (!vHashes.empty()) { + GetMainSignals().UpdatedBlockTip(pindexNewTip); + } } } - // Always notify the UI if a new block tip was connected - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(chainparams.GetConsensus()); -- cgit v1.2.3 From c0353064ddf71ad103bd19f6e7c10ff8e240ac46 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 13 Nov 2015 10:05:21 -0500 Subject: Change GetPriority calculation. Compute the value of inputs that already are in the chain at time of mempool entry and only increase priority due to aging for those inputs. This effectively changes the CTxMemPoolEntry's GetPriority calculation from an upper bound to a lower bound. --- src/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 33bd2e0ce1..55b0517349 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -950,9 +950,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; - double dPriority = view.GetPriority(tx, chainActive.Height()); + CAmount inChainInputValue; + double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx)); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block @@ -964,7 +965,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nFees < 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) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::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"); } -- cgit v1.2.3 From 3587f6a0247d12b7d50b184b16248ccec3757ff0 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Tue, 17 Nov 2015 16:59:18 -0800 Subject: Fix relay mechanism for whitelisted peers under blocks only mode. Previously in blocks only mode all inv messages where type!=MSG_BLOCK would be rejected without regard for whitelisting or whitelistalwaysrelay. As such whitelisted peers would never send the transaction (which would be processed). --- src/main.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8fb121c00d..c7e67a1d74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4210,6 +4210,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 vToFetch; @@ -4224,9 +4230,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 && !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) - pfrom->AskFor(inv); - if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -4250,6 +4253,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", "peer sent inv %s in violation of protocol peer=%d\n", inv.ToString(), pfrom->id); + else if (!fAlreadyHave && !fImporting && !fReindex) + pfrom->AskFor(inv); + } // Track requests for our stuff GetMainSignals().Inventory(inv.hash); -- cgit v1.2.3 From d8aaa51bec7d9e96eab78b88f494efb937a93bfb Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Tue, 17 Nov 2015 17:01:43 -0800 Subject: Bail early in processing transactions in blocks only mode. Previously unsolicited transactions would be processed as normal. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c7e67a1d74..243a647597 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4384,6 +4384,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "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", "peer sent transaction in violation of protocol peer=%d\n", pfrom->id); + return true; + } + vector vWorkQueue; vector vEraseQueue; CTransaction tx; -- cgit v1.2.3 From 80ae230a52ab781806876ea8be174b1793b9b683 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Fri, 20 Nov 2015 15:54:27 -0800 Subject: Improve log messages for blocks only violations. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 243a647597..7242abb0cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4256,7 +4256,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else { if (fBlocksOnly) - LogPrint("net", "peer sent inv %s in violation of protocol peer=%d\n", inv.ToString(), pfrom->id); + 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); } @@ -4388,7 +4388,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // 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", "peer sent transaction in violation of protocol peer=%d\n", pfrom->id); + LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; } -- cgit v1.2.3 From 5029698186445bf3cd69d0e720f019c472661bff Mon Sep 17 00:00:00 2001 From: kazcw Date: Wed, 16 Jul 2014 14:31:41 -0700 Subject: prevent peer flooding request queue for an inv mapAlreadyAskedFor does not keep track of which peer has a request queued for a particular tx. As a result, a peer can blind a node to a tx indefinitely by sending many invs for the same tx, and then never replying to getdatas for it. Each inv received will be placed 2 minutes farther back in mapAlreadyAskedFor, so a short message containing 10 invs would render that tx unavailable for 20 minutes. This is fixed by disallowing a peer from having more than one entry for a particular inv in mapAlreadyAskedFor at a time. --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2579b642b8..05dedb5631 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5226,6 +5226,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vGetData.clear(); } } + pto->setAskFor.erase(inv.hash); pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) -- cgit v1.2.3 From ebb25f4c23adbcb55796c402bafd6064a136f16f Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 23 Nov 2015 01:54:23 +0000 Subject: Limit setAskFor and retire requested entries only when a getdata returns. The setAskFor duplicate elimination was too eager and removed entries when we still had no getdata response, allowing the peer to keep INVing and not responding. --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 05dedb5631..2bcc4cbc54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4406,6 +4406,7 @@ 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) @@ -5225,8 +5226,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getdata", vGetData); vGetData.clear(); } + } else { + //If we're not going to ask, don't expect a response. + pto->setAskFor.erase(inv.hash); } - pto->setAskFor.erase(inv.hash); pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) -- cgit v1.2.3 From 2e29e7e247b6b74502c70612dab1f7f67de675c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 17 Apr 2015 14:40:24 +0200 Subject: Globals: Remove a bunch of Params() calls from main.cpp: 1) Chainparams: Explicit CChainParams arg for main: -AcceptBlock -AcceptBlockHeader -ActivateBestChain -ConnectTip -InitBlockIndex -LoadExternalBlockFile -VerifyDB parametric constructor 2) Also pickup more Params()\. in main.cpp 3) Pass nPruneAfterHeight explicitly to new FindFilesToPrune() in main.cpp --- src/main.cpp | 56 ++++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a1f326fb13..4d88078f41 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1939,6 +1939,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; @@ -1947,7 +1948,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { bool fFlushForPrune = false; try { if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune); + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); fCheckForPruning = false; if (!setFilesToPrune.empty()) { fFlushForPrune = true; @@ -2147,8 +2148,8 @@ 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) { - const CChainParams& chainparams = Params(); +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. @@ -2280,8 +2281,8 @@ 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) { - const CChainParams& chainparams = Params(); +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock) +{ AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2314,7 +2315,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) @@ -2355,10 +2356,10 @@ 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) { +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock) +{ CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; - const CChainParams& chainparams = Params(); do { boost::this_thread::interruption_point(); @@ -2371,7 +2372,7 @@ bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { 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(); @@ -2850,9 +2851,9 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state 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; @@ -2942,7 +2943,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c // 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(); } @@ -2951,7 +2952,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c return error("%s: AcceptBlock FAILED", __func__); } - if (!ActivateBestChain(state, pblock)) + if (!ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed", __func__); return true; @@ -3041,13 +3042,13 @@ void UnlinkPrunedFiles(std::set& setFilesToPrune) } /* Calculate the block/rev files that should be deleted to remain under target*/ -void FindFilesToPrune(std::set& setFilesToPrune) +void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight) { LOCK2(cs_main, cs_LastBlockFile); if (chainActive.Tip() == NULL || nPruneTarget == 0) { return; } - if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { + if (chainActive.Tip()->nHeight <= nPruneAfterHeight) { return; } @@ -3275,9 +3276,8 @@ 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) { - const CChainParams& chainparams = Params(); LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; @@ -3393,9 +3393,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. @@ -3413,7 +3412,7 @@ bool InitBlockIndex() { // 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(Params().GenesisBlock()); + CBlock &block = const_cast(chainparams.GenesisBlock()); // Start new block file unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; @@ -3425,7 +3424,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); @@ -3437,11 +3436,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 mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); @@ -3461,10 +3457,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; @@ -3858,7 +3854,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // 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()); } @@ -4701,7 +4697,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); -- cgit v1.2.3 From b3caa9b7fa7695e60fc4002229b77f43db8ded67 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Tue, 24 Nov 2015 01:47:32 -0800 Subject: Move bloom filter filtering logic outside of command "switch" (giant if/else). Moving this logic outside of the "switch" makes it far simpler to enable the forced disconnect by a parameter. --- src/main.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2579b642b8..fd5637a8cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3989,6 +3989,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + if (!(nLocalServices & NODE_BLOOM) && + (strCommand == "filterload" || + strCommand == "filteradd" || + strCommand == "filterclear")) + { + if (pfrom->nVersion >= NO_BLOOM_VERSION) { + Misbehaving(pfrom->GetId(), 100); + return false; + } + //TODO: Enable this after reasonable network upgrade + //else { + // pfrom->fDisconnect = true; + // return false; + //} + } if (strCommand == "version") @@ -4750,21 +4765,6 @@ 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") { CBloomFilter filter; -- cgit v1.2.3 From 0f4dc53fd6a19a763922b4c3888ce6542c594e01 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Tue, 24 Nov 2015 01:51:53 -0800 Subject: Add enforcenodebloom option. Previously peers which implement a protocol version less than NO_BLOOM_VERSION would not be disconnected for sending a filter command, regardless of the peerbloomfilter option. Many node operators do not wish to provide expensive bloom filtering for SPV clients, previously they had to cherry pick the commit which enabled the disconnect logic. The default should remain false until a sufficient percent of SPV clients have updated. --- src/main.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fd5637a8cd..b0b58141a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3997,12 +3997,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion >= NO_BLOOM_VERSION) { Misbehaving(pfrom->GetId(), 100); return false; + } else if (GetBoolArg("-enforcenodebloom", false)) { + pfrom->fDisconnect = true; + return false; } - //TODO: Enable this after reasonable network upgrade - //else { - // pfrom->fDisconnect = true; - // return false; - //} } -- cgit v1.2.3 From a9f3d3db5c0c8d1697998ed9b3e192ddbf9a31f4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 26 Nov 2015 22:05:34 +0100 Subject: Fix and improve relay from whitelisted peers This makes sure that retransmits by a whitelisted peer also actually result in a retransmit. Further, this changes the logic to never relay in case we would assign a DoS score, as we expect to get DoS banned ourselves as a result. --- src/main.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b0b58141a2..6cb9f43c0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4421,11 +4421,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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); @@ -4505,13 +4501,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; -- cgit v1.2.3 From eece63fa72566068cb2a1bf85c95a72a5ba59bc9 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 17 Nov 2015 17:35:44 +0100 Subject: Switch blocks to a constant-space Merkle root/branch algorithm. This switches the Merkle tree logic for blocks to one that runs in constant (small) space. The old code is moved to tests, and a new test is added that for various combinations of block sizes, transaction positions to compute a branch for, and mutations: * Verifies that the old code and new code agree for the Merkle root. * Verifies that the old code and new code agree for the Merkle branch. * Verifies that the computed Merkle branch is valid. * Verifies that mutations don't change the Merkle root. * Verifies that mutations are correctly detected. --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ceb5cb66f3..191b2b3c12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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" @@ -2876,7 +2877,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); -- cgit v1.2.3 From b966aa836a3bc5bfa1314248258308f0026d41bb Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 27 Jun 2015 19:21:41 +0000 Subject: Constrain constant values to a single location in code --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ceb5cb66f3..2c43d21f8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -941,7 +941,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); - } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, 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"); } @@ -963,7 +963,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; @@ -1436,7 +1436,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); @@ -3605,7 +3605,7 @@ bool InitBlockIndex(const CChainParams& chainparams) 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"); @@ -3936,7 +3936,7 @@ std::string GetWarnings(const std::string& strFor) 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 (GetBoolArg("-testsafemode", false)) + if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) strStatusBar = strRPC = "testsafemode enabled"; // Misc warnings like out of disk space and clock is wrong -- cgit v1.2.3 From 086ee67d839b33bf475177f680fcc848a0625266 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 27 Nov 2015 13:20:29 +0100 Subject: Switch to a more efficient rolling Bloom filter For each 'bit' in the filter we really maintain 2 bits, which store either: 0: not set 1-3: set in generation N After (nElements / 2) insertions, we switch to a new generation, and wipe entries which already had the new generation number, effectively switching from the last 1.5 * nElements set to the last 1.0 * nElements set. This is 25% more space efficient than the previous implementation, and can (at peak) store 1.5 times the requested amount of history (though only 1.0 times the requested history is guaranteed). The existing unit tests should be sufficient. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ceb5cb66f3..422b1e7843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -180,7 +180,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 recentRejects; uint256 hashRecentRejectsChainTip; -- cgit v1.2.3 From faf93f37fe47fe326fcc4955302a66f24eb13b65 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 9 Nov 2015 19:16:38 +0100 Subject: [trivial] Reuse translation and cleanup DEFAULT_* values * DEFAULT_DISABLE_SAFEMODE = false * Use DEFAULT_* constants for extern bools --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2c43d21f8b..6b6840ce8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,10 +66,10 @@ bool fReindex = false; bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; -bool fIsBareMultisigStd = true; +bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; bool fRequireStandard = true; bool fCheckBlockIndex = false; -bool fCheckpointsEnabled = true; +bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; -- cgit v1.2.3 From 50262d89531692473ff557c1061aee22aa4cca1c Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 18 Nov 2014 22:16:32 +0100 Subject: Allow block announcements with headers This replaces using inv messages to announce new blocks, when a peer requests (via the new "sendheaders" message) that blocks be announced with headers instead of inv's. Since headers-first was introduced, peers send getheaders messages in response to an inv, which requires generating a block locator that is large compared to the size of the header being requested, and requires an extra round-trip before a reorg can be relayed. Save time by tracking headers that a peer is likely to know about, and send a headers chain that would connect to a peer's known headers, unless the chain would be too big, in which case we revert to sending an inv instead. Based off of @sipa's commit to announce all blocks in a reorg via inv, which has been squashed into this commit. Rebased-by: Pieter Wuille --- src/main.cpp | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 213 insertions(+), 10 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 94fcd6223b..31913956b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -247,6 +247,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. @@ -256,6 +258,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; @@ -264,11 +268,13 @@ struct CNodeState { pindexBestKnownBlock = NULL; hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = NULL; + pindexBestHeaderSent = NULL; fSyncStarted = false; nStallingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; + fPreferHeaders = false; } }; @@ -398,6 +404,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) { @@ -2557,16 +2579,17 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c * 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 CChainParams& chainparams, const CBlock* pblock) -{ - CBlockIndex *pindexNewTip = NULL; +bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { CBlockIndex *pindexMostWork = NULL; 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. @@ -2577,26 +2600,44 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, 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(); + // Find the hashes of all blocks that weren't previously in the best chain. + std::vector 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)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + 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. - GetMainSignals().UpdatedBlockTip(pindexNewTip); - uiInterface.NotifyBlockTip(hashNewTip); + if (!vHashes.empty()) { + GetMainSignals().UpdatedBlockTip(pindexNewTip); + uiInterface.NotifyBlockTip(vHashes.front()); + } } } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(chainparams.GetConsensus()); @@ -4333,6 +4374,14 @@ 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("sendheaders"); + } } @@ -4402,6 +4451,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; } + else if (strCommand == "sendheaders") + { + LOCK(cs_main); + State(pfrom->GetId())->fPreferHeaders = true; + } + else if (strCommand == "inv") { @@ -4446,7 +4501,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // not a direct successor. pfrom->PushMessage("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 @@ -4554,6 +4609,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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()) { @@ -4581,6 +4638,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } + // 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("headers", vHeaders); } @@ -4772,6 +4834,53 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("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 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 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("getdata", vGetData); + } + } + } + CheckBlockIndex(chainparams.GetConsensus()); } @@ -5297,6 +5406,100 @@ bool SendMessages(CNode* pto, bool fSendTrickle) GetMainSignals().Broadcast(nTimeBestReceived); } + // + // 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 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("headers", vHeaders); + state.pindexBestHeaderSent = pBestIndex; + } + pto->vBlockHashesToAnnounce.clear(); + } + // // Message: inventory // -- cgit v1.2.3 From cb491e778828d322800793cb229884c904f172b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 27 Nov 2015 15:12:08 +0100 Subject: Trivial: Fix warning introduced by #7053 by casting to uint64_t --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ceb5cb66f3..eccc4999c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3248,7 +3248,7 @@ void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight if (chainActive.Tip() == NULL || nPruneTarget == 0) { return; } - if (chainActive.Tip()->nHeight <= nPruneAfterHeight) { + if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) { return; } -- cgit v1.2.3 From ec73ef37eccfeda76de55c4ff93ea54d4e69e1ec Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Thu, 26 Nov 2015 05:25:30 +0000 Subject: Replace setInventoryKnown with a rolling bloom filter. Mruset setInventoryKnown was reduced to a remarkably small 1000 entries as a side effect of sendbuffer size reductions in 2012. This removes setInventoryKnown filtering from merkleBlock responses because false positives there are especially unattractive and also because I'm not sure if there aren't race conditions around the relay pool that would cause some transactions there to be suppressed. (Also, ProcessGetData was accessing setInventoryKnown without taking the required lock.) --- src/main.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 901a34bdec..5e39c31bd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4138,8 +4138,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair 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("tx", block.vtx[pair.first]); } // else // no response @@ -5511,7 +5510,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { - if (pto->setInventoryKnown.count(inv)) + if (pto->setInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy @@ -5532,9 +5531,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) + if (!pto->setInventoryKnown.contains(inv.hash)) { + pto->setInventoryKnown.insert(inv.hash); vInv.push_back(inv); if (vInv.size() >= 1000) { -- cgit v1.2.3 From 6b849350ab074a7ccb80ecbef387f59e1271ded6 Mon Sep 17 00:00:00 2001 From: Patick Strateman Date: Sun, 29 Nov 2015 01:52:51 -0800 Subject: Rename setInventoryKnown filterInventoryKnown --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5e39c31bd0..98457d31e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5510,7 +5510,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { - if (pto->setInventoryKnown.contains(inv.hash)) + if (pto->filterInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy @@ -5531,9 +5531,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } - if (!pto->setInventoryKnown.contains(inv.hash)) + if (!pto->filterInventoryKnown.contains(inv.hash)) { - pto->setInventoryKnown.insert(inv.hash); + pto->filterInventoryKnown.insert(inv.hash); vInv.push_back(inv); if (vInv.size() >= 1000) { -- cgit v1.2.3 From d41e44c9accb3df84e0abbc602cc76b72754d382 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 29 Nov 2015 22:10:31 +0000 Subject: Actually only use filterInventoryKnown with MSG_TX inventory messages. Previously this logic could erroneously filter a MSG_BLOCK inventory message. --- src/main.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 98457d31e3..238e2276cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5510,7 +5510,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { - if (pto->filterInventoryKnown.contains(inv.hash)) + if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy @@ -5531,15 +5531,13 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } - if (!pto->filterInventoryKnown.contains(inv.hash)) + pto->filterInventoryKnown.insert(inv.hash); + + vInv.push_back(inv); + if (vInv.size() >= 1000) { - pto->filterInventoryKnown.insert(inv.hash); - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage("inv", vInv); - vInv.clear(); - } + pto->PushMessage("inv", vInv); + vInv.clear(); } } pto->vInventoryToSend = vInvWait; -- cgit v1.2.3 From 9b060e5cfb0d185b553b21ae19d390f81e83bd4d Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 26 Aug 2015 18:58:17 -0700 Subject: Fix removal of time-locked transactions during reorg --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 55b0517349..3422c56cfc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2350,7 +2350,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // 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.removeForReorg(pcoinsTip, pindexDelete->nHeight); mempool.check(pcoinsTip); // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); -- cgit v1.2.3 From 474b84a7413f124524cccf097dd36c7a24d406b8 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 9 Sep 2015 14:54:11 -0700 Subject: Make indentation in ActivateBestChainStep readable --- src/main.cpp | 66 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3422c56cfc..e05237da20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2525,43 +2525,43 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c 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, 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; + // 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.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); -- cgit v1.2.3 From bb8ea1f6304d7ed3f5fe0a01c060ac9f94629349 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 9 Sep 2015 16:31:20 -0700 Subject: removeForReorg calls once-per-disconnect-> once-per-reorg --- src/main.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e05237da20..8f67c03510 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2310,12 +2310,11 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } -/** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */ +/** 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, consensusParams)) @@ -2350,8 +2349,6 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // UpdateTransactionsFromBlock finds descendants of any transactions in this // block that were added back and cleans up the mempool state. mempool.UpdateTransactionsFromBlock(vHashUpdate); - mempool.removeForReorg(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); // Let wallets know transactions went from 1-confirmed to @@ -2375,7 +2372,6 @@ static int64_t nTimePostConnect = 0; 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; @@ -2412,7 +2408,6 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Remove conflicting transactions from the mempool. list 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 @@ -2515,8 +2510,11 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state, chainparams.GetConsensus())) + if (!DisconnectTip(state, chainparams.GetConsensus())) { + // Probably an AbortNode() error, but try to keep mempool consistent anyway + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); return false; + } fBlocksDisconnected = true; } @@ -2550,6 +2548,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c break; } else { // A system error occurred (disk space, database error, ...). + // Probably gonna shut down ASAP, but try to keep mempool consistent anyway + if (fBlocksDisconnected) + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); return false; } } else { @@ -2563,8 +2564,11 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c } } - if (fBlocksDisconnected) + if (fBlocksDisconnected) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + } + mempool.check(pcoinsTip); // Callbacks/notifications for a new best chain. if (fInvalidFound) @@ -2672,6 +2676,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state, consensusParams)) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); return false; } } @@ -2689,6 +2694,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus } InvalidChainFound(pindex); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); return true; } -- cgit v1.2.3 From 7e49f5f8b4e237d7212d027a7bea4bbd52c9e7b6 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 29 Oct 2015 14:06:13 -0400 Subject: Track coinbase spends in CTxMemPoolEntry This allows us to optimize CTxMemPool::removeForReorg. --- src/main.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8f67c03510..ad8819eb3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -953,7 +953,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount inChainInputValue; double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue); + // 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); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block -- cgit v1.2.3 From b7fa4aa3876b56694b27af0beef367be9e0733fd Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 12 Nov 2015 15:54:17 -0500 Subject: Don't call removeForReorg if DisconnectTip fails --- src/main.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ad8819eb3d..feb526e09c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2521,11 +2521,8 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state, chainparams.GetConsensus())) { - // Probably an AbortNode() error, but try to keep mempool consistent anyway - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); + if (!DisconnectTip(state, chainparams.GetConsensus())) return false; - } fBlocksDisconnected = true; } @@ -2559,9 +2556,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c break; } else { // A system error occurred (disk space, database error, ...). - // Probably gonna shut down ASAP, but try to keep mempool consistent anyway - if (fBlocksDisconnected) - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); return false; } } else { -- cgit v1.2.3 From 2d8860e820e2ca73000f558eb9686206bec2652a Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 23 Nov 2015 16:06:12 -0500 Subject: Fix removeForReorg to use MedianTimePast --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index feb526e09c..0b758f391a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2570,7 +2570,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c } if (fBlocksDisconnected) { - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); } mempool.check(pcoinsTip); @@ -2681,7 +2681,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state, consensusParams)) { - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return false; } } @@ -2699,7 +2699,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus } InvalidChainFound(pindex); - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return true; } -- cgit v1.2.3 From aabc897801f2513ab5bf5e8ae6e6bcffeb889e94 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 1 Dec 2015 09:47:13 +0100 Subject: rpc: Don't translate warning messages But keep translating them in the GUI. This - necessarily - requires duplication of a few messages. Alternative take on #7134, that keeps the translations from being wiped. Also document GetWarnings() input argument. Fixes #5895. --- src/main.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index eea53a58de..ea6dd4b2d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3979,29 +3979,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", DEFAULT_TESTSAFEMODE)) - strStatusBar = strRPC = "testsafemode enabled"; + 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 @@ -4013,12 +4018,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; -- cgit v1.2.3 From c49d5bc9e6c97c47c0bd78604b2c393a7e4af097 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 26 Oct 2015 11:08:46 -0400 Subject: Store the total sig op count of a tx. Store sum of legacy and P2SH sig op counts. This is calculated in AcceptToMemory pool and storing it saves redoing the expensive calculation in block template creation. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e3c77e8505..fb6d32b2fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -964,7 +964,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase); + 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 -- cgit v1.2.3 From 5f122633020ce5d9f78c73394cda576a8657a3ac Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 28 Oct 2015 14:56:28 -0400 Subject: Expose FormatStateMessage --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fb6d32b2fc..e9e9820434 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -816,7 +816,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned } /** 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(), -- cgit v1.2.3 From 45b8e278fba213fc88ff2be532f15c06accfc857 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 22 Feb 2014 02:41:01 +0000 Subject: -bytespersigop option to additionally limit sigops in transactions we relay and mine --- src/main.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e9e9820434..e860565aa9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; bool fRequireStandard = true; +unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; @@ -937,16 +938,8 @@ 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; @@ -967,6 +960,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps); unsigned int nSize = entry.GetTxSize(); + // 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)); + // Don't accept it if it can't get into a block CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); if (fLimitFree && nFees < txMinFee) -- cgit v1.2.3 From 677aa3d88c018a235d5b6d929e82f7b16e4f3e41 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 22 Oct 2015 11:52:55 -0700 Subject: Discard txn cache entries that were loaded for removed mempool txn --- src/main.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index e9e9820434..73ca8bb054 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -789,6 +789,17 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } +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); + + std::vector vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed); +} + CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) { uint256 hash = tx.GetHash(); @@ -1210,12 +1221,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // trim mempool and check if tx was trimmed if (!fOverrideMempoolLimit) { - int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); - - pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); - if (!pool.exists(tx.GetHash())) + 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"); } } @@ -2571,7 +2578,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } mempool.check(pcoinsTip); @@ -2686,7 +2693,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus } } - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + 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. -- cgit v1.2.3 From bde953e2818ecf44727da128c2aa2ec7667cf7e7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 22 Oct 2015 15:50:33 -0700 Subject: Uncache input txn in utxo cache if a tx is not accepted to mempool --- src/main.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 73ca8bb054..d1ed5c5edf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -835,8 +835,9 @@ std::string FormatStateMessage(const CValidationState &state) state.GetRejectCode()); } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) +bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, + std::vector& vHashTxnToUncache) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -917,13 +918,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; @@ -1232,6 +1239,18 @@ 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 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, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { -- cgit v1.2.3 From dd5862c4cdc02535948042fe519694166bcd2bb7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 26 Oct 2015 04:22:07 +0100 Subject: Flush coins cache also after transaction processing --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d1ed5c5edf..c41dd58d11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4830,6 +4830,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } + FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } -- cgit v1.2.3 From 8a03727d9cc975a3d0843d83ef05957b9e9fbbca Mon Sep 17 00:00:00 2001 From: paveljanik Date: Fri, 3 Jul 2015 16:36:49 +0200 Subject: Fix various typos --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c41dd58d11..fe6bc52957 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,9 +134,9 @@ namespace { set 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 mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; -- cgit v1.2.3 From 5e151a842cebdc3960cd9cff0634e5663459e86f Mon Sep 17 00:00:00 2001 From: paveljanik Date: Wed, 24 Jun 2015 11:39:26 +0200 Subject: PartitionCheck: remove useless spaces --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fe6bc52957..cb3f8f39f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1945,8 +1945,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; @@ -3047,7 +3047,7 @@ 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: -- cgit v1.2.3 From 9ef2a25603c9ec4e44c4f45c6a5d4e4386ec86d3 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 30 Nov 2015 16:42:36 -0500 Subject: Update replace-by-fee logic to use fee deltas --- src/main.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index cb3f8f39f8..23df8ca689 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1061,13 +1061,17 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C uint64_t nConflictingCount = 0; CTxMemPool::setEntries allConflicting; + CAmount nModifiedFees = nFees; + double nPriorityDummy = 0; + pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); + // 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(nFees, nSize); + CFeeRate newFeeRate(nModifiedFees, nSize); set setConflictsParents; const int maxDescendantsToVisit = 100; CTxMemPool::setEntries setIterConflicting; @@ -1110,7 +1114,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // 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->GetFee(), mi->GetTxSize()); + CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); if (newFeeRate <= oldFeeRate) { return state.DoS(0, @@ -1138,7 +1142,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C pool.CalculateDescendants(it, allConflicting); } BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) { - nConflictingFees += it->GetFee(); + nConflictingFees += it->GetModifiedFee(); nConflictingSize += it->GetTxSize(); } } else { @@ -1171,16 +1175,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // 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 (nFees < nConflictingFees) + if (nModifiedFees < nConflictingFees) { return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s", - hash.ToString(), FormatMoney(nFees), FormatMoney(nConflictingFees)), + 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 = nFees - nConflictingFees; + CAmount nDeltaFees = nModifiedFees - nConflictingFees; if (nDeltaFees < ::minRelayTxFee.GetFee(nSize)) { return state.DoS(0, @@ -1218,7 +1222,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", it->GetTx().GetHash().ToString(), hash.ToString(), - FormatMoney(nFees - nConflictingFees), + FormatMoney(nModifiedFees - nConflictingFees), (int)nSize - (int)nConflictingSize); } pool.RemoveStaged(allConflicting); -- cgit v1.2.3 From 27fae3484cdb21b0d24face833b966fce5926be5 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 2 Dec 2015 09:37:18 -0500 Subject: Use fee deltas for determining mempool acceptance --- src/main.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 23df8ca689..12642f3196 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -968,6 +968,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; + // 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); @@ -987,14 +992,17 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Don't accept it if it can't get into a block CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); + + // txMinFee takes into account priority/fee deltas, so compare using + // nFees rather than nModifiedFees if (fLimitFree && nFees < txMinFee) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("%d < %d", nFees, txMinFee)); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { + 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) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { + } 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"); } @@ -1002,7 +1010,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // 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; @@ -1061,10 +1069,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C uint64_t nConflictingCount = 0; CTxMemPool::setEntries allConflicting; - CAmount nModifiedFees = nFees; - double nPriorityDummy = 0; - pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); - // If we don't hold the lock allConflicting might be incomplete; the // subsequent RemoveStaged() and addUnchecked() calls don't guarantee // mempool consistency for us. -- cgit v1.2.3 From 901b01d674031f9aca717deeb372bafa160a24af Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 2 Dec 2015 11:04:15 -0500 Subject: Remove GetMinRelayFee One test in AcceptToMemoryPool was to compare a transaction's fee agains the value returned by GetMinRelayFee. This value was zero for all small transactions. For larger transactions (between DEFAULT_BLOCK_PRIORITY_SIZE and MAX_STANDARD_TX_SIZE), this function was preventing low fee transactions from ever being accepted. With this function removed, we will now allow transactions in that range with fees (including modifications via PrioritiseTransaction) below the minRelayTxFee, provided that they have sufficient priority. --- src/main.cpp | 35 ----------------------------------- 1 file changed, 35 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 12642f3196..9363015a5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -800,32 +800,6 @@ void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { pcoinsTip->Uncache(removed); } -CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) -{ - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - - CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - - 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; -} - /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state) { @@ -990,15 +964,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C 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, pool, nSize, true); - - // txMinFee takes into account priority/fee deltas, so compare using - // nFees rather than nModifiedFees - if (fLimitFree && nFees < txMinFee) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, - strprintf("%d < %d", nFees, txMinFee)); - 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)); -- cgit v1.2.3 From 6aadc7557823b7673b8f661b3d41cf867e2936a3 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Thu, 3 Dec 2015 20:13:10 +0000 Subject: Disconnect on mempool requests from peers when over the upload limit. Mempool requests use a fair amount of bandwidth when the mempool is large, disconnecting peers using them follows the same logic as disconnecting peers fetching historical blocks. --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bfa71a7292..22e71c0c48 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4981,6 +4981,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "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 vtxid; -- cgit v1.2.3 From 7d0bf0bb4652fad052d5bf3ca3bf883754b46ead Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 27 Jul 2015 15:33:03 +0200 Subject: include the chaintip *blockIndex in the SyncTransaction signal - allows reducing of calls to main.cpp for getting the chaintip during transaction syncing - potentially allows reducing of cs_main locks --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bfa71a7292..be14dec1cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1234,7 +1234,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C } } - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, NULL, NULL); return true; } @@ -2391,7 +2391,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, pindexDelete->pprev, NULL); } return true; } @@ -2450,11 +2450,11 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, pindexNew, NULL); } // ... and about transactions that got confirmed: BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - SyncWithWallets(tx, pblock); + SyncWithWallets(tx, pindexNew, pblock); } int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; -- cgit v1.2.3 From 96918a2f0990a8207d7631b8de73af8ae5d24aeb Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 5 Dec 2015 17:45:44 +0800 Subject: Don't do mempool lookups for "mempool" command without a filter --- src/main.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 22e71c0c48..a0e996ae78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4994,12 +4994,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vector 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); vInv.clear(); -- cgit v1.2.3 From 9fc6ed6003da42f035309240c715ce0fd063ec03 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 7 Dec 2015 14:47:58 +0100 Subject: net: Fix sent reject messages for blocks and transactions Ever since we #5913 have been sending invalid reject messages for transactions and blocks. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a0e996ae78..84f737258c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4824,7 +4824,7 @@ 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("reject", strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); @@ -4954,7 +4954,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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("reject", strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) { LOCK(cs_main); -- cgit v1.2.3 From 9bbe71b641e2fc985daf127988a14a67c99da50a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 7 Dec 2015 15:31:32 +0100 Subject: net: Add and document network messages in protocol.h - Avoids string typos (by making the compiler check) - Makes it easier to grep for handling/generation of a certain message type - Refer directly to documentation by following the symbol in IDE - Move list of valid message types to protocol.cpp: protocol.cpp is a more appropriate place for this, and having the array there makes it easier to keep things consistent. --- src/main.cpp | 114 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 57 insertions(+), 57 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 84f737258c..d2e736d421 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4171,14 +4171,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam 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 - @@ -4187,7 +4187,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) - pfrom->PushMessage("tx", block.vtx[pair.first]); + pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]); } // else // no response @@ -4201,7 +4201,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // wait for other stuff first. vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); pfrom->hashContinue.SetNull(); } } @@ -4224,7 +4224,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; - pfrom->PushMessage("tx", ss); + pfrom->PushMessage(NetMsgType::TX, ss); pushed = true; } } @@ -4251,7 +4251,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // 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); } } @@ -4268,9 +4268,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!(nLocalServices & NODE_BLOOM) && - (strCommand == "filterload" || - strCommand == "filteradd" || - strCommand == "filterclear")) + (strCommand == NetMsgType::FILTERLOAD || + strCommand == NetMsgType::FILTERADD || + strCommand == NetMsgType::FILTERCLEAR)) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { Misbehaving(pfrom->GetId(), 100); @@ -4282,12 +4282,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - 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; } @@ -4301,7 +4301,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; @@ -4346,7 +4346,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) @@ -4369,7 +4369,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); @@ -4413,7 +4413,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)); @@ -4428,12 +4428,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // 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("sendheaders"); + pfrom->PushMessage(NetMsgType::SENDHEADERS); } } - else if (strCommand == "addr") + else if (strCommand == NetMsgType::ADDR) { vector vAddr; vRecv >> vAddr; @@ -4499,14 +4499,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; } - else if (strCommand == "sendheaders") + else if (strCommand == NetMsgType::SENDHEADERS) { LOCK(cs_main); State(pfrom->GetId())->fPreferHeaders = true; } - else if (strCommand == "inv") + else if (strCommand == NetMsgType::INV) { vector vInv; vRecv >> vInv; @@ -4547,7 +4547,7 @@ 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 (CanDirectFetch(chainparams.GetConsensus()) && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { @@ -4577,11 +4577,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 vInv; vRecv >> vInv; @@ -4602,7 +4602,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getblocks") + else if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; @@ -4646,7 +4646,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getheaders") + else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; @@ -4691,11 +4691,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); - pfrom->PushMessage("headers", vHeaders); + 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 @@ -4824,7 +4824,7 @@ 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, (unsigned char)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); @@ -4833,7 +4833,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - 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 headers; @@ -4881,7 +4881,7 @@ 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()); @@ -4926,7 +4926,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { - pfrom->PushMessage("getdata", vGetData); + pfrom->PushMessage(NetMsgType::GETDATA, vGetData); } } } @@ -4934,7 +4934,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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; @@ -4954,7 +4954,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS; if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes - pfrom->PushMessage("reject", strCommand, (unsigned char)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); @@ -4970,7 +4970,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 vAddr = addrman.GetAddr(); @@ -4979,7 +4979,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "mempool") + else if (strCommand == NetMsgType::MEMPOOL) { if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted) { @@ -5002,16 +5002,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } 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) { @@ -5028,12 +5028,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; @@ -5090,7 +5090,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; @@ -5121,7 +5121,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "filterload") + else if (strCommand == NetMsgType::FILTERLOAD) { CBloomFilter filter; vRecv >> filter; @@ -5140,7 +5140,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "filteradd") + else if (strCommand == NetMsgType::FILTERADD) { vector vData; vRecv >> vData; @@ -5160,7 +5160,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; @@ -5169,7 +5169,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "reject") + else if (strCommand == NetMsgType::REJECT) { if (fDebug) { try { @@ -5179,7 +5179,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; @@ -5287,7 +5287,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 @@ -5355,11 +5355,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); } } @@ -5401,14 +5401,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()); @@ -5428,7 +5428,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 @@ -5451,7 +5451,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) 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()); } } @@ -5551,7 +5551,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); } - pto->PushMessage("headers", vHeaders); + pto->PushMessage(NetMsgType::HEADERS, vHeaders); state.pindexBestHeaderSent = pBestIndex; } pto->vBlockHashesToAnnounce.clear(); @@ -5594,14 +5594,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInv.push_back(inv); if (vInv.size() >= 1000) { - pto->PushMessage("inv", vInv); + 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(); @@ -5670,7 +5670,7 @@ 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 { @@ -5680,7 +5680,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); + pto->PushMessage(NetMsgType::GETDATA, vGetData); } return true; -- cgit v1.2.3 From 5400ef6bcb9d243b2b21697775aa6491115420f3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 8 Apr 2015 11:20:00 -0700 Subject: Replace trickle nodes with per-node/message Poisson delays We used to have a trickle node, a node which was chosen in each iteration of the send loop that was privileged and allowed to send out queued up non-time critical messages. Since the removal of the fixed sleeps in the network code, this resulted in fast and attackable treatment of such broadcasts. This pull request changes the 3 remaining trickle use cases by random delays: * Local address broadcast (while also removing the the wiping of the seen filter) * Address relay * Inv relay (for transactions; blocks are always relayed immediately) The code is based on older commits by Patrick Strateman. --- src/main.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d2e736d421..41fc0b8098 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5326,7 +5326,7 @@ bool ProcessMessages(CNode* pfrom) } -bool SendMessages(CNode* pto, bool fSendTrickle) +bool SendMessages(CNode* pto) { const Consensus::Params& consensusParams = Params().GetConsensus(); { @@ -5368,28 +5368,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 vAddr; vAddr.reserve(pto->vAddrToSend.size()); BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) @@ -5563,8 +5552,13 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vector vInv; vector 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(1000, pto->vInventoryToSend.size())); vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { @@ -5604,7 +5598,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) 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 -- cgit v1.2.3 From fa24439ff3d8ab5b9efaf66ef4dae6713b88cb35 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 13 Dec 2015 17:58:29 +0100 Subject: Bump copyright headers to 2015 --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index cb3f8f39f8..dc891fecf3 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. -- cgit v1.2.3 From 64360f13044125fbb3cdcbe2e5e8f2bfb82a8b27 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 14 Dec 2015 13:23:45 +0100 Subject: Make max tip age an option instead of chainparam After discussion in #7164 I think this is better. Max tip age was introduced in #5987 to make it possible to run testnet-in-a-box. But associating this behavior with the testnet chain is wrong conceptually, as it is not needed in normal usage. Should aim to make testnet test the software as-is. Replace it with a (debug) option `-maxtipage`, which can be specified only in the specific case. --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d2e736d421..72cfb4ca39 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,9 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +/* If the tip is older than this (in seconds), the node is considered to be in initial block download. + */ +int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -1402,7 +1405,7 @@ bool IsInitialBlockDownload() if (lockIBDState) return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - chainParams.MaxTipAge()); + pindexBestHeader->GetBlockTime() < GetTime() - nMaxTipAge); if (!state) lockIBDState = true; return state; -- cgit v1.2.3 From 39a525c21fd1b34df63ab30868423b97b708ee49 Mon Sep 17 00:00:00 2001 From: ptschip Date: Sat, 5 Dec 2015 09:02:02 -0800 Subject: Do not download transactions during inital sync --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 41fc0b8098..8752484462 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4563,7 +4563,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { 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) + else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) pfrom->AskFor(inv); } -- cgit v1.2.3 From 5246180f168c9b761b6158b0725f5718239ba66c Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 15 Dec 2015 15:40:50 -0500 Subject: Mark blocks with too many sigops as failed --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 41fc0b8098..001da9c6c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3005,7 +3005,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; -- cgit v1.2.3 From a5a0831458d8290c1e7591cf32a529669b613d86 Mon Sep 17 00:00:00 2001 From: 21E14 <21xe14@gmail.com> Date: Tue, 29 Dec 2015 22:42:27 -0500 Subject: Double semicolon cleanup. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a43eef07b5..dbfb0c812f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,8 +84,8 @@ struct COrphanTx { CTransaction tx; NodeId fromPeer; }; -map mapOrphanTransactions GUARDED_BY(cs_main);; -map > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);; +map mapOrphanTransactions GUARDED_BY(cs_main); +map > mapOrphanTransactionsByPrev GUARDED_BY(cs_main); void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** -- cgit v1.2.3 From 7ef8f3c072a8750c72a3a1cdc727b5c1d173bac8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Jan 2016 16:50:31 +0100 Subject: Report non-mandatory script failures correctly --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a43eef07b5..0766b1458b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1653,9 +1653,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 -- cgit v1.2.3 From fd836153d5c99073b290edd74c3507a00231885d Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Sun, 3 Jan 2016 20:39:05 -0800 Subject: Improve CheckInputs() comment about sig verification --- src/main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a43eef07b5..60e96bbf16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1631,9 +1631,12 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - // Skip ECDSA signature verification when connecting blocks - // before the last block chain checkpoint. This is safe because block merkle hashes are - // still computed and checked, and any change will be caught at the next checkpoint. + // Skip ECDSA signature verification when connecting blocks before the + // last block chain checkpoint. Assuming the checkpoints are valid this + // is safe because block merkle hashes are still computed and checked, + // and any change will be caught at the next checkpoint. Of course, if + // the checkpoint is for a chain that's invalid due to false scriptSigs + // this optimisation would allow an invalid chain to be accepted. if (fScriptChecks) { for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; -- cgit v1.2.3 From 2dfeaa1ad03e7768fb28bfde7f929ac57dfff120 Mon Sep 17 00:00:00 2001 From: ptschip Date: Fri, 16 Oct 2015 18:18:16 -0700 Subject: limitfreerelay edge case bugfix: If a new transaction will cause limitfreerelay to be exceeded it should not be accepted into the memory pool and the byte counter should be updated only after the fact. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 41fc0b8098..08a95aff2b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1016,7 +1016,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C 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", DEFAULT_LIMITFREERELAY) * 10 * 1000) + if (dFreeCount + nSize >= 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; -- cgit v1.2.3 From b768108d9c0b83330572711aef1e569543130d5e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 21 Jan 2016 11:11:01 +0100 Subject: Add option `-permitrbf` to set transaction replacement policy Add a configuration option `-permitrbf` to set transaction replacement policy for the mempool. Enabling it will enable (opt-in) RBF, disabling it will refuse all conflicting transactions. --- src/main.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9870beecc7..8522b0d1ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -78,6 +78,7 @@ bool fAlerts = DEFAULT_ALERTS; /* If the tip is older than this (in seconds), the node is considered to be in initial block download. */ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; +bool fPermitReplacement = DEFAULT_PERMIT_REPLACEMENT; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -868,12 +869,15 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // unconfirmed ancestors anyway; doing otherwise is hopelessly // insecure. bool fReplacementOptOut = true; - BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) + if (fPermitReplacement) { - if (txin.nSequence < std::numeric_limits::max()-1) + BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) { - fReplacementOptOut = false; - break; + if (txin.nSequence < std::numeric_limits::max()-1) + { + fReplacementOptOut = false; + break; + } } } if (fReplacementOptOut) -- cgit v1.2.3 From 77b55a00ed4a3a3a22ec3e299539a1812a0bc605 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 28 Jan 2016 05:09:29 +0000 Subject: Rename permitrbf to replacebyfee "permit" is currently used to configure transaction filtering, whereas replacement is more to do with the memory pool state than the transaction itself. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c8ea62758b..392300d578 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -78,7 +78,7 @@ bool fAlerts = DEFAULT_ALERTS; /* If the tip is older than this (in seconds), the node is considered to be in initial block download. */ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; -bool fPermitReplacement = DEFAULT_PERMIT_REPLACEMENT; +bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -869,7 +869,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // unconfirmed ancestors anyway; doing otherwise is hopelessly // insecure. bool fReplacementOptOut = true; - if (fPermitReplacement) + if (fEnableReplacement) { BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) { -- cgit v1.2.3 From 325c725fb6205e38142914acb9ed1733d8482d46 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 25 Nov 2015 23:00:23 +0000 Subject: Add whitelistforcerelay to control forced relaying. Also renames whitelistalwaysrelay. Nodes relay all transactions from whitelisted peers, this gets in the way of some useful reasons for whitelisting peers-- for example, bypassing bandwidth limitations. The purpose of this forced relaying is for specialized gateway applications where a node is being used as a P2P connection filter and multiplexer, but where you don't want it getting in the way of (re-)broadcast. This change makes it configurable with whitelistforcerelay. --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c8ea62758b..235280784a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4495,8 +4495,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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)) + // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true + if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) fBlocksOnly = false; LOCK(cs_main); @@ -4675,8 +4675,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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))) + // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; @@ -4776,7 +4776,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, assert(recentRejects); recentRejects->insert(tx.GetHash()); - if (pfrom->fWhitelisted && GetBoolArg("-whitelistalwaysrelay", DEFAULT_WHITELISTALWAYSRELAY)) { + if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due // to policy, allowing the node to function as a gateway for -- cgit v1.2.3 From 93fc58c7426b5f3c68f2657626698846fb512ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 2 Dec 2015 03:13:47 +0100 Subject: Consensus: Remove calls to error() and FormatStateMessage() from some consensus code in main --- src/main.cpp | 83 +++++++++++++++++++++++------------------------------------- 1 file changed, 32 insertions(+), 51 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 06374cc1b6..8e35dc78df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -814,12 +814,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, std::vector& vHashTxnToUncache) { + const uint256 hash = tx.GetHash(); AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return false; + return error("%s: CheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) @@ -837,7 +838,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? - uint256 hash = tx.GetHash(); if (pool.exists(hash)) return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); @@ -1170,7 +1170,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // 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)) - return false; + return error("%s: CheckInputs: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -1964,7 +1964,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Check it again in case a previous version let a bad block in if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) - return false; + return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); @@ -2909,13 +2909,11 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f { // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), - REJECT_INVALID, "high-hash"); + return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); + return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); return true; } @@ -2937,15 +2935,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo bool mutated; uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); if (block.hashMerkleRoot != hashMerkleRoot2) - return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), - REJECT_INVALID, "bad-txnmrklroot", true); + return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch"); // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. if (mutated) - return state.DoS(100, error("CheckBlock(): duplicate transaction"), - REJECT_INVALID, "bad-txns-duplicate", true); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction"); } // All potential-corruption validation must be done before we do any @@ -2954,24 +2950,20 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock(): size limits failed"), - REJECT_INVALID, "bad-blk-length"); + return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), - REJECT_INVALID, "bad-cb-missing"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock(): more than one coinbase"), - REJECT_INVALID, "bad-cb-multiple"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase"); // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) - return error("CheckBlock(): CheckTransaction of %s failed with %s", - tx.GetHash().ToString(), - FormatStateMessage(state)); + return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), + strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2979,8 +2971,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), - REJECT_INVALID, "bad-blk-sigops"); + return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); if (fCheckPOW && fCheckMerkleRoot) block.fChecked = true; @@ -3007,28 +2998,17 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta const Consensus::Params& consensusParams = Params().GetConsensus(); // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) - return state.DoS(100, error("%s: incorrect proof of work", __func__), - REJECT_INVALID, "bad-diffbits"); + return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("%s: block's timestamp is too early", __func__), - REJECT_INVALID, "time-too-old"); - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) - return state.Invalid(error("%s: rejected nVersion=1 block", __func__), - REJECT_OBSOLETE, "bad-version"); + return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); - // 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__), - 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"); + // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: + for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades + if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) + return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(v%d)", version - 1), + strprintf("rejected nVersion=%d block", version - 1)); return true; } @@ -3045,7 +3025,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn ? 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"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); } } @@ -3056,7 +3036,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); } } @@ -3083,7 +3063,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } if (!CheckBlockHeader(block, state)) - return false; + return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -3099,7 +3079,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; + return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } if (pindex == NULL) pindex = AddToBlockIndex(block); @@ -3146,7 +3126,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } - return false; + return error("%s: %s", __func__, FormatStateMessage(state)); } int nHeight = pindex->nHeight; @@ -3197,7 +3177,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; if (!checked) { - return error("%s: CheckBlock FAILED", __func__); + return error("%s: CheckBlock FAILED %s", __func__, FormatStateMessage(state)); } // Store to disk @@ -3231,11 +3211,11 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; + return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) - return false; + return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) - return false; + return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) return false; assert(state.IsValid()); @@ -3565,7 +3545,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, 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)) - return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, + pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { CBlockUndo undo; -- cgit v1.2.3 From fa331db68bcc68e4c93fb45aaa30f911b0ecfe1a Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 23 Nov 2015 20:32:36 +0100 Subject: mempool: Replace maxFeeRate of 10000*minRelayTxFee with maxTxFee --- src/main.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8beff9769a..76ad969c61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,13 +75,11 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; -/* If the tip is older than this (in seconds), the node is considered to be in initial block download. - */ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fPermitReplacement = DEFAULT_PERMIT_REPLACEMENT; -/** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); +CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); @@ -1004,10 +1002,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C dFreeCount += nSize; } - if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) + if (fRejectAbsurdFee && nFees > maxTxFee) return state.Invalid(false, REJECT_HIGHFEE, "absurdly-high-fee", - strprintf("%d > %d", nFees, ::minRelayTxFee.GetFee(nSize) * 10000)); + strprintf("%d > %d", nFees, maxTxFee)); // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; -- cgit v1.2.3 From dbb89dc793b0fc19a0d0ac5c4ef08cc2760b06bf Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 17 Dec 2015 13:45:33 -0500 Subject: Eliminate unnecessary call to CheckBlock ProcessNewBlock would return failure early if CheckBlock failed, before calling AcceptBlock. AcceptBlock also calls CheckBlock, and upon failure would update mapBlockIndex to indicate that a block was failed. By returning early in ProcessNewBlock, we were not marking blocks that fail a check in CheckBlock as permanently failed, and thus would continue to re-request and reprocess them. --- src/main.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bc7b0daaf9..01d1024b08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3171,16 +3171,10 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp) { - // Preliminary checks - bool checked = CheckBlock(*pblock, state); - { LOCK(cs_main); bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; - if (!checked) { - return error("%s: CheckBlock FAILED %s", __func__, FormatStateMessage(state)); - } // Store to disk CBlockIndex *pindex = NULL; -- cgit v1.2.3 From fa79db2641182b47b4077345d8261d28c4a87bf0 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 20 Nov 2015 16:48:06 +0100 Subject: Move maxTxFee out of mempool Also, remove default values in CMerkleTx::AcceptToMemoryPool() --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4d16b9f9ac..9ed9d0e0b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -813,7 +813,7 @@ std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, + bool* pfMissingInputs, bool fOverrideMempoolLimit, CAmount nAbsurdFee, std::vector& vHashTxnToUncache) { const uint256 hash = tx.GetHash(); @@ -1002,10 +1002,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C dFreeCount += nSize; } - if (fRejectAbsurdFee && nFees > maxTxFee) + if (nAbsurdFee && nFees > nAbsurdFee) return state.Invalid(false, REJECT_HIGHFEE, "absurdly-high-fee", - strprintf("%d > %d", nFees, maxTxFee)); + strprintf("%d > %d", nFees, nAbsurdFee)); // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; @@ -1220,10 +1220,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) + bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); -- cgit v1.2.3 From fad6244879be8b9916e85cff4ecdb4377dd62ee2 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 3 Feb 2016 13:14:23 +0100 Subject: ATMP: make nAbsurdFee const --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9ed9d0e0b2..e4e4bc62b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -812,8 +812,8 @@ std::string FormatStateMessage(const CValidationState &state) state.GetRejectCode()); } -bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, CAmount nAbsurdFee, +bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee, std::vector& vHashTxnToUncache) { const uint256 hash = tx.GetHash(); -- cgit v1.2.3 From f3757a039196c804f252a4efba294e8f2b4d301d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 2 Dec 2015 03:15:42 +0100 Subject: Consensus: Decouple pow.cpp from util.h --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4d16b9f9ac..8bed2962fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2291,8 +2291,9 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + LogPrintf("%s: new best=%s height=%d bits=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nBits, + log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); -- cgit v1.2.3 From 301bc7bc7e83f4c268c1722558b07dbb5b55fa92 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 8 Feb 2016 15:32:29 -0500 Subject: Update nQueuedValidatedHeaders after peer disconnection --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9845234084..94071d3816 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -332,8 +332,10 @@ void FinalizeNode(NodeId nodeid) { AddressCurrentlyConnected(state->address); } - BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) { + nQueuedValidatedHeaders -= entry.fValidatedHeaders; mapBlocksInFlight.erase(entry.hash); + } EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; -- cgit v1.2.3 From e4eebb604e19f67b0c7a483b1ded1229d75ecdd3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 10 Feb 2016 15:47:06 +0100 Subject: Update the wallet best block marker when pruning --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 20539c1ba7..3ad2979b6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2262,7 +2262,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return AbortNode(state, "Failed to write to coin database"); nLastFlush = nNow; } - if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { + if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { // Update best block in wallet (so we can detect restored wallets). GetMainSignals().SetBestChain(chainActive.GetLocator()); nLastSetChain = nNow; -- cgit v1.2.3 From c6c2f0fd782ccf607027414012f45c8f48561a30 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 7 Dec 2015 15:44:16 -0500 Subject: Implement SequenceLocks functions SequenceLocks functions are used to evaluate sequence lock times or heights per BIP 68. The majority of this code is copied from maaku in #6312 Further credit: btcdrak, sipa, NicolasDorier --- src/main.cpp | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a0e996ae78..d9bf3bd757 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -667,9 +667,10 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (!txin.IsFinal()) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL)) return false; + } return true; } @@ -705,6 +706,128 @@ bool CheckFinalTx(const CTransaction &tx, int flags) return IsFinalTx(tx, nBlockHeight, nBlockTime); } +/** + * Calculates the block height and previous block's median time past at + * which the transaction will be considered final in the context of BIP 68. + * Also removes from the vector of input heights any entries which did not + * correspond to sequence locked inputs as they do not affect the calculation. + */ +static std::pair CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) +{ + assert(prevHeights->size() == tx.vin.size()); + + // Will be set to the equivalent height- and time-based nLockTime + // values that would be necessary to satisfy all relative lock- + // time constraints given our view of block chain history. + // The semantics of nLockTime are the last invalid height/time, so + // use -1 to have the effect of any height or time being valid. + int nMinHeight = -1; + int64_t nMinTime = -1; + + // tx.nVersion is signed integer so requires cast to unsigned otherwise + // we would be doing a signed comparison and half the range of nVersion + // wouldn't support BIP 68. + bool fEnforceBIP68 = static_cast(tx.nVersion) >= 2 + && flags & LOCKTIME_VERIFY_SEQUENCE; + + // Do not enforce sequence numbers as a relative lock time + // unless we have been instructed to + if (!fEnforceBIP68) { + return std::make_pair(nMinHeight, nMinTime); + } + + for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { + const CTxIn& txin = tx.vin[txinIndex]; + + // Sequence numbers with the most significant bit set are not + // treated as relative lock-times, nor are they given any + // consensus-enforced meaning at this point. + if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) { + // The height of this input is not relevant for sequence locks + (*prevHeights)[txinIndex] = 0; + continue; + } + + int nCoinHeight = (*prevHeights)[txinIndex]; + + if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) { + int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast(); + // NOTE: Subtract 1 to maintain nLockTime semantics + // BIP 68 relative lock times have the semantics of calculating + // the first block or time at which the transaction would be + // valid. When calculating the effective block time or height + // for the entire transaction, we switch to using the + // semantics of nLockTime which is the last invalid block + // time or height. Thus we subtract 1 from the calculated + // time or height. + + // Time-based relative lock-times are measured from the + // smallest allowed timestamp of the block containing the + // txout being spent, which is the median time past of the + // block prior. + nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1); + } else { + nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1); + } + } + + return std::make_pair(nMinHeight, nMinTime); +} + +static bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair lockPair) +{ + assert(block.pprev); + int64_t nBlockTime = block.pprev->GetMedianTimePast(); + if (lockPair.first >= block.nHeight || lockPair.second >= nBlockTime) + return false; + + return true; +} + +bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) +{ + return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block)); +} + +bool CheckSequenceLocks(const CTransaction &tx, int flags) +{ + AssertLockHeld(cs_main); + AssertLockHeld(mempool.cs); + + CBlockIndex* tip = chainActive.Tip(); + CBlockIndex index; + index.pprev = tip; + // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate + // height based locks because when SequenceLocks() 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 + // SequenceLocks() with one more than chainActive.Height(). + index.nHeight = tip->nHeight + 1; + + // pcoinsTip contains the UTXO set for chainActive.Tip() + CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); + std::vector prevheights; + prevheights.resize(tx.vin.size()); + for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { + const CTxIn& txin = tx.vin[txinIndex]; + CCoins coins; + if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) { + return error("%s: Missing input", __func__); + } + if (coins.nHeight == MEMPOOL_HEIGHT) { + // Assume all mempool transaction confirm in the next block + prevheights[txinIndex] = tip->nHeight + 1; + } else { + prevheights[txinIndex] = coins.nHeight; + } + } + + std::pair lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); + return EvaluateSequenceLocks(index, lockPair); +} + + unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; @@ -949,6 +1072,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); + + // Only accept BIP68 sequence locked 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. + // Must keep pool.cs for this unless we change CheckSequenceLocks to take a + // CoinsViewCache instead of create its own + if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); } // Check for non-standard pay-to-script-hash in inputs @@ -2075,6 +2206,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + std::vector prevheights; + int nLockTimeFlags = 0; CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; @@ -2098,6 +2231,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); + // Check that transaction is BIP68 final + // BIP68 lock checks (as opposed to nLockTime checks) must + // be in ConnectBlock because they require the UTXO set + prevheights.resize(tx.vin.size()); + for (size_t j = 0; j < tx.vin.size(); j++) { + prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight; + } + + if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { + return state.DoS(100, error("ConnectBlock(): contains a non-BIP68-final transaction", __func__), + REJECT_INVALID, "bad-txns-nonfinal"); + } + if (fStrictPayToScriptHash) { // Add in sigops done by pay-to-script-hash inputs; -- cgit v1.2.3 From 1fb91b3496f2f07bbace1f9f8e716f7f62d889e6 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 11 Feb 2016 06:35:25 +0000 Subject: Common argument defaults for NODE_BLOOM stuff and -wallet --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index cb3f8f39f8..12c349a650 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4276,7 +4276,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion >= NO_BLOOM_VERSION) { Misbehaving(pfrom->GetId(), 100); return false; - } else if (GetBoolArg("-enforcenodebloom", false)) { + } else if (GetBoolArg("-enforcenodebloom", DEFAULT_ENFORCENODEBLOOM)) { pfrom->fDisconnect = true; return false; } -- cgit v1.2.3 From b043c4b746c8199ce948aa5e8b186e0d1a61ad68 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 11 Feb 2016 15:34:04 -0500 Subject: fix sdaftuar's nits again it boggles the mind why these nits can't be delivered on a more timely basis --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d9bf3bd757..7d75e2ea65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -799,10 +799,10 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags) index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate // height based locks because when SequenceLocks() 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 - // SequenceLocks() with one more than chainActive.Height(). + // ConnectBlock(), 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 use one more than chainActive.Height() index.nHeight = tip->nHeight + 1; // pcoinsTip contains the UTXO set for chainActive.Tip() @@ -2240,7 +2240,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { - return state.DoS(100, error("ConnectBlock(): contains a non-BIP68-final transaction", __func__), + return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } -- cgit v1.2.3 From 8aa722609d7e736b3a3763e15b552795b94f0e9b Mon Sep 17 00:00:00 2001 From: jmacwhyte Date: Thu, 11 Feb 2016 18:02:46 -0800 Subject: Fix IsInitialBlockDownload to play nice with testnet --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 3ad2979b6b..9e478b6823 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1384,7 +1384,7 @@ bool IsInitialBlockDownload() if (lockIBDState) return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - nMaxTipAge); + std::max(chainActive.Tip()->GetBlockTime(), pindexBestHeader->GetBlockTime()) < GetTime() - nMaxTipAge); if (!state) lockIBDState = true; return state; -- cgit v1.2.3 From 37767fd46f673a06864df6e14d3030622b1cb2c9 Mon Sep 17 00:00:00 2001 From: jloughry Date: Fri, 12 Feb 2016 11:35:32 -0700 Subject: fix spelling of advertise in src and doc --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6398fdad9a..23429ea2cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -376,7 +376,7 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } -/** Check whether the last unknown block a peer advertized is not yet known. */ +/** Check whether the last unknown block a peer advertised is not yet known. */ void ProcessBlockAvailability(NodeId nodeid) { CNodeState *state = State(nodeid); assert(state != NULL); @@ -4456,11 +4456,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()); + LogPrintf("ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(pfrom->addrLocal); - LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); + LogPrintf("ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr); } } @@ -5469,7 +5469,7 @@ bool SendMessages(CNode* pto) // Address refresh broadcast int64_t nNow = GetTimeMicros(); if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) { - AdvertizeLocal(pto); + AdvertiseLocal(pto); pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); } -- cgit v1.2.3 From 8fc81e098362130218f256cd6b60e81227d39db9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 24 Feb 2016 18:34:37 +0100 Subject: mempool: Reduce ERROR logging for mempool rejects Continues "Make logging for validation optional" from #6519. The idea there was to remove all ERROR logging of rejected transaction, and move it to one message in the class 'mempoolrej' which logs the state message (and debug info). The superfluous ERRORs in the log "terrify" users, see for example issue #5794. Unfortunately a lot of new logging was introduced in #6871 (RBF) and #7287 (misc refactoring). This pull updates that new code. --- src/main.cpp | 57 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index babdff54ef..f3dc3f2919 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -947,7 +947,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("%s: CheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + return false; // state filled in by CheckTransaction // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) @@ -1160,10 +1160,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C const uint256 &hashAncestor = ancestorIt->GetTx().GetHash(); if (setConflicts.count(hashAncestor)) { - return state.DoS(10, error("AcceptToMemoryPool: %s spends conflicting transaction %s", + return state.DoS(10, false, + REJECT_INVALID, "bad-txns-spends-conflicting-tx", false, + strprintf("%s spends conflicting transaction %s", hash.ToString(), - hashAncestor.ToString()), - REJECT_INVALID, "bad-txns-spends-conflicting-tx"); + hashAncestor.ToString())); } } @@ -1200,11 +1201,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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", + return state.DoS(0, false, + REJECT_NONSTANDARD, "too many potential replacements", false, + strprintf("too many potential replacements: rejecting replacement %s; cannot replace tx %s with untracked descendants", hash.ToString(), - mi->GetTx().GetHash().ToString()), - REJECT_NONSTANDARD, "too many potential replacements"); + mi->GetTx().GetHash().ToString())); } // Don't allow the replacement to reduce the feerate of the @@ -1226,12 +1227,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); if (newFeeRate <= oldFeeRate) { - return state.DoS(0, - error("AcceptToMemoryPool: rejecting replacement %s; new feerate %s <= old feerate %s", + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", hash.ToString(), newFeeRate.ToString(), - oldFeeRate.ToString()), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + oldFeeRate.ToString())); } BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin) @@ -1255,12 +1256,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C nConflictingSize += it->GetTxSize(); } } else { - return state.DoS(0, - error("AcceptToMemoryPool: rejecting replacement %s; too many potential replacements (%d > %d)\n", + return state.DoS(0, false, + REJECT_NONSTANDARD, "too many potential replacements", false, + strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", hash.ToString(), nConflictingCount, - maxDescendantsToVisit), - REJECT_NONSTANDARD, "too many potential replacements"); + maxDescendantsToVisit)); } for (unsigned int j = 0; j < tx.vin.size(); j++) @@ -1275,9 +1276,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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"); + return state.DoS(0, false, + REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false, + strprintf("replacement %s adds unconfirmed input, idx %d", + hash.ToString(), j)); } } @@ -1286,9 +1288,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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"); + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", + hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees))); } // Finally in addition to paying more fees than the conflicts the @@ -1296,19 +1299,19 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C 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", + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", hash.ToString(), FormatMoney(nDeltaFees), - FormatMoney(::minRelayTxFee.GetFee(nSize))), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + FormatMoney(::minRelayTxFee.GetFee(nSize)))); } } // 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)) - return error("%s: CheckInputs: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + return false; // state filled in by CheckInputs // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause -- cgit v1.2.3 From 9988554fc76250b1f695c616341c8dd3278c928b Mon Sep 17 00:00:00 2001 From: R E Broadley Date: Fri, 26 Jun 2015 22:38:07 +0300 Subject: No "Unknown command" for getaddr command. --- src/main.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index babdff54ef..378c454cd2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5064,13 +5064,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - // This asymmetric behavior for inbound and outbound connections was introduced - // to prevent a fingerprinting attack: an attacker can send specific fake addresses - // to users' AddrMan and later request them by sending getaddr messages. - // Making nodes which are behind NAT and can only make outgoing connections ignore - // the getaddr message mitigates the attack. - else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound)) + else if (strCommand == NetMsgType::GETADDR) { + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making nodes which are behind NAT and can only make outgoing connections ignore + // the getaddr message mitigates the attack. + if (!pfrom->fInbound) { + LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id); + return true; + } + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) -- cgit v1.2.3 From 5de2baa138cda501038a4558bc169b2cfe5b7d6b Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 19 Oct 2015 12:43:38 -0400 Subject: Rename CTxMemPool::remove -> removeRecursive remove is no longer called non-recursively, so simplify the logic and eliminate an unnecessary parameter --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 027a36394c..d5254806ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2502,7 +2502,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons list removed; CValidationState stateDummy; if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { - mempool.remove(tx, removed, true); + mempool.removeRecursive(tx, removed); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); } -- cgit v1.2.3 From 76a76321d2f36992178ddaaf4d023c5e33c14fbf Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 21 Oct 2015 10:18:24 -0400 Subject: Remove work limit in UpdateForDescendants() The work limit served to prevent the descendant walking algorithm from doing too much work by marking the parent transaction as dirty. However to implement ancestor tracking, it's not possible to similarly mark those descendant transactions as dirty without having to calculate them to begin with. This commit removes the work limit altogether. With appropriate chain limits (-limitdescendantcount) the concern about doing too much work inside this function should be mitigated. --- src/main.cpp | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d5254806ad..7a69d96668 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1194,20 +1194,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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, false, - REJECT_NONSTANDARD, "too many potential replacements", false, - strprintf("too many potential replacements: rejecting replacement %s; cannot replace tx %s with untracked descendants", - hash.ToString(), - mi->GetTx().GetHash().ToString())); - } - // Don't allow the replacement to reduce the feerate of the // mempool. // -- cgit v1.2.3 From 72abd2ce3c5ad8157d3a993693df1919a6ad79c3 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 19 Oct 2015 10:54:28 -0400 Subject: Add ancestor tracking to mempool This implements caching of ancestor state to each mempool entry, similar to descendant tracking, but also including caching sigops-with-ancestors (as that metric will be helpful to future code that implements better transaction selection in CreatenewBlock). --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7a69d96668..2ae560e01b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1323,7 +1323,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C FormatMoney(nModifiedFees - nConflictingFees), (int)nSize - (int)nConflictingSize); } - pool.RemoveStaged(allConflicting); + pool.RemoveStaged(allConflicting, false); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); -- cgit v1.2.3 From 6851107b3a52ec869e5e3a2cb4eb02d6c743b8e5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 15 Feb 2016 05:13:27 +0100 Subject: BIP9 Implementation Inspired by former implementations by Eric Lombrozo and Rusty Russell, and based on code by Jorge Timon. --- src/main.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 027a36394c..45e5881897 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,7 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" #include "validationinterface.h" +#include "versionbits.h" #include @@ -2083,6 +2084,51 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } } +// Protected by cs_main +static VersionBitsCache versionbitscache; + +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { + nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + } + } + + return nVersion; +} + +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + int bit; + +public: + WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + + int64_t BeginTime(const Consensus::Params& params) const { return 0; } + int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits::max(); } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> bit) & 1) != 0 && + ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; + } +}; + +// Protected by cs_main +static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; + static int64_t nTimeCheck = 0; static int64_t nTimeForks = 0; static int64_t nTimeVerify = 0; @@ -2452,24 +2498,42 @@ void static UpdateTip(CBlockIndex *pindexNew) { // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) + if (!IsInitialBlockDownload()) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { + WarningBitsConditionChecker checker(bit); + ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]); + if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) { + if (state == THRESHOLD_ACTIVE) { + strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } + } else { + LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + } + } + } for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; + strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } } } } @@ -3763,6 +3827,10 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); + versionbitscache.Clear(); + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + warningcache[b].clear(); + } BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; -- cgit v1.2.3 From d23f6c6a0d2dc8a3f5f159faf7a40157259a8f8f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 3 Mar 2016 21:00:03 +0100 Subject: Softfork status report in RPC --- src/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 45e5881897..e4567a8974 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5859,7 +5859,11 @@ bool SendMessages(CNode* pto) return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } - +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} class CMainCleanup { -- cgit v1.2.3 From 982670c333aff6d5660c18ed00931df764733529 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 4 Dec 2015 15:01:22 -0500 Subject: Add LockPoints Obtain LockPoints to store in CTxMemPoolEntry and during a reorg, evaluate whether they are still valid and if not, recalculate them. --- src/main.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 19 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index babdff54ef..525d9902b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -794,7 +794,25 @@ bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeig return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block)); } -bool CheckSequenceLocks(const CTransaction &tx, int flags) +bool TestLockPointValidity(const LockPoints* lp) +{ + AssertLockHeld(cs_main); + assert(lp); + // If there are relative lock times then the maxInputBlock will be set + // If there are no relative lock times, the LockPoints don't depend on the chain + if (lp->maxInputBlock) { + // Check whether chainActive is an extension of the block at which the LockPoints + // calculation was valid. If not LockPoints are no longer valid + if (!chainActive.Contains(lp->maxInputBlock)) { + return false; + } + } + + // LockPoints still valid + return true; +} + +bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints) { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); @@ -810,25 +828,57 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags) // *next* block, we need to use one more than chainActive.Height() index.nHeight = tip->nHeight + 1; - // pcoinsTip contains the UTXO set for chainActive.Tip() - CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); - std::vector prevheights; - prevheights.resize(tx.vin.size()); - for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { - const CTxIn& txin = tx.vin[txinIndex]; - CCoins coins; - if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) { - return error("%s: Missing input", __func__); + std::pair lockPair; + if (useExistingLockPoints) { + assert(lp); + lockPair.first = lp->height; + lockPair.second = lp->time; + } + else { + // pcoinsTip contains the UTXO set for chainActive.Tip() + CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); + std::vector prevheights; + prevheights.resize(tx.vin.size()); + for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { + const CTxIn& txin = tx.vin[txinIndex]; + CCoins coins; + if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) { + return error("%s: Missing input", __func__); + } + if (coins.nHeight == MEMPOOL_HEIGHT) { + // Assume all mempool transaction confirm in the next block + prevheights[txinIndex] = tip->nHeight + 1; + } else { + prevheights[txinIndex] = coins.nHeight; + } } - if (coins.nHeight == MEMPOOL_HEIGHT) { - // Assume all mempool transaction confirm in the next block - prevheights[txinIndex] = tip->nHeight + 1; - } else { - prevheights[txinIndex] = coins.nHeight; + lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); + if (lp) { + lp->height = lockPair.first; + lp->time = lockPair.second; + // Also store the hash of the block with the highest height of + // all the blocks which have sequence locked prevouts. + // This hash needs to still be on the chain + // for these LockPoint calculations to be valid + // Note: It is impossible to correctly calculate a maxInputBlock + // if any of the sequence locked inputs depend on unconfirmed txs, + // except in the special case where the relative lock time/height + // is 0, which is equivalent to no sequence lock. Since we assume + // input height of tip+1 for mempool txs and test the resulting + // lockPair from CalculateSequenceLocks against tip+1. We know + // EvaluateSequenceLocks will fail if there was a non-zero sequence + // lock on a mempool input, so we can use the return value of + // CheckSequenceLocks to indicate the LockPoints validity + int maxInputHeight = 0; + BOOST_FOREACH(int height, prevheights) { + // Can ignore mempool inputs since we'll fail if they had non-zero locks + if (height != tip->nHeight+1) { + maxInputHeight = std::max(maxInputHeight, height); + } + } + lp->maxInputBlock = tip->GetAncestor(maxInputHeight); } } - - std::pair lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); return EvaluateSequenceLocks(index, lockPair); } @@ -1017,6 +1067,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CCoinsViewCache view(&dummy); CAmount nValueIn = 0; + LockPoints lp; { LOCK(pool.cs); CCoinsViewMemPool viewMemPool(pcoinsTip, pool); @@ -1060,7 +1111,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // be mined yet. // Must keep pool.cs for this unless we change CheckSequenceLocks to take a // CoinsViewCache instead of create its own - if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); } @@ -1092,7 +1143,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp); unsigned int nSize = entry.GetTxSize(); // Check that the transaction doesn't have an excessive number of -- cgit v1.2.3 From 65751a3cf2421a9419172949cad9dc49b7383551 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 20 Feb 2016 23:37:13 +0100 Subject: Add CHECKSEQUENCEVERIFY softfork through BIP9 --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 1bc88326b6..cfa69817b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2262,6 +2262,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } + // Start enforcing CHECKSEQUENCEVERIFY using versionbits logic. + if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { + flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; + } + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); -- cgit v1.2.3 From 478fba6d5213a3f1ffeca5feeacf28aaf6844fd6 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 16 Feb 2016 16:33:31 +0000 Subject: Soft fork logic for BIP113 --- src/main.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index cfa69817b6..857bf218ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3276,12 +3276,18 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); + // Start enforcing BIP113 (Median Time Past) using versionbits logic. + int nLockTimeFlags = 0; + if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { + nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST; + } + + int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) + ? pindexPrev->GetMedianTimePast() + : block.GetBlockTime(); + // Check that all transactions are finalized 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, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); } -- cgit v1.2.3 From 02c243580295a7f1c0298fcd9afc2e76b607e724 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 16 Feb 2016 16:37:43 +0000 Subject: Soft fork logic for BIP68 --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 857bf218ba..f74cb30574 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2262,9 +2262,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } - // Start enforcing CHECKSEQUENCEVERIFY using versionbits logic. + // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. + int nLockTimeFlags = 0; if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; + nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; @@ -2275,7 +2277,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); std::vector prevheights; - int nLockTimeFlags = 0; CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; -- cgit v1.2.3 From bbb9d1d1231099122a5b0ad5dd86f3f93ce22724 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Mon, 7 Mar 2016 19:44:09 +0000 Subject: Remove p2p alert handling --- src/main.cpp | 62 ------------------------------------------------------------ 1 file changed, 62 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 1bc88326b6..d5fb047aa0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include "main.h" #include "addrman.h" -#include "alert.h" #include "arith_uint256.h" #include "chainparams.h" #include "checkpoints.h" @@ -4213,14 +4212,8 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) assert(nNodes == forward.size()); } -////////////////////////////////////////////////////////////////////////////// -// -// CAlert -// - std::string GetWarnings(const std::string& strFor) { - int nPriority = 0; string strStatusBar; string strRPC; string strGUI; @@ -4236,37 +4229,20 @@ std::string GetWarnings(const std::string& strFor) // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { - nPriority = 1000; 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."; 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."; 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 - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.AppliesToMe() && alert.nPriority > nPriority) - { - nPriority = alert.nPriority; - strStatusBar = strGUI = alert.strStatusBar; - } - } - } - if (strFor == "gui") return strGUI; else if (strFor == "statusbar") @@ -4588,13 +4564,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - // Relay alerts - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); - } - pfrom->fSuccessfullyConnected = true; string remoteAddr; @@ -5302,37 +5271,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (fAlerts && strCommand == NetMsgType::ALERT) - { - CAlert alert; - vRecv >> alert; - - uint256 alertHash = alert.GetHash(); - if (pfrom->setKnown.count(alertHash) == 0) - { - if (alert.ProcessAlert(chainparams.AlertKey())) - { - // Relay - pfrom->setKnown.insert(alertHash); - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); - } - } - else { - // Small DoS penalty so peers that send us lots of - // duplicate/expired/invalid-signature/whatever alerts - // eventually get banned. - // This isn't a Misbehaving(100) (immediate ban) because the - // peer might be an older or different implementation with - // a different signature key, etc. - Misbehaving(pfrom->GetId(), 10); - } - } - } - - else if (strCommand == NetMsgType::FILTERLOAD) { CBloomFilter filter; -- cgit v1.2.3 From 92066344fdc3eb3071cb6fc0ce6a41d79c2dda53 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Sun, 6 Mar 2016 10:07:25 +0000 Subject: Update alert notification and GUI --- src/main.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d5fb047aa0..6c193a25e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1565,6 +1565,26 @@ bool fLargeWorkForkFound = false; bool fLargeWorkInvalidChainFound = false; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; +static void AlertNotify(const std::string& strMessage, bool fThread) +{ + uiInterface.NotifyAlertChanged(); + std::string strCmd = GetArg("-alertnotify", ""); + if (strCmd.empty()) return; + + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strMessage); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + if (fThread) + boost::thread t(runCommand, strCmd); // thread runs free + else + runCommand(strCmd); +} + void CheckForkWarningConditions() { AssertLockHeld(cs_main); @@ -1584,7 +1604,7 @@ void CheckForkWarningConditions() { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + pindexBestForkBase->phashBlock->ToString() + std::string("'"); - CAlert::Notify(warning, true); + AlertNotify(warning, true); } if (pindexBestForkTip && pindexBestForkBase) { @@ -2115,7 +2135,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const if (!strWarning.empty()) { strMiscWarning = strWarning; - CAlert::Notify(strWarning, true); + AlertNotify(strWarning, true); lastAlertTime = now; } } @@ -2545,7 +2565,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { if (state == THRESHOLD_ACTIVE) { strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); if (!fWarned) { - CAlert::Notify(strMiscWarning, true); + AlertNotify(strMiscWarning, true); fWarned = true; } } else { @@ -2567,7 +2587,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); if (!fWarned) { - CAlert::Notify(strMiscWarning, true); + AlertNotify(strMiscWarning, true); fWarned = true; } } -- cgit v1.2.3 From 01fdfeffc4515ea43748230139a3bcee2eec3865 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Sun, 6 Mar 2016 11:15:20 +0000 Subject: Remove `-alerts` option --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6c193a25e3..e78ce4cdf7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,7 +74,6 @@ bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; -bool fAlerts = DEFAULT_ALERTS; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; -- cgit v1.2.3 From c90036f6645dea7c19e033c11240567371407017 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Thu, 17 Mar 2016 17:23:33 -0700 Subject: Always disconnect old nodes which request filtered connections. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 027a36394c..d74c88d235 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4377,7 +4377,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion >= NO_BLOOM_VERSION) { Misbehaving(pfrom->GetId(), 100); return false; - } else if (GetBoolArg("-enforcenodebloom", DEFAULT_ENFORCENODEBLOOM)) { + } else { pfrom->fDisconnect = true; return false; } -- cgit v1.2.3 From 9e072a6e66efbda7d39bf61eded21d2b324323be Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 12 Feb 2016 15:57:15 -0500 Subject: Implement "feefilter" P2P message. The "feefilter" p2p message is used to inform other nodes of your mempool min fee which is the feerate that any new transaction must meet to be accepted to your mempool. This will allow them to filter invs to you according to this feerate. --- src/main.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 13 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fc443cfb72..36189f4ffc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,10 +17,12 @@ #include "init.h" #include "merkleblock.h" #include "net.h" +#include "policy/fees.h" #include "policy/policy.h" #include "pow.h" #include "primitives/block.h" #include "primitives/transaction.h" +#include "random.h" #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" @@ -81,6 +83,7 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); +FeeFilterRounder filterRounder(::minRelayTxFee); struct COrphanTx { CTransaction tx; @@ -987,7 +990,7 @@ std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee, + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) { const uint256 hash = tx.GetHash(); @@ -1144,6 +1147,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp); unsigned int nSize = entry.GetTxSize(); + if (txFeeRate) { + *txFeeRate = CFeeRate(nFees, nSize); + } // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1392,10 +1398,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -2620,7 +2626,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, NULL, true)) { mempool.removeRecursive(tx, removed); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); @@ -4916,10 +4922,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) - { + CFeeRate txFeeRate = CFeeRate(0); + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) { mempool.check(pcoinsTip); - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); vWorkQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", @@ -4950,10 +4956,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) - { + CFeeRate orphanFeeRate = CFeeRate(0); + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); + RelayTransaction(orphanTx, orphanFeeRate); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); } @@ -5006,7 +5012,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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); + RelayTransaction(tx, txFeeRate); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state)); } @@ -5200,6 +5206,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue; } + if (pfrom->minFeeFilter) { + CFeeRate feeRate; + mempool.lookupFeeRate(hash, feeRate); + LOCK(pfrom->cs_feeFilter); + if (feeRate.GetFeePerK() < pfrom->minFeeFilter) + continue; + } vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage(NetMsgType::INV, vInv); @@ -5362,8 +5375,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else - { + else if (strCommand == NetMsgType::FEEFILTER) { + CAmount newFeeFilter = 0; + vRecv >> newFeeFilter; + if (MoneyRange(newFeeFilter)) { + { + LOCK(pfrom->cs_feeFilter); + pfrom->minFeeFilter = newFeeFilter; + } + LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id); + } + } + + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } @@ -5845,6 +5869,29 @@ bool SendMessages(CNode* pto) if (!vGetData.empty()) pto->PushMessage(NetMsgType::GETDATA, vGetData); + // + // Message: feefilter + // + // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay + if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { + CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + int64_t timeNow = GetTimeMicros(); + if (timeNow > pto->nextSendTimeFeeFilter) { + CAmount filterToSend = filterRounder.round(currentFilter); + if (filterToSend != pto->lastSentFeeFilter) { + pto->PushMessage(NetMsgType::FEEFILTER, filterToSend); + pto->lastSentFeeFilter = filterToSend; + } + pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); + } + // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY + // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. + else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter && + (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) { + pto->nextSendTimeFeeFilter = timeNow + (insecure_rand() % MAX_FEEFILTER_CHANGE_DELAY) * 1000000; + } + } } return true; } -- cgit v1.2.3 From eff736e55e84441f9509285e83a1a6ee9a0c6e69 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 29 Mar 2016 16:46:20 +0200 Subject: Reformat version in UpdateTip and other messages Also remove the hardly-readable nBits from UpdateTip's log message. --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 36189f4ffc..d22b4c80e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2549,8 +2549,8 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d bits=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nBits, + LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)\n", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); @@ -3284,8 +3284,8 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) - return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(v%d)", version - 1), - strprintf("rejected nVersion=%d block", version - 1)); + return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", version - 1), + strprintf("rejected nVersion=0x%08x block", version - 1)); return true; } -- cgit v1.2.3 From fada0c422c081ba53a324aaf63c0a750cb56498e Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 3 Apr 2016 11:49:36 +0200 Subject: [doc] Fix doxygen comments for members --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2c0b3bfee7..b68c6affae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,10 +194,10 @@ namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; - CBlockIndex *pindex; //! Optional. - int64_t nTime; //! Time of "getdata" request in microseconds. - bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. - int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) + CBlockIndex* pindex; //!< Optional. + int64_t nTime; //!< Time of "getdata" request in microseconds. + bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. + int64_t nTimeDisconnect; //!< The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; -- cgit v1.2.3 From f20d42ed2b1b8de9c41525f96c40b172ab82ec99 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 3 Apr 2016 12:08:14 +0200 Subject: UpdateTip: log only one line at most per block Avoid logging two or more lines per block in UpdateTip by adding the warning into the UpdateTip log message. --- src/main.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2c0b3bfee7..1044c3556b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -2555,16 +2556,10 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, - log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - cvBlockChange.notify_all(); - // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; + std::vector warningMessages; if (!IsInitialBlockDownload()) { int nUpgraded = 0; @@ -2580,10 +2575,11 @@ void static UpdateTip(CBlockIndex *pindexNew) { fWarned = true; } } else { - LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + warningMessages.push_back(strprintf("unknown new rules are about to activate (versionbit %i)", bit)); } } } + // Check the version of the last 100 blocks to see if we need to upgrade: for (int i = 0; i < 100 && pindex != NULL; i++) { int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); @@ -2592,7 +2588,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); + warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded)); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: @@ -2603,6 +2599,15 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } } + LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, + log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + if (!warningMessages.empty()) + LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); + LogPrintf("\n"); + } /** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ -- cgit v1.2.3 From 07398e8e9d2ef807e63abd0978a6e98549bdf271 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 6 Apr 2016 10:27:51 +0200 Subject: init: allow shutdown during 'Activating best chain...' Two-line patch to make it possible to shut down bitcoind cleanly during the initial ActivateBestChain. Fixes #6459 (among other complaints). To reproduce: - shutdown bitcoind - copy chainstate - start bitcoind - let the chain sync a bit - shutdown bitcoind - copy back old chainstate - start bitcoind - bitcoind will catch up with all blocks during Init() (the `boost::this_thread::interruption_point` / `ShutdownRequested()` dance is ugly, this should be refactored all over bitcoind at some point when moving from boost::threads to c++11 threads, but it works...) --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b68c6affae..c1137c9b1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2877,6 +2877,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, CBlockIndex *pindexMostWork = NULL; do { boost::this_thread::interruption_point(); + if (ShutdownRequested()) + break; CBlockIndex *pindexNewTip = NULL; const CBlockIndex *pindexFork; -- cgit v1.2.3 From bf477bcc794360c145156c9e8f155e0215f70dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 6 Apr 2016 16:00:25 +0200 Subject: Trivial: Globals: Explicitly pass const CChainParams& to ProcessMessage() --- src/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b68c6affae..4e94ab7809 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4475,9 +4475,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams) { - const CChainParams& chainparams = Params(); RandAddSeedPerfmon(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) @@ -5487,7 +5486,7 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); + fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams); boost::this_thread::interruption_point(); } catch (const std::ios_base::failure& e) -- cgit v1.2.3 From 2d1d6581eca4508838cd339cc19c72efc42d6ea0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Apr 2016 15:24:09 +0200 Subject: Track block download times per individual block Currently, we're keeping a timeout for each requested block, starting from when it is requested, with a correction factor for the number of blocks in the queue. That's unnecessarily complicated and inaccurate. As peers process block requests in order, we can make the timeout for each block start counting only when all previous ones have been received, and have a correction based on the number of peers, rather than the total number of blocks. --- src/main.cpp | 60 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b68c6affae..de74593bb1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -195,15 +195,10 @@ namespace { struct QueuedBlock { uint256 hash; CBlockIndex* pindex; //!< Optional. - int64_t nTime; //!< Time of "getdata" request in microseconds. bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. - int64_t nTimeDisconnect; //!< The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; - /** Number of blocks in flight with validated headers. */ - int nQueuedValidatedHeaders = 0; - /** Number of preferable block download peers. */ int nPreferredDownload = 0; @@ -212,6 +207,9 @@ namespace { /** Dirty block file entries. */ set setDirtyFileInfo; + + /** Number of peers from which we're downloading blocks. */ + int nPeersWithValidatedDownloads = 0; } // anon namespace ////////////////////////////////////////////////////////////////////////////// @@ -259,6 +257,8 @@ struct CNodeState { //! Since when we're stalling block download progress (in microseconds), or 0. int64_t nStallingSince; list vBlocksInFlight; + //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty. + int64_t nDownloadingSince; int nBlocksInFlight; int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. @@ -276,6 +276,7 @@ struct CNodeState { pindexBestHeaderSent = NULL; fSyncStarted = false; nStallingSince = 0; + nDownloadingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; @@ -310,12 +311,6 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } -// Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) -{ - return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); -} - void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; @@ -335,11 +330,12 @@ void FinalizeNode(NodeId nodeid) { } BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) { - nQueuedValidatedHeaders -= entry.fValidatedHeaders; mapBlocksInFlight.erase(entry.hash); } EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; + nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); + assert(nPeersWithValidatedDownloads >= 0); mapNodeState.erase(nodeid); } @@ -350,8 +346,15 @@ bool MarkBlockAsReceived(const uint256& hash) { map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); - nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; + if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { + // Last validated block on the queue was received. + nPeersWithValidatedDownloads--; + } + if (state->vBlocksInFlight.begin() == itInFlight->second.second) { + // First block on the queue was received, update the start download time for the next one + state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); + } state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; @@ -369,12 +372,17 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; + QueuedBlock newentry = {hash, pindex, pindex != NULL}; list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + if (state->nBlocksInFlight == 1) { + // We're starting a block download (batch) from this peer. + state->nDownloadingSince = GetTimeMicros(); + } + if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) { + nPeersWithValidatedDownloads++; + } mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } @@ -3894,7 +3902,6 @@ void UnloadBlockIndex() nBlockSequenceId = 1; mapBlockSource.clear(); mapBlocksInFlight.clear(); - nQueuedValidatedHeaders = 0; nPreferredDownload = 0; setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); @@ -5811,24 +5818,15 @@ bool SendMessages(CNode* pto) LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } - // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval - // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to - // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link + // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval + // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout. + // We compensate for other peers to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. - // We also compare the block download timeout originally calculated against the time at which we'd disconnect - // if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're - // only looking at this peer's oldest request). This way a large queue in the past doesn't result in a - // permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing - // more quickly than once every 5 minutes, then we'll shorten the download window for this block). if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); - int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams); - if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { - LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); - queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; - } - if (queuedBlock.nTimeDisconnect < nNow) { + int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); + if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); pto->fDisconnect = true; } -- cgit v1.2.3 From 0e24bbf679c95784ed5514a6a1f2fbf99dd97725 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 4 Apr 2016 13:35:26 +0200 Subject: Self check after the last peer is removed --- src/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index de74593bb1..87c727b28f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -338,6 +338,13 @@ void FinalizeNode(NodeId nodeid) { assert(nPeersWithValidatedDownloads >= 0); mapNodeState.erase(nodeid); + + if (mapNodeState.empty()) { + // Do a consistency check after the last peer is removed. + assert(mapBlocksInFlight.empty()); + assert(nPreferredDownload == 0); + assert(nPeersWithValidatedDownloads == 0); + } } // Requires cs_main. -- cgit v1.2.3 From e4ba9f6b0402cf7a2ad0d74f617c434a26c6e124 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 7 Apr 2016 14:33:08 -0400 Subject: Version 2 transactions remain non-standard until CSV activates Before activation, such transactions might not be mined, so don't allow into the mempool. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a9f104c88b..f5c7e11d6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1025,6 +1025,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (fRequireStandard && !IsStandardTx(tx, reason)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); + // Don't relay version 2 transactions until CSV is active, and we can be + // sure that such transactions will be mined (unless we're on + // -testnet/-regtest). + const CChainParams& chainparams = Params(); + if (fRequireStandard && tx.nVersion >= 2 && VersionBitsTipState(chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV) != THRESHOLD_ACTIVE) { + return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); + } + // 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. -- cgit v1.2.3 From 66b07247a7a9e48e082502338176cc06edf61474 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 11 Apr 2016 01:09:34 +0000 Subject: Only send one GetAddr response per connection. This conserves resources from abusive peers that just send getaddr in a loop. Also makes correlating addr messages against INVs less effective. --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f5c7e11d6e..df494b226d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5214,6 +5214,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } + // Only send one GetAddr response per connection to reduce resource waste + // and discourage addr stamping of INV announcements. + if (pfrom->fSentAddr) { + LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); + return true; + } + pfrom->fSentAddr = true; + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) -- cgit v1.2.3 From 7e91f632c70ff1848a152f24ee67a06796803943 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Mon, 11 Apr 2016 12:52:29 -0400 Subject: Use txid as key in mapAlreadyAskedFor Previously we used the CInv that would be sent to the peer announcing the transaction as the key, but using the txid instead allows us to decouple the p2p layer from the application logic (which relies on this map to avoid duplicate tx requests). --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f5c7e11d6e..5c1af9eccc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4956,7 +4956,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CValidationState state; pfrom->setAskFor.erase(inv.hash); - mapAlreadyAskedFor.erase(inv); + mapAlreadyAskedFor.erase(inv.hash); CFeeRate txFeeRate = CFeeRate(0); if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) { -- cgit v1.2.3 From c6cb6f7d4c4bbfe96256c66b3aef4e7e3ff6285f Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 13 Apr 2016 16:02:46 -0400 Subject: Avoid unnecessary database access for unknown transactions --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9b164c7999..f5baf35595 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4344,10 +4344,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) recentRejects->reset(); } + // Use pcoinsTip->HaveCoinsInCache as a quick approximation to exclude + // requesting or processing some txs which have already been included in a block return recentRejects->contains(inv.hash) || mempool.exists(inv.hash) || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + pcoinsTip->HaveCoinsInCache(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash); -- cgit v1.2.3 From 38c310299cfef419d42744362b90c1700b598953 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 14 Apr 2016 16:04:50 +0200 Subject: Change mapRelay to store CTransactions --- src/main.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fff1cc346e..f84fb8dd56 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4456,7 +4456,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam bool pushed = false; { LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv); + map::iterator mi = mapRelay.find(inv.hash); if (mi != mapRelay.end()) { pfrom->PushMessage(inv.GetCommand(), (*mi).second); pushed = true; @@ -4465,10 +4465,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (!pushed && inv.type == MSG_TX) { CTransaction tx; if (mempool.lookup(inv.hash, tx)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << tx; - pfrom->PushMessage(NetMsgType::TX, ss); + pfrom->PushMessage(NetMsgType::TX, tx); pushed = true; } } -- cgit v1.2.3 From 99e70751f23aa59ac297f6746dd8f09a140d48ae Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 5 Apr 2016 15:14:48 +0200 Subject: =?UTF-8?q?Break=20circular=20dependency=20main=20=E2=86=94=20txdb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break the circular dependency between main and txdb by: - Moving `CBlockFileInfo` from `main.h` to `chain.h`. I think this makes sense, as the other block-file stuff is there too. - Moving `CDiskTxPos` from `main.h` to `txdb.h`. This type seems specific to txdb. - Pass a functor `insertBlockIndex` to `LoadBlockIndexGuts`. This leaves it up to the caller how to insert block indices. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a94d52f895..2db726cb45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3706,7 +3706,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); - if (!pblocktree->LoadBlockIndexGuts()) + if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex)) return false; boost::this_thread::interruption_point(); -- cgit v1.2.3 From 764d2377727c8945d648a018492a61add4fcf042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 6 Apr 2016 16:36:32 +0200 Subject: Globals: Explicitly pass const CChainParams& to UpdateTip() --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a94d52f895..aaf5f9ae64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2570,8 +2570,7 @@ void PruneAndFlush() { } /** Update chainActive and related internal data structures. */ -void static UpdateTip(CBlockIndex *pindexNew) { - const CChainParams& chainParams = Params(); +void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { chainActive.SetTip(pindexNew); // New best block @@ -2631,6 +2630,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { /** 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) { + const CChainParams& chainparams = Params(); // TODO replace consensusParams parameter CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); // Read block from disk. @@ -2668,7 +2668,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // block that were added back and cleans up the mempool state. mempool.UpdateTransactionsFromBlock(vHashUpdate); // Update chainActive and related variables. - UpdateTip(pindexDelete->pprev); + UpdateTip(pindexDelete->pprev, chainparams); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { @@ -2727,7 +2727,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); // Update chainActive & related variables. - UpdateTip(pindexNew); + UpdateTip(pindexNew, chainparams); // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { -- cgit v1.2.3 From d0a6353dec48f365c38de3c76b42f67eda737ed5 Mon Sep 17 00:00:00 2001 From: face Date: Sun, 17 Apr 2016 10:58:50 +0300 Subject: Pass CChainParams to DisconnectTip() --- src/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index aaf5f9ae64..5183930001 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2628,14 +2628,13 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { } /** 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) +bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams) { - const CChainParams& chainparams = Params(); // TODO replace consensusParams parameter CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); // Read block from disk. CBlock block; - if (!ReadBlockFromDisk(block, pindexDelete, consensusParams)) + if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus())) return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); @@ -2828,7 +2827,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state, chainparams.GetConsensus())) + if (!DisconnectTip(state, chainparams)) return false; fBlocksDisconnected = true; } @@ -2973,7 +2972,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, return true; } -bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex *pindex) +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { AssertLockHeld(cs_main); @@ -2989,7 +2988,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus setBlockIndexCandidates.erase(pindexWalk); // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. - if (!DisconnectTip(state, consensusParams)) { + if (!DisconnectTip(state, chainparams)) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return false; } -- cgit v1.2.3 From f2d3ba73860e875972738d1da1507124d0971ae5 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 4 Apr 2016 02:36:47 +0000 Subject: Eliminate TX trickle bypass, sort TX invs for privacy and priority. Previously Bitcoin would send 1/4 of transactions out to all peers instantly. This causes high overhead because it makes >80% of INVs size 1. Doing so harms privacy, because it limits the amount of source obscurity a transaction can receive. These randomized broadcasts also disobeyed transaction dependencies and required use of the orphan pool. Because the orphan pool is so small this leads to poor propagation for dependent transactions. When the bypass wasn't in effect, transactions were sent in the order they were received. This avoided creating orphans but undermines privacy fairly significantly. This commit: Eliminates the bypass. The bypass is replaced by halving the average delay for outbound peers. Sorts candidate transactions for INV by their topological depth then by their feerate (then hash); removing the information leakage and providing priority service to higher fee transactions. Limits the amount of transactions sent in a single INV to 7tx/sec (and twice that for outbound); this limits the harm of low fee transaction floods, gives faster relay service to higher fee transactions. The 7 sounds lower than it really is because received advertisements need not be sent, and because the aggregate rate is multipled by the number of peers. --- src/main.cpp | 60 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 24 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a94d52f895..4a28bbb00c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5560,6 +5560,29 @@ bool ProcessMessages(CNode* pfrom) return fOk; } +class CompareInvMempoolOrder +{ + CTxMemPool *mp; +public: + CompareInvMempoolOrder(CTxMemPool *mempool) + { + mp = mempool; + } + + bool operator()(const CInv &a, const CInv &b) + { + if (a.type != MSG_TX && b.type != MSG_TX) { + return false; + } else { + if (a.type != MSG_TX) { + return true; + } else if (b.type != MSG_TX) { + return false; + } + return mp->CompareDepthAndScore(a.hash, b.hash); + } + } +}; bool SendMessages(CNode* pto) { @@ -5790,42 +5813,31 @@ bool SendMessages(CNode* pto) bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; - pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL); + // Use half the delay for outbound peers, as their is less privacy concern for them. + pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } LOCK(pto->cs_inventory); - vInv.reserve(std::min(1000, pto->vInventoryToSend.size())); + if (fSendTrickle && pto->vInventoryToSend.size() > 1) { + // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. + CompareInvMempoolOrder compareInvMempoolOrder(&mempool); + std::stable_sort(pto->vInventoryToSend.begin(), pto->vInventoryToSend.end(), compareInvMempoolOrder); + } + vInv.reserve(std::min(INVENTORY_BROADCAST_MAX, pto->vInventoryToSend.size())); vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) continue; - - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) - { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); - uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); - - if (fTrickleWait) - { - vInvWait.push_back(inv); - continue; - } + // No reason to drain out at many times the network's capacity, + // especially since we have many peers and some will draw much shorter delays. + if (vInv.size() >= INVENTORY_BROADCAST_MAX || (inv.type == MSG_TX && !fSendTrickle)) { + vInvWait.push_back(inv); + continue; } pto->filterInventoryKnown.insert(inv.hash); vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); - } } pto->vInventoryToSend = vInvWait; } -- cgit v1.2.3 From 176869f9b367b6eaf2800de0c30d9b31311e3819 Mon Sep 17 00:00:00 2001 From: face Date: Tue, 19 Apr 2016 16:16:39 +0300 Subject: Explicitly pass CChainParams to ConnectBlock --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 5183930001..1015e538d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2221,9 +2221,9 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, + CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) { - const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); int64_t nTimeStart = GetTimeMicros(); @@ -2703,7 +2703,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) @@ -3523,7 +3523,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; assert(state.IsValid()); @@ -3891,7 +3891,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlock block; 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)) + if (!ConnectBlock(block, state, pindex, coins, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } -- cgit v1.2.3 From 3a99fb2cb14955f5e029d315041a093e957e6c3e Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 20 Apr 2016 13:49:55 -0400 Subject: Fix headers announcements edge case Previously we would assert that if every block in vBlockHashesToAnnounce is in chainActive, then the blocks to be announced must connect. However, there are edge cases where this assumption could be violated (eg using invalidateblock / reconsiderblock), so just check for this case and revert to inv-announcement instead. --- src/main.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a94d52f895..e2fbfcf12d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5718,7 +5718,21 @@ bool SendMessages(CNode* pto) fRevertToInv = true; break; } - assert(pBestIndex == NULL || pindex->pprev == pBestIndex); + if (pBestIndex != NULL && pindex->pprev != pBestIndex) { + // This means that the list of blocks to announce don't + // connect to each other. + // This shouldn't really be possible to hit during + // regular operation (because reorgs should take us to + // a chain that has some block not on the prior chain, + // which should be caught by the prior check), but one + // way this could happen is by using invalidateblock / + // reconsiderblock repeatedly on the tip, causing it to + // be added multiple times to vBlockHashesToAnnounce. + // Robustly deal with this rare situation by reverting + // to an inv. + fRevertToInv = true; + break; + } pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message -- cgit v1.2.3 From dc13dcd2bec2613a1cd5e0395b09b449d176146f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 7 Apr 2016 13:57:36 +0200 Subject: Split up and optimize transaction and block inv queues --- src/main.cpp | 76 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 31 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4a28bbb00c..61d9301f8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5569,18 +5569,11 @@ public: mp = mempool; } - bool operator()(const CInv &a, const CInv &b) + bool operator()(std::set::iterator a, std::set::iterator b) { - if (a.type != MSG_TX && b.type != MSG_TX) { - return false; - } else { - if (a.type != MSG_TX) { - return true; - } else if (b.type != MSG_TX) { - return false; - } - return mp->CompareDepthAndScore(a.hash, b.hash); - } + /* As std::make_heap produces a max-heap, we want the entries with the + * fewest ancestors/highest fee to sort later. */ + return mp->CompareDepthAndScore(*b, *a); } }; @@ -5808,38 +5801,59 @@ bool SendMessages(CNode* pto) // Message: inventory // vector vInv; - vector vInvWait; { + LOCK(pto->cs_inventory); + vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); + + // Add blocks + BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) { + vInv.push_back(CInv(MSG_BLOCK, hash)); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } + } + pto->vInventoryBlockToSend.clear(); + + // Determine transactions to relay bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; - // Use half the delay for outbound peers, as their is less privacy concern for them. + // Use half the delay for outbound peers, as there is less privacy concern for them. pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } - LOCK(pto->cs_inventory); - if (fSendTrickle && pto->vInventoryToSend.size() > 1) { + if (fSendTrickle) { + // Produce a vector with all candidates for sending + vector::iterator> vInvTx; + vInvTx.reserve(pto->setInventoryTxToSend.size()); + for (std::set::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { + vInvTx.push_back(it); + } // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. + // A heap is used so that not all items need sorting if only a few are being sent. CompareInvMempoolOrder compareInvMempoolOrder(&mempool); - std::stable_sort(pto->vInventoryToSend.begin(), pto->vInventoryToSend.end(), compareInvMempoolOrder); - } - vInv.reserve(std::min(INVENTORY_BROADCAST_MAX, pto->vInventoryToSend.size())); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) - { - if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) - continue; + std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); // No reason to drain out at many times the network's capacity, // especially since we have many peers and some will draw much shorter delays. - if (vInv.size() >= INVENTORY_BROADCAST_MAX || (inv.type == MSG_TX && !fSendTrickle)) { - vInvWait.push_back(inv); - continue; + unsigned int nRelayedTransactions = 0; + while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) { + // Fetch the top element from the heap + std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + std::set::iterator it = vInvTx.back(); + vInvTx.pop_back(); + uint256 hash = *it; + // Remove it from the to-be-sent set + pto->setInventoryTxToSend.erase(it); + // Check if not in the filter already + if (pto->filterInventoryKnown.contains(hash)) { + continue; + } + // Send + vInv.push_back(CInv(MSG_TX, hash)); + nRelayedTransactions++; + pto->filterInventoryKnown.insert(hash); } - - pto->filterInventoryKnown.insert(inv.hash); - - vInv.push_back(inv); } - pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage(NetMsgType::INV, vInv); -- cgit v1.2.3 From ed7068302c7490e8061cb3a558a0f83a465beeea Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 8 Apr 2016 16:26:41 +0200 Subject: Handle mempool requests in send loop, subject to trickle By eliminating queued entries from the mempool response and responding only at trickle time, this makes the mempool no longer leak transaction arrival order information (as the mempool itself is also sorted)-- at least no more than relay itself leaks it. --- src/main.cpp | 74 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 28 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 61d9301f8f..282c8cdb66 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5235,34 +5235,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return true; } - LOCK2(cs_main, pfrom->cs_filter); - std::vector vtxid; - mempool.queryHashes(vtxid); - vector vInv; - BOOST_FOREACH(uint256& hash, vtxid) { - CInv inv(MSG_TX, hash); - 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; - } - if (pfrom->minFeeFilter) { - CFeeRate feeRate; - mempool.lookupFeeRate(hash, feeRate); - LOCK(pfrom->cs_feeFilter); - if (feeRate.GetFeePerK() < pfrom->minFeeFilter) - continue; - } - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); - } - } - if (vInv.size() > 0) - pfrom->PushMessage(NetMsgType::INV, vInv); + LOCK(pfrom->cs_inventory); + pfrom->fSendMempool = true; } @@ -5815,13 +5790,52 @@ bool SendMessages(CNode* pto) } pto->vInventoryBlockToSend.clear(); - // Determine transactions to relay + // Check whether periodic sends should happen bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; // Use half the delay for outbound peers, as there is less privacy concern for them. pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } + + // Respond to BIP35 mempool requests + if (fSendTrickle && pto->fSendMempool) { + std::vector vtxid; + mempool.queryHashes(vtxid); + pto->fSendMempool = false; + CAmount filterrate = 0; + { + LOCK(pto->cs_feeFilter); + filterrate = pto->minFeeFilter; + } + + LOCK(pto->cs_filter); + + BOOST_FOREACH(const uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + pto->setInventoryTxToSend.erase(hash); + if (filterrate) { + CFeeRate feeRate; + mempool.lookupFeeRate(hash, feeRate); + if (feeRate.GetFeePerK() < filterrate) + continue; + } + if (pto->pfilter) { + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } + pto->filterInventoryKnown.insert(hash); + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } + } + } + + // Determine transactions to relay if (fSendTrickle) { // Produce a vector with all candidates for sending vector::iterator> vInvTx; @@ -5851,6 +5865,10 @@ bool SendMessages(CNode* pto) // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } pto->filterInventoryKnown.insert(hash); } } -- cgit v1.2.3 From b5599147533103efea896a1fc4ff51f2d3ad5808 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 20 Apr 2016 07:05:23 +0000 Subject: Move bloom and feerate filtering to just prior to tx sending. This will avoid sending more pointless INVs around updates, and prevents using filter updates to timetag transactions. Also adds locking for fRelayTxes. --- src/main.cpp | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 282c8cdb66..b707de2e58 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4557,12 +4557,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } - if (!vRecv.empty()) + if (!vRecv.empty()) { vRecv >> pfrom->nStartingHeight; - if (!vRecv.empty()) - vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message - else - pfrom->fRelayTxes = true; + } + { + LOCK(pfrom->cs_filter); + if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; + } // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) @@ -5325,12 +5329,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBloomFilter filter; vRecv >> filter; + LOCK(pfrom->cs_filter); + if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter Misbehaving(pfrom->GetId(), 100); else { - LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); pfrom->pfilter->UpdateEmptyFull(); @@ -5798,6 +5803,12 @@ bool SendMessages(CNode* pto) pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } + // Time to send but the peer has requested we not relay transactions. + if (fSendTrickle) { + LOCK(pto->cs_filter); + if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear(); + } + // Respond to BIP35 mempool requests if (fSendTrickle && pto->fSendMempool) { std::vector vtxid; @@ -5843,6 +5854,11 @@ bool SendMessages(CNode* pto) for (std::set::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { vInvTx.push_back(it); } + CAmount filterrate = 0; + { + LOCK(pto->cs_feeFilter); + filterrate = pto->minFeeFilter; + } // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. // A heap is used so that not all items need sorting if only a few are being sent. CompareInvMempoolOrder compareInvMempoolOrder(&mempool); @@ -5850,6 +5866,7 @@ bool SendMessages(CNode* pto) // No reason to drain out at many times the network's capacity, // especially since we have many peers and some will draw much shorter delays. unsigned int nRelayedTransactions = 0; + LOCK(pto->cs_filter); while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) { // Fetch the top element from the heap std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); @@ -5862,6 +5879,19 @@ bool SendMessages(CNode* pto) if (pto->filterInventoryKnown.contains(hash)) { continue; } + // Not in the mempool anymore? don't bother sending it. + CFeeRate feeRate; + if (!mempool.lookupFeeRate(hash, feeRate)) { + continue; + } + if (filterrate && feeRate.GetFeePerK() < filterrate) { + continue; + } + if (pto->pfilter) { + CTransaction tx; + if (!mempool.lookup(hash, tx)) continue; + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; -- cgit v1.2.3 From efb54ba065e41e0d36383bcabfcc01bbca7b2340 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 25 Apr 2016 13:13:52 -0700 Subject: lock cs_main for State/Misbehaving ProcessMessage calls State(...) and Misbehaving(...) without holding the required lock; add LOCK(cs_main) blocks. --- src/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f6a89fa2e6..a85ee46d5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4514,6 +4514,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, strCommand == NetMsgType::FILTERCLEAR)) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } else { @@ -4529,6 +4530,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion != 0) { pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -4584,7 +4586,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); // Potentially mark this peer as a preferred download peer. + { + LOCK(cs_main); UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + } // Change version pfrom->PushMessage(NetMsgType::VERACK); @@ -4642,6 +4647,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (pfrom->nVersion == 0) { // Must have a version message before anything else + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -4677,6 +4683,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; if (vAddr.size() > 1000) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } @@ -4746,6 +4753,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } @@ -4821,6 +4829,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } @@ -5074,6 +5083,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("headers message size = %u", nCount); } @@ -5350,8 +5360,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> filter; if (!filter.IsWithinSizeConstraints()) + { // There is no excuse for sending a too-large filter + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } else { LOCK(pfrom->cs_filter); @@ -5372,13 +5385,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else + { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } } } -- cgit v1.2.3 From 719de56ab2c8e5bc6ce9f67c7bf159adc242d49b Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 25 Apr 2016 12:31:32 -0700 Subject: lock cs_main for chainActive ActivateBestChain uses chainActive after releasing the lock; reorder operations to move all access to synchronized object into existing LOCK(cs_main) block. --- src/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a85ee46d5a..83fc4d6bd8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2897,14 +2897,15 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c */ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { CBlockIndex *pindexMostWork = NULL; + CBlockIndex *pindexNewTip = NULL; do { boost::this_thread::interruption_point(); if (ShutdownRequested()) break; - CBlockIndex *pindexNewTip = NULL; const CBlockIndex *pindexFork; bool fInitialDownload; + int nNewHeight; { LOCK(cs_main); CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2920,6 +2921,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); + nNewHeight = chainActive.Height(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -2948,7 +2950,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { + if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { pnode->PushBlockHash(hash); } @@ -2961,7 +2963,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, } } } - } while(pindexMostWork != chainActive.Tip()); + } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); // Write changes periodically to disk, after relay. -- cgit v1.2.3 From 61c01706cd1d38023facea7f25bc1d84980f8c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Tue, 26 Apr 2016 22:22:59 +0200 Subject: Log invalid block hash to make debugging easier. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f6a89fa2e6..a8f8e6fa5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3371,7 +3371,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (ppindex) *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate"); return true; } -- cgit v1.2.3 From 383fc10ebb8e9ffe88c1b9e2ea706c76e5a6343e Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 27 Apr 2016 09:57:02 -0400 Subject: Only use AddInventoryKnown for transactions filterInventoryKnown is only used when relaying transactions, so stop adding block hashes to the filter. --- src/main.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f6a89fa2e6..90db9de14e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4765,7 +4765,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, const CInv &inv = vInv[nInv]; boost::this_thread::interruption_point(); - pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); @@ -4795,6 +4794,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { + pfrom->AddInventoryKnown(inv); 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 && !IsInitialBlockDownload()) @@ -5173,10 +5173,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBlock block; vRecv >> block; - CInv inv(MSG_BLOCK, block.GetHash()); - LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - - pfrom->AddInventoryKnown(inv); + LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id); CValidationState state; // Process all blocks from whitelisted peers, even if not requested, @@ -5189,7 +5186,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash()); if (nDoS > 0) { LOCK(cs_main); Misbehaving(pfrom->GetId(), nDoS); @@ -5769,9 +5766,7 @@ bool SendMessages(CNode* pto) 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 the peer's chain has this block, don't inv it back. if (!PeerHasHeader(&state, pindex)) { pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__, -- cgit v1.2.3 From b02119e4634f2453260bb1b195e39bdc0a3b5691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Wed, 27 Apr 2016 18:04:02 +0200 Subject: Remove useless argument to AlertNotify. It is always 'true', so useless. --- src/main.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f6a89fa2e6..418ef704b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1593,7 +1593,7 @@ bool fLargeWorkForkFound = false; bool fLargeWorkInvalidChainFound = false; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; -static void AlertNotify(const std::string& strMessage, bool fThread) +static void AlertNotify(const std::string& strMessage) { uiInterface.NotifyAlertChanged(); std::string strCmd = GetArg("-alertnotify", ""); @@ -1607,10 +1607,7 @@ static void AlertNotify(const std::string& strMessage, bool fThread) safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); - if (fThread) - boost::thread t(runCommand, strCmd); // thread runs free - else - runCommand(strCmd); + boost::thread t(runCommand, strCmd); // thread runs free } void CheckForkWarningConditions() @@ -1632,7 +1629,7 @@ void CheckForkWarningConditions() { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + pindexBestForkBase->phashBlock->ToString() + std::string("'"); - AlertNotify(warning, true); + AlertNotify(warning); } if (pindexBestForkTip && pindexBestForkBase) { @@ -2163,7 +2160,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const if (!strWarning.empty()) { strMiscWarning = strWarning; - AlertNotify(strWarning, true); + AlertNotify(strWarning); lastAlertTime = now; } } @@ -2598,7 +2595,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { if (state == THRESHOLD_ACTIVE) { strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); if (!fWarned) { - AlertNotify(strMiscWarning, true); + AlertNotify(strMiscWarning); fWarned = true; } } else { @@ -2620,7 +2617,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); if (!fWarned) { - AlertNotify(strMiscWarning, true); + AlertNotify(strMiscWarning); fWarned = true; } } -- cgit v1.2.3 From f7c4f79f07c777801db7dc047cd45eaf2bba81c9 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 30 Apr 2016 19:25:00 +0200 Subject: [trivial] Add missing const qualifiers. Add some const qualifiers to references that are not modified and should be marked as const. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 11ccab253e..2777245654 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1468,7 +1468,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { - CCoinsViewCache &view = *pcoinsTip; + const CCoinsViewCache& view = *pcoinsTip; const CCoins* coins = view.AccessCoins(hash); if (coins) nHeight = coins->nHeight; -- cgit v1.2.3 From c8b92486c4eca1287546b0683b6af3551db1fe67 Mon Sep 17 00:00:00 2001 From: 21E14 <21xe14@gmail.com> Date: Fri, 29 Apr 2016 23:45:20 -0400 Subject: Remove obsolete reference to CValidationState from UpdateCoins. --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 11ccab253e..c5c6146930 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1744,7 +1744,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state } } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { // mark inputs spent if (!tx.IsCoinBase()) { @@ -1770,10 +1770,10 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach inputs.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight); } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) { CTxUndo txundo; - UpdateCoins(tx, state, inputs, txundo, nHeight); + UpdateCoins(tx, inputs, txundo, nHeight); } bool CScriptCheck::operator()() { @@ -2385,7 +2385,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); -- cgit v1.2.3 From addb9d2a092b4fd0384d4ff82e9607e5411eab97 Mon Sep 17 00:00:00 2001 From: instagibbs Date: Fri, 6 May 2016 12:53:23 -0400 Subject: Remove state arg from ReconsiderBlock --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 92a38f230f..9489bb1c46 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3011,7 +3011,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C return true; } -bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { +bool ReconsiderBlock(CBlockIndex *pindex) { AssertLockHeld(cs_main); int nHeight = pindex->nHeight; -- cgit v1.2.3 From 657e07efa34ffe9d8614a618e00452c9ee7c0e0d Mon Sep 17 00:00:00 2001 From: instagibbs Date: Mon, 9 May 2016 11:26:37 -0400 Subject: Rename ReconsiderBlock func to reflect real behavior --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9489bb1c46..11eaa055d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3011,7 +3011,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C return true; } -bool ReconsiderBlock(CBlockIndex *pindex) { +bool ResetBlockFailureFlags(CBlockIndex *pindex) { AssertLockHeld(cs_main); int nHeight = pindex->nHeight; -- cgit v1.2.3 From 581ddff05c92eb835a20c2be9ccf5d2d37b94883 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2016 14:00:22 +0200 Subject: net: Add fRelayTxes flag Add a fRelayTxes to keep track of the relay transaction flag we send to other peers. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ee30954b48..f813056a7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4751,7 +4751,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return error("message inv size() = %u", vInv.size()); } - bool fBlocksOnly = GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + bool fBlocksOnly = !fRelayTxes; // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) @@ -4934,7 +4934,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) + if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; -- cgit v1.2.3 From f93c2a1b7ee912f0651ebb4c8a5eca220e434f4a Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sun, 15 May 2016 20:15:02 +0200 Subject: net: Avoid duplicate getheaders requests. The current logic for syncing headers may lead to lots of duplicate getheaders requests being sent: If a new block arrives while the node is in headers sync, it will send getheaders in response to the block announcement. When the headers arrive, the message will be of maximum size and so a follow-up request will be sent---all of that in addition to the existing headers syncing. This will create a second "chain" of getheaders requests. If more blocks arrive, this may even lead to arbitrarily many parallel chains of redundant requests. This patch changes the behaviour to only request more headers after a maximum-sized message when it contained at least one unknown header. This avoids sustaining parallel chains of redundant requests. Note that this patch avoids the issues raised in the discussion of https://github.com/bitcoin/bitcoin/pull/6821: There is no risk of the node being permanently blocked. At the latest when a new block arrives this will trigger a new getheaders request and restart syncing. --- src/main.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ee30954b48..5816f799a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5091,6 +5091,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } + // If we already know the last header in the message, then it contains + // no new information for us. In this case, we do not request + // more headers later. This prevents multiple chains of redundant + // getheader requests from running in parallel if triggered by incoming + // blocks while the node is still in initial headers sync. + const bool hasNewHeaders = (mapBlockIndex.count(headers.back().GetHash()) == 0); + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5111,7 +5118,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindexLast) UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - if (nCount == MAX_HEADERS_RESULTS && pindexLast) { + if (nCount == MAX_HEADERS_RESULTS && pindexLast && hasNewHeaders) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. -- cgit v1.2.3 From d87b198b7334317952ca6a1377e25b5c859a1767 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 20 Apr 2016 19:38:19 +0000 Subject: Remove unneeded feerate param from RelayTransaction/AcceptToMemoryPool. --- src/main.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ee30954b48..09f82312a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1005,7 +1005,7 @@ std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, - bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, + bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) { const uint256 hash = tx.GetHash(); @@ -1170,9 +1170,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp); unsigned int nSize = entry.GetTxSize(); - if (txFeeRate) { - *txFeeRate = CFeeRate(nFees, nSize); - } // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1421,10 +1418,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -2651,7 +2648,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, NULL, true)) { + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { mempool.removeRecursive(tx, removed); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); @@ -4956,10 +4953,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv.hash); - CFeeRate txFeeRate = CFeeRate(0); - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) { + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); - RelayTransaction(tx, txFeeRate); + RelayTransaction(tx); vWorkQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", @@ -4990,10 +4986,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (setMisbehaving.count(fromPeer)) continue; - CFeeRate orphanFeeRate = CFeeRate(0); - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) { + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx, orphanFeeRate); + RelayTransaction(orphanTx); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); } @@ -5046,7 +5041,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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, txFeeRate); + RelayTransaction(tx); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state)); } -- cgit v1.2.3 From d253ec4baa21cc292cf72d453f71b4043b53e591 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 20 Apr 2016 14:53:36 +0200 Subject: Make ProcessNewBlock dbp const and update comment --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 09f82312a9..981d987711 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3398,7 +3398,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } /** 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) +static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp) { AssertLockHeld(cs_main); @@ -3474,7 +3474,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, 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, const CDiskBlockPos* dbp) { { LOCK(cs_main); -- cgit v1.2.3 From 316623f2c197971db9b5bcb9c84e446254b552c3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 20 Apr 2016 15:45:41 +0200 Subject: Switch reindexing to AcceptBlock in-loop and ActivateBestChain afterwards --- src/main.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 981d987711..42733ee2de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3402,7 +3402,8 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha { AssertLockHeld(cs_main); - CBlockIndex *&pindex = *ppindex; + CBlockIndex *pindexDummy = NULL; + CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; if (!AcceptBlockHeader(block, state, chainparams, &pindex)) return false; @@ -4037,13 +4038,14 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { + LOCK(cs_main); CValidationState state; - if (ProcessNewBlock(state, chainparams, NULL, &block, true, dbp)) + if (AcceptBlock(block, state, chainparams, NULL, true, dbp)) nLoaded++; if (state.IsError()) break; } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } // Recursively process earlier encountered successors of this block @@ -4057,10 +4059,11 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB std::multimap::iterator it = range.first; if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())) { - LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); + LOCK(cs_main); CValidationState dummy; - if (ProcessNewBlock(dummy, chainparams, NULL, &block, true, &it->second)) + if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second)) { nLoaded++; queue.push_back(block.GetHash()); -- cgit v1.2.3 From fb8fad1586ced69fa37c665a11916ae4c4d0df05 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 20 Apr 2016 16:02:19 +0200 Subject: Optimize ActivateBestChain for long chains --- src/main.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 42733ee2de..38851cda3e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2811,10 +2811,9 @@ 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, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock) +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound) { AssertLockHeld(cs_main); - bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); @@ -2902,15 +2901,22 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, { LOCK(cs_main); CBlockIndex *pindexOldTip = chainActive.Tip(); - pindexMostWork = FindMostWorkChain(); + if (pindexMostWork == NULL) { + pindexMostWork = FindMostWorkChain(); + } // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + bool fInvalidFound = false; + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound)) return false; + if (fInvalidFound) { + // Wipe cache, we may need another branch now. + pindexMostWork = NULL; + } pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); -- cgit v1.2.3 From b4d24e142e25a21c78ab74146c3520f2259fd7c2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 28 Apr 2016 16:18:45 +0200 Subject: Report reindexing progress in GUI --- src/main.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 38851cda3e..66930b3b06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2883,6 +2883,28 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c return true; } +static void NotifyHeaderTip() { + bool fNotify = false; + bool fInitialBlockDownload = false; + static CBlockIndex* pindexHeaderOld = NULL; + CBlockIndex* pindexHeader = NULL; + { + LOCK(cs_main); + if (!setBlockIndexCandidates.empty()) { + pindexHeader = *setBlockIndexCandidates.rbegin(); + } + if (pindexHeader != pindexHeaderOld) { + fNotify = true; + fInitialBlockDownload = IsInitialBlockDownload(); + pindexHeaderOld = pindexHeader; + } + } + // Send block tip changed notifications without cs_main + if (fNotify) { + uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); + } +} + /** * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either NULL or a pointer to a block @@ -3499,6 +3521,8 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c return error("%s: AcceptBlock FAILED", __func__); } + NotifyHeaderTip(); + if (!ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed", __func__); @@ -4054,6 +4078,16 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } + // Activate the genesis block so normal node progress can continue + if (hash == chainparams.GetConsensus().hashGenesisBlock) { + CValidationState state; + if (!ActivateBestChain(state, chainparams)) { + break; + } + } + + NotifyHeaderTip(); + // Recursively process earlier encountered successors of this block deque queue; queue.push_back(hash); @@ -4077,6 +4111,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB } range.first++; mapBlocksUnknownParent.erase(it); + NotifyHeaderTip(); } } } catch (const std::exception& e) { @@ -5088,6 +5123,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } + { LOCK(cs_main); if (nCount == 0) { @@ -5171,6 +5207,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } CheckBlockIndex(chainparams.GetConsensus()); + } + + NotifyHeaderTip(); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing -- cgit v1.2.3 From a68ec21f7ed2978d8945a0f4cfd7e80bfa5fd917 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 May 2016 21:53:38 +0200 Subject: Use SipHash-2-4 for address relay selection --- src/main.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 09f82312a9..a1c027bef3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4698,25 +4698,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); + static uint64_t salt0 = 0, salt1 = 0; + while (salt0 == 0 && salt1 == 0) { + GetRandBytes((unsigned char*)&salt0, sizeof(salt0)); + GetRandBytes((unsigned char*)&salt1, sizeof(salt1)); + } uint64_t hashAddr = addr.GetHash(); - uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - multimap mapMix; + multimap mapMix; + const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); - hashKey = Hash(BEGIN(hashKey), END(hashKey)); + uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize(); mapMix.insert(make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } -- cgit v1.2.3 From ee9f4a5b1505113eae0fe8d70fd0414bd5bce0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 20 May 2016 14:43:37 +0200 Subject: Consensus: Decouple from chainparams.o and timedata.o Do it for the consensus-critical functions: - CheckBlockHeader - CheckBlock - ContextualCheckBlockHeader --- src/main.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9ba90b4ead..f66de42aa8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2223,7 +2223,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), !fJustCheck, !fJustCheck)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); // verify that the view's current state corresponds to the previous block @@ -3234,20 +3234,20 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW) { // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3256,7 +3256,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) + if (!CheckBlockHeader(block, state, consensusParams, nAdjustedTime, fCheckPOW)) return false; // Check the merkle root. @@ -3322,9 +3322,8 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev) { - const Consensus::Params& consensusParams = Params().GetConsensus(); // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); @@ -3397,7 +3396,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return true; } - if (!CheckBlockHeader(block, state)) + if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), GetAdjustedTime())) return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index @@ -3413,7 +3412,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } if (pindex == NULL) @@ -3457,7 +3456,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha if (fTooFarAhead) return true; // Block height is too high } - if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3542,9 +3541,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, indexDummy.nHeight = pindexPrev->nHeight + 1; // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); - if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); @@ -3876,7 +3875,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, 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)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity -- cgit v1.2.3 From beceac9bbf14bf4a81f6f63b9cca2a64157054ae Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Fri, 20 May 2016 14:44:32 +0100 Subject: Disable the mempool P2P command when bloom filters disabled Only useful to SPV peers, and attackers... like bloom is a DoS vector as far more data is sent than received. --- src/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9ba90b4ead..2ad2a8e3f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5277,6 +5277,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::MEMPOOL) { + if (!(nLocalServices & NODE_BLOOM) && !pfrom->fWhitelisted) + { + LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId()); + pfrom->fDisconnect = true; + return true; + } + if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); -- cgit v1.2.3 From bd477f4e8ba9e632f42649f2fca10d698c6b7181 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Fri, 20 May 2016 04:59:57 -0400 Subject: VerifyDB: don't check blocks that have been pruned --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9ba90b4ead..33abf39578 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3871,6 +3871,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; + if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, only go back as far as we have data. + LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight); + break; + } CBlock block; // check level 0: read from disk if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) -- cgit v1.2.3 From 7e908c7b826cedbf29560ce7a668af809ee71524 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Fri, 20 May 2016 16:19:26 +0000 Subject: Do not use mempool for GETDATA for tx accepted after the last mempool req. The ability to GETDATA a transaction which has not (yet) been relayed is a privacy loss vector. The use of the mempool for this was added as part of the mempool p2p message and is only needed to fetch transactions returned by it. --- src/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 9ba90b4ead..2ee6bc5310 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4503,7 +4503,10 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } if (!pushed && inv.type == MSG_TX) { CTransaction tx; - if (mempool.lookup(inv.hash, tx)) { + int64_t txtime; + // To protect privacy, do not answer getdata using the mempool when + // that TX couldn't have been INVed in reply to a MEMPOOL request. + if (mempool.lookup(inv.hash, tx, txtime) && txtime <= pfrom->timeLastMempoolReq) { pfrom->PushMessage(NetMsgType::TX, tx); pushed = true; } @@ -5902,6 +5905,7 @@ bool SendMessages(CNode* pto) vInv.clear(); } } + pto->timeLastMempoolReq = GetTime(); } // Determine transactions to relay -- cgit v1.2.3 From fa2637a3beb8677067015df3d9d7b394fa837c2f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 16 Apr 2016 12:25:12 +0200 Subject: Always require OS randomness when generating secret keys --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ed157b53dc..ffc57d48be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4547,7 +4547,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams) { - RandAddSeedPerfmon(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { -- cgit v1.2.3 From 4d8993b3469915d8c9ba4cd3b918f16782edf0de Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 21 May 2016 09:45:32 +0000 Subject: Defer inserting into maprelay until just before relaying. This reduces the rate of not founds by better matching the far end expectations, it also improves privacy by removing the ability to use getdata to probe for a node having a txn before it has been relayed. --- src/main.cpp | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c2905784f0..f9b8ab37e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,6 +80,10 @@ uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; +std::map mapRelay; +std::deque > vRelayExpiration; +CCriticalSection cs_mapRelay; + CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -4501,27 +4505,28 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } else if (inv.IsKnownType()) { + CTransaction tx; // Send stream from relay memory - bool pushed = false; + bool push = false; { LOCK(cs_mapRelay); map::iterator mi = mapRelay.find(inv.hash); if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); - pushed = true; + tx = (*mi).second; + push = true; } } - if (!pushed && inv.type == MSG_TX) { - CTransaction tx; + if (!push && inv.type == MSG_TX) { int64_t txtime; // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (mempool.lookup(inv.hash, tx, txtime) && txtime <= pfrom->timeLastMempoolReq) { - pfrom->PushMessage(NetMsgType::TX, tx); - pushed = true; + push = true; } } - if (!pushed) { + if (push) { + pfrom->PushMessage(inv.GetCommand(), tx); + } else { vNotFound.push_back(inv); } } @@ -5958,14 +5963,26 @@ bool SendMessages(CNode* pto) if (filterrate && feeRate.GetFeePerK() < filterrate) { continue; } - if (pto->pfilter) { - CTransaction tx; - if (!mempool.lookup(hash, tx)) continue; - if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; - } + CTransaction tx; + if (!mempool.lookup(hash, tx)) continue; + if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(tx)) continue; // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; + { + LOCK(cs_mapRelay); + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + auto ret = mapRelay.insert(std::make_pair(hash, tx)); + if (ret.second) { + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, hash)); + } + } if (vInv.size() == MAX_INV_SZ) { pto->PushMessage(NetMsgType::INV, vInv); vInv.clear(); -- cgit v1.2.3 From 12c5a16c4e635799cfb6cadbca79dfa83555da72 Mon Sep 17 00:00:00 2001 From: mrbandrews Date: Thu, 2 Jun 2016 14:43:18 -0400 Subject: Catch exceptions from non-canonical encoding and print only to log --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ed157b53dc..885b1b56fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5567,6 +5567,11 @@ bool ProcessMessages(CNode* pfrom) // Allow exceptions from over-long size LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } + else if (strstr(e.what(), "non-canonical ReadCompactSize()")) + { + // Allow exceptions from non-canonical encoding + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); + } else { PrintExceptionContinue(&e, "ProcessMessages()"); -- cgit v1.2.3 From 9805f4af7ecb6becf8a146bd845fb131ffa625c9 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Sat, 30 Apr 2016 21:45:26 -0700 Subject: mapNextTx: use pointer as key, simplify value Saves about 10% of application memory usage once the mempool warms up. Since the mempool is DynamicUsage-regulated, this will translate to a larger mempool in the same amount of space. Map value type: eliminate the vin index; no users of the map need to know which input of the transaction is spending the prevout. Map key type: replace the COutPoint with a pointer to a COutPoint. A COutPoint is 36 bytes, but each COutPoint is accessible from the same map entry's value. A trivial DereferencingComparator functor allows indirect map keys, but the resulting syntax is misleading: `map.find(&outpoint)`. Implement an indirectmap that acts as a wrapper to a map that uses a DereferencingComparator, supporting a syntax that accurately reflect the container's semantics: inserts and iterators use pointers since they store pointers and need them to remain constant and dereferenceable, but lookup functions take const references. --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 11ccab253e..13da2137e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1049,9 +1049,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) { - if (pool.mapNextTx.count(txin.prevout)) + auto itConflicting = pool.mapNextTx.find(txin.prevout); + if (itConflicting != pool.mapNextTx.end()) { - const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx; + const CTransaction *ptxConflicting = itConflicting->second; if (!setConflicts.count(ptxConflicting->GetHash())) { // Allow opt-out of transaction replacement by setting -- cgit v1.2.3 From e39dc698adf46dc3306526971ca6f0f6401f30ca Mon Sep 17 00:00:00 2001 From: instagibbs Date: Fri, 3 Jun 2016 10:05:51 -0400 Subject: comment nit: miners don't vote --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c98f42d7c9..527d4f570e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1886,8 +1886,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // such nodes as they are not following the protocol. That // said during an upgrade careful thought should be taken // as to the correct behavior - we may want to continue - // peering with non-upgraded nodes even after a soft-fork - // super-majority vote has passed. + // peering with non-upgraded nodes even after soft-fork + // super-majority signaling has occurred. return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } -- cgit v1.2.3 From 8d39d7a2cf1559e0ba40681b0ab90f13ea6c0618 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 30 May 2016 17:06:24 +0200 Subject: Switch CTransaction storage in mempool to std::shared_ptr --- src/main.cpp | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 527d4f570e..d71f35bf7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4519,10 +4519,11 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } if (!push && inv.type == MSG_TX) { - int64_t txtime; + auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. - if (mempool.lookup(inv.hash, tx, txtime) && txtime <= pfrom->timeLastMempoolReq) { + if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { + tx = *txinfo.tx; push = true; } } @@ -5900,8 +5901,7 @@ bool SendMessages(CNode* pto) // Respond to BIP35 mempool requests if (fSendTrickle && pto->fSendMempool) { - std::vector vtxid; - mempool.queryHashes(vtxid); + auto vtxinfo = mempool.infoAll(); pto->fSendMempool = false; CAmount filterrate = 0; { @@ -5911,20 +5911,16 @@ bool SendMessages(CNode* pto) LOCK(pto->cs_filter); - BOOST_FOREACH(const uint256& hash, vtxid) { + for (const auto& txinfo : vtxinfo) { + const uint256& hash = txinfo.tx->GetHash(); CInv inv(MSG_TX, hash); pto->setInventoryTxToSend.erase(hash); if (filterrate) { - CFeeRate feeRate; - mempool.lookupFeeRate(hash, feeRate); - if (feeRate.GetFeePerK() < filterrate) + if (txinfo.feeRate.GetFeePerK() < filterrate) continue; } if (pto->pfilter) { - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; } pto->filterInventoryKnown.insert(hash); vInv.push_back(inv); @@ -5970,16 +5966,14 @@ bool SendMessages(CNode* pto) continue; } // Not in the mempool anymore? don't bother sending it. - CFeeRate feeRate; - if (!mempool.lookupFeeRate(hash, feeRate)) { + auto txinfo = mempool.info(hash); + if (!txinfo.tx) { continue; } - if (filterrate && feeRate.GetFeePerK() < filterrate) { + if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { continue; } - CTransaction tx; - if (!mempool.lookup(hash, tx)) continue; - if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(tx)) continue; + if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; @@ -5992,7 +5986,7 @@ bool SendMessages(CNode* pto) vRelayExpiration.pop_front(); } - auto ret = mapRelay.insert(std::make_pair(hash, tx)); + auto ret = mapRelay.insert(std::make_pair(hash, *txinfo.tx)); if (ret.second) { vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, hash)); } -- cgit v1.2.3 From f0fdda0181e1b05b66541bf235c6702c41664170 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Tue, 26 Apr 2016 17:21:22 -0700 Subject: IsInitialBlockDownload: usually avoid locking Optimistically test the latch bool before taking the lock. For all IsInitialBlockDownload calls after the first to return false, this avoids the need to lock cs_main. --- src/main.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d2b7c6bc4f..aed8a7c421 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,7 @@ #include "validationinterface.h" #include "versionbits.h" +#include #include #include @@ -1574,18 +1575,24 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) bool IsInitialBlockDownload() { const CChainParams& chainParams = Params(); + + // Once this function has returned false, it must remain false. + static std::atomic latchToFalse{false}; + // Optimization: pre-test latch before taking the lock. + if (latchToFalse.load(std::memory_order_relaxed)) + return false; + LOCK(cs_main); + if (latchToFalse.load(std::memory_order_relaxed)) + return false; if (fImporting || fReindex) return true; if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) return true; - static bool lockIBDState = false; - if (lockIBDState) - return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || std::max(chainActive.Tip()->GetBlockTime(), pindexBestHeader->GetBlockTime()) < GetTime() - nMaxTipAge); if (!state) - lockIBDState = true; + latchToFalse.store(true, std::memory_order_relaxed); return state; } -- cgit v1.2.3 From dbfb426b96fbd79fb76734c6b747ef8ee10ad5ab Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 31 May 2016 19:51:45 +0200 Subject: Optimize the relay map to use shared_ptr's * Switch mapRelay to use shared_ptr * Switch the relay code to copy mempool shared_ptr's, rather than copying the transaction itself. * Change vRelayExpiration to store mapRelay iterators rather than hashes (smaller and faster). --- src/main.cpp | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d71f35bf7e..bf0f0d8964 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,9 +80,6 @@ uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; -std::map mapRelay; -std::deque > vRelayExpiration; -CCriticalSection cs_mapRelay; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -215,6 +212,12 @@ namespace { /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads = 0; + + /** Relay map, protected by cs_main. */ + typedef std::map> MapRelay; + MapRelay mapRelay; + /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */ + std::deque> vRelayExpiration; } // anon namespace ////////////////////////////////////////////////////////////////////////////// @@ -4505,31 +4508,24 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } } - else if (inv.IsKnownType()) + else if (inv.type == MSG_TX) { - CTransaction tx; // Send stream from relay memory bool push = false; - { - LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv.hash); - if (mi != mapRelay.end()) { - tx = (*mi).second; - push = true; - } - } - if (!push && inv.type == MSG_TX) { + auto mi = mapRelay.find(inv.hash); + if (mi != mapRelay.end()) { + pfrom->PushMessage(NetMsgType::TX, *mi->second); + push = true; + } else { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { - tx = *txinfo.tx; + pfrom->PushMessage(NetMsgType::TX, *txinfo.tx); push = true; } } - if (push) { - pfrom->PushMessage(inv.GetCommand(), tx); - } else { + if (!push) { vNotFound.push_back(inv); } } @@ -5978,7 +5974,6 @@ bool SendMessages(CNode* pto) vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; { - LOCK(cs_mapRelay); // Expire old relay messages while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) { @@ -5986,9 +5981,9 @@ bool SendMessages(CNode* pto) vRelayExpiration.pop_front(); } - auto ret = mapRelay.insert(std::make_pair(hash, *txinfo.tx)); + auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx))); if (ret.second) { - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, hash)); + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, ret.first)); } } if (vInv.size() == MAX_INV_SZ) { -- cgit v1.2.3 From e9b4780b292122fd727426471f025ec3d3eb7b08 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 31 May 2016 20:34:27 +0200 Subject: Optimization: don't check the mempool at all if no mempool req ever --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bf0f0d8964..68368e402b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4516,7 +4516,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (mi != mapRelay.end()) { pfrom->PushMessage(NetMsgType::TX, *mi->second); push = true; - } else { + } else if (pfrom->timeLastMempoolReq) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. -- cgit v1.2.3 From c2a4724642400bc9200aeef4c725b5c07eee9d90 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 31 May 2016 20:35:44 +0200 Subject: Optimization: use usec in expiration and reuse nNow --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 68368e402b..db0580bda6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5975,7 +5975,7 @@ bool SendMessages(CNode* pto) nRelayedTransactions++; { // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow) { mapRelay.erase(vRelayExpiration.front().second); vRelayExpiration.pop_front(); @@ -5983,7 +5983,7 @@ bool SendMessages(CNode* pto) auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx))); if (ret.second) { - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, ret.first)); + vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first)); } } if (vInv.size() == MAX_INV_SZ) { -- cgit v1.2.3 From d3df40e51a29bd98830043dd19829126390d1bc4 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 23 Apr 2016 23:30:20 +0000 Subject: Implement BIP 9 GBT changes - BIP9DeploymentInfo struct for static deployment info - VersionBitsDeploymentInfo: Avoid C++11ism by commenting parameter names - getblocktemplate: Make sure to set deployments in the version if it is LOCKED_IN - In this commit, all rules are considered required for clients to support --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index f74cb30574..623d77985c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2122,7 +2122,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } // Protected by cs_main -static VersionBitsCache versionbitscache; +VersionBitsCache versionbitscache; int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) { -- cgit v1.2.3 From d3d02d51453943bfe3a9edb944eb48f9f1e01aca Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 6 Jun 2016 14:42:34 -0700 Subject: drop vAddrToSend after sending big addr message We send a newly-accepted peer a 1000-entry addr message, and then only use vAddrToSend for small messages. Deallocate vAddrToSend after it's been used for the big message to save about 40 kB per connected inbound peer. --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index fc8e72a7db..5fff4187ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5713,6 +5713,9 @@ bool SendMessages(CNode* pto) pto->vAddrToSend.clear(); if (!vAddr.empty()) pto->PushMessage(NetMsgType::ADDR, vAddr); + // we only send the big addr message once + if (pto->vAddrToSend.capacity() > 40) + pto->vAddrToSend.shrink_to_fit(); } CNodeState &state = *State(pto->GetId()); -- cgit v1.2.3 From 8b78486d02ed3c1c217d79ebca78a70d73e0d570 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 2 Jun 2016 13:52:09 +0200 Subject: Log/report in 10% steps during VerifyDB --- src/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 162c8b986c..a712698210 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3873,10 +3873,18 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; + int reportDone = 0; + LogPrintf("[0%]..."); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); + int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); + if (reportDone < percentageDone/10) { + // report every 10% step + LogPrintf("[%d%%]...", percentageDone); + reportDone = percentageDone/10; + } + uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { @@ -3934,6 +3942,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } } + LogPrintf("[DONE].\n"); LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); return true; -- cgit v1.2.3 From 288d85ddf2e0a0c9d25a23db56052883170466d0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 7 Jun 2016 13:44:56 +0200 Subject: Get rid of CTxMempool::lookup() entirely --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index db0580bda6..b581ece09a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1445,8 +1445,10 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P LOCK(cs_main); - if (mempool.lookup(hash, txOut)) + std::shared_ptr ptx = mempool.get(hash); + if (ptx) { + txOut = *ptx; return true; } -- cgit v1.2.3 From 888483098e60f2a944f1d246bbfec4d14a2975f8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 7 Jun 2016 16:29:03 +0200 Subject: Use C++11 thread-safe static initializers --- src/main.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bf6e6d04b5..da140cffac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4784,11 +4784,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats - static uint64_t salt0 = 0, salt1 = 0; - while (salt0 == 0 && salt1 == 0) { - GetRandBytes((unsigned char*)&salt0, sizeof(salt0)); - GetRandBytes((unsigned char*)&salt1, sizeof(salt1)); - } + static const uint64_t salt0 = GetRand(std::numeric_limits::max()); + static const uint64_t salt1 = GetRand(std::numeric_limits::max()); uint64_t hashAddr = addr.GetHash(); multimap mapMix; const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); -- cgit v1.2.3 From 1b0bcc5f9573406bff1c3ffaf73826b0142d23cc Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 10 Jun 2016 16:07:14 +0200 Subject: Track orphan by prev COutPoint rather than prev hash --- src/main.cpp | 58 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 22 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6d006e8789..17867c869c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -88,12 +88,21 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); FeeFilterRounder filterRounder(::minRelayTxFee); +struct IteratorComparator +{ + template + bool operator()(const I& a, const I& b) + { + return &(*a) < &(*b); + } +}; + struct COrphanTx { CTransaction tx; NodeId fromPeer; }; map mapOrphanTransactions GUARDED_BY(cs_main); -map > mapOrphanTransactionsByPrev GUARDED_BY(cs_main); +map::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main); void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** @@ -632,31 +641,33 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c return false; } - mapOrphanTransactions[hash].tx = tx; - mapOrphanTransactions[hash].fromPeer = peer; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer}); + assert(ret.second); + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); + } - LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), + LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; } -void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { map::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) - return; + return 0; BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin) { - map >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); + auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); if (itPrev == mapOrphanTransactionsByPrev.end()) continue; - itPrev->second.erase(hash); + itPrev->second.erase(it); if (itPrev->second.empty()) mapOrphanTransactionsByPrev.erase(itPrev); } mapOrphanTransactions.erase(it); + return 1; } void EraseOrphansFor(NodeId peer) @@ -668,8 +679,7 @@ void EraseOrphansFor(NodeId peer) map::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid if (maybeErase->second.fromPeer == peer) { - EraseOrphanTx(maybeErase->second.tx.GetHash()); - ++nErased; + nErased += EraseOrphanTx(maybeErase->second.tx.GetHash()); } } if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); @@ -5019,7 +5029,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - vector vWorkQueue; + deque vWorkQueue; vector vEraseQueue; CTransaction tx; vRecv >> tx; @@ -5038,7 +5048,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); RelayTransaction(tx); - vWorkQueue.push_back(inv.hash); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + vWorkQueue.emplace_back(inv.hash, i); + } LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, @@ -5047,18 +5059,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Recursively process any orphan transactions that depended on this one set setMisbehaving; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - map >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); + while (!vWorkQueue.empty()) { + auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front()); + vWorkQueue.pop_front(); if (itByPrev == mapOrphanTransactionsByPrev.end()) continue; - for (set::iterator mi = itByPrev->second.begin(); + for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) { - const uint256& orphanHash = *mi; - const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; - NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; + const CTransaction& orphanTx = (*mi)->second.tx; + const uint256& orphanHash = orphanTx.GetHash(); + NodeId fromPeer = (*mi)->second.fromPeer; bool fMissingInputs2 = false; // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get @@ -5071,7 +5083,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx); - vWorkQueue.push_back(orphanHash); + for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { + vWorkQueue.emplace_back(orphanHash, i); + } vEraseQueue.push_back(orphanHash); } else if (!fMissingInputs2) -- cgit v1.2.3 From db0ffe80a0919653f058ab0f1fc735f46bb902f3 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Fri, 10 Jun 2016 20:41:49 +0000 Subject: This eliminates the primary leak that causes the orphan map to always grow to its maximum size. This does not go so far as to attempt to connect orphans made connectable by a new block. Keeping the orphan map less full helps improve the reliability of relaying chains of transactions. --- src/main.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 17867c869c..c80a4ac922 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2345,6 +2345,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + std::vector vOrphanErase; std::vector prevheights; CAmount nFees = 0; int nInputs = 0; @@ -2377,6 +2378,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight; } + // Which orphan pool entries must we evict? + for (size_t j = 0; j < tx.vin.size(); j++) { + auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout); + if (itByPrev == mapOrphanTransactionsByPrev.end()) continue; + for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) { + const CTransaction& orphanTx = (*mi)->second.tx; + const uint256& orphanHash = orphanTx.GetHash(); + vOrphanErase.push_back(orphanHash); + } + } + if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); @@ -2464,6 +2476,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.vtx[0].GetHash(); + // Erase orphan transactions include or precluded by this block + if (vOrphanErase.size()) { + int nErased = 0; + BOOST_FOREACH(uint256 &orphanHash, vOrphanErase) { + nErased += EraseOrphanTx(orphanHash); + } + LogPrint("mempool", "Erased %d orphan tx included or conflicted by block\n", nErased); + } + int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); -- cgit v1.2.3 From 3764dec36c815267174951a4c64e17c07ee6302f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Mar 2016 18:58:00 +0100 Subject: Keep addrman's nService bits consistent with outbound observations --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6092e7a12e..ac89945bf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4612,6 +4612,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrFrom; uint64_t nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (!pfrom->fInbound) + { + addrman.SetServices(pfrom->addr, pfrom->nServices); + } if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version -- cgit v1.2.3 From fc83f181530fb566726e5f3f4197fc5586d77fd8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Mar 2016 19:09:22 +0100 Subject: Verify that outbound connections have expected services --- src/main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ac89945bf2..7818056d21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4616,6 +4616,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { addrman.SetServices(pfrom->addr, pfrom->nServices); } + if (pfrom->nServicesExpected & ~pfrom->nServices) + { + LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected); + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + strprintf("Expected to offer services %08x", pfrom->nServicesExpected)); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version -- cgit v1.2.3 From 5e7ab16d29ac66a5a5753dd4f59b6fb12e60654e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Mar 2016 13:31:25 +0100 Subject: Only store and connect to NODE_NETWORK nodes --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7818056d21..bcaf095ab3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4785,6 +4785,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { boost::this_thread::interruption_point(); + if (!(addr.nServices & NODE_NETWORK)) + continue; + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); -- cgit v1.2.3 From ee06e04369c37da21e048fda849cce2a1f066f84 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 8 Jun 2016 19:12:22 +0200 Subject: Introduce enum ServiceFlags for service flags --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bcaf095ab3..c1569545b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4611,7 +4611,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + uint64_t nServiceInt; + vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe; + pfrom->nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { addrman.SetServices(pfrom->addr, pfrom->nServices); -- cgit v1.2.3 From ecd7fd37c888f8ebc64cf3d92272975b37ae54ca Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 13 Jun 2016 16:01:21 +0200 Subject: Introduce REQUIRED_SERVICES constant --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c1569545b4..62012bf567 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4787,7 +4787,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { boost::this_thread::interruption_point(); - if (!(addr.nServices & NODE_NETWORK)) + if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) -- cgit v1.2.3 From ab8be98fdb25b678a8cd7e89adf06d1b1f6bdd62 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 14 Jun 2016 12:26:59 +0100 Subject: Remove bad chain alert partition check As per meeting 2016-03-31 https://bitcoincore.org/en/meetings/2016/03/31/#bad-chain-alerts The partition checker was producing huge number of false-positives and was disabled in 0.12.1 on the understanding it would either be fixed in 0.13 or removed entirely from master if not. --- src/main.cpp | 62 ------------------------------------------------------------ 1 file changed, 62 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 62012bf567..91da3f47a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2118,68 +2118,6 @@ void ThreadScriptCheck() { scriptcheckqueue.Thread(); } -// -// Called periodically asynchronously; alerts if it smells like -// we're being fed a bad chain (blocks being generated much -// too slowly or too quickly). -// -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, - int64_t nPowTargetSpacing) -{ - if (bestHeader == NULL || initialDownloadCheck()) return; - - static int64_t lastAlertTime = 0; - int64_t now = GetAdjustedTime(); - if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - - const int SPAN_HOURS=4; - const int SPAN_SECONDS=SPAN_HOURS*60*60; - int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - - boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); - - std::string strWarning; - int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - - LOCK(cs); - const CBlockIndex* i = bestHeader; - int nBlocks = 0; - while (i->GetBlockTime() >= startTime) { - ++nBlocks; - i = i->pprev; - if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed - } - - // 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); - - // Aim for one false-positive about every fifty years of normal running: - const int FIFTY_YEARS = 50*365*24*60*60; - double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); - - if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) - { - // Many fewer blocks than expected: alert! - strWarning = strprintf(_("WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)"), - nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); - } - else if (p <= alertThreshold && nBlocks > BLOCKS_EXPECTED) - { - // Many more blocks than expected: alert! - strWarning = strprintf(_("WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)"), - nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); - } - if (!strWarning.empty()) - { - strMiscWarning = strWarning; - AlertNotify(strWarning); - lastAlertTime = now; - } -} - // Protected by cs_main VersionBitsCache versionbitscache; -- cgit v1.2.3 From 11cc143895e730002749f0881c4c95635fa54bd5 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 11 Jun 2016 00:26:16 +0000 Subject: Adds an expiration time for orphan tx. This prevents higher order orphans and other junk from holding positions in the orphan map. Parents delayed twenty minutes are more are unlikely to ever arrive. The freed space will improve the orphan matching success rate for other transactions. --- src/main.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c80a4ac922..44d96d614e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -100,6 +100,7 @@ struct IteratorComparator struct COrphanTx { CTransaction tx; NodeId fromPeer; + int64_t nTimeExpire; }; map mapOrphanTransactions GUARDED_BY(cs_main); map::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main); @@ -641,7 +642,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c return false; } - auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer}); + auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); assert(ret.second); BOOST_FOREACH(const CTxIn& txin, tx.vin) { mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); @@ -689,6 +690,26 @@ void EraseOrphansFor(NodeId peer) unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { unsigned int nEvicted = 0; + static int64_t nNextSweep; + int64_t nNow = GetTime(); + if (nNextSweep <= nNow) { + // Sweep out expired orphan pool entries: + int nErased = 0; + int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; + map::iterator iter = mapOrphanTransactions.begin(); + while (iter != mapOrphanTransactions.end()) + { + map::iterator maybeErase = iter++; + if (maybeErase->second.nTimeExpire <= nNow) { + nErased += EraseOrphanTx(maybeErase->second.tx.GetHash()); + } else { + nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime); + } + } + // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan. + nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; + if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased); + } while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: -- cgit v1.2.3 From 8c99d1b525562ab3b733e9d7ef770882646bad5c Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 13 Jun 2016 17:27:44 +0000 Subject: Treat orphans as implicit inv for parents, discard when parents rejected. An orphan whos parents were rejected is never going to connect, so there is little utility in keeping it. Orphans also helpfully tell us what we're missing, so go ahead and treat it as INVed. --- src/main.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 44d96d614e..6a2290bc05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5156,13 +5156,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else if (fMissingInputs) { - AddOrphanTx(tx, pfrom->GetId()); + bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + if (recentRejects->contains(txin.prevout.hash)) { + fRejectedParents = true; + break; + } + } + if (!fRejectedParents) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + CInv inv(MSG_TX, txin.prevout.hash); + pfrom->AddInventoryKnown(inv); + if (!AlreadyHave(inv)) pfrom->AskFor(inv); + } + AddOrphanTx(tx, pfrom->GetId()); - // DoS prevention: do not allow mapOrphanTransactions to grow unbounded - unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); - if (nEvicted > 0) - LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); + if (nEvicted > 0) + LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + } else { + LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); + } } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); -- cgit v1.2.3 From 54326a6808a7026eef9d3a26f91f93b77f00a793 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 11 Jun 2016 00:28:48 +0000 Subject: Increase maximum orphan size to 100,000 bytes. Although this increases node memory usage in the worst case by perhaps 30MB, the current behavior causes severe issues with dependent tx relay. --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6a2290bc05..d4ab32744f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -633,10 +633,10 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c // large transaction with a missing parent then we assume // it will rebroadcast it later, after the parent transaction(s) // have been mined or received. - // 10,000 orphans, each of which is at most 5,000 bytes big is - // at most 500 megabytes of orphans: + // 100 orphans, each of which is at most 99,999 bytes big is + // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case): unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz > 5000) + if (sz >= MAX_STANDARD_TX_SIZE) { LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; -- cgit v1.2.3 From 5d0ca81f7422b7eac0a5129c6fbccad77e36b85d Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 22 May 2016 05:55:15 +0000 Subject: Add recently accepted blocks and txn to AttemptToEvictConnection. This protects any not-already-protected peers who were the most recent four to relay transactions and most recent four to send blocks to us. --- src/main.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6d006e8789..24ae61b74d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3449,8 +3449,9 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } /** 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, const CDiskBlockPos* dbp) +static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) { + if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); CBlockIndex *pindexDummy = NULL; @@ -3479,6 +3480,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } + if (fNewBlock) *fNewBlock = true; if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { @@ -3526,7 +3528,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) { { LOCK(cs_main); @@ -3535,9 +3537,11 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c // Store to disk CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp); + bool fNewBlock = false; + bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); + if (fNewBlock) pfrom->nLastBlockTime = GetTime(); } CheckBlockIndex(chainparams.GetConsensus()); if (!ret) @@ -4107,7 +4111,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { LOCK(cs_main); CValidationState state; - if (AcceptBlock(block, state, chainparams, NULL, true, dbp)) + if (AcceptBlock(block, state, chainparams, NULL, true, dbp, NULL)) nLoaded++; if (state.IsError()) break; @@ -4140,7 +4144,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB head.ToString()); LOCK(cs_main); CValidationState dummy; - if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second)) + if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second, NULL)) { nLoaded++; queue.push_back(block.GetHash()); @@ -5040,6 +5044,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); vWorkQueue.push_back(inv.hash); + pfrom->nLastTXTime = GetTime(); + LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, tx.GetHash().ToString(), -- cgit v1.2.3 From 7c29ec94490a2fb0b5f9aec56c89d0a9a58eec79 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 10 Apr 2016 18:52:32 -0700 Subject: If AcceptBlockHeader returns true, pindex will be set. Assert this instead of checking (and then dref'ing later anyway) to make sure no one thinks they can change that postcondition of AcceptBlockHeader.. --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index bdb3457f8e..378d2c7022 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5218,10 +5218,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - if (pindexLast) - UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + assert(pindexLast); + UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - if (nCount == MAX_HEADERS_RESULTS && pindexLast && hasNewHeaders) { + if (nCount == MAX_HEADERS_RESULTS && hasNewHeaders) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. -- cgit v1.2.3 From cbda71cf04ef6f2abe6eaa56c3140a6f5cff4feb Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 8 Jun 2016 16:12:52 -0700 Subject: Move context-required checks from CheckBlockHeader to Contextual... --- src/main.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 378d2c7022..7d267bef7d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2241,7 +2241,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); // verify that the view's current state corresponds to the previous block @@ -3258,20 +3258,16 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW) +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW) { // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); - // Check timestamp - if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) - return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); - return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3280,7 +3276,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, consensusParams, nAdjustedTime, fCheckPOW)) + if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW)) return false; // Check the merkle root. @@ -3346,7 +3342,7 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev) +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime) { // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) @@ -3356,6 +3352,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); + // Check timestamp + if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) + return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); + // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) @@ -3420,7 +3420,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return true; } - if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), GetAdjustedTime())) + if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index @@ -3436,7 +3436,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } if (pindex == NULL) @@ -3569,9 +3569,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, indexDummy.nHeight = pindexPrev->nHeight + 1; // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); - if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), fCheckPOW, fCheckMerkleRoot)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); @@ -3916,7 +3916,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, 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, chainparams.GetConsensus(), GetAdjustedTime())) + if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity -- cgit v1.2.3 From 9c837d5468063917009aef8569ce6ce9ddd340d2 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 11 Apr 2016 01:00:17 -0700 Subject: Add sender-side protocol implementation for CMPCTBLOCK stuff --- src/main.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 15 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7d267bef7d..911c0a648b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -274,6 +274,10 @@ struct CNodeState { bool fPreferredDownload; //! Whether this peer wants invs or headers (when possible) for block announcements. bool fPreferHeaders; + //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements. + bool fPreferHeaderAndIDs; + //! Whether this peer will send us cmpctblocks if we request them + bool fProvidesHeaderAndIDs; CNodeState() { fCurrentlyConnected = false; @@ -290,6 +294,8 @@ struct CNodeState { nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; fPreferHeaders = false; + fPreferHeaderAndIDs = false; + fProvidesHeaderAndIDs = false; } }; @@ -4454,7 +4460,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam boost::this_thread::interruption_point(); it++; - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) { bool send = false; BlockMap::iterator mi = mapBlockIndex.find(inv.hash); @@ -4496,7 +4502,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) pfrom->PushMessage(NetMsgType::BLOCK, block); - else // MSG_FILTERED_BLOCK) + else if (inv.type == MSG_FILTERED_BLOCK) { LOCK(pfrom->cs_filter); if (pfrom->pfilter) @@ -4516,6 +4522,18 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // else // no response } + else if (inv.type == MSG_CMPCT_BLOCK) + { + // If a peer is asking for old blocks, we're almost guaranteed + // they wont have a useful mempool to match against a compact block, + // and we dont feel like constructing the object for them, so + // instead we respond with the full, non-compact block. + if (mi->second->nHeight >= chainActive.Height() - 10) { + CBlockHeaderAndShortTxIDs cmpctblock(block); + pfrom->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock); + } else + pfrom->PushMessage(NetMsgType::BLOCK, block); + } // Trigger the peer node to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) @@ -4839,6 +4857,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, State(pfrom->GetId())->fPreferHeaders = true; } + else if (strCommand == NetMsgType::SENDCMPCT) + { + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = 1; + vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; + if (nCMPCTBLOCKVersion == 1) { + LOCK(cs_main); + State(pfrom->GetId())->fProvidesHeaderAndIDs = true; + State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK; + } + } + else if (strCommand == NetMsgType::INV) { @@ -4982,6 +5012,39 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + else if (strCommand == NetMsgType::GETBLOCKTXN) + { + BlockTransactionsRequest req; + vRecv >> req; + + BlockMap::iterator it = mapBlockIndex.find(req.blockhash); + if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) { + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id); + return true; + } + + if (it->second->nHeight < chainActive.Height() - 10) { + LogPrint("net", "Peer %d sent us a getblocktxn for a block > 10 deep", pfrom->id); + return true; + } + + CBlock block; + assert(ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())); + + BlockTransactions resp(req); + for (size_t i = 0; i < req.indexes.size(); i++) { + if (req.indexes[i] >= block.vtx.size()) { + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id); + return true; + } + resp.txn[i] = block.vtx[req.indexes[i]]; + } + pfrom->PushMessage(NetMsgType::BLOCKTXN, resp); + } + + else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; @@ -5824,7 +5887,9 @@ bool SendMessages(CNode* pto) // add all to the inv queue. LOCK(pto->cs_inventory); vector vHeaders; - bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); + bool fRevertToInv = ((!state.fPreferHeaders && + (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || + pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); CBlockIndex *pBestIndex = NULL; // last header queued for delivery ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date @@ -5876,6 +5941,33 @@ bool SendMessages(CNode* pto) } } } + if (!fRevertToInv && !vHeaders.empty()) { + if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) { + // We only send up to 1 block as header-and-ids, as otherwise + // probably means we're doing an initial-ish-sync or they're slow + LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__, + vHeaders.front().GetHash().ToString(), pto->id); + //TODO: Shouldn't need to reload block from disk, but requires refactor + CBlock block; + assert(ReadBlockFromDisk(block, pBestIndex, consensusParams)); + CBlockHeaderAndShortTxIDs cmpctblock(block); + pto->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock); + state.pindexBestHeaderSent = pBestIndex; + } else if (state.fPreferHeaders) { + 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; + } else + fRevertToInv = true; + } 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 @@ -5901,18 +5993,6 @@ bool SendMessages(CNode* pto) 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(); } -- cgit v1.2.3 From d25cd3ec4e8961c5f36c29a65395f52d0db294c5 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 14 Apr 2016 17:45:49 -0700 Subject: Add receiver-side protocol implementation for CMPCTBLOCK stuff --- src/main.cpp | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 207 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 911c0a648b..26b215f94c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "addrman.h" #include "arith_uint256.h" +#include "blockencodings.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -197,8 +198,9 @@ namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; - CBlockIndex* pindex; //!< Optional. - bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. + CBlockIndex* pindex; //!< Optional. + bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. + std::unique_ptr partialBlock; //!< Optional, used for CMPCTBLOCK downloads }; map::iterator> > mapBlocksInFlight; @@ -364,6 +366,7 @@ void FinalizeNode(NodeId nodeid) { // Requires cs_main. // Returns a bool indicating whether we requested this block. +// Also used if a block was /not/ received and timed out or started with another peer bool MarkBlockAsReceived(const uint256& hash) { map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { @@ -387,17 +390,26 @@ bool MarkBlockAsReceived(const uint256& hash) { } // Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { +// returns false, still setting pit, if the block was already in flight from the same peer +// pit will only be valid as long as the same cs_main lock is being held +bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list::iterator **pit = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); + // Short-circuit most stuff in case its from the same node + map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) { + *pit = &itInFlight->second.second; + return false; + } + // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - QueuedBlock newentry = {hash, pindex, pindex != NULL}; - list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), + {hash, pindex, pindex != NULL, std::unique_ptr(pit ? new PartiallyDownloadedBlock(&mempool) : NULL)}); state->nBlocksInFlight++; - state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + state->nBlocksInFlightValidHeaders += it->fValidatedHeaders; if (state->nBlocksInFlight == 1) { // We're starting a block download (batch) from this peer. state->nDownloadingSince = GetTimeMicros(); @@ -405,7 +417,10 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) { nPeersWithValidatedDownloads++; } - mapBlocksInFlight[hash] = std::make_pair(nodeid, it); + itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first; + if (pit) + *pit = &itInFlight->second.second; + return true; } /** Check whether the last unknown block a peer advertised is not yet known. */ @@ -4783,6 +4798,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // nodes) pfrom->PushMessage(NetMsgType::SENDHEADERS); } + if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { + // Tell our peer we are willing to provide version-1 cmpctblocks + // However, we do not request new block announcements using + // cmpctblock messages. + // We send this to non-NODE NETWORK peers as well, because + // they may wish to request compact blocks from us + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = 1; + pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + } } @@ -4915,7 +4940,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vToFetch.push_back(inv); + if (nodestate->fProvidesHeaderAndIDs) + vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash)); + else + vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out // later (within the same cs_main lock, though). MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); @@ -5232,6 +5260,174 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing + { + CBlockHeaderAndShortTxIDs cmpctblock; + vRecv >> cmpctblock; + + LOCK(cs_main); + + if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { + // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers + if (!IsInitialBlockDownload()) + pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()); + return true; + } + + CBlockIndex *pindex = NULL; + CValidationState state; + if (!AcceptBlockHeader(cmpctblock.header, state, chainparams, &pindex)) { + int nDoS; + if (state.IsInvalid(nDoS)) { + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->id); + return true; + } + } + + // If AcceptBlockHeader returned true, it set pindex + assert(pindex); + UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); + + std::map::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash()); + bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end(); + + if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here + return true; + + if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better + pindex->nTx != 0) { // We had this block at some point, but pruned it + if (fAlreadyInFlight) { + // We requested this block for some reason, but our mempool will probably be useless + // so we just grab the block via normal getdata + std::vector vInv(1); + vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); + pfrom->PushMessage(NetMsgType::GETDATA, vInv); + return true; + } + } + + // If we're not close to tip yet, give up and let parallel block fetch work its magic + if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus())) + return true; + + CNodeState *nodestate = State(pfrom->GetId()); + + // We want to be a bit conservative just to be extra careful about DoS + // possibilities in compact block processing... + if (pindex->nHeight <= chainActive.Height() + 2) { + if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || + (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) { + list::iterator *queuedBlockIt = NULL; + if (!MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) { + if (!(*queuedBlockIt)->partialBlock) + (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool)); + else { + // The block was already in flight using compact blocks from the same peer + LogPrint("net", "Peer sent us compact block we were already syncing!\n"); + return true; + } + } + + PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock; + ReadStatus status = partialBlock.InitData(cmpctblock); + if (status == READ_STATUS_INVALID) { + MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us invalid compact block\n", pfrom->id); + return true; + } else if (status == READ_STATUS_FAILED) { + // Duplicate txindexes, the block is now in-flight, so just request it + std::vector vInv(1); + vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); + pfrom->PushMessage(NetMsgType::GETDATA, vInv); + return true; + } + + BlockTransactionsRequest req; + for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { + if (!partialBlock.IsTxAvailable(i)) + req.indexes.push_back(i); + } + if (req.indexes.empty()) { + // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions) + BlockTransactions txn; + txn.blockhash = cmpctblock.header.GetHash(); + CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION); + blockTxnMsg << txn; + return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams); + } else { + req.blockhash = pindex->GetBlockHash(); + pfrom->PushMessage(NetMsgType::GETBLOCKTXN, req); + } + } + } else { + if (fAlreadyInFlight) { + // We requested this block, but its far into the future, so our + // mempool will probably be useless - request the block normally + std::vector vInv(1); + vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); + pfrom->PushMessage(NetMsgType::GETDATA, vInv); + return true; + } else { + // If this was an announce-cmpctblock, we want the same treatment as a header message + // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions) + std::vector headers; + headers.push_back(cmpctblock.header); + CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION); + vHeadersMsg << headers; + return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams); + } + } + + CheckBlockIndex(chainparams.GetConsensus()); + } + + else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing + { + BlockTransactions resp; + vRecv >> resp; + + LOCK(cs_main); + + map::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash); + if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock || + it->second.first != pfrom->GetId()) { + LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id); + return true; + } + + PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock; + CBlock block; + ReadStatus status = partialBlock.FillBlock(block, resp.txn); + if (status == READ_STATUS_INVALID) { + MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id); + return true; + } else if (status == READ_STATUS_FAILED) { + // Might have collided, fall back to getdata now :( + std::vector invs; + invs.push_back(CInv(MSG_BLOCK, resp.blockhash)); + pfrom->PushMessage(NetMsgType::GETDATA, invs); + } else { + CValidationState state; + ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL); + int nDoS; + if (state.IsInvalid(nDoS)) { + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes + pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash()); + if (nDoS > 0) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), nDoS); + } + } + } + } + + else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing { std::vector headers; @@ -5334,6 +5530,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { + if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { + vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); + } pfrom->PushMessage(NetMsgType::GETDATA, vGetData); } } -- cgit v1.2.3 From 2f34a2e476ae9d0585c67e275d238e44119c56cf Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 6 May 2016 18:33:46 -0400 Subject: Get our "best three" peers to announce blocks using cmpctblocks --- src/main.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 26b215f94c..60a33f9c2f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -204,6 +204,9 @@ namespace { }; map::iterator> > mapBlocksInFlight; + /** Stack of nodes which we have set to announce using compact blocks */ + list lNodesAnnouncingHeaderAndIDs; + /** Number of preferable block download peers. */ int nPreferredDownload = 0; @@ -456,6 +459,28 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } +void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) { + if (nodestate->fProvidesHeaderAndIDs) { + BOOST_FOREACH(const NodeId nodeid, lNodesAnnouncingHeaderAndIDs) + if (nodeid == pfrom->GetId()) + return; + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = 1; + if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { + // As per BIP152, we only get 3 of our peers to announce + // blocks using compact encodings. + CNode* pnodeStop = FindNode(lNodesAnnouncingHeaderAndIDs.front()); + if (pnodeStop) { + pnodeStop->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + lNodesAnnouncingHeaderAndIDs.pop_front(); + } + } + fAnnounceUsingCMPCTBLOCK = true; + pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + } +} + // Requires cs_main bool CanDirectFetch(const Consensus::Params &consensusParams) { @@ -5531,6 +5556,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (vGetData.size() > 0) { if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { + // We seem to be rather well-synced, so it appears pfrom was the first to provide us + // with this block! Let's get them to announce using compact blocks in the future. + MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom); + // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } pfrom->PushMessage(NetMsgType::GETDATA, vGetData); -- cgit v1.2.3 From 48efec82f3a18364c019577495914fcebb425e35 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 21 Jun 2016 16:09:46 -0700 Subject: Fix some minor compact block issues that came up in review --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 60a33f9c2f..f5b1410114 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4613,7 +4613,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) break; } } @@ -5077,8 +5077,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - if (it->second->nHeight < chainActive.Height() - 10) { - LogPrint("net", "Peer %d sent us a getblocktxn for a block > 10 deep", pfrom->id); + if (it->second->nHeight < chainActive.Height() - 15) { + LogPrint("net", "Peer %d sent us a getblocktxn for a block > 15 deep", pfrom->id); return true; } -- cgit v1.2.3 From 7030d9eb47254499bba14f1c00abc6bf493efd91 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:32:04 +0100 Subject: BIP144: Serialization, hashes, relay (sender side) Contains refactorings by Eric Lombrozo. Contains fixup by Nicolas Dorier. Contains cleanup of CInv::GetCommand by Alex Morcos --- src/main.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ae195ecc40..2184940232 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1029,8 +1029,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); - // Size limits - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values @@ -3396,7 +3396,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // because we receive the wrong transactions for it. // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // First transaction must be coinbase, the rest must not be @@ -4508,6 +4508,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) switch (inv.type) { case MSG_TX: + case MSG_WITNESS_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) @@ -4528,6 +4529,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) pcoinsTip->HaveCoinsInCache(inv.hash); } case MSG_BLOCK: + case MSG_WITNESS_BLOCK: return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one @@ -4552,7 +4554,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam boost::this_thread::interruption_point(); it++; - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { bool send = false; BlockMap::iterator mi = mapBlockIndex.find(inv.hash); @@ -4593,6 +4595,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (!ReadBlockFromDisk(block, (*mi).second, consensusParams)) assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block); + else if (inv.type == MSG_WITNESS_BLOCK) pfrom->PushMessage(NetMsgType::BLOCK, block); else if (inv.type == MSG_FILTERED_BLOCK) { @@ -4609,7 +4613,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) - pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]); + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, block.vtx[pair.first]); } // else // no response @@ -4622,9 +4626,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // instead we respond with the full, non-compact block. if (mi->second->nHeight >= chainActive.Height() - 10) { CBlockHeaderAndShortTxIDs cmpctblock(block); - pfrom->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock); + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); } else - pfrom->PushMessage(NetMsgType::BLOCK, block); + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block); } // Trigger the peer node to send a getblocks request for the next batch of inventory @@ -4640,20 +4644,20 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } } - else if (inv.type == MSG_TX) + else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) { // Send stream from relay memory bool push = false; auto mi = mapRelay.find(inv.hash); if (mi != mapRelay.end()) { - pfrom->PushMessage(NetMsgType::TX, *mi->second); + pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *mi->second); push = true; } else if (pfrom->timeLastMempoolReq) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { - pfrom->PushMessage(NetMsgType::TX, *txinfo.tx); + pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *txinfo.tx); push = true; } } @@ -4665,7 +4669,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) break; } } @@ -5146,7 +5150,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } resp.txn[i] = block.vtx[req.indexes[i]]; } - pfrom->PushMessage(NetMsgType::BLOCKTXN, resp); + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp); } -- cgit v1.2.3 From 449f9b8debcceb61a92043bc7031528a53627c47 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 8 Nov 2015 01:16:45 +0100 Subject: BIP141: Witness program --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 2184940232..d1945a5094 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1867,7 +1867,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; + if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { return false; } return true; -- cgit v1.2.3 From 8b49040854be2e26b66366aeae1cba4716f93d93 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:42:38 +0100 Subject: BIP141: Commitment structure and deployment Includes a fix by Suhas Daftuar and LongShao007 --- src/main.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 138 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d1945a5094..8d91d6d625 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -293,6 +293,8 @@ struct CNodeState { bool fPreferHeaderAndIDs; //! Whether this peer will send us cmpctblocks if we request them bool fProvidesHeaderAndIDs; + //! Whether this peer can give us witnesses + bool fHaveWitness; CNodeState() { fCurrentlyConnected = false; @@ -1119,6 +1121,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); } + // Don't accept witness transactions before the final threshold passes + if (!tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) { + return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); + } + // 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. @@ -1459,8 +1466,17 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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)) - return false; // state filled in by CheckInputs + if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { + // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we + // need to turn both off, and compare against just turning off CLEANSTACK + // to see if the failure is specifically due to witness validation. + if (CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && + !CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_CLEANSTACK, true)) { + // Only the witness is wrong, so the transaction itself may be fine. + state.SetCorruptionPossible(); + } + return false; + } // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -2406,6 +2422,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } + // Start enforcing WITNESS rules using versionbits logic. + if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus())) { + flags |= SCRIPT_VERIFY_WITNESS; + } + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); @@ -3441,6 +3462,71 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } +bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE); +} + +// Compute at which vout of the block's coinbase transaction the witness +// commitment occurs, or -1 if not found. +static int GetWitnessCommitmentIndex(const CBlock& block) +{ + int commitpos = -1; + for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + return commitpos; +} + +void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + int commitpos = GetWitnessCommitmentIndex(block); + static const std::vector nonce(32, 0x00); + if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) { + block.vtx[0].wit.vtxinwit.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce; + } +} + +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + std::vector commitment; + int commitpos = GetWitnessCommitmentIndex(block); + bool fHaveWitness = false; + for (size_t t = 1; t < block.vtx.size(); t++) { + if (!block.vtx[t].wit.IsNull()) { + fHaveWitness = true; + break; + } + } + std::vector ret(32, 0x00); + if (fHaveWitness && IsWitnessEnabled(pindexPrev, consensusParams)) { + if (commitpos == -1) { + uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); + CTxOut out; + out.nValue = 0; + out.scriptPubKey.resize(38); + out.scriptPubKey[0] = OP_RETURN; + out.scriptPubKey[1] = 0x24; + out.scriptPubKey[2] = 0xaa; + out.scriptPubKey[3] = 0x21; + out.scriptPubKey[4] = 0xa9; + out.scriptPubKey[5] = 0xed; + memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32); + commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end()); + const_cast*>(&block.vtx[0].vout)->push_back(out); + block.vtx[0].UpdateHash(); + } + } + UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams); + return commitment; +} + bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime) { // Check proof of work @@ -3497,6 +3583,43 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } + // Validation for witness commitments. + // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the + // coinbase (where 0x0000....0000 is used instead). + // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). + // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). + // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are + // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are + // multiple, the last one is used. + bool fHaveWitness = false; + if (IsWitnessEnabled(pindexPrev, consensusParams)) { + int commitpos = GetWitnessCommitmentIndex(block); + if (commitpos != -1) { + bool malleated = false; + uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); + // The malleation check is ignored; as the transaction tree itself + // already does not permit it, it is impossible to trigger in the + // witness tree. + if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { + return state.DoS(100, error("%s : invalid witness nonce size", __func__), REJECT_INVALID, "bad-witness-nonce-size", true); + } + CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); + if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { + return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); + } + fHaveWitness = true; + } + } + + // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam + if (!fHaveWitness) { + for (size_t i = 0; i < block.vtx.size(); i++) { + if (!block.vtx[i].wit.IsNull()) { + return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true); + } + } + } + return true; } @@ -5278,7 +5401,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (!fMissingInputs2) { int nDos = 0; - if (stateDummy.IsInvalid(nDos) && nDos > 0) + if (stateDummy.IsInvalid(nDos) && nDos > 0 && (!state.CorruptionPossible() || State(fromPeer)->fHaveWitness)) { // Punish peer that gave us an invalid orphan tx Misbehaving(fromPeer, nDos); @@ -5289,8 +5412,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); - assert(recentRejects); - recentRejects->insert(orphanHash); + if (!stateDummy.CorruptionPossible()) { + assert(recentRejects); + recentRejects->insert(orphanHash); + } } mempool.check(pcoinsTip); } @@ -5325,8 +5450,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); } } else { - assert(recentRejects); - recentRejects->insert(tx.GetHash()); + if (!state.CorruptionPossible()) { + assert(recentRejects); + recentRejects->insert(tx.GetHash()); + } if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even @@ -5355,8 +5482,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) + if (nDoS > 0 && (!state.CorruptionPossible() || State(pfrom->id)->fHaveWitness)) { + // When a non-witness-supporting peer gives us a transaction that would + // be accepted if witness validation was off, we can't blame them for it. Misbehaving(pfrom->GetId(), nDoS); + } } FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } -- cgit v1.2.3 From b8a97498df1e83f8dcc49bc3fa4344f9e9799242 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 17 Nov 2015 00:20:49 +0100 Subject: BIP144: Handshake and relay (receiver side) Service bit logic by Nicolas Dorier. Only download blocks from witness peers after fork. --- src/main.cpp | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 8d91d6d625..73e4c8f51c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -313,6 +313,7 @@ struct CNodeState { fPreferHeaders = false; fPreferHeaderAndIDs = false; fProvidesHeaderAndIDs = false; + fHaveWitness = false; } }; @@ -4812,6 +4813,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } +uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) { + uint32_t nFetchFlags = 0; + if (IsWitnessEnabled(pprev, chainparams) && State(pfrom->GetId())->fHaveWitness) { + nFetchFlags |= MSG_WITNESS_FLAG; + } + return nFetchFlags; +} + bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams) { LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); @@ -4918,6 +4927,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + if((pfrom->nServices & NODE_WITNESS)) + { + LOCK(cs_main); + State(pfrom->GetId())->fHaveWitness = true; + } + // Potentially mark this peer as a preferred download peer. { LOCK(cs_main); @@ -5119,17 +5134,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); + uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()); + std::vector vToFetch; for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { - const CInv &inv = vInv[nInv]; + CInv &inv = vInv[nInv]; boost::this_thread::interruption_point(); bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); + if (inv.type == MSG_TX) { + inv.type |= nFetchFlags; + } + if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -5144,7 +5165,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && - nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER && + (!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { + inv.type |= nFetchFlags; if (nodestate->fProvidesHeaderAndIDs) vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash)); else @@ -5730,7 +5753,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // 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())) { + !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && + (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } @@ -5752,7 +5776,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Can't download any more from this peer break; } - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + uint32_t nFetchFlags = GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()); + vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); LogPrint("net", "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom->id); @@ -6598,10 +6623,13 @@ bool SendMessages(CNode* pto) NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); - LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); + if (State(pto->GetId())->fHaveWitness || !IsWitnessEnabled(pindex->pprev, consensusParams)) { + uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams); + vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); + LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), + pindex->nHeight, pto->id); + } } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { -- cgit v1.2.3 From 0ef1dd3e11dd573b6e443852ef0c72e34093ac68 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 31 Mar 2016 14:51:29 +0200 Subject: Refactor script validation to observe amounts This is a preparation for BIP143 support. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 73e4c8f51c..c22faf6db6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1885,7 +1885,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; - if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) { return false; } return true; -- cgit v1.2.3 From 2b1f6f9ccf36f1e0a2c9d99154e1642f796d7c2b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Jan 2016 18:54:50 +0100 Subject: BIP141: Other consensus critical limits, and BIP145 Includes changes by Suhas Daftuar, Luke-jr, and mruddy. --- src/main.cpp | 78 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 27 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c22faf6db6..df758bc41f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -684,8 +684,8 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c // have been mined or received. // 100 orphans, each of which is at most 99,999 bytes big is // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case): - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + unsigned int sz = GetTransactionCost(tx); + if (sz >= MAX_STANDARD_TX_COST) { LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; @@ -1018,8 +1018,24 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } +int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags) +{ + int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR; + + if (tx.IsCoinBase()) + return nSigOps; + if (flags & SCRIPT_VERIFY_P2SH) { + nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR; + } + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, i < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[i].scriptWitness : NULL, flags); + } + return nSigOps; +} @@ -1033,7 +1049,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values @@ -1239,8 +1255,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (fRequireStandard && !AreInputsStandard(tx, view)) return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); - unsigned int nSigOps = GetLegacySigOpCount(tx); - nSigOps += GetP2SHSigOpCount(tx, view); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -1263,7 +1278,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp); unsigned int nSize = entry.GetTxSize(); // Check that the transaction doesn't have an excessive number of @@ -1271,9 +1286,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // 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)) + if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOp && nSigOpsCost > nSize * WITNESS_SCALE_FACTOR / nBytesPerSigOp)) return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, - strprintf("%d", nSigOps)); + strprintf("%d", nSigOpsCost)); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { @@ -2439,7 +2454,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector prevheights; CAmount nFees = 0; int nInputs = 0; - unsigned int nSigOps = 0; + int64_t nSigOpsCost = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; vPos.reserve(block.vtx.size()); @@ -2449,10 +2464,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin const CTransaction &tx = block.vtx[i]; nInputs += tx.vin.size(); - nSigOps += GetLegacySigOpCount(tx); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock(): too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); if (!tx.IsCoinBase()) { @@ -2483,18 +2494,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } + } - if (fStrictPayToScriptHash) - { - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock(): too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - } + // GetTransactionSigOpCost counts 3 types of sigops: + // * legacy (always) + // * p2sh (when P2SH enabled in flags and excludes coinbase) + // * witness (when witness enabled in flags and excludes coinbase) + nSigOpsCost += GetTransactionSigOpCost(tx, view, flags); + if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST) + return state.DoS(100, error("ConnectBlock(): too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + if (!tx.IsCoinBase()) + { nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector vChecks; @@ -3417,9 +3429,11 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. + // Note that witness malleability is checked in ContextualCheckBlock, so no + // checks that use witness data may be performed here. // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) + if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_BASE_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // First transaction must be coinbase, the rest must not be @@ -3440,7 +3454,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P { nSigOps += GetLegacySigOpCount(tx); } - if (nSigOps > MAX_BLOCK_SIGOPS) + if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST) return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); if (fCheckPOW && fCheckMerkleRoot) @@ -3621,6 +3635,16 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } + // After the coinbase witness nonce and commitment are verified, + // we can check if the block cost passes (before we've checked the + // coinbase witness, it would be possible for the cost to be too + // large by filling up the coinbase witness, which doesn't change + // the block hash, so we couldn't mark the block as permanently + // failed). + if (GetBlockCost(block) > MAX_BLOCK_COST) { + return state.DoS(100, error("ContextualCheckBlock(): cost limit failed"), REJECT_INVALID, "bad-blk-cost"); + } + return true; } @@ -4284,7 +4308,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -4303,7 +4327,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB continue; // read size blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE) continue; } catch (const std::exception&) { // no valid block header found; don't complain -- cgit v1.2.3 From 6032f6930a56c107dad8f30c05fec4aab79c8c22 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 18 Mar 2016 17:20:12 +0100 Subject: Add rewind logic to deal with post-fork software updates Includes logic for dealing with pruning by Suhas Daftuar. --- src/main.cpp | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 19 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index df758bc41f..ec43a66701 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -658,6 +658,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc CBlockIndex* pindex = (*mi).second; if (chain.Contains(pindex)) return pindex; + if (pindex->GetAncestor(chain.Height()) == chain.Tip()) { + return chain.Tip(); + } } } return chain.Genesis(); @@ -2777,7 +2780,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { } /** 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 CChainParams& chainparams) +bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, bool fBare = false) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); @@ -2797,24 +2800,28 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; - // Resurrect mempool transactions from the disconnected block. - std::vector vHashUpdate; - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - // ignore validation errors in resurrected transactions - list removed; - CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { - mempool.removeRecursive(tx, removed); - } else if (mempool.exists(tx.GetHash())) { - vHashUpdate.push_back(tx.GetHash()); - } - } - // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have - // no in-mempool children, which is generally not true when adding - // previously-confirmed transactions back to the mempool. - // UpdateTransactionsFromBlock finds descendants of any transactions in this - // block that were added back and cleans up the mempool state. - mempool.UpdateTransactionsFromBlock(vHashUpdate); + + if (!fBare) { + // Resurrect mempool transactions from the disconnected block. + std::vector vHashUpdate; + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + // ignore validation errors in resurrected transactions + list removed; + CValidationState stateDummy; + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { + mempool.removeRecursive(tx, removed); + } else if (mempool.exists(tx.GetHash())) { + vHashUpdate.push_back(tx.GetHash()); + } + } + // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have + // no in-mempool children, which is generally not true when adding + // previously-confirmed transactions back to the mempool. + // UpdateTransactionsFromBlock finds descendants of any transactions in this + // block that were added back and cleans up the mempool state. + mempool.UpdateTransactionsFromBlock(vHashUpdate); + } + // Update chainActive and related variables. UpdateTip(pindexDelete->pprev, chainparams); // Let wallets know transactions went from 1-confirmed to @@ -3266,6 +3273,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; + if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) { + pindexNew->nStatus |= BLOCK_OPT_WITNESS; + } pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); @@ -4214,6 +4224,90 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return true; } +bool RewindBlockIndex(const CChainParams& params) +{ + LOCK(cs_main); + + int nHeight = 1; + while (nHeight <= chainActive.Height()) { + if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { + break; + } + nHeight++; + } + + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + CValidationState state; + CBlockIndex* pindex = chainActive.Tip(); + while (chainActive.Height() >= nHeight) { + if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, don't try rewinding past the HAVE_DATA point; + // since older blocks can't be served anyway, there's + // no need to walk further, and trying to DisconnectTip() + // will fail (and require a needless reindex/redownload + // of the blockchain). + break; + } + if (!DisconnectTip(state, params, true)) { + return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + } + // Occasionally flush state to disk. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) + return false; + } + + // Reduce validity flag and have-data flags. + // We do this after actual disconnecting, otherwise we'll end up writing the lack of data + // to disk before writing the chainstate, resulting in a failure to continue if interrupted. + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + CBlockIndex* pindexIter = it->second; + + // Note: If we encounter an insufficiently validated block that + // is on chainActive, it must be because we are a pruning node, and + // this block or some successor doesn't HAVE_DATA, so we were unable to + // rewind all the way. Blocks remaining on chainActive at this point + // must not have their validity reduced. + if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) { + // Reduce validity + pindexIter->nStatus = std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK); + // Remove have-data flags. + pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO); + // Remove storage location. + pindexIter->nFile = 0; + pindexIter->nDataPos = 0; + pindexIter->nUndoPos = 0; + // Remove various other things + pindexIter->nTx = 0; + pindexIter->nChainTx = 0; + pindexIter->nSequenceId = 0; + // Make sure it gets written. + setDirtyBlockIndex.insert(pindexIter); + // Update indexes + setBlockIndexCandidates.erase(pindexIter); + std::pair::iterator, std::multimap::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); + while (ret.first != ret.second) { + if (ret.first->second == pindexIter) { + mapBlocksUnlinked.erase(ret.first++); + } else { + ++ret.first; + } + } + } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) { + setBlockIndexCandidates.insert(pindexIter); + } + } + + PruneBlockIndexCandidates(); + + CheckBlockIndex(params.GetConsensus()); + + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { + return false; + } + + return true; +} + void UnloadBlockIndex() { LOCK(cs_main); -- cgit v1.2.3 From af87a67eff8ce7bf2c7fb29f760da9fc610f162f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 22 Jun 2016 15:39:26 +0200 Subject: Do not use compact blocks when segwit is enabled --- src/main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ec43a66701..d1ba70313e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -473,6 +473,10 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) { + if (nLocalServices & NODE_WITNESS) { + // Don't ever request compact blocks when segwit is enabled. + return; + } if (nodestate->fProvidesHeaderAndIDs) { BOOST_FOREACH(const NodeId nodeid, lNodesAnnouncingHeaderAndIDs) if (nodeid == pfrom->GetId()) @@ -5286,7 +5290,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER && (!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { inv.type |= nFetchFlags; - if (nodestate->fProvidesHeaderAndIDs) + if (nodestate->fProvidesHeaderAndIDs && !(nLocalServices & NODE_WITNESS)) vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash)); else vToFetch.push_back(inv); @@ -5905,7 +5909,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { - if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { + if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN) && !(nLocalServices & NODE_WITNESS)) { // We seem to be rather well-synced, so it appears pfrom was the first to provide us // with this block! Let's get them to announce using compact blocks in the future. MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom); -- cgit v1.2.3 From 4f7ff00497803fddc5a0fb5340502a73e395134d Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 1 Jan 2016 23:18:34 -0600 Subject: [qa] Add rpc test for segwit Amended by Pieter Wuille to use multisig 1-of-1 for P2WSH tests, and BIP9 based switchover logic. Fixes and py3 conversion by Marco Falke. --- src/main.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index d1ba70313e..6cdd55e397 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1145,8 +1145,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); } - // Don't accept witness transactions before the final threshold passes - if (!tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) { + // Reject transactions with witness before segregated witness activates (override with -prematurewitness) + if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) { return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); } @@ -1487,14 +1487,19 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } + unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + if (!Params().RequireStandard()) { + scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); + } + // 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)) { + if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. - if (CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && - !CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_CLEANSTACK, true)) { + if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && + !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true)) { // Only the witness is wrong, so the transaction itself may be fine. state.SetCorruptionPossible(); } -- cgit v1.2.3 From 252675efc69ec0638d96a69fbc349a612cd1e2b7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 26 Jun 2016 20:19:00 +0200 Subject: Do not send witnesses in cmpctblock --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6cdd55e397..0bc90ab4a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6538,7 +6538,7 @@ bool SendMessages(CNode* pto) CBlock block; assert(ReadBlockFromDisk(block, pBestIndex, consensusParams)); CBlockHeaderAndShortTxIDs cmpctblock(block); - pto->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock); + pto->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { if (vHeaders.size() > 1) { -- cgit v1.2.3 From 4fbdc4365ba73e6947dd9052b28818a4a99c0d64 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Tue, 5 Jul 2016 20:45:14 +0000 Subject: Revert "net: Avoid duplicate getheaders requests." PR #8054 This reverts commit f93c2a1b7ee912f0651ebb4c8a5eca220e434f4a. This can cause synchronization to get stuck. --- src/main.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6cdd55e397..e26a73bc4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5835,13 +5835,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - // If we already know the last header in the message, then it contains - // no new information for us. In this case, we do not request - // more headers later. This prevents multiple chains of redundant - // getheader requests from running in parallel if triggered by incoming - // blocks while the node is still in initial headers sync. - const bool hasNewHeaders = (mapBlockIndex.count(headers.back().GetHash()) == 0); - CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5862,7 +5855,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - if (nCount == MAX_HEADERS_RESULTS && hasNewHeaders) { + if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. -- cgit v1.2.3 From bb66a11396335b5f4e5914806fcb2dc6165edf6f Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Thu, 7 Jul 2016 15:49:26 -0400 Subject: Fix DoS vulnerability in mempool acceptance Moves the IsStandard check to happen after the premature-witness check, so that adding a witness to a transaction can't prevent mempool acceptance. Note that this doesn't address the broader category of potential mempool DoS issues that affect transactions after segwit activation. --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b86bbda1b8..2f16a4c8ea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1132,11 +1132,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (tx.IsCoinBase()) return state.DoS(100, false, REJECT_INVALID, "coinbase"); - // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; - if (fRequireStandard && !IsStandardTx(tx, reason)) - return state.DoS(0, false, REJECT_NONSTANDARD, reason); - // Don't relay version 2 transactions until CSV is active, and we can be // sure that such transactions will be mined (unless we're on // -testnet/-regtest). @@ -1150,6 +1145,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); } + // Rather not work on nonstandard transactions (unless -testnet/-regtest) + string reason; + if (fRequireStandard && !IsStandardTx(tx, reason)) + return state.DoS(0, false, REJECT_NONSTANDARD, reason); + // 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. -- cgit v1.2.3 From 96fa95361f68dd8169fa60e73290afae47445299 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 6 Jul 2016 21:18:38 -0400 Subject: Improve handling of unconnecting headers When processing a headers message that looks like a block announcement, send peer a getheaders if the headers message won't connect. Apply DoS points after too many consecutive unconnecting headers messages. --- src/main.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index b86bbda1b8..dd71ee5510 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -276,6 +276,8 @@ struct CNodeState { CBlockIndex *pindexLastCommonBlock; //! The best header we have sent our peer. CBlockIndex *pindexBestHeaderSent; + //! Length of current-streak of unconnecting headers announcements + int nUnconnectingHeaders; //! Whether we've started headers synchronization with this peer. bool fSyncStarted; //! Since when we're stalling block download progress (in microseconds), or 0. @@ -304,6 +306,7 @@ struct CNodeState { hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = NULL; pindexBestHeaderSent = NULL; + nUnconnectingHeaders = 0; fSyncStarted = false; nStallingSince = 0; nDownloadingSince = 0; @@ -5773,6 +5776,35 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } + CNodeState *nodestate = State(pfrom->GetId()); + + // If this looks like it could be a block announcement (nCount < + // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that + // don't connect: + // - Send a getheaders message in response to try to connect the chain. + // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that + // don't connect before giving DoS points + // - Once a headers message is received that is valid and does connect, + // nUnconnectingHeaders gets reset back to 0. + if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + nodestate->nUnconnectingHeaders++; + pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()); + LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", + headers[0].GetHash().ToString(), + headers[0].hashPrevBlock.ToString(), + pindexBestHeader->nHeight, + pfrom->id, nodestate->nUnconnectingHeaders); + // Set hashLastUnknownBlock for this peer, so that if we + // eventually get the headers - even from a different peer - + // we can use this peer to download. + UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); + + if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { + Misbehaving(pfrom->GetId(), 20); + } + return true; + } + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5790,6 +5822,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } + if (nodestate->nUnconnectingHeaders > 0) { + LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders); + } + nodestate->nUnconnectingHeaders = 0; + assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); @@ -5802,7 +5839,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } 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) { -- cgit v1.2.3