Skip to content

Add BEP 11 PEX and a growable shared peer pool to the swarm#12

Merged
iksnerd merged 1 commit into
mainfrom
feat/pex-bep11
Jun 13, 2026
Merged

Add BEP 11 PEX and a growable shared peer pool to the swarm#12
iksnerd merged 1 commit into
mainfrom
feat/pex-bep11

Conversation

@iksnerd

@iksnerd iksnerd commented Jun 13, 2026

Copy link
Copy Markdown
Owner

What

Replaces the swarm's static peer-list partitioning with a shared, growable peer pool and wires in BEP 11 peer exchange (ut_pex). PEX discovers peers during a download; the old Start() split the initial list across workers with no way to add peers mid-flight, so PEX peers had nowhere to go — doing both together also closes the deferred static-partitioning reliability gap.

Changes

Protocol (peer.go)

  • Advertise ut_pex (local id 2) alongside ut_metadata in the BEP 10 extended handshake.
  • parsePexPeers: validates against PeerMessageLimits, unpacks added (compact v4) + added6 (compact v6) via the existing unpackPeers, and drops unconnectable entries (port 0 / unspecified IP) at the trust boundary.
  • PeerConn buffers harvested peers (handlePexMessage / DrainPexPeers).

Swarm (swarm.go)

  • peerChan (buffered) + mutex-guarded knownAddrs replace the static split; AddPeers dedups + non-blocking push.
  • Workers acquire a connection from the pool before pulling work, so a worker waiting on a peer can't strand a piece marked in-progress.
  • inFlight (atomic) is incremented at the pull, before dialing, so the new exhaustion supervisor can't mistake an in-flight connect for a dead swarm. It cancels only after the pool is empty, inFlight==0, and pieces remain — sustained across a short grace window.
  • The unbuffered-resultChan / drain / close-after-WaitGroup shutdown is preserved verbatim.

Wiring (downloader.go)

  • downloadPiece harvests ut_pex messages; the worker drains them into the pool after each piece. connectToAny → single-addr dialAndHandshake.

Testing

  • Unit (-race): dead-peer skip, PEX discovery end-to-end, peer-supply exhaustion, no goroutine leak, plus the parser/handshake/drain tests.
  • Real-world e2e: against a live transmission-daemon seeder (5 MiB / 20 pieces) — checksum verified. Transmission's ut_pex was harvested and used mid-download, validating the PEX path against a real client. This run also surfaced (and fixed) Transmission advertising an unconnectable port-0 peer.

🤖 Generated with Claude Code

The swarm statically partitioned the initial peer list across workers at
Start, so a worker that lost its peers couldn't get others' and PEX peers
had nowhere to go. Replace that with a shared, growable pool and wire in
BEP 11 peer exchange.

Protocol (peer.go):
- Advertise ut_pex (local id 2) alongside ut_metadata in the BEP 10
  extended handshake.
- parsePexPeers validates against PeerMessageLimits, then unpacks added
  (compact v4) and added6 (compact v6) via the existing unpackPeers, and
  drops unconnectable entries (port 0 / unspecified IP) at the boundary.
- PeerConn buffers harvested peers (handlePexMessage / DrainPexPeers).

Swarm (swarm.go):
- peerChan (buffered) + mutex-guarded knownAddrs replace the static split;
  AddPeers dedups and pushes non-blocking.
- Workers acquire a connection from the pool BEFORE pulling work, so a
  worker waiting on a peer can't strand a piece marked in-progress.
- inFlight (atomic) is incremented at the pull, before dialing, so the new
  exhaustion supervisor can't mistake an in-flight connect for a dead swarm;
  it cancels only after the pool is empty, inFlight==0, and pieces remain,
  sustained across a short grace window.
- The unbuffered-resultChan / drain / close-after-WaitGroup shutdown is
  preserved verbatim.

Wiring (downloader.go): downloadPiece harvests ut_pex messages; the worker
drains them into the pool after each piece. connectToAny is replaced by a
single-addr dialAndHandshake.

Tested unit (dead-peer skip, PEX discovery e2e, exhaustion, no-leak) under
-race, and end-to-end against a real Transmission seeder (5 MiB / 20 pieces,
checksum verified) — during which Transmission's ut_pex was harvested and
used, validating the path against a real client.
@iksnerd iksnerd merged commit bc99dfb into main Jun 13, 2026
1 check passed
@iksnerd iksnerd deleted the feat/pex-bep11 branch June 13, 2026 16:35
iksnerd added a commit that referenced this pull request Jun 21, 2026
Add BEP 11 PEX and a growable shared peer pool to the swarm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant