Skip to content

Commit f8d470e

Browse files
committed
Merge #13298: Net: Bucketing INV delays (1 bucket) for incoming connections to hide tx time
d45b344 Bucket for inbound when scheduling invs to hide tx time (Gleb) Pull request description: It has been brought up to my attention that current random delays mechanism (originally intended to obfuscate transaction metadata) allows to easily estimate the time a transaction was received by a node. It may be done by connecting multiple observer nodes to the same node. Each of those nodes will generate its own schedule of delays. Combined metadata regarding those events from different sources allows an observer to estimate transaction time. After this patch a spy won't gain additional information by just creating multiple connections to a target. Tree-SHA512: c71dae5ff350b614cb40a8e201fd0562d3e03e3e72a5099718cd451f0d84c66d5e52bbaf0d5b4b75137514c8efdedcc6ef4df90142b360153f04ad0721545ab1
2 parents 17943f7 + d45b344 commit f8d470e

File tree

4 files changed

+45
-19
lines changed

4 files changed

+45
-19
lines changed

src/net.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,8 +2864,20 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
28642864
return found != nullptr && NodeFullyConnected(found) && func(found);
28652865
}
28662866

2867-
int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
2868-
return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
2867+
int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
2868+
{
2869+
if (m_next_send_inv_to_incoming < now) {
2870+
// If this function were called from multiple threads simultaneously
2871+
// it would possible that both update the next send variable, and return a different result to their caller.
2872+
// This is not possible in practice as only the net processing thread invokes this function.
2873+
m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
2874+
}
2875+
return m_next_send_inv_to_incoming;
2876+
}
2877+
2878+
int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
2879+
{
2880+
return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
28692881
}
28702882

28712883
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const

src/net.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,13 @@ class CConnman
310310
unsigned int GetReceiveFloodSize() const;
311311

312312
void WakeMessageHandler();
313+
314+
/** Attempts to obfuscate tx time through exponentially distributed emitting.
315+
Works assuming that a single interval is used.
316+
Variable intervals will result in privacy decrease.
317+
*/
318+
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
319+
313320
private:
314321
struct ListenSocket {
315322
SOCKET socket;
@@ -434,6 +441,8 @@ class CConnman
434441
* This takes the place of a feeler connection */
435442
std::atomic_bool m_try_another_outbound_peer;
436443

444+
std::atomic<int64_t> m_next_send_inv_to_incoming;
445+
437446
friend struct CConnmanTest;
438447
};
439448
extern std::unique_ptr<CConnman> g_connman;
@@ -863,6 +872,6 @@ class CNode
863872

864873

865874
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
866-
int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds);
875+
int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
867876

868877
#endif // BITCOIN_NET_H

src/net_processing.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ void EraseOrphansFor(NodeId peer);
7878
/** Increase a node's misbehavior score. */
7979
void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
8080

81+
/** Average delay between local address broadcasts in seconds. */
82+
static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
83+
/** Average delay between peer address broadcasts in seconds. */
84+
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
85+
/** Average delay between trickled inventory transmissions in seconds.
86+
* Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
87+
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
88+
/** Maximum number of inventory items to send per transmission.
89+
* Limits the impact of low-fee transaction floods. */
90+
static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
91+
/** Average delay between feefilter broadcasts in seconds. */
92+
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
93+
/** Maximum feefilter broadcast delay after significant change. */
94+
static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
95+
8196
// Internal stuff
8297
namespace {
8398
/** Number of nodes with fSyncStarted. */
@@ -3515,8 +3530,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
35153530
bool fSendTrickle = pto->fWhitelisted;
35163531
if (pto->nNextInvSend < nNow) {
35173532
fSendTrickle = true;
3518-
// Use half the delay for outbound peers, as there is less privacy concern for them.
3519-
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
3533+
if (pto->fInbound) {
3534+
pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
3535+
} else {
3536+
// Use half the delay for outbound peers, as there is less privacy concern for them.
3537+
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
3538+
}
35203539
}
35213540

35223541
// Time to send but the peer has requested we not relay transactions.

src/validation.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,6 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
104104
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
105105
/** Maximum length of reject messages. */
106106
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
107-
/** Average delay between local address broadcasts in seconds. */
108-
static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
109-
/** Average delay between peer address broadcasts in seconds. */
110-
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
111-
/** Average delay between trickled inventory transmissions in seconds.
112-
* Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
113-
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
114-
/** Maximum number of inventory items to send per transmission.
115-
* Limits the impact of low-fee transaction floods. */
116-
static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
117-
/** Average delay between feefilter broadcasts in seconds. */
118-
static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
119-
/** Maximum feefilter broadcast delay after significant change. */
120-
static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
121107
/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
122108
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
123109
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */

0 commit comments

Comments
 (0)