|
1 |
| -use std::collections::BTreeMap; |
| 1 | +use std::collections::{BTreeMap, BTreeSet}; |
2 | 2 | use std::fmt::Debug;
|
| 3 | +use std::str::FromStr; |
3 | 4 | use std::time::Duration;
|
4 | 5 |
|
| 6 | +use fedimint_core::config::FederationId; |
| 7 | +use fedimint_core::invite_code::InviteCode; |
5 | 8 | use iced::Subscription;
|
6 | 9 | 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)); |
8 | 14 |
|
9 | 15 | #[derive(Default, Debug, Clone, PartialEq, Eq)]
|
10 | 16 | pub struct NostrState {
|
@@ -69,6 +75,55 @@ impl NostrModule {
|
69 | 75 | )
|
70 | 76 | }
|
71 | 77 |
|
| 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 | + |
72 | 127 | /// Fetches the current state of the Nostr SDK client.
|
73 | 128 | /// Note: This is async because it's grabbing read locks
|
74 | 129 | /// on the relay `RwLock`s. No network requests are made.
|
|
0 commit comments