Add capability bits to announce TLV (#1375)

Announces now carry an optional capabilities TLV (0x05): a little-endian
bitfield with named bits for upcoming features (prekeys, wifiBulk,
gateway, groups, board, vouch, meshDiagnostics). Old clients skip the
unknown TLV; peers without it decode as nil so features can distinguish
"legacy peer" from "advertises nothing".

PeerCapabilities lives in BitFoundation with a minimal-length encoding
that preserves unknown bits for forward compatibility. Peer capabilities
are stored in the BLE peer registry on verified announce and exposed via
BLEService.peerCapabilities(_:). The local advertisement set is empty
until each feature ships its bit.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-07-07 14:07:52 +02:00
committed by GitHub
co-authored by jack Claude Fable 5
parent c2a5668569
commit 5fdc15d5af
7 changed files with 182 additions and 5 deletions
+37
View File
@@ -1,3 +1,4 @@
import BitFoundation
import Foundation
import Testing
@@ -108,6 +109,42 @@ struct PacketsTests {
#expect(decoded.directNeighbors == nil)
}
@Test
func announcementPacketRoundTripsCapabilities() throws {
let capabilities: PeerCapabilities = [.prekeys, .board, .meshDiagnostics]
let packet = AnnouncementPacket(
nickname: "alice",
noisePublicKey: Data(repeating: 0x11, count: 32),
signingPublicKey: Data(repeating: 0x22, count: 32),
directNeighbors: nil,
capabilities: capabilities
)
let encoded = try #require(packet.encode())
let decoded = try #require(AnnouncementPacket.decode(from: encoded))
#expect(decoded.capabilities == capabilities)
}
@Test
func announcementPacketWithoutCapabilitiesDecodesNilAndUnknownBitsSurvive() throws {
let legacy = try #require(
AnnouncementPacket(
nickname: "alice",
noisePublicKey: Data(repeating: 0x11, count: 32),
signingPublicKey: Data(repeating: 0x22, count: 32),
directNeighbors: nil
).encode()
)
// The TLV is emitted only when capabilities are set, so legacy peers
// (and this packet) decode as nil rather than empty.
#expect(try #require(AnnouncementPacket.decode(from: legacy)).capabilities == nil)
var withFutureBits = legacy
withFutureBits.append(makeTLV(type: 0x05, value: Data([0x80, 0x01])))
let decoded = try #require(AnnouncementPacket.decode(from: withFutureBits))
#expect(decoded.capabilities?.rawValue == 0x0180)
}
@Test
func privateMessagePacketRejectsUnknownTypeAndTruncation() {
let unknownTLV = Data([0x7F, 0x01, 0x41])