@@ -70,11 +70,13 @@ static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
70
70
/* * Maximum number of announced transactions from a peer */
71
71
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
72
72
/* * How many microseconds to delay requesting transactions from inbound peers */
73
- static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000 ;
73
+ static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000 ; // 2 seconds
74
74
/* * How long to wait (in microseconds) before downloading a transaction from an additional peer */
75
- static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000 ;
75
+ static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000 ; // 1 minute
76
76
/* * Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
77
- static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000 ;
77
+ static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000 ; // 2 seconds
78
+ /* * How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
79
+ static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL;
78
80
static_assert (INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
79
81
" To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY" );
80
82
/* * Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
@@ -345,8 +347,11 @@ struct CNodeState {
345
347
// ! Store all the transactions a peer has recently announced
346
348
std::set<uint256> m_tx_announced;
347
349
348
- // ! Store transactions which were requested by us
349
- std::set<uint256> m_tx_in_flight;
350
+ // ! Store transactions which were requested by us, with timestamp
351
+ std::map<uint256, int64_t > m_tx_in_flight;
352
+
353
+ // ! Periodically check for stuck getdata requests
354
+ int64_t m_check_expiry_timer{0 };
350
355
};
351
356
352
357
TxDownloadState m_tx_download;
@@ -3939,6 +3944,27 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
3939
3944
//
3940
3945
// Message: getdata (non-blocks)
3941
3946
//
3947
+
3948
+ // For robustness, expire old requests after a long timeout, so that
3949
+ // we can resume downloading transactions from a peer even if they
3950
+ // were unresponsive in the past.
3951
+ // Eventually we should consider disconnecting peers, but this is
3952
+ // conservative.
3953
+ if (state.m_tx_download .m_check_expiry_timer <= nNow) {
3954
+ for (auto it=state.m_tx_download .m_tx_in_flight .begin (); it != state.m_tx_download .m_tx_in_flight .end ();) {
3955
+ if (it->second <= nNow - TX_EXPIRY_INTERVAL) {
3956
+ LogPrint (BCLog::NET, " timeout of inflight tx %s from peer=%d\n " , it->first .ToString (), pto->GetId ());
3957
+ state.m_tx_download .m_tx_announced .erase (it->first );
3958
+ state.m_tx_download .m_tx_in_flight .erase (it++);
3959
+ } else {
3960
+ ++it;
3961
+ }
3962
+ }
3963
+ // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
3964
+ // so that we're not doing this for all peers at the same time.
3965
+ state.m_tx_download .m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand (TX_EXPIRY_INTERVAL);
3966
+ }
3967
+
3942
3968
auto & tx_process_time = state.m_tx_download .m_tx_process_time ;
3943
3969
while (!tx_process_time.empty () && tx_process_time.begin ()->first <= nNow && state.m_tx_download .m_tx_in_flight .size () < MAX_PEER_TX_IN_FLIGHT) {
3944
3970
const uint256& txid = tx_process_time.begin ()->second ;
@@ -3955,7 +3981,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
3955
3981
vGetData.clear ();
3956
3982
}
3957
3983
UpdateTxRequestTime (inv.hash , nNow);
3958
- state.m_tx_download .m_tx_in_flight .insert (inv.hash );
3984
+ state.m_tx_download .m_tx_in_flight .emplace (inv.hash , nNow );
3959
3985
} else {
3960
3986
// This transaction is in flight from someone else; queue
3961
3987
// up processing to happen after the download times out
0 commit comments