mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 19:05:20 +00:00
Part A — source-route origination policy: - Gate route application (BLESourceRouteOriginationPolicy): only packets we author, directed at a single peer, with TTL headroom, whose recipient is not directly connected. Relays no longer attach routes to (and re-sign) packets they merely forward. - Version-gate paths: MeshTopologyTracker records the highest protocol version observed per peer; BFS routes require every intermediate hop and the recipient to be v2-observed, capped at 4 intermediate hops. - Degrade on failure: BLESourceRouteFailureCache marks a routed send that sees no inbound traffic from the recipient within 10s as failed and floods for 60s before retrying routes. Part B — REQUEST_SYNC fragmentIdFilter (TLV 0x06): - Requester: BLEFragmentAssemblyBuffer reports stalled broadcast reassemblies (no new fragment for 5s, retried at most every 10s); the maintenance pass sends a types=fragment REQUEST_SYNC naming the stalled 8-byte fragment stream IDs to each connected peer. - Responder: GossipSyncManager restricts the fragment diff to exactly the named streams, bypassing the since-cursor while the GCS filter still excludes pieces the requester holds; RSR/TTL-0/rate-limit semantics unchanged and REQUEST_SYNC stays link-local. - Bounds: at most 60 IDs per request (60*17-1 = 1019 bytes <= the 1024-byte decoder cap); oversized 0x06 values are ignored, not fatal. Docs: SOURCE_ROUTING.md gains the iOS origination policy (§8); REQUEST_SYNC_MANAGER.md documents 0x05/0x06 as implemented. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99 lines
3.7 KiB
Swift
99 lines
3.7 KiB
Swift
//
|
|
// BLESourceRouteFailureCacheTests.swift
|
|
// bitchatTests
|
|
//
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, see <https://unlicense.org>
|
|
//
|
|
|
|
import Testing
|
|
import Foundation
|
|
import BitFoundation
|
|
@testable import bitchat
|
|
|
|
struct BLESourceRouteFailureCacheTests {
|
|
private let recipient = PeerID(str: "0102030405060708")
|
|
private let config = BLESourceRouteFailureCache.Config(
|
|
confirmationWindowSeconds: 10,
|
|
suppressionSeconds: 60
|
|
)
|
|
|
|
private func attempts(_ cache: inout BLESourceRouteFailureCache, at date: Date) -> Bool {
|
|
cache.shouldAttemptRoute(to: recipient, now: date)
|
|
}
|
|
|
|
@Test func allowsRoutingByDefault() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
#expect(attempts(&cache, at: Date()))
|
|
}
|
|
|
|
@Test func unconfirmedRoutedSendSuppressesRouting() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
// Inside the confirmation window: keep routing.
|
|
#expect(attempts(&cache, at: t0.addingTimeInterval(5)))
|
|
// Past the window with no inbound traffic: route failed, flood.
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(11)))
|
|
// Still suppressed for the suppression TTL.
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(40)))
|
|
// Suppression lapses: routing may be attempted again.
|
|
#expect(attempts(&cache, at: t0.addingTimeInterval(11 + 61)))
|
|
}
|
|
|
|
@Test func inboundActivityConfirmsPendingSend() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
cache.noteInboundActivity(from: recipient)
|
|
// Confirmed: no suppression even long after the window.
|
|
#expect(attempts(&cache, at: t0.addingTimeInterval(30)))
|
|
}
|
|
|
|
@Test func inboundActivityDoesNotLiftActiveSuppression() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
// Trip the failure → suppression starts at t0+15.
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(15)))
|
|
// Inbound traffic may have arrived via flood; suppression holds.
|
|
cache.noteInboundActivity(from: recipient)
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(20)))
|
|
#expect(attempts(&cache, at: t0.addingTimeInterval(15 + 61)))
|
|
}
|
|
|
|
@Test func backToBackSendsShareOneDeadline() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
cache.noteRoutedSend(to: recipient, now: t0.addingTimeInterval(8))
|
|
// Deadline runs from the first unconfirmed send.
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(11)))
|
|
}
|
|
|
|
@Test func pruneDropsExpiredEntries() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
// Past confirmation + suppression: the entry can no longer matter.
|
|
cache.prune(now: t0.addingTimeInterval(75))
|
|
#expect(attempts(&cache, at: t0.addingTimeInterval(76)))
|
|
}
|
|
|
|
@Test func pruneKeepsEntriesThatStillMatter() {
|
|
var cache = BLESourceRouteFailureCache(config: config)
|
|
let t0 = Date()
|
|
|
|
cache.noteRoutedSend(to: recipient, now: t0)
|
|
cache.prune(now: t0.addingTimeInterval(30))
|
|
// The unconverted pending entry survives pruning and still converts
|
|
// into a suppression on the next routing decision.
|
|
#expect(!attempts(&cache, at: t0.addingTimeInterval(31)))
|
|
}
|
|
}
|