mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:45:19 +00:00
Three confirmed defects from adversarial review of the per-relay inbound pipeline (PR #1352): - MEDIUM-2: a detached gift-wrap decrypt spawned just before a panic wipe strongly captures the pre-wipe Nostr private key and could deliver plaintext into ChatViewModel after the wipe. Add a per-pipeline monotonic wipe generation (NostrInboundPipeline.wipeGeneration), bumped from panicClearAllData via invalidateInFlightDecrypts(); spawn sites capture it and every main-actor hop drops the task's result on mismatch. The account-mailbox path (processNostrMessage) had the same pre-existing hazard and gets the identical guard, capturing the generation atomically with the identity fetch. - MEDIUM-1: the per-relay stream cap bounds FRAMES (256) but not BYTES; with the URLSession default of 1 MiB per WebSocket frame a hostile relay could pile up ~256 MiB. Set URLSessionWebSocketTask .maximumMessageSize to TransportConfig.nostrInboundMaxFrameBytes (512 KiB — an order of magnitude above any legitimate Nostr event or gift wrap we produce or expect), halving the worst case to 128 MiB, and correct the "cannot exhaust memory" comments to state the actual cap × maxFrameBytes bound. - LOW-5: three NostrRelayManagerTests gated on messagesReceived (first main hop) then immediately asserted delivery-side state that only lands after off-main verification plus a second main hop. Wait on the delivery-side state (receivedIDs / duplicate-drop counts) directly, keeping all assertions. Also: brief comment documenting pendingGiftWrapIDs growth (LOW-6) and a regression test that a panic wipe issued after spawn drops the decrypted result while leaving the pipeline usable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Test Harness Guide
This test suite uses an in-memory networking harness to make end-to-end and integration tests deterministic, fast, and race-free without touching production code.
In-Memory Bus
- File:
bitchatTests/Mocks/MockBLEService.swift - Registry/Adjacency: Global
registrymapspeerIDto aMockBLEServiceinstance;adjacencyrecords simulated links between peers. - Setup: Call
MockBLEService.resetTestBus()insetUp()to clear state between tests. - Topology: Use
simulateConnectedPeer(_:)andsimulateDisconnectedPeer(_:)to add/remove links.connectFullMesh()helpers in tests build larger topologies. - Handlers: Tests can observe data via
messageDeliveryHandler(decodedBitchatMessage) andpacketDeliveryHandler(rawBitchatPacket). - De‑duplication: A thread-safe
seenMessageIDsprevents duplicate deliveries during flooding/relays.
Broadcast Flooding
- Flag:
MockBLEService.autoFloodEnabled - Intent: When
true, public broadcasts propagate across the entire connected component (ignores TTL for reach) while still de‑duping to prevent loops. - Usage: Enabled in Integration tests (
setUp) to simulate large-network broadcast; disabled in E2E tests to keep routing explicit and verify TTL behavior (seePublicChatE2ETests.testZeroTTLNotRelayed).
Rehandshake Flow (Noise)
- Why: The legacy NACK recovery path was removed; recovery now relies on Noise session rehandshake after decrypt failure or desync.
- Manager:
NoiseSessionManagermanages per-peer sessions. - Pattern: On decrypt failure, proactively clear the local session and re-initiate a handshake. The peer accepts and replaces their session.
- Test:
IntegrationTests.testRehandshakeAfterDecryptionFailure- Corrupts ciphertext to induce a decrypt error.
- Calls
removeSession(for:)on the initiator’s manager beforeinitiateHandshake(with:)to avoidalreadyEstablished. - Verifies encrypt/decrypt succeeds post-rehandshake.
Tips
- Determinism: Add small async delays only where handler installation/topology changes could race the first send.
- Scoping: Keep
autoFloodEnabledtoggled only within Integration tests; always reset intearDown()to avoid cross-test contamination. - Direct vs Relay: Private messages target a specific peer when adjacent; otherwise they are surfaced to neighbors for relay and, if known, also delivered to the target.
Quick Start
- Create nodes and connect them:
let svc = MockBLEService(); svc.myPeerID = "PEER1"svc.simulateConnectedPeer("PEER2")
- Observe messages:
svc.messageDeliveryHandler = { msg in /* asserts */ }
- Enable broadcast flooding for Integration suites only:
MockBLEService.autoFloodEnabled = true