diff --git a/bindings/nostr-ffi/src/message/subscription.rs b/bindings/nostr-ffi/src/message/subscription.rs index d7c13fd08..71081926a 100644 --- a/bindings/nostr-ffi/src/message/subscription.rs +++ b/bindings/nostr-ffi/src/message/subscription.rs @@ -42,27 +42,27 @@ impl Filter { } } - pub fn id(self: Arc, id: String) -> Arc { + pub fn id(self: Arc, id: Arc) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.id(id); + builder.inner = builder.inner.id(**id); Arc::new(builder) } - pub fn ids(self: Arc, ids: Vec) -> Arc { + pub fn ids(self: Arc, ids: Vec>) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.ids(ids); + builder.inner = builder.inner.ids(ids.into_iter().map(|id| **id)); Arc::new(builder) } - pub fn author(self: Arc, author: String) -> Arc { + pub fn author(self: Arc, author: Arc) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.author(author); + builder.inner = builder.inner.author(**author); Arc::new(builder) } - pub fn authors(self: Arc, authors: Vec) -> Arc { + pub fn authors(self: Arc, authors: Vec>) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.authors(authors); + builder.inner = builder.inner.authors(authors.into_iter().map(|pk| **pk)); Arc::new(builder) } @@ -74,9 +74,7 @@ impl Filter { pub fn kinds(self: Arc, kinds: Vec) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder - .inner - .kinds(kinds.into_iter().map(|k| k.into()).collect()); + builder.inner = builder.inner.kinds(kinds.into_iter().map(|k| k.into())); Arc::new(builder) } diff --git a/bindings/nostr-ffi/src/nostr.udl b/bindings/nostr-ffi/src/nostr.udl index 4666696c8..ad3de1f89 100644 --- a/bindings/nostr-ffi/src/nostr.udl +++ b/bindings/nostr-ffi/src/nostr.udl @@ -184,13 +184,13 @@ enum Alphabet { interface Filter { constructor(); [Self=ByArc] - Filter id(string id); + Filter id(EventId id); [Self=ByArc] - Filter ids(sequence ids); + Filter ids(sequence ids); [Self=ByArc] - Filter author(string author); + Filter author(PublicKey author); [Self=ByArc] - Filter authors(sequence authors); + Filter authors(sequence authors); [Self=ByArc] Filter kind(u64 kind); [Self=ByArc] diff --git a/bindings/nostr-js/src/message/subscription.rs b/bindings/nostr-js/src/message/subscription.rs index 463bae383..d13a65600 100644 --- a/bindings/nostr-js/src/message/subscription.rs +++ b/bindings/nostr-js/src/message/subscription.rs @@ -73,16 +73,19 @@ impl JsFilter { /// Set subscription id #[wasm_bindgen] - pub fn id(&self, id: String) -> Self { + pub fn id(&self, id: &JsEventId) -> Self { Self { - inner: self.inner.to_owned().id(id), + inner: self.inner.to_owned().id(id.into()), } } /// Set subscription ids #[wasm_bindgen] - pub fn ids(&self, ids: Vec) -> Result { - let ids: Vec = ids.iter().filter_map(|id| id.as_string()).collect(); + pub fn ids(&self, ids: Array) -> Result { + let ids = ids + .iter() + .map(|v| Ok(util::downcast::(&v, "EventId")?.inner)) + .collect::, JsError>>()?; Ok(Self { inner: self.inner.to_owned().ids(ids), }) @@ -90,16 +93,19 @@ impl JsFilter { /// Set author #[wasm_bindgen] - pub fn author(&self, author: String) -> Self { + pub fn author(&self, author: &JsPublicKey) -> Self { Self { - inner: self.inner.to_owned().author(author), + inner: self.inner.to_owned().author(author.into()), } } /// Set authors #[wasm_bindgen] - pub fn authors(&self, authors: Vec) -> Result { - let authors: Vec = authors.iter().filter_map(|a| a.as_string()).collect(); + pub fn authors(&self, authors: Array) -> Result { + let authors = authors + .iter() + .map(|v| Ok(util::downcast::(&v, "PublicKey")?.inner)) + .collect::, JsError>>()?; Ok(Self { inner: self.inner.to_owned().authors(authors), }) diff --git a/bindings/nostr-sdk-ffi/src/nostr_sdk.udl b/bindings/nostr-sdk-ffi/src/nostr_sdk.udl index 5458cdd9c..9bf807f04 100644 --- a/bindings/nostr-sdk-ffi/src/nostr_sdk.udl +++ b/bindings/nostr-sdk-ffi/src/nostr_sdk.udl @@ -203,14 +203,14 @@ enum Alphabet { interface Filter { constructor(); + [Self=ByArc] + Filter id(EventId id); [Self=ByArc] - Filter id(string id); + Filter ids(sequence ids); [Self=ByArc] - Filter ids(sequence ids); + Filter author(PublicKey author); [Self=ByArc] - Filter author(string author); - [Self=ByArc] - Filter authors(sequence authors); + Filter authors(sequence authors); [Self=ByArc] Filter kind(u64 kind); [Self=ByArc] diff --git a/crates/nostr-sdk/examples/get-events-of.rs b/crates/nostr-sdk/examples/get-events-of.rs index 6fcb614d3..1401ddc5c 100644 --- a/crates/nostr-sdk/examples/get-events-of.rs +++ b/crates/nostr-sdk/examples/get-events-of.rs @@ -22,7 +22,7 @@ async fn main() -> Result<()> { client.connect().await; let filter = Filter::new() - .author(my_keys.public_key().to_string()) + .author(my_keys.public_key()) .kind(Kind::Metadata); let events = client .get_events_of(vec![filter], Some(Duration::from_secs(10))) diff --git a/crates/nostr-sdk/examples/negentropy.rs b/crates/nostr-sdk/examples/negentropy.rs index 3dc41961b..7cf8eca96 100644 --- a/crates/nostr-sdk/examples/negentropy.rs +++ b/crates/nostr-sdk/examples/negentropy.rs @@ -20,9 +20,7 @@ async fn main() -> Result<()> { client.connect().await; let my_items = Vec::new(); - let filter = Filter::new() - .author(my_keys.public_key().to_string()) - .limit(10); + let filter = Filter::new().author(my_keys.public_key()).limit(10); let relay = client.relay("wss://relay.damus.io").await?; relay .reconcilie(filter, my_items, Duration::from_secs(30)) diff --git a/crates/nostr-sdk/examples/nip65.rs b/crates/nostr-sdk/examples/nip65.rs index f86db81ac..3cb58cac0 100644 --- a/crates/nostr-sdk/examples/nip65.rs +++ b/crates/nostr-sdk/examples/nip65.rs @@ -23,9 +23,7 @@ async fn main() -> Result<()> { println!("Subscribing to Relay List Metadata"); client - .subscribe(vec![Filter::new() - .author(public_key.to_string()) - .kind(Kind::RelayList)]) + .subscribe(vec![Filter::new().author(public_key).kind(Kind::RelayList)]) .await; client diff --git a/crates/nostr-sdk/examples/subscriptions.rs b/crates/nostr-sdk/examples/subscriptions.rs index 737123f93..490e652c8 100644 --- a/crates/nostr-sdk/examples/subscriptions.rs +++ b/crates/nostr-sdk/examples/subscriptions.rs @@ -21,7 +21,7 @@ async fn main() -> Result<()> { client.connect().await; let subscription = Filter::new() - .author(my_keys.public_key().to_string()) + .author(my_keys.public_key()) .kind(Kind::Metadata) .since(Timestamp::now()); @@ -54,7 +54,7 @@ async fn main() -> Result<()> { let relay = client.relay("wss://relay.damus.io").await?; let other_filters = Filter::new() .kind(Kind::TextNote) - .author(my_keys.public_key().to_string()) + .author(my_keys.public_key()) .since(Timestamp::now()); relay .subscribe_with_internal_id( diff --git a/crates/nostr-sdk/src/client/blocking.rs b/crates/nostr-sdk/src/client/blocking.rs index 82b63d76f..005b67207 100644 --- a/crates/nostr-sdk/src/client/blocking.rs +++ b/crates/nostr-sdk/src/client/blocking.rs @@ -17,7 +17,7 @@ use tokio::sync::broadcast; #[cfg(feature = "nip46")] use super::signer::remote::RemoteSigner; -use super::{Entity, Error, Options, TryIntoUrl}; +use super::{Error, Options, TryIntoUrl}; use crate::relay::{pool, Relay, RelayOptions, RelayPoolNotification}; use crate::RUNTIME; @@ -424,13 +424,6 @@ impl Client { RUNTIME.block_on(async { self.client.get_channels(timeout).await }) } - pub fn get_entity_of(&self, entity: S, timeout: Option) -> Result - where - S: Into, - { - RUNTIME.block_on(async { self.client.get_entity_of(entity, timeout).await }) - } - pub fn handle_notifications(&self, func: F) -> Result<(), Error> where F: Fn(RelayPoolNotification) -> Result, diff --git a/crates/nostr-sdk/src/client/mod.rs b/crates/nostr-sdk/src/client/mod.rs index 0cc43432d..57605f1dd 100644 --- a/crates/nostr-sdk/src/client/mod.rs +++ b/crates/nostr-sdk/src/client/mod.rs @@ -6,7 +6,6 @@ use std::collections::HashMap; #[cfg(not(target_arch = "wasm32"))] use std::net::SocketAddr; -use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; @@ -20,8 +19,8 @@ use nostr::nips::nip94::FileMetadata; use nostr::types::metadata::Error as MetadataError; use nostr::url::Url; use nostr::{ - ChannelId, ClientMessage, Contact, Entity, Event, EventBuilder, EventId, Filter, JsonUtil, - Keys, Kind, Metadata, Result, Tag, + ChannelId, ClientMessage, Contact, Event, EventBuilder, EventId, Filter, JsonUtil, Keys, Kind, + Metadata, Result, Tag, }; use nostr_sdk_net::futures_util::Future; use tokio::sync::{broadcast, RwLock}; @@ -852,10 +851,10 @@ impl Client { .await .ok_or(Error::SignerPublicKeyNotFound)?; - filter = filter.author(signer_public_key.to_string()); + filter = filter.author(signer_public_key); } else { let keys = self.keys.read().await; - filter = filter.author(keys.public_key().to_string()); + filter = filter.author(keys.public_key()); } filter @@ -865,7 +864,7 @@ impl Client { let filter: Filter = { let keys = self.keys.read().await; Filter::new() - .author(keys.public_key().to_string()) + .author(keys.public_key()) .kind(Kind::ContactList) .limit(1) }; @@ -948,7 +947,7 @@ impl Client { for public_key in chunk.iter() { filters.push( Filter::new() - .author(public_key.to_string()) + .author(*public_key) .kind(Kind::Metadata) .limit(1), ); @@ -1273,43 +1272,6 @@ impl Client { .await } - /// Get entity of hex string - pub async fn get_entity_of( - &self, - entity: S, - timeout: Option, - ) -> Result - where - S: Into, - { - let entity: String = entity.into(); - let events: Vec = self - .get_events_of( - vec![Filter::new() - .id(&entity) - .kind(Kind::ChannelCreation) - .limit(1)], - timeout, - ) - .await?; - if events.is_empty() { - let pubkey = XOnlyPublicKey::from_str(&entity)?; - let events: Vec = self - .get_events_of( - vec![Filter::new().author(pubkey.to_string()).limit(1)], - timeout, - ) - .await?; - if events.is_empty() { - Ok(Entity::Unknown) - } else { - Ok(Entity::Account) - } - } else { - Ok(Entity::Channel) - } - } - /// Handle notifications pub async fn handle_notifications(&self, func: F) -> Result<(), Error> where diff --git a/crates/nostr-sdk/src/relay/mod.rs b/crates/nostr-sdk/src/relay/mod.rs index 77938568f..7032f59fc 100644 --- a/crates/nostr-sdk/src/relay/mod.rs +++ b/crates/nostr-sdk/src/relay/mod.rs @@ -1482,14 +1482,13 @@ impl Relay { return Err(Error::ReadDisabled); } - let id_size: usize = 16; + let id_size: usize = 32; - let mut negentropy = Negentropy::new(id_size, Some(5_000))?; + let mut negentropy = Negentropy::new(id_size, Some(2_500))?; for (id, timestamp) in my_items.into_iter() { - let cutted_id: &[u8] = &id.as_bytes()[..id_size]; - let cutted_id = Bytes::from_slice(cutted_id); - negentropy.add_item(timestamp.as_u64(), cutted_id)?; + let id = Bytes::from_slice(id.as_bytes()); + negentropy.add_item(timestamp.as_u64(), id)?; } negentropy.seal()?; @@ -1519,8 +1518,9 @@ impl Relay { &mut need_ids, )?; - let ids: Vec = - need_ids.into_iter().map(|id| id.to_hex()).collect(); + let ids = need_ids + .into_iter() + .filter_map(|id| EventId::from_slice(&id).ok()); let filter = Filter::new().ids(ids); self.req_events_of( vec![filter], @@ -1585,7 +1585,7 @@ impl Relay { /// Check if relay support negentropy protocol pub async fn support_negentropy(&self) -> Result { let pk = Keys::generate(); - let filter = Filter::new().author(pk.public_key().to_string()); + let filter = Filter::new().author(pk.public_key()); match self .reconcilie(filter, Vec::new(), Duration::from_secs(5)) .await diff --git a/crates/nostr/src/message/subscription.rs b/crates/nostr/src/message/subscription.rs index 3c4837b12..68acc0d5c 100644 --- a/crates/nostr/src/message/subscription.rs +++ b/crates/nostr/src/message/subscription.rs @@ -225,18 +225,18 @@ impl<'de> Deserialize<'de> for SubscriptionId { /// Subscription filters #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Filter { - /// List of event ids or prefixes - #[serde(skip_serializing_if = "Vec::is_empty")] + /// List of [`EventId`] + #[serde(skip_serializing_if = "AllocSet::is_empty")] #[serde(default)] - pub ids: Vec, - /// List of pubkeys or prefixes - #[serde(skip_serializing_if = "Vec::is_empty")] + pub ids: AllocSet, + /// List of [`XOnlyPublicKey`] + #[serde(skip_serializing_if = "AllocSet::is_empty")] #[serde(default)] - pub authors: Vec, + pub authors: AllocSet, /// List of a kind numbers - #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(skip_serializing_if = "AllocSet::is_empty")] #[serde(default)] - pub kinds: Vec, + pub kinds: AllocSet, /// It's a string describing a query in a human-readable form, i.e. "best nostr apps" /// /// @@ -271,132 +271,82 @@ impl Filter { Self::default() } - /// Add event id or prefix - pub fn id(self, id: S) -> Self - where - S: Into, - { - let id: String = id.into(); - let mut ids: Vec = self.ids; - if !ids.contains(&id) { - ids.push(id); - } - Self { ids, ..self } + /// Add [`EventId`] + pub fn id(mut self, id: EventId) -> Self { + self.ids.insert(id); + self } /// Add event ids or prefixes - pub fn ids(self, ids: Vec) -> Self + pub fn ids(mut self, ids: I) -> Self where - S: Into, + I: IntoIterator, { - let mut current_ids: Vec = self.ids; - for value in ids.into_iter().map(|value| value.into()) { - if !current_ids.contains(&value) { - current_ids.push(value); - } - } - Self { - ids: current_ids, - ..self - } + self.ids.extend(ids); + self } - /// Remove event ids or prefixes - pub fn remove_ids(self, ids: Vec) -> Self + /// Remove event ids + pub fn remove_ids(mut self, ids: I) -> Self where - S: Into, + I: IntoIterator, { - let ids: AllocSet = ids.into_iter().map(|id| id.into()).collect(); - Self { - ids: self - .ids - .into_iter() - .filter(|id| !ids.contains(id)) - .collect(), - ..self + for id in ids { + self.ids.remove(&id); } + self } /// Add author - pub fn author(self, author: S) -> Self - where - S: Into, - { - let author: String = author.into(); - let mut authors: Vec = self.authors; - if !authors.contains(&author) { - authors.push(author); - } - Self { authors, ..self } + pub fn author(mut self, author: XOnlyPublicKey) -> Self { + self.authors.insert(author); + self } /// Add authors - pub fn authors(self, authors: Vec) -> Self + pub fn authors(mut self, authors: I) -> Self where - S: Into, + I: IntoIterator, { - let mut current_authors: Vec = self.authors; - for value in authors.into_iter().map(|value| value.into()) { - if !current_authors.contains(&value) { - current_authors.push(value); - } - } - Self { - authors: current_authors, - ..self - } + self.authors.extend(authors); + self } /// Remove authors - pub fn remove_authors(self, authors: Vec) -> Self + pub fn remove_authors(mut self, authors: I) -> Self where - S: Into, + I: IntoIterator, { - let authors: AllocSet = authors.into_iter().map(|id| id.into()).collect(); - Self { - authors: self - .authors - .into_iter() - .filter(|value| !authors.contains(value)) - .collect(), - ..self + for author in authors { + self.authors.remove(&author); } + self } /// Add kind - pub fn kind(self, kind: Kind) -> Self { - let mut kinds: Vec = self.kinds; - if !kinds.contains(&kind) { - kinds.push(kind); - } - Self { kinds, ..self } + pub fn kind(mut self, kind: Kind) -> Self { + self.kinds.insert(kind); + self } /// Add kinds - pub fn kinds(self, kinds: Vec) -> Self { - let mut current_kinds: Vec = self.kinds; - for value in kinds.into_iter() { - if !current_kinds.contains(&value) { - current_kinds.push(value); - } - } - Self { - kinds: current_kinds, - ..self - } + pub fn kinds(mut self, kinds: I) -> Self + where + I: IntoIterator, + { + self.kinds.extend(kinds); + self } /// Remove kinds - pub fn remove_kinds(self, kinds: Vec) -> Self { - let kinds: AllocSet = kinds.into_iter().collect(); - Self { - kinds: self - .kinds - .into_iter() - .filter(|value| !kinds.contains(value)) - .collect(), - ..self + pub fn remove_kinds(mut self, kinds: I) -> Self + where + I: IntoIterator, + { + for kind in kinds { + self.kinds.remove(&kind); } + self } /// Add event @@ -410,7 +360,7 @@ impl Filter { } /// Remove events - pub fn remove_events(self, events: Vec) -> Self { + pub fn remove_events(self, events: Vec) -> Self { self.remove_custom_tag(Alphabet::E, events) } @@ -428,7 +378,7 @@ impl Filter { } /// Remove pubkeys - pub fn remove_pubkeys(self, pubkeys: Vec) -> Self { + pub fn remove_pubkeys(self, pubkeys: Vec) -> Self { self.remove_custom_tag( Alphabet::P, pubkeys.into_iter().map(|p| p.to_string()).collect(), @@ -616,10 +566,6 @@ impl Filter { } } -fn prefix_match(prefixes: &[String], target: &str) -> bool { - prefixes.iter().any(|prefix| target.starts_with(prefix)) -} - fn single_char_tagname(tagname: &str) -> Option { tagname .chars() @@ -642,11 +588,11 @@ fn tag_idx(event: &Event) -> AllocMap> { impl Filter { fn ids_match(&self, event: &Event) -> bool { - self.ids.is_empty() || prefix_match(&self.ids, &event.id.to_hex()) + self.ids.is_empty() || self.ids.contains(&event.id) } fn authors_match(&self, event: &Event) -> bool { - self.authors.is_empty() || prefix_match(&self.authors, &event.pubkey.to_string()) + self.authors.is_empty() || self.authors.contains(&event.pubkey) } fn tag_match(&self, event: &Event) -> bool { @@ -776,9 +722,12 @@ mod test { #[test] fn test_remove_ids() { - let filter = Filter::new().id("abcdefg").id("12345678").id("xyz"); - let filter = filter.remove_ids(vec!["12345678", "xyz"]); - assert_eq!(filter, Filter::new().id("abcdefg")); + let event_id = + EventId::from_hex("70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5") + .unwrap(); + let filter = Filter::new().id(EventId::all_zeros()).id(event_id); + let filter = filter.remove_ids(vec![EventId::all_zeros()]); + assert_eq!(filter, Filter::new().id(event_id)); } #[test] @@ -813,12 +762,15 @@ mod test { #[test] fn test_filter_deserialization() { - let json = r##"{"#a":["...", "test"],"search":"test","ids":["myid", "mysecondid"]}"##; + let json = r##"{"#a":["...", "test"],"search":"test","ids":["70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5"]}"##; let filter = Filter::from_json(json).unwrap(); + let event_id = + EventId::from_hex("70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5") + .unwrap(); assert_eq!( filter, Filter::new() - .ids(vec!["myid".to_string(), "mysecondid".to_string()]) + .ids(vec![event_id]) .search("test") .custom_tag(Alphabet::A, vec!["...".to_string(), "test".to_string()]) ); @@ -834,6 +786,13 @@ mod test { #[test] fn test_match_event() { + let event_id = + EventId::from_hex("70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5") + .unwrap(); + let pubkey = XOnlyPublicKey::from_str( + "379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe", + ) + .unwrap(); let event = Event::new_dummy( "70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5", @@ -849,23 +808,23 @@ mod test { ); // ID match - let filter = Filter::new().id("70b10f70c"); + let filter = Filter::new().id(event_id); assert!(filter.match_event(&event)); // Not match (kind) - let filter = Filter::new().id("70b10f70c").kind(Kind::Metadata); + let filter = Filter::new().id(event_id).kind(Kind::Metadata); assert!(!filter.match_event(&event)); // Match (author, kind and since) let filter = Filter::new() - .author("379e863e") + .author(pubkey) .kind(Kind::TextNote) .since(Timestamp::from(1612808000)); assert!(filter.match_event(&event)); // Not match (since) let filter = Filter::new() - .author("379e863e") + .author(pubkey) .kind(Kind::TextNote) .since(Timestamp::from(1700000000)); assert!(!filter.match_event(&event)); @@ -916,7 +875,7 @@ mod test { let filters: Vec = vec![ // Filter that match Filter::new() - .author("379e863e") + .author(pubkey) .kind(Kind::TextNote) .since(Timestamp::from(1612808000)), // Filter that not match