From 3824e95f296719fd8cf8a266f89e163cfce15cc4 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 15:23:34 -0800 Subject: [PATCH] relays: keep in sync with user relay list Still need to figure out how to ensure new bootstrap relays are added... Changelog-Changed: Ensure contact relay list is kept in sync with internal relay pool --- damus/ContentView.swift | 1 + damus/Models/Contacts.swift | 9 +--- damus/Models/HomeModel.swift | 79 +++++++++++++++++++++------------- damus/Nostr/Relay.swift | 2 + damus/Nostr/RelayPool.swift | 18 +++++++- damus/Util/Notifications.swift | 6 +++ damus/Views/ConfigView.swift | 11 ++--- damus/Views/RelayView.swift | 10 +++-- 8 files changed, 87 insertions(+), 49 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 989ccd12..d0416b39 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -15,6 +15,7 @@ var BOOTSTRAP_RELAYS = [ "wss://nostr.fmt.wiz.biz", "wss://relay.nostr.bg", "wss://nostr.oxtr.dev", + "wss://nostr.v0l.io", ] struct TimestampedProfile { diff --git a/damus/Models/Contacts.swift b/damus/Models/Contacts.swift index 9397d355..69159ee6 100644 --- a/damus/Models/Contacts.swift +++ b/damus/Models/Contacts.swift @@ -139,13 +139,8 @@ func decode_json_relays(_ content: String) -> [String: RelayInfo]? { return decode_json(content) } -func remove_relay(ev: NostrEvent, privkey: String, relay: String) -> NostrEvent? { - let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw) - - var relays = ensure_relay_info(relays: [damus_relay], content: ev.content) - guard relays.index(forKey: relay) != nil else { - return nil - } +func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: String, relay: String) -> NostrEvent? { + var relays = ensure_relay_info(relays: current_relays, content: ev.content) relays.removeValue(forKey: relay) diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index e587b416..2eb84a87 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -446,21 +446,7 @@ func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) { contacts.add_friend_contact(ev) } -func load_our_contacts(contacts: Contacts, our_pubkey: String, ev: NostrEvent) { - guard ev.pubkey == our_pubkey else { - return - } - - // only use new stuff - if let current_ev = contacts.event { - guard ev.created_at > current_ev.created_at else { - return - } - } - - let m_old_ev = contacts.event - contacts.event = ev - +func load_our_contacts(contacts: Contacts, our_pubkey: String, m_old_ev: NostrEvent?, ev: NostrEvent) { var new_pks = Set() // our contacts for tag in ev.tags { @@ -572,35 +558,68 @@ func robohash(_ pk: String) -> String { return "https://robohash.org/" + pk } -func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { - load_our_contacts(contacts: contacts, our_pubkey: pubkey, ev: ev) - load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, ev: ev) - add_contact_if_friend(contacts: contacts, ev: ev) -} - -func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, ev: NostrEvent) { - guard ev.pubkey == our_pubkey else { +func load_our_stuff(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { + guard ev.pubkey == pubkey else { return } - // only load new stuff - if let old_contacts = contacts.event { - guard ev.created_at > old_contacts.created_at else { + // only use new stuff + if let current_ev = contacts.event { + guard ev.created_at > current_ev.created_at else { return } } + + let m_old_ev = contacts.event + contacts.event = ev + load_our_contacts(contacts: contacts, our_pubkey: pubkey, m_old_ev: m_old_ev, ev: ev) + load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, m_old_ev: m_old_ev, ev: ev) +} + +func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { + load_our_stuff(pool: pool, contacts: contacts, pubkey: pubkey, ev: ev) + add_contact_if_friend(contacts: contacts, ev: ev) +} + +func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_old_ev: NostrEvent?, ev: NostrEvent) { + let bootstrap_dict: [String: RelayInfo] = [:] + let old_decoded = m_old_ev.flatMap { decode_json_relays($0.content) } ?? BOOTSTRAP_RELAYS.reduce(into: bootstrap_dict) { (d, r) in + d[r] = .rw + } + guard let decoded = decode_json_relays(ev.content) else { return } - + + var changed = false + + var new = Set() for key in decoded.keys { - if let url = URL(string: key) { - if let _ = try? pool.add_relay(url, info: decoded[key]!) { - pool.connect(to: [key]) + new.insert(key) + } + + var old = Set() + for key in old_decoded.keys { + old.insert(key) + } + + let diff = old.symmetricDifference(new) + + for d in diff { + changed = true + if new.contains(d) { + if let url = URL(string: d) { + try? pool.add_relay(url, info: decoded[d] ?? .rw) } + } else { + pool.remove_relay(d) } } + + if changed { + notify(.relays_changed, ()) + } } diff --git a/damus/Nostr/Relay.swift b/damus/Nostr/Relay.swift index cfdbf231..20cf767d 100644 --- a/damus/Nostr/Relay.swift +++ b/damus/Nostr/Relay.swift @@ -28,12 +28,14 @@ class Relay: Identifiable { let descriptor: RelayDescriptor let connection: RelayConnection + var last_pong: UInt32 var flags: Int init(descriptor: RelayDescriptor, connection: RelayConnection) { self.flags = 0 self.descriptor = descriptor self.connection = connection + self.last_pong = 0 } func mark_broken() { diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift index cdb204fa..59d32e73 100644 --- a/damus/Nostr/RelayPool.swift +++ b/damus/Nostr/RelayPool.swift @@ -179,8 +179,23 @@ class RelayPool { return nil } - + + func record_last_pong(relay_id: String, event: NostrConnectionEvent) { + if case .ws_event(let ws_event) = event { + if case .pong = ws_event { + for relay in relays { + if relay.id == relay_id { + relay.last_pong = UInt32(Date.now.timeIntervalSince1970) + return + } + } + } + } + } + func handle_event(relay_id: String, event: NostrConnectionEvent) { + record_last_pong(relay_id: relay_id, event: event) + // handle reconnect logic, etc? for handler in handlers { handler.callback(relay_id, event) @@ -193,3 +208,4 @@ func add_rw_relay(_ pool: RelayPool, _ url: String) { try? pool.add_relay(url_, info: RelayInfo.rw) } + diff --git a/damus/Util/Notifications.swift b/damus/Util/Notifications.swift index 8b494849..a3374834 100644 --- a/damus/Util/Notifications.swift +++ b/damus/Util/Notifications.swift @@ -13,6 +13,12 @@ extension Notification.Name { } } +extension Notification.Name { + static var relays_changed: Notification.Name { + return Notification.Name("relays_changed") + } +} + extension Notification.Name { static var select_event: Notification.Name { return Notification.Name("select_event") diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 88798102..dab1bea1 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -41,13 +41,9 @@ struct ConfigView: View { var body: some View { ZStack(alignment: .leading) { Form { - if let ev = state.contacts.event { - Section("Relays") { - if let relays = decode_json_relays(ev.content) { - List(Array(relays.keys.sorted()), id: \.self) { relay in - RelayView(state: state, ev: ev, relay: relay) - } - } + Section("Relays") { + List(Array(state.pool.relays), id: \.descriptor.url) { relay in + RelayView(state: state, relay: relay.descriptor.url.absoluteString) } } @@ -113,7 +109,6 @@ struct ConfigView: View { } .sheet(isPresented: $show_add_relay) { AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in - guard let relay = m_relay else { return } diff --git a/damus/Views/RelayView.swift b/damus/Views/RelayView.swift index 47c5d2ec..e08bb0b4 100644 --- a/damus/Views/RelayView.swift +++ b/damus/Views/RelayView.swift @@ -9,7 +9,6 @@ import SwiftUI struct RelayView: View { let state: DamusState - let ev: NostrEvent let relay: String let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect() @@ -46,7 +45,12 @@ struct RelayView: View { .swipeActions { if let privkey = state.keypair.privkey { Button { - guard let new_ev = remove_relay( ev: ev, privkey: privkey, relay: relay) else { + guard let ev = state.contacts.event else { + return + } + + let descriptors = state.pool.descriptors + guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else { return } @@ -64,6 +68,6 @@ struct RelayView: View { struct RelayView_Previews: PreviewProvider { static var previews: some View { - RelayView(state: test_damus_state(), ev: NostrEvent(content: "content", pubkey: "pk"), relay: "wss://relay.damus.io", conn_color: .red) + RelayView(state: test_damus_state(), relay: "wss://relay.damus.io", conn_color: .red) } }