@@ -162,6 +162,29 @@ namespace {
162162 */
163163 map<uint256, NodeId> mapBlockSource;
164164
165+ /* *
166+ * Filter for transactions that were recently rejected by
167+ * AcceptToMemoryPool. These are not rerequested until the chain tip
168+ * changes, at which point the entire filter is reset. Protected by
169+ * cs_main.
170+ *
171+ * Without this filter we'd be re-requesting txs from each of our peers,
172+ * increasing bandwidth consumption considerably. For instance, with 100
173+ * peers, half of which relay a tx we don't accept, that might be a 50x
174+ * bandwidth increase. A flooding attacker attempting to roll-over the
175+ * filter using minimum-sized, 60byte, transactions might manage to send
176+ * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a
177+ * two minute window to send invs to us.
178+ *
179+ * Decreasing the false positive rate is fairly cheap, so we pick one in a
180+ * million to make it highly unlikely for users to have issues with this
181+ * filter.
182+ *
183+ * Memory used: 1.7MB
184+ */
185+ boost::scoped_ptr<CRollingBloomFilter> recentRejects;
186+ uint256 hashRecentRejectsChainTip;
187+
165188 /* * Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
166189 struct QueuedBlock {
167190 uint256 hash;
@@ -3248,6 +3271,7 @@ void UnloadBlockIndex()
32483271 setDirtyBlockIndex.clear ();
32493272 setDirtyFileInfo.clear ();
32503273 mapNodeState.clear ();
3274+ recentRejects.reset (NULL );
32513275
32523276 BOOST_FOREACH (BlockMap::value_type& entry, mapBlockIndex) {
32533277 delete entry.second ;
@@ -3268,6 +3292,10 @@ bool LoadBlockIndex()
32683292bool InitBlockIndex () {
32693293 const CChainParams& chainparams = Params ();
32703294 LOCK (cs_main);
3295+
3296+ // Initialize global variables that cannot be constructed at startup.
3297+ recentRejects.reset (new CRollingBloomFilter (120000 , 0.000001 ));
3298+
32713299 // Check whether we're already initialized
32723300 if (chainActive.Genesis () != NULL )
32733301 return true ;
@@ -3670,10 +3698,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
36703698 {
36713699 case MSG_TX :
36723700 {
3673- bool txInMap = false ;
3674- txInMap = mempool.exists (inv.hash );
3675- return txInMap || mapOrphanTransactions.count (inv.hash ) ||
3676- pcoinsTip->HaveCoins (inv.hash );
3701+ assert (recentRejects);
3702+ if (chainActive.Tip ()->GetBlockHash () != hashRecentRejectsChainTip)
3703+ {
3704+ // If the chain tip has changed previously rejected transactions
3705+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
3706+ // or a double-spend. Reset the rejects filter and give those
3707+ // txs a second chance.
3708+ hashRecentRejectsChainTip = chainActive.Tip ()->GetBlockHash ();
3709+ recentRejects->reset ();
3710+ }
3711+
3712+ return recentRejects->contains (inv.hash ) ||
3713+ mempool.exists (inv.hash ) ||
3714+ mapOrphanTransactions.count (inv.hash ) ||
3715+ pcoinsTip->HaveCoins (inv.hash );
36773716 }
36783717 case MSG_BLOCK :
36793718 return mapBlockIndex.count (inv.hash );
@@ -4273,6 +4312,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
42734312 // Probably non-standard or insufficient fee/priority
42744313 LogPrint (" mempool" , " removed orphan tx %s\n " , orphanHash.ToString ());
42754314 vEraseQueue.push_back (orphanHash);
4315+ assert (recentRejects);
4316+ recentRejects->insert (orphanHash);
42764317 }
42774318 mempool.check (pcoinsTip);
42784319 }
@@ -4290,11 +4331,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
42904331 unsigned int nEvicted = LimitOrphanTxSize (nMaxOrphanTx);
42914332 if (nEvicted > 0 )
42924333 LogPrint (" mempool" , " mapOrphan overflow, removed %u tx\n " , nEvicted);
4293- } else if (pfrom->fWhitelisted ) {
4294- // Always relay transactions received from whitelisted peers, even
4295- // if they are already in the mempool (allowing the node to function
4296- // as a gateway for nodes hidden behind it).
4297- RelayTransaction (tx);
4334+ } else {
4335+ // AcceptToMemoryPool() returned false, possibly because the tx is
4336+ // already in the mempool; if the tx isn't in the mempool that
4337+ // means it was rejected and we shouldn't ask for it again.
4338+ if (!mempool.exists (tx.GetHash ())) {
4339+ assert (recentRejects);
4340+ recentRejects->insert (tx.GetHash ());
4341+ }
4342+ if (pfrom->fWhitelisted ) {
4343+ // Always relay transactions received from whitelisted peers, even
4344+ // if they were rejected from the mempool, allowing the node to
4345+ // function as a gateway for nodes hidden behind it.
4346+ //
4347+ // FIXME: This includes invalid transactions, which means a
4348+ // whitelisted peer could get us banned! We may want to change
4349+ // that.
4350+ RelayTransaction (tx);
4351+ }
42984352 }
42994353 int nDoS = 0 ;
43004354 if (state.IsInvalid (nDoS))
@@ -4797,7 +4851,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
47974851 {
47984852 // Periodically clear addrKnown to allow refresh broadcasts
47994853 if (nLastRebroadcast)
4800- pnode->addrKnown .clear ();
4854+ pnode->addrKnown .reset ();
48014855
48024856 // Rebroadcast our address
48034857 AdvertizeLocal (pnode);
0 commit comments