mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 23:45:18 +00:00
New MessageType 0x23 carries TLV-encoded board posts and tombstones,
self-signed with the author's Ed25519 key ("bitchat-board-v1" /
"bitchat-board-del-v1" domains) so notices verify without the author
present. BoardStore persists raw signed packets under Application
Support/board/ (200 posts, 5 per author, oldest evicted; expiry sweep;
tombstones retained until the deleted post's original expiry) and is
wiped on panic.
Board packets join gossip sync as bit 8 of the existing variable-length
types bitfield (a second byte old decoders already accept and ignore),
with a 60s round and its own capacity, served straight from the board
store so retention has one owner. Posts relay like broadcasts; urgent
posts get the announce-class TTL cap.
UI: a pin button in the header opens the board for the current channel
(geohash board, or mesh-local board), with urgent-pinned newest-first
listing, compose with urgent toggle and 1/3/7-day expiry, and
swipe-delete on own posts. Geohash posts also publish one-way as
Nostr kind-1 location notes when relays are reachable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
79 lines
3.2 KiB
Swift
79 lines
3.2 KiB
Swift
//
|
|
// SyncTypeFlagsBoardTests.swift
|
|
// bitchatTests
|
|
//
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, see <https://unlicense.org>
|
|
//
|
|
|
|
import BitFoundation
|
|
import Foundation
|
|
import Testing
|
|
@testable import bitchat
|
|
|
|
/// The board sync flag is the first bit outside the original single byte of
|
|
/// type flags. These tests pin down the wire compatibility contract: the
|
|
/// types TLV has been a variable-length (1-8 byte) little-endian bitfield
|
|
/// since type-aware sync, so widening to two bytes must decode everywhere
|
|
/// and unknown bits must be ignored, not rejected.
|
|
struct SyncTypeFlagsBoardTests {
|
|
|
|
@Test func boardFlagEncodesIntoSecondByte() throws {
|
|
let data = try #require(SyncTypeFlags.board.toData())
|
|
// Little-endian: low byte first, board bit (bit 8) in byte 2.
|
|
#expect(data == Data([0x00, 0x01]))
|
|
}
|
|
|
|
@Test func boardFlagRoundTrips() throws {
|
|
let flags = SyncTypeFlags(messageTypes: [.message, .boardPost])
|
|
let data = try #require(flags.toData())
|
|
let decoded = try #require(SyncTypeFlags.decode(data))
|
|
#expect(decoded.contains(.message))
|
|
#expect(decoded.contains(.boardPost))
|
|
#expect(!decoded.contains(.fragment))
|
|
#expect(Set(decoded.toMessageTypes()) == Set([.message, .boardPost]))
|
|
}
|
|
|
|
/// An old decoder is modeled by bits it has no mapping for: the shared
|
|
/// decode path accepts the bytes and simply maps unknown bits to no
|
|
/// message type, so a board-only request reads as "nothing I can serve".
|
|
@Test func unknownBitsDecodeToNoTypes() throws {
|
|
// Bits 9-15 are unassigned; a future (or unknown) two-byte bitfield
|
|
// must decode without error and yield no known types.
|
|
let decoded = try #require(SyncTypeFlags.decode(Data([0x00, 0xFE])))
|
|
#expect(decoded.toMessageTypes().isEmpty)
|
|
for type in [MessageType.announce, .message, .fragment, .fileTransfer, .boardPost] {
|
|
#expect(!decoded.contains(type))
|
|
}
|
|
}
|
|
|
|
@Test func mixedKnownAndUnknownBitsKeepKnownTypes() throws {
|
|
// Known low-byte flags survive alongside unknown high bits.
|
|
let decoded = try #require(SyncTypeFlags.decode(Data([0x03, 0xFE])))
|
|
#expect(decoded.contains(.announce))
|
|
#expect(decoded.contains(.message))
|
|
#expect(Set(decoded.toMessageTypes()) == Set([.announce, .message]))
|
|
}
|
|
|
|
@Test func requestSyncPacketRoundTripsBoardFlag() throws {
|
|
let request = RequestSyncPacket(
|
|
p: 4,
|
|
m: 128,
|
|
data: Data([0xAB, 0xCD]),
|
|
types: SyncTypeFlags(messageTypes: [.boardPost])
|
|
)
|
|
let decoded = try #require(RequestSyncPacket.decode(from: request.encode()))
|
|
let types = try #require(decoded.types)
|
|
#expect(types.contains(.boardPost))
|
|
#expect(!types.contains(.message))
|
|
}
|
|
|
|
@Test func singleByteLegacyEncodingStillDecodes() throws {
|
|
// Requests from old clients keep the one-byte bitfield.
|
|
let decoded = try #require(SyncTypeFlags.decode(Data([0x03])))
|
|
#expect(decoded.contains(.announce))
|
|
#expect(decoded.contains(.message))
|
|
#expect(!decoded.contains(.boardPost))
|
|
}
|
|
}
|