Skip to content

Commit

Permalink
sdk: add support to NIP17 relay list
Browse files Browse the repository at this point in the history
When `gossip` is enabled, automatically discovery NIP17 for sending and or receiving private direct messages

Closes #640

Signed-off-by: Yuki Kishimoto <[email protected]>
  • Loading branch information
yukibtc committed Nov 22, 2024
1 parent a4ebf69 commit 2b5a86f
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 95 deletions.
134 changes: 79 additions & 55 deletions crates/nostr-sdk/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,57 +1014,7 @@ impl Client {
return Ok(self.pool.send_event(event).await?);
}

// ########## Gossip ##########

// Get all public keys involved in the event
let public_keys = event
.tags
.public_keys()
.copied()
.chain(iter::once(event.pubkey));

// Check what are up-to-date in the gossip graph and which ones require an update
let outdated_public_keys = self.gossip_graph.check_outdated(public_keys).await;
self.update_outdated_gossip_graph(outdated_public_keys)
.await?;

// Get relays
let mut outbox = self.gossip_graph.get_outbox_relays(&[event.pubkey]).await;
let inbox = self
.gossip_graph
.get_inbox_relays(event.tags.public_keys())
.await;

// Add outbox relays
for url in outbox.iter() {
if self.add_gossip_relay(url).await? {
self.connect_relay(url).await?;
}
}

// Add inbox relays
for url in inbox.iter() {
if self.add_gossip_relay(url).await? {
self.connect_relay(url).await?;
}
}

// Get WRITE relays
// TODO: avoid clone of both url and relay
let write_relays = self
.pool
.relays_with_flag(RelayServiceFlags::WRITE, FlagCheck::All)
.await
.into_keys();

// Extend OUTBOX relays with WRITE ones
outbox.extend(write_relays);

// Union of OUTBOX (and WRITE) with INBOX relays
let urls = outbox.union(&inbox);

// Send event
Ok(self.pool.send_event_to(urls, event).await?)
self.gossip_send_event(event, false).await
}

/// Send multiple events at once to all relays with [`RelayServiceFlags::WRITE`] flag.
Expand Down Expand Up @@ -1325,7 +1275,10 @@ impl Client {
Ok(contacts)
}

/// Send private direct message to all relays
/// Send a private direct message
///
/// If `gossip` is enabled (see [`Options::gossip`]) the event will be sent to NIP17 relays (automatically discovered),
/// otherwise to all relays with [`RelayServiceFlags::WRITE`] flag.
///
/// <https://github.com/nostr-protocol/nips/blob/master/17.md>
#[inline]
Expand All @@ -1343,10 +1296,16 @@ impl Client {
let signer = self.signer().await?;
let event: Event =
EventBuilder::private_msg(&signer, receiver, message, rumor_extra_tags).await?;
self.send_event(event).await

// NOT gossip, send to all relays
if !self.opts.gossip {
return self.send_event(event).await;
}

self.gossip_send_event(event, true).await
}

/// Send private direct message to specific relays
/// Send a private direct message to specific relays
///
/// <https://github.com/nostr-protocol/nips/blob/master/17.md>
#[inline]
Expand Down Expand Up @@ -1694,7 +1653,7 @@ impl Client {
// Compose filters
let filter: Filter = Filter::default()
.authors(outdated_public_keys)
.kind(Kind::RelayList);
.kinds([Kind::RelayList, Kind::InboxRelays]);

// Query from database
let database = self.database();
Expand Down Expand Up @@ -1779,6 +1738,71 @@ impl Client {
Ok(broken_down.filters)
}

async fn gossip_send_event(&self, event: Event, nip17: bool) -> Result<Output<EventId>, Error> {
// Get all public keys involved in the event
let public_keys = event
.tags
.public_keys()
.copied()
.chain(iter::once(event.pubkey));

// Check what are up to date in the gossip graph and which ones require an update
let outdated_public_keys = self.gossip_graph.check_outdated(public_keys).await;
self.update_outdated_gossip_graph(outdated_public_keys)
.await?;

let urls: HashSet<Url> = if nip17 {
// Get NIP17 relays
let relays = self
.gossip_graph
.get_nip17_inbox_relays(event.tags.public_keys().chain(iter::once(&event.pubkey)))
.await;

// Add outbox and inbox relays
for url in relays.iter() {
if self.add_gossip_relay(url).await? {
self.connect_relay(url).await?;
}
}

relays
} else {
// Get NIP65 relays
let mut outbox = self
.gossip_graph
.get_nip65_outbox_relays(&[event.pubkey])
.await;
let inbox = self
.gossip_graph
.get_nip65_inbox_relays(event.tags.public_keys())
.await;

// Add outbox and inbox relays
for url in outbox.iter().chain(inbox.iter()) {
if self.add_gossip_relay(url).await? {
self.connect_relay(url).await?;
}
}

// Get WRITE relays
// TODO: avoid clone of both url and relay
let write_relays = self
.pool
.relays_with_flag(RelayServiceFlags::WRITE, FlagCheck::All)
.await
.into_keys();

// Extend OUTBOX relays with WRITE ones
outbox.extend(write_relays);

// Union of OUTBOX (and WRITE) with INBOX relays
outbox.union(&inbox).cloned().collect()
};

// Send event
Ok(self.pool.send_event_to(urls, event).await?)
}

async fn gossip_stream_events(
&self,
filters: Vec<Filter>,
Expand Down
Loading

0 comments on commit 2b5a86f

Please sign in to comment.