mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-24 19:02:42 +02:00
Delay getdata requests from peers using txid-based relay
Using both txid and wtxid-based relay with peers means that we could sometimes download the same transaction twice, if announced via two different hashes from different peers. Use a heuristic of delaying txid-peer-getdata requests by 2 seconds, if we have at least one wtxid-based peer.
This commit is contained in:
parent
46d78d47de
commit
97141ca442
@ -75,6 +75,8 @@ static const unsigned int MAX_INV_SZ = 50000;
|
|||||||
static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
|
static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
|
||||||
/** Maximum number of announced transactions from a peer */
|
/** Maximum number of announced transactions from a peer */
|
||||||
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
|
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
|
||||||
|
/** How many microseconds to delay requesting transactions via txids, if we have wtxid-relaying peers */
|
||||||
|
static constexpr std::chrono::microseconds TXID_RELAY_DELAY{std::chrono::seconds{2}};
|
||||||
/** How many microseconds to delay requesting transactions from inbound peers */
|
/** How many microseconds to delay requesting transactions from inbound peers */
|
||||||
static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
|
static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
|
||||||
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
|
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
|
||||||
@ -219,6 +221,9 @@ namespace {
|
|||||||
/** Number of peers from which we're downloading blocks. */
|
/** Number of peers from which we're downloading blocks. */
|
||||||
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
|
int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
|
||||||
|
|
||||||
|
/** Number of peers with wtxid relay. */
|
||||||
|
int g_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
|
||||||
|
|
||||||
/** Number of outbound peers with m_chain_sync.m_protect. */
|
/** Number of outbound peers with m_chain_sync.m_protect. */
|
||||||
int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
|
int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
|
||||||
|
|
||||||
@ -764,7 +769,7 @@ void UpdateTxRequestTime(const uint256& txid, std::chrono::microseconds request_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chrono::microseconds current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chrono::microseconds current_time, bool use_inbound_delay, bool use_txid_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
std::chrono::microseconds process_time;
|
std::chrono::microseconds process_time;
|
||||||
const auto last_request_time = GetTxRequestTime(txid);
|
const auto last_request_time = GetTxRequestTime(txid);
|
||||||
@ -780,6 +785,9 @@ std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chron
|
|||||||
// We delay processing announcements from inbound peers
|
// We delay processing announcements from inbound peers
|
||||||
if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
|
if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
|
||||||
|
|
||||||
|
// We delay processing announcements from peers that use txid-relay (instead of wtxid)
|
||||||
|
if (use_txid_delay) process_time += TXID_RELAY_DELAY;
|
||||||
|
|
||||||
return process_time;
|
return process_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +805,7 @@ void RequestTx(CNodeState* state, const uint256& txid, std::chrono::microseconds
|
|||||||
|
|
||||||
// Calculate the time to try requesting this transaction. Use
|
// Calculate the time to try requesting this transaction. Use
|
||||||
// fPreferredDownload as a proxy for outbound peers.
|
// fPreferredDownload as a proxy for outbound peers.
|
||||||
const auto process_time = CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload);
|
const auto process_time = CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload, !state->m_wtxid_relay && g_wtxid_relay_peers > 0);
|
||||||
|
|
||||||
peer_download_state.m_tx_process_time.emplace(process_time, txid);
|
peer_download_state.m_tx_process_time.emplace(process_time, txid);
|
||||||
}
|
}
|
||||||
@ -874,6 +882,8 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
|
|||||||
assert(nPeersWithValidatedDownloads >= 0);
|
assert(nPeersWithValidatedDownloads >= 0);
|
||||||
g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
|
g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
|
||||||
assert(g_outbound_peers_with_protect_from_disconnect >= 0);
|
assert(g_outbound_peers_with_protect_from_disconnect >= 0);
|
||||||
|
g_wtxid_relay_peers -= state->m_wtxid_relay;
|
||||||
|
assert(g_wtxid_relay_peers >= 0);
|
||||||
|
|
||||||
mapNodeState.erase(nodeid);
|
mapNodeState.erase(nodeid);
|
||||||
|
|
||||||
@ -883,6 +893,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
|
|||||||
assert(nPreferredDownload == 0);
|
assert(nPreferredDownload == 0);
|
||||||
assert(nPeersWithValidatedDownloads == 0);
|
assert(nPeersWithValidatedDownloads == 0);
|
||||||
assert(g_outbound_peers_with_protect_from_disconnect == 0);
|
assert(g_outbound_peers_with_protect_from_disconnect == 0);
|
||||||
|
assert(g_wtxid_relay_peers == 0);
|
||||||
}
|
}
|
||||||
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
|
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
|
||||||
}
|
}
|
||||||
@ -2489,6 +2500,7 @@ void ProcessMessage(
|
|||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if (!State(pfrom.GetId())->m_wtxid_relay) {
|
if (!State(pfrom.GetId())->m_wtxid_relay) {
|
||||||
State(pfrom.GetId())->m_wtxid_relay = true;
|
State(pfrom.GetId())->m_wtxid_relay = true;
|
||||||
|
g_wtxid_relay_peers++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -4490,7 +4502,15 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
// up processing to happen after the download times out
|
// up processing to happen after the download times out
|
||||||
// (with a slight delay for inbound peers, to prefer
|
// (with a slight delay for inbound peers, to prefer
|
||||||
// requests to outbound peers).
|
// requests to outbound peers).
|
||||||
const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload);
|
// Don't apply the txid-delay to re-requests of a
|
||||||
|
// transaction; the heuristic of delaying requests to
|
||||||
|
// txid-relay peers is to save bandwidth on initial
|
||||||
|
// announcement of a transaction, and doesn't make sense
|
||||||
|
// for a followup request if our first peer times out (and
|
||||||
|
// would open us up to an attacker using inbound
|
||||||
|
// wtxid-relay to prevent us from requesting transactions
|
||||||
|
// from outbound txid-relay peers).
|
||||||
|
const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload, false);
|
||||||
tx_process_time.emplace(next_process_time, txid);
|
tx_process_time.emplace(next_process_time, txid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -44,12 +44,13 @@ class TestP2PConn(P2PInterface):
|
|||||||
GETDATA_TX_INTERVAL = 60 # seconds
|
GETDATA_TX_INTERVAL = 60 # seconds
|
||||||
MAX_GETDATA_RANDOM_DELAY = 2 # seconds
|
MAX_GETDATA_RANDOM_DELAY = 2 # seconds
|
||||||
INBOUND_PEER_TX_DELAY = 2 # seconds
|
INBOUND_PEER_TX_DELAY = 2 # seconds
|
||||||
|
TXID_RELAY_DELAY = 2 # seconds
|
||||||
MAX_GETDATA_IN_FLIGHT = 100
|
MAX_GETDATA_IN_FLIGHT = 100
|
||||||
TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
|
TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
|
||||||
|
|
||||||
# Python test constants
|
# Python test constants
|
||||||
NUM_INBOUND = 10
|
NUM_INBOUND = 10
|
||||||
MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
|
MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY + TXID_RELAY_DELAY
|
||||||
|
|
||||||
|
|
||||||
class TxDownloadTest(BitcoinTestFramework):
|
class TxDownloadTest(BitcoinTestFramework):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user