@@ -4556,12 +4556,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
4556
4556
vRecv >> LIMITED_STRING (pfrom->strSubVer , MAX_SUBVERSION_LENGTH);
4557
4557
pfrom->cleanSubVer = SanitizeString (pfrom->strSubVer );
4558
4558
}
4559
- if (!vRecv.empty ())
4559
+ if (!vRecv.empty ()) {
4560
4560
vRecv >> pfrom->nStartingHeight ;
4561
- if (!vRecv.empty ())
4562
- vRecv >> pfrom->fRelayTxes ; // set to true after we get the first filter* message
4563
- else
4564
- pfrom->fRelayTxes = true ;
4561
+ }
4562
+ {
4563
+ LOCK (pfrom->cs_filter );
4564
+ if (!vRecv.empty ())
4565
+ vRecv >> pfrom->fRelayTxes ; // set to true after we get the first filter* message
4566
+ else
4567
+ pfrom->fRelayTxes = true ;
4568
+ }
4565
4569
4566
4570
// Disconnect if we connected to ourself
4567
4571
if (nNonce == nLocalHostNonce && nNonce > 1 )
@@ -5234,34 +5238,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5234
5238
pfrom->fDisconnect = true ;
5235
5239
return true ;
5236
5240
}
5237
- LOCK2 (cs_main, pfrom->cs_filter );
5238
5241
5239
- std::vector<uint256> vtxid;
5240
- mempool.queryHashes (vtxid);
5241
- vector<CInv> vInv;
5242
- BOOST_FOREACH (uint256& hash, vtxid) {
5243
- CInv inv (MSG_TX, hash);
5244
- if (pfrom->pfilter ) {
5245
- CTransaction tx;
5246
- bool fInMemPool = mempool.lookup (hash, tx);
5247
- if (!fInMemPool ) continue ; // another thread removed since queryHashes, maybe...
5248
- if (!pfrom->pfilter ->IsRelevantAndUpdate (tx)) continue ;
5249
- }
5250
- if (pfrom->minFeeFilter ) {
5251
- CFeeRate feeRate;
5252
- mempool.lookupFeeRate (hash, feeRate);
5253
- LOCK (pfrom->cs_feeFilter );
5254
- if (feeRate.GetFeePerK () < pfrom->minFeeFilter )
5255
- continue ;
5256
- }
5257
- vInv.push_back (inv);
5258
- if (vInv.size () == MAX_INV_SZ) {
5259
- pfrom->PushMessage (NetMsgType::INV, vInv);
5260
- vInv.clear ();
5261
- }
5262
- }
5263
- if (vInv.size () > 0 )
5264
- pfrom->PushMessage (NetMsgType::INV, vInv);
5242
+ LOCK (pfrom->cs_inventory );
5243
+ pfrom->fSendMempool = true ;
5265
5244
}
5266
5245
5267
5246
@@ -5349,12 +5328,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5349
5328
CBloomFilter filter;
5350
5329
vRecv >> filter;
5351
5330
5331
+ LOCK (pfrom->cs_filter );
5332
+
5352
5333
if (!filter.IsWithinSizeConstraints ())
5353
5334
// There is no excuse for sending a too-large filter
5354
5335
Misbehaving (pfrom->GetId (), 100 );
5355
5336
else
5356
5337
{
5357
- LOCK (pfrom->cs_filter );
5358
5338
delete pfrom->pfilter ;
5359
5339
pfrom->pfilter = new CBloomFilter (filter);
5360
5340
pfrom->pfilter ->UpdateEmptyFull ();
@@ -5559,6 +5539,22 @@ bool ProcessMessages(CNode* pfrom)
5559
5539
return fOk ;
5560
5540
}
5561
5541
5542
+ class CompareInvMempoolOrder
5543
+ {
5544
+ CTxMemPool *mp;
5545
+ public:
5546
+ CompareInvMempoolOrder (CTxMemPool *mempool)
5547
+ {
5548
+ mp = mempool;
5549
+ }
5550
+
5551
+ bool operator ()(std::set<uint256>::iterator a, std::set<uint256>::iterator b)
5552
+ {
5553
+ /* As std::make_heap produces a max-heap, we want the entries with the
5554
+ * fewest ancestors/highest fee to sort later. */
5555
+ return mp->CompareDepthAndScore (*b, *a);
5556
+ }
5557
+ };
5562
5558
5563
5559
bool SendMessages (CNode* pto)
5564
5560
{
@@ -5798,49 +5794,127 @@ bool SendMessages(CNode* pto)
5798
5794
// Message: inventory
5799
5795
//
5800
5796
vector<CInv> vInv;
5801
- vector<CInv> vInvWait;
5802
5797
{
5798
+ LOCK (pto->cs_inventory );
5799
+ vInv.reserve (std::max<size_t >(pto->vInventoryBlockToSend .size (), INVENTORY_BROADCAST_MAX));
5800
+
5801
+ // Add blocks
5802
+ BOOST_FOREACH (const uint256& hash, pto->vInventoryBlockToSend ) {
5803
+ vInv.push_back (CInv (MSG_BLOCK, hash));
5804
+ if (vInv.size () == MAX_INV_SZ) {
5805
+ pto->PushMessage (NetMsgType::INV, vInv);
5806
+ vInv.clear ();
5807
+ }
5808
+ }
5809
+ pto->vInventoryBlockToSend .clear ();
5810
+
5811
+ // Check whether periodic sends should happen
5803
5812
bool fSendTrickle = pto->fWhitelisted ;
5804
5813
if (pto->nNextInvSend < nNow) {
5805
5814
fSendTrickle = true ;
5806
- pto->nNextInvSend = PoissonNextSend (nNow, AVG_INVENTORY_BROADCAST_INTERVAL);
5815
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
5816
+ pto->nNextInvSend = PoissonNextSend (nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound );
5817
+ }
5818
+
5819
+ // Time to send but the peer has requested we not relay transactions.
5820
+ if (fSendTrickle ) {
5821
+ LOCK (pto->cs_filter );
5822
+ if (!pto->fRelayTxes ) pto->setInventoryTxToSend .clear ();
5807
5823
}
5808
- LOCK (pto->cs_inventory );
5809
- vInv.reserve (std::min<size_t >(1000 , pto->vInventoryToSend .size ()));
5810
- vInvWait.reserve (pto->vInventoryToSend .size ());
5811
- BOOST_FOREACH (const CInv& inv, pto->vInventoryToSend )
5812
- {
5813
- if (inv.type == MSG_TX && pto->filterInventoryKnown .contains (inv.hash ))
5814
- continue ;
5815
5824
5816
- // trickle out tx inv to protect privacy
5817
- if (inv.type == MSG_TX && !fSendTrickle )
5825
+ // Respond to BIP35 mempool requests
5826
+ if (fSendTrickle && pto->fSendMempool ) {
5827
+ std::vector<uint256> vtxid;
5828
+ mempool.queryHashes (vtxid);
5829
+ pto->fSendMempool = false ;
5830
+ CAmount filterrate = 0 ;
5818
5831
{
5819
- // 1/4 of tx invs blast to all immediately
5820
- static uint256 hashSalt;
5821
- if (hashSalt.IsNull ())
5822
- hashSalt = GetRandHash ();
5823
- uint256 hashRand = ArithToUint256 (UintToArith256 (inv.hash ) ^ UintToArith256 (hashSalt));
5824
- hashRand = Hash (BEGIN (hashRand), END (hashRand));
5825
- bool fTrickleWait = ((UintToArith256 (hashRand) & 3 ) != 0 );
5832
+ LOCK (pto->cs_feeFilter );
5833
+ filterrate = pto->minFeeFilter ;
5834
+ }
5826
5835
5827
- if (fTrickleWait )
5828
- {
5829
- vInvWait.push_back (inv);
5830
- continue ;
5836
+ LOCK (pto->cs_filter );
5837
+
5838
+ BOOST_FOREACH (const uint256& hash, vtxid) {
5839
+ CInv inv (MSG_TX, hash);
5840
+ pto->setInventoryTxToSend .erase (hash);
5841
+ if (filterrate) {
5842
+ CFeeRate feeRate;
5843
+ mempool.lookupFeeRate (hash, feeRate);
5844
+ if (feeRate.GetFeePerK () < filterrate)
5845
+ continue ;
5846
+ }
5847
+ if (pto->pfilter ) {
5848
+ CTransaction tx;
5849
+ bool fInMemPool = mempool.lookup (hash, tx);
5850
+ if (!fInMemPool ) continue ; // another thread removed since queryHashes, maybe...
5851
+ if (!pto->pfilter ->IsRelevantAndUpdate (tx)) continue ;
5852
+ }
5853
+ pto->filterInventoryKnown .insert (hash);
5854
+ vInv.push_back (inv);
5855
+ if (vInv.size () == MAX_INV_SZ) {
5856
+ pto->PushMessage (NetMsgType::INV, vInv);
5857
+ vInv.clear ();
5831
5858
}
5832
5859
}
5860
+ }
5833
5861
5834
- pto->filterInventoryKnown .insert (inv.hash );
5835
-
5836
- vInv.push_back (inv);
5837
- if (vInv.size () >= 1000 )
5862
+ // Determine transactions to relay
5863
+ if (fSendTrickle ) {
5864
+ // Produce a vector with all candidates for sending
5865
+ vector<std::set<uint256>::iterator> vInvTx;
5866
+ vInvTx.reserve (pto->setInventoryTxToSend .size ());
5867
+ for (std::set<uint256>::iterator it = pto->setInventoryTxToSend .begin (); it != pto->setInventoryTxToSend .end (); it++) {
5868
+ vInvTx.push_back (it);
5869
+ }
5870
+ CAmount filterrate = 0 ;
5838
5871
{
5839
- pto->PushMessage (NetMsgType::INV, vInv);
5840
- vInv.clear ();
5872
+ LOCK (pto->cs_feeFilter );
5873
+ filterrate = pto->minFeeFilter ;
5874
+ }
5875
+ // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
5876
+ // A heap is used so that not all items need sorting if only a few are being sent.
5877
+ CompareInvMempoolOrder compareInvMempoolOrder (&mempool);
5878
+ std::make_heap (vInvTx.begin (), vInvTx.end (), compareInvMempoolOrder);
5879
+ // No reason to drain out at many times the network's capacity,
5880
+ // especially since we have many peers and some will draw much shorter delays.
5881
+ unsigned int nRelayedTransactions = 0 ;
5882
+ LOCK (pto->cs_filter );
5883
+ while (!vInvTx.empty () && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
5884
+ // Fetch the top element from the heap
5885
+ std::pop_heap (vInvTx.begin (), vInvTx.end (), compareInvMempoolOrder);
5886
+ std::set<uint256>::iterator it = vInvTx.back ();
5887
+ vInvTx.pop_back ();
5888
+ uint256 hash = *it;
5889
+ // Remove it from the to-be-sent set
5890
+ pto->setInventoryTxToSend .erase (it);
5891
+ // Check if not in the filter already
5892
+ if (pto->filterInventoryKnown .contains (hash)) {
5893
+ continue ;
5894
+ }
5895
+ // Not in the mempool anymore? don't bother sending it.
5896
+ CFeeRate feeRate;
5897
+ if (!mempool.lookupFeeRate (hash, feeRate)) {
5898
+ continue ;
5899
+ }
5900
+ if (filterrate && feeRate.GetFeePerK () < filterrate) {
5901
+ continue ;
5902
+ }
5903
+ if (pto->pfilter ) {
5904
+ CTransaction tx;
5905
+ if (!mempool.lookup (hash, tx)) continue ;
5906
+ if (!pto->pfilter ->IsRelevantAndUpdate (tx)) continue ;
5907
+ }
5908
+ // Send
5909
+ vInv.push_back (CInv (MSG_TX, hash));
5910
+ nRelayedTransactions++;
5911
+ if (vInv.size () == MAX_INV_SZ) {
5912
+ pto->PushMessage (NetMsgType::INV, vInv);
5913
+ vInv.clear ();
5914
+ }
5915
+ pto->filterInventoryKnown .insert (hash);
5841
5916
}
5842
5917
}
5843
- pto->vInventoryToSend = vInvWait;
5844
5918
}
5845
5919
if (!vInv.empty ())
5846
5920
pto->PushMessage (NetMsgType::INV, vInv);
0 commit comments