Skip to content

Commit

Permalink
Merge bitcoin#13298: Net: Bucketing INV delays (1 bucket) for incomin…
Browse files Browse the repository at this point in the history
…g 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
  • Loading branch information
sipa authored and codablock committed Apr 12, 2020
1 parent c1d1bd8 commit 89f6c6e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
16 changes: 14 additions & 2 deletions src/net.cpp
Expand Up @@ -3457,8 +3457,20 @@ bool CConnman::IsMasternodeOrDisconnectRequested(const CService& addr) {
});
}

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

int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
{
return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
}

std::vector<CNode*> CConnman::CopyNodeVector(std::function<bool(const CNode* pnode)> cond)
Expand Down
10 changes: 9 additions & 1 deletion src/net.h
Expand Up @@ -453,6 +453,12 @@ class CConnman
void WakeMessageHandler();
void WakeSelect();

/** Attempts to obfuscate tx time through exponentially distributed emitting.
Works assuming that a single interval is used.
Variable intervals will result in privacy decrease.
*/
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);

private:
struct ListenSocket {
SOCKET socket;
Expand Down Expand Up @@ -596,6 +602,8 @@ class CConnman
* This takes the place of a feeler connection */
std::atomic_bool m_try_another_outbound_peer;

std::atomic<int64_t> m_next_send_inv_to_incoming;

friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
Expand Down Expand Up @@ -1056,6 +1064,6 @@ class CExplicitNetCleanup


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

#endif // BITCOIN_NET_H
23 changes: 20 additions & 3 deletions src/net_processing.cpp
Expand Up @@ -113,6 +113,19 @@ static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
/// limiting block relay. Set to one week, denominated in seconds.
static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;

/** Average delay between local address broadcasts in seconds. */
static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
/** Average delay between peer address broadcasts in seconds. */
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
/** Average delay between trickled inventory transmissions in seconds.
* Blocks and whitelisted receivers bypass this, regular outbound peers get half this delay,
* Masternode outbound peers get quarter this delay. */
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
/** Maximum number of inventory items to send per transmission.
* Limits the impact of low-fee transaction floods.
* We have 4 times smaller block times in Dash, so we need to push 4 times more invs per 1MB. */
static constexpr unsigned int INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK = 4 * 7 * INVENTORY_BROADCAST_INTERVAL;

// Internal stuff
namespace {
/** Number of nodes with fSyncStarted. */
Expand Down Expand Up @@ -4076,9 +4089,13 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
bool fSendTrickle = pto->fWhitelisted || fMasternodeMode;
if (pto->nNextInvSend < nNow) {
fSendTrickle = true;
// Use half the delay for regular outbound peers, as there is less privacy concern for them,
// and quarter the delay for Masternode outbound peers, as there is even less privacy concern in this case.
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound >> !pto->verifiedProRegTxHash.IsNull());
if (pto->fInbound) {
pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
} else {
// Use half the delay for regular outbound peers, as there is less privacy concern for them.
// and quarter the delay for Masternode outbound peers, as there is even less privacy concern in this case.
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1 >> !pto->verifiedProRegTxHash.IsNull());
}
}

// Time to send but the peer has requested we not relay transactions.
Expand Down
12 changes: 0 additions & 12 deletions src/validation.h
Expand Up @@ -106,18 +106,6 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
/** Average delay between local address broadcasts in seconds. */
static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
/** Average delay between peer address broadcasts in seconds. */
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
/** Average delay between trickled inventory transmissions in seconds.
* Blocks and whitelisted receivers bypass this, regular outbound peers get half this delay,
* Masternode outbound peers get quarter this delay. */
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
/** Maximum number of inventory items to send per transmission.
* Limits the impact of low-fee transaction floods.
* We have 4 times smaller block times in Dash, so we need to push 4 times more invs per 1MB. */
static const unsigned int INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK = 4 * 7 * INVENTORY_BROADCAST_INTERVAL;
/** Block download timeout base, expressed in millionths of the block interval (i.e. 2.5 min) */
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
/** Additional block download timeout per parallel downloading peer (i.e. 1.25 min) */
Expand Down

0 comments on commit 89f6c6e

Please sign in to comment.