mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 19:25:20 +00:00
Announces: TTL 7 (sparse only) via RelayController; no fanout subset for announces; neighbor-change rebroadcast of last 2–3 announces. Fragments: faster pacing (5ms global, 4ms directed).
This commit is contained in:
@@ -104,6 +104,10 @@ final class BLEService: NSObject {
|
|||||||
private var pendingMessagesAfterHandshake: [String: [(content: String, messageID: String)]] = [:]
|
private var pendingMessagesAfterHandshake: [String: [(content: String, messageID: String)]] = [:]
|
||||||
// Noise typed payloads (ACKs, read receipts, etc.) pending handshake
|
// Noise typed payloads (ACKs, read receipts, etc.) pending handshake
|
||||||
private var pendingNoisePayloadsAfterHandshake: [String: [Data]] = [:]
|
private var pendingNoisePayloadsAfterHandshake: [String: [Data]] = [:]
|
||||||
|
// Keep a tiny buffer of the last few unique announces we've seen (by sender)
|
||||||
|
private var recentAnnounceBySender: [String: BitchatPacket] = [:]
|
||||||
|
private var recentAnnounceOrder: [String] = []
|
||||||
|
private let recentAnnounceBufferCap = 3
|
||||||
|
|
||||||
// Queue for notifications that failed due to full queue
|
// Queue for notifications that failed due to full queue
|
||||||
private var pendingNotifications: [(data: Data, centrals: [CBCentral]?)] = []
|
private var pendingNotifications: [(data: Data, centrals: [CBCentral]?)] = []
|
||||||
@@ -1134,6 +1138,21 @@ final class BLEService: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func rebroadcastRecentAnnounces() {
|
||||||
|
// Snapshot sender order to preserve ordering and avoid holding locks while sending
|
||||||
|
let packets: [BitchatPacket] = collectionsQueue.sync {
|
||||||
|
recentAnnounceOrder.compactMap { recentAnnounceBySender[$0] }
|
||||||
|
}
|
||||||
|
guard !packets.isEmpty else { return }
|
||||||
|
for (idx, pkt) in packets.enumerated() {
|
||||||
|
// Stagger slightly to avoid bursts
|
||||||
|
let delayMs = idx * 20
|
||||||
|
messageQueue.asyncAfter(deadline: .now() + .milliseconds(delayMs)) { [weak self] in
|
||||||
|
self?.broadcastPacket(pkt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func sendData(_ data: Data, to peripheral: CBPeripheral) {
|
private func sendData(_ data: Data, to peripheral: CBPeripheral) {
|
||||||
// Fire-and-forget: Simple send without complex fallback logic
|
// Fire-and-forget: Simple send without complex fallback logic
|
||||||
guard peripheral.state == .connected else { return }
|
guard peripheral.state == .connected else { return }
|
||||||
@@ -1198,7 +1217,8 @@ final class BLEService: NSObject {
|
|||||||
ttl: packet.ttl
|
ttl: packet.ttl
|
||||||
)
|
)
|
||||||
// Pace fragments with small jitter to avoid bursts
|
// Pace fragments with small jitter to avoid bursts
|
||||||
let delayMs = index * TransportConfig.bleFragmentSpacingMs // ~6ms spacing per fragment
|
let perFragMs = (directedOnlyPeer != nil || packet.recipientID != nil) ? TransportConfig.bleFragmentSpacingDirectedMs : TransportConfig.bleFragmentSpacingMs
|
||||||
|
let delayMs = index * perFragMs
|
||||||
messageQueue.asyncAfter(deadline: .now() + .milliseconds(delayMs)) { [weak self] in
|
messageQueue.asyncAfter(deadline: .now() + .milliseconds(delayMs)) { [weak self] in
|
||||||
self?.broadcastPacket(fragmentPacket)
|
self?.broadcastPacket(fragmentPacket)
|
||||||
}
|
}
|
||||||
@@ -1359,6 +1379,7 @@ final class BLEService: NSObject {
|
|||||||
isDirectedEncrypted: (packet.type == MessageType.noiseEncrypted.rawValue) && (packet.recipientID != nil),
|
isDirectedEncrypted: (packet.type == MessageType.noiseEncrypted.rawValue) && (packet.recipientID != nil),
|
||||||
isDirectedFragment: packet.type == MessageType.fragment.rawValue && packet.recipientID != nil,
|
isDirectedFragment: packet.type == MessageType.fragment.rawValue && packet.recipientID != nil,
|
||||||
isHandshake: packet.type == MessageType.noiseHandshake.rawValue,
|
isHandshake: packet.type == MessageType.noiseHandshake.rawValue,
|
||||||
|
isAnnounce: packet.type == MessageType.announce.rawValue,
|
||||||
degree: degree,
|
degree: degree,
|
||||||
highDegreeThreshold: highDegreeThreshold
|
highDegreeThreshold: highDegreeThreshold
|
||||||
)
|
)
|
||||||
@@ -1498,6 +1519,20 @@ final class BLEService: NSObject {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record this announce for lightweight rebroadcast buffer (exclude self)
|
||||||
|
if peerID != myPeerID {
|
||||||
|
collectionsQueue.async(flags: .barrier) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.recentAnnounceBySender[peerID] = packet
|
||||||
|
if !self.recentAnnounceOrder.contains(peerID) { self.recentAnnounceOrder.append(peerID) }
|
||||||
|
// Trim to cap, oldest first
|
||||||
|
while self.recentAnnounceOrder.count > self.recentAnnounceBufferCap {
|
||||||
|
let victim = self.recentAnnounceOrder.removeFirst()
|
||||||
|
self.recentAnnounceBySender.removeValue(forKey: victim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Notify UI on main thread
|
// Notify UI on main thread
|
||||||
notifyUI { [weak self] in
|
notifyUI { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@@ -2572,6 +2607,8 @@ extension BLEService: CBPeripheralDelegate {
|
|||||||
self?.sendAnnounce(forceSend: true)
|
self?.sendAnnounce(forceSend: true)
|
||||||
// Try flushing any spooled directed packets now that we have a link
|
// Try flushing any spooled directed packets now that we have a link
|
||||||
self?.flushDirectedSpool()
|
self?.flushDirectedSpool()
|
||||||
|
// Rebroadcast a couple of recent announces to seed the new link
|
||||||
|
self?.rebroadcastRecentAnnounces()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SecureLogger.log("⚠️ Characteristic does not support notifications", category: SecureLogger.session, level: .warning)
|
SecureLogger.log("⚠️ Characteristic does not support notifications", category: SecureLogger.session, level: .warning)
|
||||||
@@ -2736,6 +2773,8 @@ extension BLEService: CBPeripheralManagerDelegate {
|
|||||||
self?.sendAnnounce(forceSend: true)
|
self?.sendAnnounce(forceSend: true)
|
||||||
// Flush any spooled directed packets now that we have a central subscribed
|
// Flush any spooled directed packets now that we have a central subscribed
|
||||||
self?.flushDirectedSpool()
|
self?.flushDirectedSpool()
|
||||||
|
// Rebroadcast a couple of recent announces to seed the new link
|
||||||
|
self?.rebroadcastRecentAnnounces()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct RelayController {
|
|||||||
isDirectedEncrypted: Bool,
|
isDirectedEncrypted: Bool,
|
||||||
isDirectedFragment: Bool,
|
isDirectedFragment: Bool,
|
||||||
isHandshake: Bool,
|
isHandshake: Bool,
|
||||||
|
isAnnounce: Bool,
|
||||||
degree: Int,
|
degree: Int,
|
||||||
highDegreeThreshold: Int) -> RelayDecision {
|
highDegreeThreshold: Int) -> RelayDecision {
|
||||||
// Suppress obvious non-relays
|
// Suppress obvious non-relays
|
||||||
@@ -46,7 +47,11 @@ struct RelayController {
|
|||||||
// TTL clamping for broadcast
|
// TTL clamping for broadcast
|
||||||
// - Dense graphs: keep very low to avoid floods
|
// - Dense graphs: keep very low to avoid floods
|
||||||
// - Sparse graphs: allow slightly longer reach for multi-hop discovery
|
// - Sparse graphs: allow slightly longer reach for multi-hop discovery
|
||||||
let ttlCap: UInt8 = degree >= highDegreeThreshold ? 3 : 6
|
// - Announces in sparse graphs get a bit more headroom
|
||||||
|
let ttlCap: UInt8 = {
|
||||||
|
if degree >= highDegreeThreshold { return 3 }
|
||||||
|
return isAnnounce ? 7 : 6
|
||||||
|
}()
|
||||||
let clamped = max(1, min(ttl, ttlCap))
|
let clamped = max(1, min(ttl, ttlCap))
|
||||||
let newTTL = clamped &- 1
|
let newTTL = clamped &- 1
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ enum TransportConfig {
|
|||||||
static let bleThreadSleepWriteShortDelaySeconds: TimeInterval = 0.05
|
static let bleThreadSleepWriteShortDelaySeconds: TimeInterval = 0.05
|
||||||
static let bleExpectedWritePerFragmentMs: Int = 8
|
static let bleExpectedWritePerFragmentMs: Int = 8
|
||||||
static let bleExpectedWriteMaxMs: Int = 2000
|
static let bleExpectedWriteMaxMs: Int = 2000
|
||||||
static let bleFragmentSpacingMs: Int = 6
|
// Faster fragment pacing; use slightly tighter spacing for directed trains
|
||||||
|
static let bleFragmentSpacingMs: Int = 5
|
||||||
|
static let bleFragmentSpacingDirectedMs: Int = 4
|
||||||
static let bleAnnounceIntervalSeconds: TimeInterval = 4.0
|
static let bleAnnounceIntervalSeconds: TimeInterval = 4.0
|
||||||
static let bleDutyOnDurationDense: TimeInterval = 3.0
|
static let bleDutyOnDurationDense: TimeInterval = 3.0
|
||||||
static let bleDutyOffDurationDense: TimeInterval = 15.0
|
static let bleDutyOffDurationDense: TimeInterval = 15.0
|
||||||
|
|||||||
Reference in New Issue
Block a user