@@ -177,6 +177,29 @@ uint32_t nBlockSequenceId = 1;
177177 */
178178std::map<uint256, NodeId> mapBlockSource;
179179
180+ /* *
181+ * Filter for transactions that were recently rejected by
182+ * AcceptToMemoryPool. These are not rerequested until the chain tip
183+ * changes, at which point the entire filter is reset. Protected by
184+ * cs_main.
185+ *
186+ * Without this filter we'd be re-requesting txs from each of our peers,
187+ * increasing bandwidth consumption considerably. For instance, with 100
188+ * peers, half of which relay a tx we don't accept, that might be a 50x
189+ * bandwidth increase. A flooding attacker attempting to roll-over the
190+ * filter using minimum-sized, 60byte, transactions might manage to send
191+ * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
192+ * two minute window to send invs to us.
193+ *
194+ * Decreasing the false positive rate is fairly cheap, so we pick one in a
195+ * million to make it highly unlikely for users to have issues with this
196+ * filter.
197+ *
198+ * Memory used: 1.7MB
199+ */
200+ boost::scoped_ptr<CRollingBloomFilter> recentRejects;
201+ uint256 hashRecentRejectsChainTip;
202+
180203/* * Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
181204struct QueuedBlock {
182205 uint256 hash;
@@ -4667,6 +4690,7 @@ void UnloadBlockIndex()
46674690 setDirtyBlockIndex.clear ();
46684691 setDirtyFileInfo.clear ();
46694692 mapNodeState.clear ();
4693+ recentRejects.reset (nullptr );
46704694
46714695 for (BlockMap::value_type& entry : mapBlockIndex) {
46724696 delete entry.second ;
@@ -4717,6 +4741,9 @@ bool InitBlockIndex()
47174741 }
47184742 }
47194743
4744+ // Initialize global variables that cannot be constructed at startup.
4745+ recentRejects.reset (new CRollingBloomFilter (120000 , 0.000001 ));
4746+
47204747 return true ;
47214748}
47224749
@@ -5006,9 +5033,18 @@ bool static AlreadyHave(const CInv& inv)
50065033{
50075034 switch (inv.type ) {
50085035 case MSG_TX: {
5009- bool txInMap = false ;
5010- txInMap = mempool.exists (inv.hash );
5011- return txInMap || mapOrphanTransactions.count (inv.hash ) ||
5036+ if (chainActive.Tip ()->GetBlockHash () != hashRecentRejectsChainTip) {
5037+ // If the chain tip has changed previously rejected transactions
5038+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
5039+ // or a double-spend. Reset the rejects filter and give those
5040+ // txs a second chance.
5041+ hashRecentRejectsChainTip = chainActive.Tip ()->GetBlockHash ();
5042+ recentRejects->reset ();
5043+ }
5044+
5045+ return recentRejects->contains (inv.hash ) ||
5046+ mempool.exists (inv.hash ) ||
5047+ mapOrphanTransactions.count (inv.hash ) ||
50125048 pcoinsTip->HaveCoins (inv.hash );
50135049 }
50145050 case MSG_BLOCK:
@@ -5737,6 +5773,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
57375773 // Probably non-standard or insufficient fee/priority
57385774 LogPrint (BCLog::MEMPOOL, " removed orphan tx %s\n " , orphanHash.ToString ());
57395775 vEraseQueue.push_back (orphanHash);
5776+ recentRejects->insert (orphanHash);
57405777 }
57415778 mempool.check (pcoinsTip);
57425779 }
@@ -5760,12 +5797,23 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
57605797 unsigned int nEvicted = LimitOrphanTxSize (nMaxOrphanTx);
57615798 if (nEvicted > 0 )
57625799 LogPrint (BCLog::MEMPOOL, " mapOrphan overflow, removed %u tx\n " , nEvicted);
5763- } else if (pfrom->fWhitelisted ) {
5764- // Always relay transactions received from whitelisted peers, even
5765- // if they are already in the mempool (allowing the node to function
5766- // as a gateway for nodes hidden behind it).
5767-
5768- RelayTransaction (tx);
5800+ } else {
5801+ // AcceptToMemoryPool() returned false, possibly because the tx is
5802+ // already in the mempool; if the tx isn't in the mempool that
5803+ // means it was rejected and we shouldn't ask for it again.
5804+ if (!mempool.exists (tx.GetHash ())) {
5805+ recentRejects->insert (tx.GetHash ());
5806+ }
5807+ if (pfrom->fWhitelisted ) {
5808+ // Always relay transactions received from whitelisted peers, even
5809+ // if they were rejected from the mempool, allowing the node to
5810+ // function as a gateway for nodes hidden behind it.
5811+ //
5812+ // FIXME: This includes invalid transactions, which means a
5813+ // whitelisted peer could get us banned! We may want to change
5814+ // that.
5815+ RelayTransaction (tx);
5816+ }
57695817 }
57705818
57715819 int nDoS = 0 ;
0 commit comments