From 504a7cb0517ae3f922cbb10ddc97ca0b368436f1 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 26 May 2026 11:27:47 +0200 Subject: [PATCH] Track seen relays from successful OKs Record successful relay OK acknowledgements in RelayPool.seen so posts and manual broadcasts update relay provenance. Failed OK responses remain ignored. Changelog-Fixed: Update note relay counts after successful post or broadcast relay acknowledgements. Signed-off-by: Sjors Provoost --- damus/Core/Nostr/RelayPool.swift | 2 ++ damusTests/RelayPoolTests.swift | 56 ++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/damus/Core/Nostr/RelayPool.swift b/damus/Core/Nostr/RelayPool.swift index 99101f4e..c4df69c8 100644 --- a/damus/Core/Nostr/RelayPool.swift +++ b/damus/Core/Nostr/RelayPool.swift @@ -749,6 +749,8 @@ class RelayPool { if case .nostr_event(let ev) = event { if case .event(_, let nev) = ev { record_seen(relay_id: relay_id, note_id: nev.id) + } else if case .ok(let result) = ev, result.ok { + record_seen(relay_id: relay_id, note_id: result.event_id) } } } diff --git a/damusTests/RelayPoolTests.swift b/damusTests/RelayPoolTests.swift index 70f79dad..380ceb6d 100644 --- a/damusTests/RelayPoolTests.swift +++ b/damusTests/RelayPoolTests.swift @@ -57,6 +57,60 @@ final class RelayPoolTests: XCTestCase { ], expectedError: .RelayAlreadyExists) } + /// Creates fresh inputs for seen-recording tests. + func makeRecordSeenFixture() throws -> (pool: RelayPool, relay: RelayURL, noteID: NoteId) { + let relay = try XCTUnwrap(RelayURL("wss://relay.example.com")) + let noteID = try XCTUnwrap(NoteId(hex: String(repeating: "a", count: 64))) + return (RelayPool(ndb: nil), relay, noteID) + } + + /// Verifies successful relay OK responses update the relay provenance map. + func testRecordSeenRecordsSuccessfulOKResponses() async throws { + let (pool, relay, noteID) = try makeRecordSeenFixture() + let result = CommandResult(event_id: noteID, ok: true, msg: "") + + await pool.record_seen(relay_id: relay, event: .nostr_event(.ok(result))) + + let seenRelays = await pool.seen[noteID] + let relayCount = await pool.counts[relay] + + XCTAssertEqual(seenRelays, Set([relay])) + XCTAssertEqual(relayCount, Optional(UInt64(1))) + } + + /// Verifies duplicate relay OK responses do not increment provenance twice. + func testRecordSeenRecordsDuplicateSuccessfulOKResponsesOnce() async throws { + let (pool, relay, noteID) = try makeRecordSeenFixture() + let acceptedResult = CommandResult(event_id: noteID, ok: true, msg: "") + let duplicateResult = CommandResult( + event_id: noteID, + ok: true, + msg: "duplicate: already have this event" + ) + + await pool.record_seen(relay_id: relay, event: .nostr_event(.ok(acceptedResult))) + await pool.record_seen(relay_id: relay, event: .nostr_event(.ok(duplicateResult))) + + let seenRelays = await pool.seen[noteID] + let relayCount = await pool.counts[relay] + + XCTAssertEqual(seenRelays, Set([relay])) + XCTAssertEqual(relayCount, Optional(UInt64(1))) + } + + /// Verifies failed relay OK responses do not count as relay provenance. + func testRecordSeenIgnoresFailedOKResponses() async throws { + let (pool, relay, noteID) = try makeRecordSeenFixture() + let result = CommandResult(event_id: noteID, ok: false, msg: "blocked: test") + + await pool.record_seen(relay_id: relay, event: .nostr_event(.ok(result))) + + let seenRelays = await pool.seen[noteID] + let relayCount = await pool.counts[relay] + + XCTAssertNil(seenRelays) + XCTAssertNil(relayCount) + } } /// Adds relay URLs to a pool and verifies duplicate URL handling. @@ -84,5 +138,3 @@ func testAddRelays(urls: [String], expectedError: RelayPool.RelayError? = nil) a XCTFail("An unexpected error was thrown: \(error)") } } - -