mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-27 09:05:19 +00:00
Retry unacknowledged DMs after Noise replacement (#1462)
DMs sent through an established Noise session are retained in the durable MessageRouter outbox until an authenticated delivery/read ack, and retried under the same message ID when the peer's replacement handshake authenticates — fixing DMs silently lost into stale local sessions after a remote app restart. Mesh ack handling is peer-scoped end-to-end (alias-scoped delivery status, peer-bound markDelivered, PeerMessageKey retry state), so a colliding message ID from another conversation can no longer clear or promote foreign state; bridge-drop dedup keys are recipient-scoped with hashed persistence. Includes review fix: acks arriving after a relaunch (durable outbox restored, conversation not) now clear the peer-scoped router entry unconditionally — only the UI status transition is gated on conversation presence — so a delivered message can no longer re-send to the attempt cap and be marked failed despite delivery. Regression test simulates the relaunch through real store persistence; the dead allowedPeerIDs parameter (unscoped-tombstone trap) is removed.
This commit is contained in:
@@ -65,11 +65,15 @@ final class MockTransport: Transport, PrivateMediaDeletionPersisting {
|
||||
var peerFingerprints: [PeerID: String] = [:]
|
||||
var peerNoiseStates: [PeerID: LazyHandshakeState] = [:]
|
||||
var privateMediaPolicies: [PeerID: PrivateMediaSendPolicy] = [:]
|
||||
var privateMediaReceiptSessionGenerations: [PeerID: UUID] = [:]
|
||||
var persistDeletedPrivateMediaResult = true
|
||||
var deferDeletedPrivateMediaPersistence = false
|
||||
private var pendingDeletedPrivateMediaCompletions: [
|
||||
@MainActor (Bool) -> Void
|
||||
] = []
|
||||
/// Optional synchronous hook for send-ordering tests (for example, an ack
|
||||
/// arriving before the router's send call returns).
|
||||
var onSendPrivateMessage: (@MainActor (_ messageID: String) -> Void)?
|
||||
private let mockKeychain = MockKeychain()
|
||||
|
||||
// MARK: - Transport Protocol Implementation
|
||||
@@ -174,6 +178,11 @@ final class MockTransport: Transport, PrivateMediaDeletionPersisting {
|
||||
|
||||
func sendPrivateMessage(_ content: String, to peerID: PeerID, recipientNickname: String, messageID: String) {
|
||||
sentPrivateMessages.append((content, peerID, recipientNickname, messageID))
|
||||
if let onSendPrivateMessage {
|
||||
MainActor.assumeIsolated {
|
||||
onSendPrivateMessage(messageID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendReadReceipt(_ receipt: ReadReceipt, to peerID: PeerID) {
|
||||
@@ -215,6 +224,12 @@ final class MockTransport: Transport, PrivateMediaDeletionPersisting {
|
||||
privateMediaPolicies[peerID] ?? .encrypted
|
||||
}
|
||||
|
||||
func authenticatedPrivateMediaReceiptSessionGeneration(
|
||||
to peerID: PeerID
|
||||
) -> UUID? {
|
||||
privateMediaReceiptSessionGenerations[peerID]
|
||||
}
|
||||
|
||||
func resolvePrivateMediaSendPolicy(
|
||||
to peerID: PeerID,
|
||||
completion: @escaping @MainActor (PrivateMediaSendPolicy) -> Void
|
||||
|
||||
Reference in New Issue
Block a user