fix: subscription event filtering bugs

Subscriptions properly filter using the authors tag.  Petname/keys are
correctly filtered (previously the event tags were incorrectly used).
This commit is contained in:
Greg Heartsfield 2021-12-14 21:38:26 -06:00
parent 54e6e0e5ce
commit 49598b2c9e
4 changed files with 36 additions and 16 deletions

View File

@ -45,15 +45,15 @@ impl ClientConn {
self.client_id.to_string().chars().take(8).collect() self.client_id.to_string().chars().take(8).collect()
} }
/// Find the first subscription identifier that matches the event, /// Find all matching subscriptions.
/// if any do. pub fn get_matching_subscriptions(&self, e: &Event) -> Vec<&str> {
pub fn get_matching_subscription(&self, e: &Event) -> Option<&str> { let mut v: Vec<&str> = vec![];
for (id, sub) in self.subscriptions.iter() { for (id, sub) in self.subscriptions.iter() {
if sub.interested_in_event(e) { if sub.interested_in_event(e) {
return Some(id); v.push(id);
} }
} }
None return v;
} }
/// Add a new subscription for this connection. /// Add a new subscription for this connection.

View File

@ -154,6 +154,11 @@ impl Event {
pub fn event_tag_match(&self, eventid: &str) -> bool { pub fn event_tag_match(&self, eventid: &str) -> bool {
self.get_event_tags().contains(&eventid) 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)] #[cfg(test)]

View File

@ -129,16 +129,16 @@ async fn nostr_server(
Ok(global_event) = bcast_rx.recv() => { Ok(global_event) = bcast_rx.recv() => {
// an event has been broadcast to all clients // an event has been broadcast to all clients
// first check if there is a subscription for this event. // first check if there is a subscription for this event.
let sub_name_opt = conn.get_matching_subscription(&global_event); let matching_subs = conn.get_matching_subscriptions(&global_event);
if let Some(sub_name) = sub_name_opt { for s in matching_subs {
// TODO: serialize at broadcast time, instead of // TODO: serialize at broadcast time, instead of
// once for each consumer. // once for each consumer.
if let Ok(event_str) = serde_json::to_string(&global_event) { if let Ok(event_str) = serde_json::to_string(&global_event) {
debug!("sub match: client: {}, sub: {}, event: {}", debug!("sub match: client: {}, sub: {}, event: {}",
cid, sub_name, cid, s,
global_event.get_event_id_prefix()); global_event.get_event_id_prefix());
// create an event response and send it // 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(); nostr_stream.send(res).await.ok();
} else { } else {
warn!("could not convert event to string"); warn!("could not convert event to string");

View File

@ -105,17 +105,21 @@ impl Subscription {
} }
impl ReqFilter { impl ReqFilter {
/// Check if this filter either matches, or does not care about an author. /// Check for a match within the authors list.
fn author_match(&self, event: &Event) -> bool { // 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 self.authors
.as_ref() .as_ref()
.map(|vs| vs.contains(&event.pubkey.to_owned())) .map(|vs| vs.contains(&event.pubkey.to_owned()))
.unwrap_or(true) .unwrap_or(true)
&& self }
.author /// Check for a specific author match
.as_ref() fn author_match(&self, event: &Event) -> bool {
.map(|v| v == &event.pubkey) self.author
.unwrap_or(true) .as_ref()
.map(|v| v == &event.pubkey)
.unwrap_or(true)
} }
/// Check if this filter either matches, or does not care about the event tags. /// Check if this filter either matches, or does not care about the event tags.
fn event_match(&self, event: &Event) -> bool { fn event_match(&self, event: &Event) -> bool {
@ -125,6 +129,15 @@ impl ReqFilter {
.unwrap_or(true) .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. /// Check if this filter either matches, or does not care about the kind.
fn kind_match(&self, kind: u64) -> bool { fn kind_match(&self, kind: u64) -> bool {
self.kind.map(|v| v == kind).unwrap_or(true) 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.since.map(|t| event.created_at > t).unwrap_or(true)
&& self.kind_match(event.kind) && self.kind_match(event.kind)
&& self.author_match(event) && self.author_match(event)
&& self.authors_match(event)
&& self.pubkey_match(event)
&& self.event_match(event) && self.event_match(event)
} }
} }