TCP hole punching for Bitcoin nodes behind home NATs?

I came up with option 6: coordinator-less hole punching

Every option so far needs a coordinator, because two nodes behind NATs have to (a) learn each other’s IP:port and (b) agree on when to fire. In option 6 addresses come from normal addr-relay, and holepunch timing is derived from the next blocks (e.g. header timestamp) that arrive.

  1. A node that wants to hole punch announces a new addrv2 type with its hole-punchable IP:port and an ephemeral EC public key P. Nodes keep their ephemeral private key p.
  2. Nodes receiving it store the address and calculate a ECDH shared secret with P_other and p_own.
  3. On every new block, each node computes SHA256(tag || shared_secret || block_hash) for every entry it had received before the block. It sorts the resulting hashes and keeps the few lowest. Those are the peers it tries this round.
  4. For each candidate, it calls connect() to that IP:port from its punch port and leaves the socket pending.

Both sides derive the same shared secret, which also causes them to derive the same hash. Two peers find themself through being their sets of the lowest hash candidates. The timing is derived from the block header (exact way is tbd) and the same for both nodes. We don’t need the timing to be exact. During testing, a few seconds plus or minus didn’t matter. So clocks can be slightly out of sync.

The (next) block hash is used to stop an attacker from grinding. Without it, someone with my P could try private keys until their hash is tiny and be my top pick forever. When using the block hash as salt, there’s nothing to grind, since the block doesn’t exist yet when they publish their key. Nodes just have to ignore blocks they heard about before receiving the address. Privacy-wise, two public keys don’t reveal the shared secret, so an outside observer can’t tell who connects to whom or when. Participation is public though, since both sides publish an IP:port.

Both sides have to announce and have each other’s address, so success depends on how widely these spread. Additionally, it needs careful design of an holepunch address manager and rate-limiting to mitigate an attacker flooding the node with fake addresses or public keys that will never result in a holepunch connection.

2 Likes