Scope authenticated delivery cleanup

This commit is contained in:
jack
2026-07-25 21:20:46 +02:00
parent 377145763d
commit ec6e98afeb
12 changed files with 684 additions and 73 deletions
@@ -203,6 +203,134 @@ struct BridgeCourierServiceTests {
#expect(confirmed.sealRequests.isEmpty)
}
@Test func sameMessageIDIsScopedByRecipientAcrossRejectedActiveAndPersistedState() {
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent("bridge-dedup-\(UUID().uuidString).json")
defer { try? FileManager.default.removeItem(at: fileURL) }
let rejectedKey = Fixture.randomKey()
let firstKey = Fixture.randomKey()
let secondKey = Fixture.randomKey()
let thirdKey = Fixture.randomKey()
let messageID = "recipient-scoped-collision"
let fixture = Fixture(dedupStore: BridgeDropDedupStore(fileURL: fileURL))
fixture.sealResult = makeEnvelope(
recipientKey: rejectedKey,
ciphertext: Data(
repeating: 7,
count: BridgeCourierService.Limits.maxDropEnvelopeBytes + 1
)
)
var rejectedResults: [Bool] = []
fixture.service.depositDrop(
content: "rejected",
messageID: messageID,
recipientNoiseKey: rejectedKey
) { rejectedResults.append($0) }
#expect(rejectedResults == [false])
fixture.sealResult = makeEnvelope(recipientKey: firstKey)
fixture.automaticPublishResult = nil
var firstResults: [Bool] = []
var secondResults: [Bool] = []
var duplicateFirstResults: [Bool] = []
fixture.service.depositDrop(
content: "first",
messageID: messageID,
recipientNoiseKey: firstKey
) { firstResults.append($0) }
fixture.service.depositDrop(
content: "second",
messageID: messageID,
recipientNoiseKey: secondKey
) { secondResults.append($0) }
fixture.service.depositDrop(
content: "first duplicate",
messageID: messageID,
recipientNoiseKey: firstKey
) { duplicateFirstResults.append($0) }
#expect(fixture.publishedEvents.count == 2)
#expect(fixture.pendingPublishCompletions.count == 2)
#expect(duplicateFirstResults == [false])
#expect(firstResults.isEmpty)
#expect(secondResults.isEmpty)
fixture.resolveNextPublish(true)
fixture.resolveNextPublish(true)
#expect(firstResults == [true])
#expect(secondResults == [true])
fixture.service.flushDedupSnapshot()
let relaunched = Fixture(
dedupStore: BridgeDropDedupStore(fileURL: fileURL)
)
relaunched.sealResult = makeEnvelope(recipientKey: thirdKey)
var relaunchResults: [Bool] = []
relaunched.service.depositDrop(
content: "first",
messageID: messageID,
recipientNoiseKey: firstKey
) { relaunchResults.append($0) }
relaunched.service.depositDrop(
content: "second",
messageID: messageID,
recipientNoiseKey: secondKey
) { relaunchResults.append($0) }
relaunched.service.depositDrop(
content: "third",
messageID: messageID,
recipientNoiseKey: thirdKey
) { relaunchResults.append($0) }
#expect(relaunchResults == [false, false, true])
#expect(relaunched.sealRequests.count == 1)
#expect(relaunched.sealRequests.first?.key == thirdKey)
#expect(relaunched.publishedEvents.count == 1)
}
@Test func legacyPublishedMessageIDIsWildcardUntilItsOriginalExpiry() {
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent("bridge-dedup-\(UUID().uuidString).json")
defer { try? FileManager.default.removeItem(at: fileURL) }
var date = Date()
let messageID = "legacy-wildcard"
let recipientKey = Fixture.randomKey()
let store = BridgeDropDedupStore(fileURL: fileURL)
store.save(BridgeDropDedupStore.Snapshot(
publishedDropKeys: [messageID: date],
seenDropEventIDs: [:]
))
let fixture = Fixture(
now: { date },
dedupStore: BridgeDropDedupStore(fileURL: fileURL)
)
fixture.sealResult = makeEnvelope(recipientKey: recipientKey)
var results: [Bool] = []
fixture.service.depositDrop(
content: "legacy",
messageID: messageID,
recipientNoiseKey: recipientKey
) { results.append($0) }
#expect(results == [false])
#expect(fixture.sealRequests.isEmpty)
date = date.addingTimeInterval(CourierEnvelope.maxLifetimeSeconds + 1)
fixture.service.depositDrop(
content: "after expiry",
messageID: messageID,
recipientNoiseKey: recipientKey
) { results.append($0) }
#expect(results == [false, true])
#expect(fixture.publishedEvents.count == 1)
fixture.service.flushDedupSnapshot()
let snapshot = BridgeDropDedupStore(fileURL: fileURL).load()
#expect(snapshot.publishedDropKeys[messageID] == nil)
#expect(snapshot.publishedDropKeys.count == 1)
}
@Test func panicWipeInvalidatesInFlightPublishCompletion() throws {
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent("bridge-dedup-\(UUID().uuidString).json")
@@ -297,8 +425,10 @@ struct BridgeCourierServiceTests {
#expect(firstResults == [false])
// The evicted first drop is deposit-able again (slot released).
let sealCountBeforeRetry = fixture.sealRequests.count
fixture.service.depositDrop(content: "0-retry", messageID: firstID, recipientNoiseKey: key)
#expect(fixture.service.pendingDrops.last?.dedupKey == firstID)
#expect(fixture.sealRequests.count == sealCountBeforeRetry + 1)
#expect(fixture.service.pendingDrops.count == BridgeCourierService.Limits.maxPendingDrops)
}
@Test func oversizeDropConsumesSlotInsteadOfChurning() {
+102 -1
View File
@@ -188,6 +188,58 @@ struct MessageRouterTests {
router.markDelivered("normal-handshake")
}
@Test @MainActor
func authenticationRetry_scopesCollidingMessageIDsByPeer() async {
let securePeer = PeerID(str: "0000000000000025")
let pendingPeer = PeerID(str: "0000000000000026")
let transport = MockTransport()
transport.connectedPeers = [securePeer, pendingPeer]
transport.securePeers = [securePeer]
let router = MessageRouter(transports: [transport])
let promotedID = "collision-promoted"
let clearedID = "collision-cleared"
// Pending B then secure A: an ID-global marker falsely promotes B.
router.sendPrivate(
"pending promoted",
to: pendingPeer,
recipientNickname: "Pending",
messageID: promotedID
)
router.sendPrivate(
"secure promoted",
to: securePeer,
recipientNickname: "Secure",
messageID: promotedID
)
// Secure A then pending B: an ID-global removal falsely clears A.
router.sendPrivate(
"secure cleared",
to: securePeer,
recipientNickname: "Secure",
messageID: clearedID
)
router.sendPrivate(
"pending cleared",
to: pendingPeer,
recipientNickname: "Pending",
messageID: clearedID
)
transport.resetRecordings()
transport.securePeers = [securePeer, pendingPeer]
router.retrySecurePrivateMessagesAfterAuthentication(for: [pendingPeer])
#expect(transport.sentPrivateMessages.isEmpty)
router.retrySecurePrivateMessagesAfterAuthentication(for: [securePeer])
#expect(transport.sentPrivateMessages.count == 2)
#expect(Set(transport.sentPrivateMessages.map(\.messageID)) == [promotedID, clearedID])
#expect(transport.sentPrivateMessages.allSatisfy { $0.peerID == securePeer })
}
@Test @MainActor
func authenticationRetry_doesNotDuplicateMessageRequeuedByBLEForHandshake() async {
let peerID = PeerID(str: "0000000000000021")
@@ -713,6 +765,52 @@ struct MessageRouterTests {
#expect(carried == ["bridge-ack"])
}
@Test @MainActor
func bridgeDepositsScopeCollidingMessageIDsByRecipient() async {
let firstRecipient = PeerID(str: "00000000000000b1")
let secondRecipient = PeerID(str: "00000000000000b2")
let firstKey = Data(repeating: 0xB1, count: 32)
let secondKey = Data(repeating: 0xB2, count: 32)
let recipientKeys = [
firstRecipient: firstKey,
secondRecipient: secondKey
]
let router = MessageRouter(
transports: [MockTransport()],
courierDirectory: CourierDirectory(
noiseKey: { recipientKeys[$0] },
isTrustedCourier: { _ in false }
)
)
var requestedKeys: [Data] = []
var completions: [@MainActor (Bool) -> Void] = []
router.bridgeCourierDeposit = { _, _, recipientKey, completion in
requestedKeys.append(recipientKey)
completions.append(completion)
}
var carriedPeers: [PeerID] = []
router.onMessageCarried = { _, peerID in carriedPeers.append(peerID) }
router.sendPrivate(
"First",
to: firstRecipient,
recipientNickname: "First",
messageID: "bridge-collision"
)
router.sendPrivate(
"Second",
to: secondRecipient,
recipientNickname: "Second",
messageID: "bridge-collision"
)
#expect(completions.count == 2)
#expect(Set(requestedKeys) == [firstKey, secondKey])
completions.forEach { $0(true) }
#expect(Set(carriedPeers) == [firstRecipient, secondRecipient])
}
// MARK: - Outbox persistence
@Test @MainActor
@@ -829,7 +927,10 @@ struct MessageRouterTests {
transport.reachablePeers.formUnion([acknowledgedPeer, otherPeer])
let router = MessageRouter(transports: [transport], outboxStore: restoredStore)
#expect(!router.markDelivered("shared-locked-id", for: [acknowledgedPeer]))
router.markDelivered(
"shared-locked-id",
from: [acknowledgedPeer]
)
protectedDataUnavailable = false
restoredStore.retryDeferredLoad()
await Task.yield()