Skip to content

Complete BEP 6 Fast Extension: send Allowed Fast Set + record received#1052

Open
guangtoutong wants to merge 1 commit into
anacrolix:masterfrom
guangtoutong:feat/bep6-allowed-fast-set
Open

Complete BEP 6 Fast Extension: send Allowed Fast Set + record received#1052
guangtoutong wants to merge 1 commit into
anacrolix:masterfrom
guangtoutong:feat/bep6-allowed-fast-set

Conversation

@guangtoutong
Copy link
Copy Markdown

Closes #235.

Completes BEP 6 Fast Extension by adding the two missing pieces:

1. Bug fix: received AllowedFast was a no-op

Before this change, case pp.AllowedFast in peerconn.mainReadLoop logged the message but never wrote to peerAllowedFast. Every downstream check (requesting.go, shouldRequestWithoutBias, peer.go:peerAllowedFast.Iterate, torrent.go:1548) read an empty bitmap, so honoring a peer's allowed-fast set was effectively disabled.

2. New: send our Allowed Fast Set

When fastEnabled() and torrent metadata are available, we now compute the BEP 6 allowed-fast set for the remote peer and send one AllowedFast message per piece after the Have-all/Have-none/Bitfield decision in sendInitialMessages. Peers can request these pieces while choked, removing the cold-start penalty.

Algorithm

internal/fast/fast.go — SHA1-based pseudorandom indices seeded with the /24-masked peer IP and v1 info hash. Pure function so peers can independently re-derive and verify. Returns nil for IPv6 (BEP 6 defines the algorithm for IPv4 only). Reference: cenkalti/rain/internal/fast (as suggested in #235).

Tests

internal/fast/fast_test.go:

  • BEP 6 canonical example: ip=80.4.4.200, infohash=0xAA*20, numPieces=1313, k=7 ⇒ [1059, 431, 808, 1217, 287, 376, 1188]
  • Determinism (peers must reproduce the same set)
  • /24 subnet stability (same subnet ⇒ same set, different subnet ⇒ different)
  • IPv6 returns nil
  • Uniqueness in collision-prone configurations (k = numPieces)
  • Edge cases: k=0, numPieces=0

All existing tests pass. storage/possum fails locally on Windows due to a missing native libpossum library — unrelated to this change.

Files

  • internal/fast/fast.go — new package
  • internal/fast/fast_test.go — new tests
  • peerconn.go — fix receive path, add sendAllowedFastSet
  • client.go — invoke sendAllowedFastSet after Have-all/Have-none/Bitfield

cc @cenkalti (parts of the algorithm are based on rain's implementation).

Closes anacrolix#235.

Adds the missing pieces of BEP 6 Fast Extension. Before this change two
behaviours were broken:

1. AllowedFast received from peers was logged but never recorded into the
   peerAllowedFast bitmap, so every downstream check (requesting.go,
   shouldRequestWithoutBias, peer.go, torrent.go) read an empty set and
   the feature was effectively a no-op on the receive side.

2. We never sent AllowedFast to remote peers. Peers that support Fast
   Extension expect a small set of allowed-fast pieces after handshake
   so they can request data while choked; without it the cold-start
   penalty is one full choke cycle.

This change:

  internal/fast/fast.go
    Implements the BEP 6 generator: SHA1-based pseudorandom indices
    seeded with the /24-masked peer IP and the v1 info hash. Pure
    function so peers can independently re-derive and verify.
    Returns nil for IPv6 (the spec defines the algorithm for IPv4
    only) and for zero k / zero piece count.

  peerconn.go
    - Fixes the receive path: case pp.AllowedFast now records the
      piece into peerAllowedFast so downstream bitmap reads work.
    - Adds (*PeerConn).sendAllowedFastSet, called from
      sendInitialMessages when fastEnabled() and torrent metadata
      are available. Skips when peer has no IPv4 or when only the
      v2 info hash is known (algorithm requires v1 hash).
    - Bumps the "allowed fasts sent" expvar counter, mirroring the
      existing "allowed fasts received".

  client.go
    sendInitialMessages calls pc.sendAllowedFastSet() after the
    Have-all/Have-none/Bitfield decision, before Port (DHT).

  internal/fast/fast_test.go
    - Verifies output against BEP 6's canonical example
      (ip=80.4.4.200, infohash=0xAA*20, numPieces=1313, k=7 ->
       [1059, 431, 808, 1217, 287, 376, 1188]).
    - Determinism, /24 subnet stability, IPv6 nil, uniqueness in
      collision-prone configurations, edge cases (k=0, numPieces=0).

Algorithm reference: cenkalti/rain (suggested by @anacrolix in anacrolix#235).
All existing tests pass. New tests included.
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.

Finish implementing BEP 6 Fast Extension

2 participants