From 5960b7f343bf98e775ce2f014fd55b959d761884 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 31 Jul 2026 14:19:50 +0200 Subject: [PATCH] Address review: split semantics, status note, DM size constraint, vacuous parity test - Parsing Rule 1 now says 'at most 5 parts' and spells out the Swift maxSplits:4 vs Rust splitn(5) off-by-one, which would otherwise make a splitn(4) implementor reject every valid reference. - Overview gains an explicit Status note: nothing parses, routes, fetches, or renders sticker references yet (reviewer ask on #1544). - Document the 255-byte DM content constraint for maximal-length references, pointing at #784 (Codex P2 thread). - parseAcceptsSonarFfiEncodedContent now #requires the parse result: #expect(ref == makeRef(...)) could pass vacuously as nil == nil. Flagged by the multi-model review panel. --- .../Protocols/StickerRefCodecTests.swift | 6 ++++-- docs/SONAR-STICKERS.md | 20 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/bitchatTests/Protocols/StickerRefCodecTests.swift b/bitchatTests/Protocols/StickerRefCodecTests.swift index de1668a0..efa75bcd 100644 --- a/bitchatTests/Protocols/StickerRefCodecTests.swift +++ b/bitchatTests/Protocols/StickerRefCodecTests.swift @@ -51,10 +51,12 @@ struct StickerRefCodecTests { #expect(StickerRefCodec.encode(ref).hasPrefix("\u{1F}sticker\u{1F}")) } - @Test func parseAcceptsSonarFfiEncodedContent() { + @Test func parseAcceptsSonarFfiEncodedContent() throws { // Hand-built exactly as mesh_parse_sticker_content would see it. let wire = "\u{1F}sticker\u{1F}30031:\(pubkey):pack\u{1F}wave\u{1F}\(sha256)" - let ref = StickerRefCodec.parse(wire) + // #require (not #expect against makeRef): if a regression made parse + // return nil here, `nil == nil` would let the test pass vacuously. + let ref = try #require(StickerRefCodec.parse(wire)) #expect(ref == makeRef(coordinate: "30031:\(pubkey):pack")) } diff --git a/docs/SONAR-STICKERS.md b/docs/SONAR-STICKERS.md index 2e842b3b..f21f9b00 100644 --- a/docs/SONAR-STICKERS.md +++ b/docs/SONAR-STICKERS.md @@ -15,6 +15,19 @@ specifies only the bitchat wire format and client behavior. The reference implementation is byte-identical to sonar-ffi's `mesh_sticker_content` / `mesh_parse_sticker_content`. +> **Status:** this lands in slices. The wire codec below is merged first as +> dead-on-arrival infrastructure — **nothing in the client parses, routes, +> fetches, or renders sticker references yet.** Parsing, consent-gated +> rendering, and opt-in sync arrive in the follow-up PRs (see the split of +> #1517). Until then, sticker content is ordinary text to every client. + +> **DM size constraint:** a maximally long reference (80-char identifier + +> 64-char shortcode) is ~290 bytes, which exceeds the current 255-byte +> private-message content limit (`PrivateMessagePacket`). Senders SHOULD keep +> the encoded reference within 255 bytes for DMs until that limit is raised +> (tracked in #784); oversized references remain valid on public mesh and +> geohash channels. + ## Wire Format ### Content Prefix @@ -46,8 +59,11 @@ Field order and count are fixed: Parsers MUST: -1. Split the content on `0x1F` with **at most 4 splits**, preserving empty - subsequences. +1. Split the content on `0x1F` into **at most 5 parts**, preserving empty + subsequences. Beware off-by-one semantics across languages: this is + Swift's `split(separator:maxSplits:)` with `maxSplits: 4` (4 splits → up + to 5 parts), but Rust's `splitn(5, ...)` — `splitn(4, ...)` yields at + most 4 parts and would reject every valid reference. 2. Accept only if the split yields **exactly 5 parts**, `parts[0]` is empty, and `parts[1] == "sticker"`. 3. Validate fields 2–4 against the table above; any violation MUST cause the