Skip to content

Commit 51555b8

Browse files
committed
feat: NIP-87 federation discovery
1 parent 84c2330 commit 51555b8

File tree

4 files changed

+346
-10
lines changed

4 files changed

+346
-10
lines changed

assets/icons/search.svg

+1
Loading

src/nostr.rs

+57-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
use std::collections::BTreeMap;
1+
use std::collections::{BTreeMap, BTreeSet};
22
use std::fmt::Debug;
3+
use std::str::FromStr;
34
use std::time::Duration;
45

6+
use fedimint_core::config::FederationId;
7+
use fedimint_core::invite_code::InviteCode;
58
use iced::Subscription;
69
use nostr_relay_pool::RelayStatus;
7-
use nostr_sdk::Url;
10+
use nostr_sdk::{Alphabet, EventSource, Filter, Kind, PublicKey, SingleLetterTag, TagKind, Url};
11+
12+
const D_TAG: TagKind = TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::D));
13+
const U_TAG: TagKind = TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::U));
814

915
#[derive(Default, Debug, Clone, PartialEq, Eq)]
1016
pub struct NostrState {
@@ -69,6 +75,55 @@ impl NostrModule {
6975
)
7076
}
7177

78+
pub async fn find_federations(
79+
&self,
80+
) -> Result<
81+
BTreeMap<FederationId, (BTreeSet<PublicKey>, BTreeSet<InviteCode>)>,
82+
nostr_sdk::client::Error,
83+
> {
84+
let fedimint_recommendation_events = self
85+
.client
86+
.get_events_of(
87+
vec![Filter::new()
88+
.kind(Kind::Custom(38_000))
89+
.custom_tag(SingleLetterTag::lowercase(Alphabet::K), vec!["38173"])
90+
.custom_tag(SingleLetterTag::lowercase(Alphabet::N), vec!["mainnet"])],
91+
EventSource::both(None),
92+
)
93+
.await?;
94+
95+
let mut federations = BTreeMap::new();
96+
97+
for recommendation_event in &fedimint_recommendation_events {
98+
for d_tag in recommendation_event.get_tags_content(D_TAG) {
99+
let Ok(federation_id) = FederationId::from_str(d_tag) else {
100+
continue;
101+
};
102+
103+
let (recommenders, invite_codes) = federations
104+
.entry(federation_id)
105+
.or_insert_with(|| (BTreeSet::new(), BTreeSet::new()));
106+
107+
recommenders.insert(recommendation_event.pubkey);
108+
109+
for u_tag in recommendation_event.get_tags_content(U_TAG) {
110+
if let Ok(invite_code) = InviteCode::from_str(u_tag) {
111+
if invite_code.federation_id() == federation_id {
112+
invite_codes.insert(invite_code);
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
// It's possible for a user to recommend a federation without providing any invite codes.
120+
// If a federation has no recommendations that include any invite codes, we don't want to
121+
// include it in the list of federations since it's not possible to join it.
122+
federations.retain(|_, (_, invite_codes)| !invite_codes.is_empty());
123+
124+
Ok(federations)
125+
}
126+
72127
/// Fetches the current state of the Nostr SDK client.
73128
/// Note: This is async because it's grabbing read locks
74129
/// on the relay `RwLock`s. No network requests are made.

0 commit comments

Comments
 (0)