Complete Dialer-side birthday attack integration:
- SignalState stores peer_birthday_ports from HardNatBirthdayStart
- connect command: if peer's HardNatProbe shows non-cone NAT, waits
up to 3s for birthday ports to arrive (Acceptor needs time to open
32 sockets + STUN-probe each)
- When birthday ports arrive, generate_dialer_targets() builds hit
list (known ports + random fill) and adds them to PeerCandidates
- All birthday targets go into the dual-path race as extra candidates
- LAN/cone calls skip the wait entirely (gated on allocation type)
Full waterfall now:
1. Standard candidates (reflexive + mapped) → immediate
2. Port prediction (sequential delta) → immediate
3. Birthday targets (if non-cone peer) → +3s wait
4. All of above raced in parallel via JoinSet
5. Relay runs concurrently with 500ms head-start
599 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>