diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 214 |
1 files changed, 144 insertions, 70 deletions
diff --git a/src/main.cpp b/src/main.cpp index 418ef704b5..ebe91f7fa9 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; @@ -1583,7 +1583,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; @@ -1741,7 +1741,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()) { @@ -1767,10 +1767,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()() { @@ -2382,7 +2382,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); @@ -3368,7 +3368,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; } @@ -3702,7 +3702,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(); @@ -4553,12 +4553,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) @@ -5231,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<uint256> vtxid; - mempool.queryHashes(vtxid); - vector<CInv> 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; } @@ -5346,12 +5325,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(); @@ -5556,6 +5536,22 @@ bool ProcessMessages(CNode* pfrom) return fOk; } +class CompareInvMempoolOrder +{ + CTxMemPool *mp; +public: + CompareInvMempoolOrder(CTxMemPool *mempool) + { + mp = mempool; + } + + bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b) + { + /* 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); + } +}; bool SendMessages(CNode* pto) { @@ -5795,49 +5791,127 @@ bool SendMessages(CNode* pto) // Message: inventory // vector<CInv> vInv; - vector<CInv> vInvWait; { + LOCK(pto->cs_inventory); + vInv.reserve(std::max<size_t>(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(); + + // Check whether periodic sends should happen 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 there is less privacy concern for them. + 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(); } - LOCK(pto->cs_inventory); - vInv.reserve(std::min<size_t>(1000, 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) + // Respond to BIP35 mempool requests + if (fSendTrickle && pto->fSendMempool) { + std::vector<uint256> vtxid; + mempool.queryHashes(vtxid); + pto->fSendMempool = false; + CAmount filterrate = 0; { - // 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); + LOCK(pto->cs_feeFilter); + filterrate = pto->minFeeFilter; + } - if (fTrickleWait) - { - vInvWait.push_back(inv); - continue; + 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(); } } + } - pto->filterInventoryKnown.insert(inv.hash); - - vInv.push_back(inv); - if (vInv.size() >= 1000) + // Determine transactions to relay + if (fSendTrickle) { + // Produce a vector with all candidates for sending + vector<std::set<uint256>::iterator> vInvTx; + vInvTx.reserve(pto->setInventoryTxToSend.size()); + for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { + vInvTx.push_back(it); + } + CAmount filterrate = 0; { - pto->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); + 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); + 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. + 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); + std::set<uint256>::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; + } + // 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++; + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } + pto->filterInventoryKnown.insert(hash); } } - pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage(NetMsgType::INV, vInv); |