upgrade url string to RelaySpec for [read|write] markers

I think RelaySpec wants to move to enostr so the RelayPool can support
read and write relays ...
This commit is contained in:
Ken Sedgwick
2025-01-22 12:43:05 -08:00
parent fe3e2dad14
commit 3278d3ba16
3 changed files with 128 additions and 18 deletions

View File

@@ -1,7 +1,8 @@
use tracing::{debug, error, info};
use crate::{
KeyStorageResponse, KeyStorageType, MuteFun, Muted, SingleUnkIdAction, UnknownIds, UserAccount,
KeyStorageResponse, KeyStorageType, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds,
UserAccount,
};
use enostr::{ClientMessage, FilledKeypair, Keypair, RelayPool};
use nostrdb::{Filter, Ndb, Note, NoteKey, Subscription, Transaction};
@@ -38,8 +39,8 @@ pub struct AccountRelayData {
filter: Filter,
subid: String,
sub: Option<Subscription>,
local: BTreeSet<String>, // used locally but not advertised
advertised: BTreeSet<String>, // advertised via NIP-65
local: BTreeSet<RelaySpec>, // used locally but not advertised
advertised: BTreeSet<RelaySpec>, // advertised via NIP-65
}
#[derive(Default)]
@@ -107,7 +108,7 @@ impl AccountRelayData {
}
}
fn harvest_nip65_relays(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Vec<String> {
fn harvest_nip65_relays(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Vec<RelaySpec> {
let mut relays = Vec::new();
for nk in nks.iter() {
if let Ok(note) = ndb.get_note_by_key(txn, *nk) {
@@ -115,7 +116,17 @@ impl AccountRelayData {
match tag.get(0).and_then(|t| t.variant().str()) {
Some("r") => {
if let Some(url) = tag.get(1).and_then(|f| f.variant().str()) {
relays.push(Self::canonicalize_url(url));
let has_read_marker = tag
.get(2)
.map_or(false, |m| m.variant().str() == Some("read"));
let has_write_marker = tag
.get(2)
.map_or(false, |m| m.variant().str() == Some("write"));
relays.push(RelaySpec::new(
Self::canonicalize_url(url),
has_read_marker,
has_write_marker,
));
}
}
Some("alt") => {
@@ -236,8 +247,8 @@ pub struct Accounts {
accounts: Vec<UserAccount>,
key_store: KeyStorageType,
account_data: BTreeMap<[u8; 32], AccountData>,
forced_relays: BTreeSet<String>,
bootstrap_relays: BTreeSet<String>,
forced_relays: BTreeSet<RelaySpec>,
bootstrap_relays: BTreeSet<RelaySpec>,
needs_relay_config: bool,
}
@@ -251,9 +262,9 @@ impl Accounts {
let currently_selected_account = get_selected_index(&accounts, &key_store);
let account_data = BTreeMap::new();
let forced_relays: BTreeSet<String> = forced_relays
let forced_relays: BTreeSet<RelaySpec> = forced_relays
.into_iter()
.map(|u| AccountRelayData::canonicalize_url(&u))
.map(|u| RelaySpec::new(AccountRelayData::canonicalize_url(&u), false, false))
.collect();
let bootstrap_relays = [
"wss://relay.damus.io",
@@ -264,7 +275,7 @@ impl Accounts {
]
.iter()
.map(|&url| url.to_string())
.map(|u| AccountRelayData::canonicalize_url(&u))
.map(|u| RelaySpec::new(AccountRelayData::canonicalize_url(&u), false, false))
.collect();
Accounts {
@@ -526,20 +537,26 @@ impl Accounts {
debug!("current relays: {:?}", pool.urls());
debug!("desired relays: {:?}", desired_relays);
let add: BTreeSet<String> = desired_relays.difference(&pool.urls()).cloned().collect();
let mut sub: BTreeSet<String> = pool.urls().difference(&desired_relays).cloned().collect();
let pool_specs = pool
.urls()
.iter()
.map(|url| RelaySpec::new(url.clone(), false, false))
.collect();
let add: BTreeSet<RelaySpec> = desired_relays.difference(&pool_specs).cloned().collect();
let mut sub: BTreeSet<RelaySpec> =
pool_specs.difference(&desired_relays).cloned().collect();
if !add.is_empty() {
debug!("configuring added relays: {:?}", add);
let _ = pool.add_urls(add, wakeup);
let _ = pool.add_urls(add.iter().map(|r| r.url.clone()).collect(), wakeup);
}
if !sub.is_empty() {
debug!("removing unwanted relays: {:?}", sub);
// certain relays are persistent like the multicast relay,
// although we should probably have a way to explicitly
// disable it
sub.remove("multicast");
pool.remove_urls(&sub);
sub.remove(&RelaySpec::new("multicast", false, false));
debug!("removing unwanted relays: {:?}", sub);
pool.remove_urls(&sub.iter().map(|r| r.url.clone()).collect());
}
debug!("current relays: {:?}", pool.urls());
@@ -591,6 +608,7 @@ impl Accounts {
}
pub fn add_advertised_relay(&mut self, relay_to_add: &str) {
let relay_to_add = AccountRelayData::canonicalize_url(relay_to_add);
info!("add advertised relay \"{}\"", relay_to_add);
match self.currently_selected_account {
None => error!("no account is currently selected."),
@@ -607,7 +625,7 @@ impl Accounts {
// iniitialize with the bootstrapping set.
advertised.extend(self.bootstrap_relays.iter().cloned());
}
advertised.insert(relay_to_add.to_string());
advertised.insert(RelaySpec::new(relay_to_add, false, false));
self.needs_relay_config = true;
// FIXME - need to publish the advertised set
}

View File

@@ -10,6 +10,7 @@ mod muted;
pub mod note;
mod notecache;
mod persist;
pub mod relayspec;
mod result;
pub mod storage;
mod style;
@@ -33,6 +34,7 @@ pub use muted::{MuteFun, Muted};
pub use note::{NoteRef, RootIdError, RootNoteId, RootNoteIdBuf};
pub use notecache::{CachedNote, NoteCache};
pub use persist::*;
pub use relayspec::RelaySpec;
pub use result::Result;
pub use storage::{
DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageResponse, KeyStorageType,

View File

@@ -0,0 +1,90 @@
use std::cmp::Ordering;
use std::fmt;
// A Relay specification includes NIP-65 defined "markers" which
// indicate if the relay should be used for reading or writing (or
// both).
#[derive(Clone)]
pub struct RelaySpec {
pub url: String,
pub has_read_marker: bool,
pub has_write_marker: bool,
}
impl RelaySpec {
pub fn new(
url: impl Into<String>,
mut has_read_marker: bool,
mut has_write_marker: bool,
) -> Self {
// if both markers are set turn both off ...
if has_read_marker && has_write_marker {
has_read_marker = false;
has_write_marker = false;
}
RelaySpec {
url: url.into(),
has_read_marker,
has_write_marker,
}
}
// The "marker" fields are a little counter-intuitive ... from NIP-65:
//
// "The event MUST include a list of r tags with relay URIs and a read
// or write marker. Relays marked as read / write are called READ /
// WRITE relays, respectively. If the marker is omitted, the relay is
// used for both purposes."
//
pub fn is_readable(&self) -> bool {
!self.has_write_marker // only "write" relays are not readable
}
pub fn is_writable(&self) -> bool {
!self.has_read_marker // only "read" relays are not writable
}
}
// just the url part
impl fmt::Display for RelaySpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.url)
}
}
// add the read and write markers if present
impl fmt::Debug for RelaySpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self)?;
if self.has_read_marker {
write!(f, " [r]")?;
}
if self.has_write_marker {
write!(f, " [w]")?;
}
Ok(())
}
}
// For purposes of set arithmetic only the url is considered, two
// RelaySpec which differ only in markers are the same ...
impl PartialEq for RelaySpec {
fn eq(&self, other: &Self) -> bool {
self.url == other.url
}
}
impl Eq for RelaySpec {}
impl PartialOrd for RelaySpec {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.url.cmp(&other.url))
}
}
impl Ord for RelaySpec {
fn cmp(&self, other: &Self) -> Ordering {
self.url.cmp(&other.url)
}
}