commit 49598b2c9ee62e3adcb3cb75c48cf68a00e1e8d8
parent 54e6e0e5ce7b6e2eca01a86cbcc2b2931e290438
Author: Greg Heartsfield <scsibug@imap.cc>
Date: Tue, 14 Dec 2021 21:38:26 -0600
fix: subscription event filtering bugs
Subscriptions properly filter using the authors tag. Petname/keys are
correctly filtered (previously the event tags were incorrectly used).
Diffstat:
4 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/src/conn.rs b/src/conn.rs
@@ -45,15 +45,15 @@ impl ClientConn {
self.client_id.to_string().chars().take(8).collect()
}
- /// Find the first subscription identifier that matches the event,
- /// if any do.
- pub fn get_matching_subscription(&self, e: &Event) -> Option<&str> {
+ /// Find all matching subscriptions.
+ pub fn get_matching_subscriptions(&self, e: &Event) -> Vec<&str> {
+ let mut v: Vec<&str> = vec![];
for (id, sub) in self.subscriptions.iter() {
if sub.interested_in_event(e) {
- return Some(id);
+ v.push(id);
}
}
- None
+ return v;
}
/// Add a new subscription for this connection.
diff --git a/src/event.rs b/src/event.rs
@@ -154,6 +154,11 @@ impl Event {
pub fn event_tag_match(&self, eventid: &str) -> bool {
self.get_event_tags().contains(&eventid)
}
+
+ /// Check if a given event is referenced in an event tag.
+ pub fn pubkey_tag_match(&self, pubkey: &str) -> bool {
+ self.get_pubkey_tags().contains(&pubkey)
+ }
}
#[cfg(test)]
diff --git a/src/main.rs b/src/main.rs
@@ -129,16 +129,16 @@ async fn nostr_server(
Ok(global_event) = bcast_rx.recv() => {
// an event has been broadcast to all clients
// first check if there is a subscription for this event.
- let sub_name_opt = conn.get_matching_subscription(&global_event);
- if let Some(sub_name) = sub_name_opt {
+ let matching_subs = conn.get_matching_subscriptions(&global_event);
+ for s in matching_subs {
// TODO: serialize at broadcast time, instead of
// once for each consumer.
if let Ok(event_str) = serde_json::to_string(&global_event) {
debug!("sub match: client: {}, sub: {}, event: {}",
- cid, sub_name,
+ cid, s,
global_event.get_event_id_prefix());
// create an event response and send it
- let res = EventRes(sub_name.to_owned(),event_str);
+ let res = EventRes(s.to_owned(),event_str);
nostr_stream.send(res).await.ok();
} else {
warn!("could not convert event to string");
diff --git a/src/subscription.rs b/src/subscription.rs
@@ -105,17 +105,21 @@ impl Subscription {
}
impl ReqFilter {
- /// Check if this filter either matches, or does not care about an author.
- fn author_match(&self, event: &Event) -> bool {
+ /// Check for a match within the authors list.
+ // TODO: Ambiguity; what if the array is empty? Should we
+ // consider that the same as null?
+ fn authors_match(&self, event: &Event) -> bool {
self.authors
.as_ref()
.map(|vs| vs.contains(&event.pubkey.to_owned()))
.unwrap_or(true)
- && self
- .author
- .as_ref()
- .map(|v| v == &event.pubkey)
- .unwrap_or(true)
+ }
+ /// Check for a specific author match
+ fn author_match(&self, event: &Event) -> bool {
+ self.author
+ .as_ref()
+ .map(|v| v == &event.pubkey)
+ .unwrap_or(true)
}
/// Check if this filter either matches, or does not care about the event tags.
fn event_match(&self, event: &Event) -> bool {
@@ -125,6 +129,15 @@ impl ReqFilter {
.unwrap_or(true)
}
+ /// Check if this filter either matches, or does not care about
+ /// the pubkey/petname tags.
+ fn pubkey_match(&self, event: &Event) -> bool {
+ self.pubkey
+ .as_ref()
+ .map(|t| event.pubkey_tag_match(t))
+ .unwrap_or(true)
+ }
+
/// Check if this filter either matches, or does not care about the kind.
fn kind_match(&self, kind: u64) -> bool {
self.kind.map(|v| v == kind).unwrap_or(true)
@@ -136,6 +149,8 @@ impl ReqFilter {
&& self.since.map(|t| event.created_at > t).unwrap_or(true)
&& self.kind_match(event.kind)
&& self.author_match(event)
+ && self.authors_match(event)
+ && self.pubkey_match(event)
&& self.event_match(event)
}
}