Skip to content

Commit

Permalink
Add bootstrapping, local, advertised, and forced relay sets
Browse files Browse the repository at this point in the history
  • Loading branch information
ksedgwic committed Oct 29, 2024
1 parent 4439ed6 commit e625e33
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 56 deletions.
51 changes: 49 additions & 2 deletions enostr/src/relay/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::relay::{Relay, RelayStatus};
use crate::{ClientMessage, Result};
use nostrdb::Filter;

use std::collections::BTreeSet;
use std::collections::HashMap;
use std::collections::HashSet;
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -46,6 +47,14 @@ pub struct RelayPool {
pub relays: Vec<PoolRelay>,
pub subs: HashMap<String, Vec<Filter>>,
pub ping_rate: Duration,
/// Used when there are no others
pub bootstrapping_relays: BTreeSet<String>,
/// Locally specified relays
pub local_relays: BTreeSet<String>,
/// NIP-65 specified relays
pub advertised_relays: BTreeSet<String>,
/// If non-empty force the relay pool to use exactly this set
pub forced_relays: BTreeSet<String>,
}

impl Default for RelayPool {
Expand All @@ -61,6 +70,10 @@ impl RelayPool {
relays: vec![],
subs: HashMap::new(),
ping_rate: Duration::from_secs(25),
bootstrapping_relays: BTreeSet::new(),
local_relays: BTreeSet::new(),
advertised_relays: BTreeSet::new(),
forced_relays: BTreeSet::new(),
}
}

Expand Down Expand Up @@ -154,8 +167,37 @@ impl RelayPool {
}
}

pub fn configure_relays(
&mut self,
wakeup: impl Fn() + Send + Sync + Clone + 'static,
) -> Result<()> {
let urls = if !self.forced_relays.is_empty() {
debug!("using forced relays");
self.forced_relays.iter().cloned().collect::<Vec<_>>()
} else {
let mut combined_relays = self
.local_relays
.union(&self.advertised_relays)
.cloned()
.collect::<BTreeSet<_>>();

// If the combined set is empty, use `bootstrapping_relays`.
if combined_relays.is_empty() {
debug!("using bootstrapping relays");
combined_relays = self.bootstrapping_relays.clone();
} else {
debug!("using local+advertised relays");
}

// Collect the resulting set into a vector.
combined_relays.into_iter().collect::<Vec<_>>()
};

self.set_relays(&urls, wakeup)
}

// Adds a websocket url to the RelayPool.
pub fn add_url(
fn add_url(
&mut self,
url: String,
wakeup: impl Fn() + Send + Sync + Clone + 'static,
Expand All @@ -177,6 +219,7 @@ impl RelayPool {

Ok(())
}

// Add and remove relays to match the provided list
pub fn set_relays(
&mut self,
Expand Down Expand Up @@ -206,13 +249,17 @@ impl RelayPool {

// Remove the relays that are in old_urls but not in new_urls.
let to_remove: HashSet<_> = old_urls.difference(&new_urls).cloned().collect();
for url in &to_remove {
debug!("removing relay {}", url);
}
self.relays.retain(|pr| !to_remove.contains(&pr.relay.url));

// FIXME - how do we close connections the removed relays?

// Add the relays that are in new_urls but not in old_urls.
let to_add: HashSet<_> = new_urls.difference(&old_urls).cloned().collect();
for url in to_add {
debug!("adding relay {}", url);
if let Err(e) = self.add_url(url.clone(), wakeup.clone()) {
error!("Failed to add relay with URL {}: {:?}", url, e);
}
Expand All @@ -222,7 +269,7 @@ impl RelayPool {
}

// standardize the format (ie, trailing slashes) to avoid dups
fn canonicalize_url(url: &String) -> String {
pub fn canonicalize_url(url: &String) -> String {
match Url::parse(&url) {
Ok(parsed_url) => parsed_url.to_string(),
Err(_) => url.clone(), // If parsing fails, return the original URL.
Expand Down
56 changes: 14 additions & 42 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use egui_extras::{Size, StripBuilder};

use nostrdb::{Config, Filter, Ndb, Note, Transaction};

use std::collections::BTreeSet;
use std::collections::HashMap;
use std::path::Path;
use std::time::Duration;
Expand Down Expand Up @@ -74,31 +75,6 @@ pub struct Damus {
pub textmode: bool,
}

fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) {
let ctx = ctx.clone();
let wakeup = move || {
ctx.request_repaint();
};
if let Err(e) = pool.add_url("ws://localhost:8080".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
if let Err(e) = pool.add_url("wss://relay.damus.io".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
//if let Err(e) = pool.add_url("wss://pyramid.fiatjaf.com".to_string(), wakeup.clone()) {
//error!("{:?}", e)
//}
if let Err(e) = pool.add_url("wss://nos.lol".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
if let Err(e) = pool.add_url("wss://nostr.wine".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
if let Err(e) = pool.add_url("wss://purplepag.es".to_string(), wakeup) {
error!("{:?}", e)
}
}

fn send_initial_timeline_filter(
ndb: &Ndb,
can_since_optimize: bool,
Expand Down Expand Up @@ -711,23 +687,19 @@ impl Damus {
}

// setup relays if we have them
let pool = if parsed_args.relays.is_empty() {
let mut pool = RelayPool::new();
relay_setup(&mut pool, &cc.egui_ctx);
pool
} else {
let ctx = cc.egui_ctx.clone();
let wakeup = move || {
ctx.request_repaint();
};
let mut pool = RelayPool::new();
for relay in parsed_args.relays {
if let Err(e) = pool.add_url(relay.clone(), wakeup.clone()) {
error!("error adding relay {}: {}", relay, e);
}
}
pool
};
let mut pool = RelayPool::new();
let bootstrapping_urls = [
"ws://localhost:8080",
"wss://relay.damus.io",
//"wss://pyramid.fiatjaf.com",
"wss://nos.lol",
"wss://nostr.wine",
"wss://purplepag.es",
];
pool.bootstrapping_relays = bootstrapping_urls.iter().map(|&s| s.to_string()).collect();
let forced_urls = parsed_args.relays.into_iter().collect::<BTreeSet<_>>(); // normally empty
pool.forced_relays = forced_urls;
// the user tracker will call configure_relays after filling in advertised

let account = accounts
.get_selected_account()
Expand Down
5 changes: 5 additions & 0 deletions src/relay_pool_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ impl<'a> RelayPoolManager<'a> {
indices.iter().for_each(|index| self.remove_relay(*index));
}

// This wants to add the relay to one of the pool collections
// (bootstrapping, local, advertised, or forced) and then call
// configure_relays ...
/*
pub fn add_relay(&mut self, ctx: &egui::Context, relay_url: String) {
let _ = self.pool.add_url(relay_url, create_wakeup(ctx));
}
*/
}

pub fn create_wakeup(ctx: &egui::Context) -> impl Fn() + Send + Sync + Clone + 'static {
Expand Down
9 changes: 5 additions & 4 deletions src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub async fn track_user_relays(damus: &mut Damus) {
let txn = Transaction::new(&damus.ndb).expect("transaction");
let relays = query_nip65_relays(&damus.ndb, &txn, &filter);
debug!("track_user_relays: initial from nostrdb: {:#?}", relays);
set_relays(&mut damus.pool, relays);
set_advertised_relays(&mut damus.pool, relays);
drop(txn);

// Subscribe to user relay list updates
Expand All @@ -44,7 +44,7 @@ pub async fn track_user_relays(damus: &mut Damus) {
"track_user_relays: subscription from nostrdb: {:#?}",
relays
);
set_relays(&mut damus.pool, relays);
set_advertised_relays(&mut damus.pool, relays);
}
Err(err) => error!("err: {:?}", err),
}
Expand Down Expand Up @@ -99,11 +99,12 @@ fn query_nip65_relays(ndb: &Ndb, txn: &Transaction, filter: &Filter) -> Vec<Stri
.collect()
}

fn set_relays(pool: &mut RelayPool, relays: Vec<String>) {
fn set_advertised_relays(pool: &mut RelayPool, relays: Vec<String>) {
let wakeup = move || {
// FIXME - how do we repaint?
};
if let Err(e) = pool.set_relays(&relays, wakeup) {
pool.advertised_relays = relays.into_iter().collect();
if let Err(e) = pool.configure_relays(wakeup) {
error!("{:?}", e)
}
}
Expand Down
18 changes: 10 additions & 8 deletions src/test_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ use crate::{user_account::UserAccount, Damus};
pub fn sample_pool() -> RelayPool {
let mut pool = RelayPool::new();
let wakeup = move || {};

pool.add_url("wss://relay.damus.io".to_string(), wakeup);
pool.add_url("wss://eden.nostr.land".to_string(), wakeup);
pool.add_url("wss://nostr.wine".to_string(), wakeup);
pool.add_url("wss://nos.lol".to_string(), wakeup);
pool.add_url("wss://test_relay_url_long_00000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string(), wakeup);
let bootstrapping_urls = [
"wss://relay.damus.io",
"wss://eden.nostr.land",
"wss://nostr.wine",
"wss://nos.lol",
"wss://test_relay_url_long_00000000000000000000000000000000000000000000000000000000000000000000000000000000000",
];
pool.bootstrapping_relays = bootstrapping_urls.iter().map(|&s| s.to_string()).collect();

for _ in 0..20 {
pool.add_url("tmp".to_string(), wakeup);
pool.local_relays.insert("tmp".to_string());
}

pool.configure_relays(wakeup);
pool
}

Expand Down

0 comments on commit e625e33

Please sign in to comment.