2021-12-11 22:43:41 -05:00
|
|
|
//! Subscription and filter parsing
|
|
|
|
use crate::error::Result;
|
2021-12-05 18:15:50 -05:00
|
|
|
use crate::event::Event;
|
|
|
|
use serde::{Deserialize, Deserializer, Serialize};
|
2022-01-01 19:38:52 -05:00
|
|
|
use std::collections::HashSet;
|
2021-12-05 18:15:50 -05:00
|
|
|
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Subscription identifier and set of request filters
|
2021-12-05 18:15:50 -05:00
|
|
|
#[derive(Serialize, PartialEq, Debug, Clone)]
|
|
|
|
pub struct Subscription {
|
|
|
|
pub id: String,
|
|
|
|
pub filters: Vec<ReqFilter>,
|
|
|
|
}
|
|
|
|
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Filter for requests
|
|
|
|
///
|
|
|
|
/// Corresponds to client-provided subscription request elements. Any
|
|
|
|
/// element can be present if it should be used in filtering, or
|
|
|
|
/// absent ([`None`]) if it should be ignored.
|
2021-12-05 18:15:50 -05:00
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
|
|
|
pub struct ReqFilter {
|
2022-01-01 19:38:52 -05:00
|
|
|
/// Event hashes
|
|
|
|
pub ids: Option<Vec<String>>,
|
|
|
|
/// Event kinds
|
|
|
|
pub kinds: Option<Vec<u64>>,
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Referenced event hash
|
2021-12-11 16:48:59 -05:00
|
|
|
#[serde(rename = "#e")]
|
2022-01-01 19:38:52 -05:00
|
|
|
pub events: Option<Vec<String>>,
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Referenced public key for a petname
|
2021-12-11 16:48:59 -05:00
|
|
|
#[serde(rename = "#p")]
|
2022-01-01 19:38:52 -05:00
|
|
|
pub pubkeys: Option<Vec<String>>,
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Events published after this time
|
2021-12-05 18:15:50 -05:00
|
|
|
pub since: Option<u64>,
|
2021-12-23 22:38:32 -05:00
|
|
|
/// Events published before this time
|
|
|
|
pub until: Option<u64>,
|
2021-12-11 22:43:41 -05:00
|
|
|
/// List of author public keys
|
2021-12-05 18:15:50 -05:00
|
|
|
pub authors: Option<Vec<String>>,
|
2022-01-05 17:33:53 -05:00
|
|
|
/// Set of event tags, for quick indexing
|
|
|
|
#[serde(skip)]
|
|
|
|
event_tag_set: Option<HashSet<String>>,
|
|
|
|
/// Set of pubkey tags, for quick indexing
|
|
|
|
#[serde(skip)]
|
|
|
|
pubkey_tag_set: Option<HashSet<String>>,
|
2021-12-05 18:15:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Subscription {
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Custom deserializer for subscriptions, which have a more
|
|
|
|
/// complex structure than the other message types.
|
2021-12-05 18:15:50 -05:00
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Subscription, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let mut v: serde_json::Value = Deserialize::deserialize(deserializer)?;
|
|
|
|
// this shoud be a 3-or-more element array.
|
|
|
|
// verify the first element is a String, REQ
|
|
|
|
// get the subscription from the second element.
|
|
|
|
// convert each of the remaining objects into filters
|
|
|
|
|
|
|
|
// check for array
|
|
|
|
let va = v
|
|
|
|
.as_array_mut()
|
2021-12-11 22:56:52 -05:00
|
|
|
.ok_or_else(|| serde::de::Error::custom("not array"))?;
|
2021-12-05 18:15:50 -05:00
|
|
|
|
|
|
|
// check length
|
|
|
|
if va.len() < 3 {
|
|
|
|
return Err(serde::de::Error::custom("not enough fields"));
|
|
|
|
}
|
2021-12-11 22:56:52 -05:00
|
|
|
let mut i = va.iter_mut();
|
2021-12-05 18:15:50 -05:00
|
|
|
// get command ("REQ") and ensure it is a string
|
|
|
|
let req_cmd_str: serde_json::Value = i.next().unwrap().take();
|
2021-12-11 22:56:52 -05:00
|
|
|
let req = req_cmd_str
|
|
|
|
.as_str()
|
|
|
|
.ok_or_else(|| serde::de::Error::custom("first element of request was not a string"))?;
|
2021-12-05 18:15:50 -05:00
|
|
|
if req != "REQ" {
|
|
|
|
return Err(serde::de::Error::custom("missing REQ command"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure sub id is a string
|
|
|
|
let sub_id_str: serde_json::Value = i.next().unwrap().take();
|
|
|
|
let sub_id = sub_id_str
|
|
|
|
.as_str()
|
2021-12-11 22:56:52 -05:00
|
|
|
.ok_or_else(|| serde::de::Error::custom("missing subscription id"))?;
|
2021-12-05 18:15:50 -05:00
|
|
|
|
|
|
|
let mut filters = vec![];
|
|
|
|
for fv in i {
|
2022-01-05 17:33:53 -05:00
|
|
|
let mut f: ReqFilter = serde_json::from_value(fv.take())
|
2021-12-05 18:15:50 -05:00
|
|
|
.map_err(|_| serde::de::Error::custom("could not parse filter"))?;
|
2022-01-05 17:33:53 -05:00
|
|
|
// create indexes
|
|
|
|
f.update_tag_indexes();
|
2021-12-05 18:15:50 -05:00
|
|
|
filters.push(f);
|
|
|
|
}
|
|
|
|
Ok(Subscription {
|
|
|
|
id: sub_id.to_owned(),
|
|
|
|
filters,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Subscription {
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Get a copy of the subscription identifier.
|
2021-12-05 18:15:50 -05:00
|
|
|
pub fn get_id(&self) -> String {
|
|
|
|
self.id.clone()
|
|
|
|
}
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Determine if this subscription matches a given [`Event`]. Any
|
|
|
|
/// individual filter match is sufficient.
|
2021-12-05 18:15:50 -05:00
|
|
|
pub fn interested_in_event(&self, event: &Event) -> bool {
|
|
|
|
for f in self.filters.iter() {
|
|
|
|
if f.interested_in_event(event) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2021-12-11 22:56:52 -05:00
|
|
|
false
|
2021-12-05 18:15:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ReqFilter {
|
2022-01-05 17:33:53 -05:00
|
|
|
/// Update pubkey and event indexes
|
|
|
|
fn update_tag_indexes(&mut self) {
|
|
|
|
if let Some(event_vec) = &self.events {
|
|
|
|
self.event_tag_set = Some(HashSet::from_iter(event_vec.iter().cloned()));
|
|
|
|
}
|
|
|
|
if let Some(pubkey_vec) = &self.pubkeys {
|
|
|
|
self.pubkey_tag_set = Some(HashSet::from_iter(pubkey_vec.iter().cloned()));
|
|
|
|
}
|
|
|
|
}
|
2021-12-14 22:38:26 -05:00
|
|
|
/// Check for a match within the authors list.
|
2022-01-01 19:38:52 -05:00
|
|
|
fn ids_match(&self, event: &Event) -> bool {
|
|
|
|
self.ids
|
|
|
|
.as_ref()
|
|
|
|
.map(|vs| vs.contains(&event.id.to_owned()))
|
|
|
|
.unwrap_or(true)
|
|
|
|
}
|
|
|
|
|
2021-12-14 22:38:26 -05:00
|
|
|
fn authors_match(&self, event: &Event) -> bool {
|
2021-12-05 18:15:50 -05:00
|
|
|
self.authors
|
|
|
|
.as_ref()
|
|
|
|
.map(|vs| vs.contains(&event.pubkey.to_owned()))
|
|
|
|
.unwrap_or(true)
|
2021-12-14 22:38:26 -05:00
|
|
|
}
|
2022-01-05 17:33:53 -05:00
|
|
|
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Check if this filter either matches, or does not care about the event tags.
|
2021-12-05 18:15:50 -05:00
|
|
|
fn event_match(&self, event: &Event) -> bool {
|
2022-01-05 17:33:53 -05:00
|
|
|
// an event match is performed by looking at the ReqFilter events field, and sending a hashset to the event to intersect with.
|
|
|
|
if let Some(es) = &self.event_tag_set {
|
|
|
|
// if there exists event tags in this filter, find if any intersect.
|
|
|
|
event.generic_tag_val_intersect("e", es)
|
2022-01-01 19:38:52 -05:00
|
|
|
} else {
|
2022-01-05 17:33:53 -05:00
|
|
|
// if no event tags were requested in a filter, we do match
|
2022-01-01 19:38:52 -05:00
|
|
|
true
|
|
|
|
}
|
2021-12-05 18:15:50 -05:00
|
|
|
}
|
|
|
|
|
2022-01-05 17:33:53 -05:00
|
|
|
/// Check if this filter either matches, or does not care about the event tags.
|
2021-12-14 22:38:26 -05:00
|
|
|
fn pubkey_match(&self, event: &Event) -> bool {
|
2022-01-05 17:33:53 -05:00
|
|
|
// an event match is performed by looking at the ReqFilter events field, and sending a hashset to the event to intersect with.
|
|
|
|
if let Some(ps) = &self.pubkey_tag_set {
|
|
|
|
// if there exists event tags in this filter, find if any intersect.
|
|
|
|
event.generic_tag_val_intersect("p", ps)
|
2022-01-01 19:38:52 -05:00
|
|
|
} else {
|
2022-01-05 17:33:53 -05:00
|
|
|
// if no event tags were requested in a filter, we do match
|
2022-01-01 19:38:52 -05:00
|
|
|
true
|
|
|
|
}
|
2021-12-14 22:38:26 -05:00
|
|
|
}
|
|
|
|
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Check if this filter either matches, or does not care about the kind.
|
2021-12-05 18:15:50 -05:00
|
|
|
fn kind_match(&self, kind: u64) -> bool {
|
2022-01-01 19:38:52 -05:00
|
|
|
self.kinds
|
|
|
|
.as_ref()
|
|
|
|
.map(|ks| ks.contains(&kind))
|
|
|
|
.unwrap_or(true)
|
2021-12-05 18:15:50 -05:00
|
|
|
}
|
|
|
|
|
2021-12-11 22:43:41 -05:00
|
|
|
/// Determine if all populated fields in this filter match the provided event.
|
2021-12-05 18:15:50 -05:00
|
|
|
pub fn interested_in_event(&self, event: &Event) -> bool {
|
2022-01-01 19:38:52 -05:00
|
|
|
// self.id.as_ref().map(|v| v == &event.id).unwrap_or(true)
|
|
|
|
self.ids_match(event)
|
2021-12-05 18:15:50 -05:00
|
|
|
&& self.since.map(|t| event.created_at > t).unwrap_or(true)
|
|
|
|
&& self.kind_match(event.kind)
|
2021-12-14 22:38:26 -05:00
|
|
|
&& self.authors_match(event)
|
|
|
|
&& self.pubkey_match(event)
|
2021-12-11 22:56:52 -05:00
|
|
|
&& self.event_match(event)
|
2021-12-05 18:15:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_request_parse() -> Result<()> {
|
|
|
|
let raw_json = "[\"REQ\",\"some-id\",{}]";
|
|
|
|
let s: Subscription = serde_json::from_str(raw_json)?;
|
|
|
|
assert_eq!(s.id, "some-id");
|
|
|
|
assert_eq!(s.filters.len(), 1);
|
2021-12-16 19:53:53 -05:00
|
|
|
assert_eq!(s.filters.get(0).unwrap().authors, None);
|
2021-12-05 18:15:50 -05:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn incorrect_header() {
|
|
|
|
let raw_json = "[\"REQUEST\",\"some-id\",\"{}\"]";
|
|
|
|
assert!(serde_json::from_str::<Subscription>(raw_json).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn req_missing_filters() {
|
|
|
|
let raw_json = "[\"REQ\",\"some-id\"]";
|
|
|
|
assert!(serde_json::from_str::<Subscription>(raw_json).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn invalid_filter() {
|
|
|
|
// unrecognized field in filter
|
|
|
|
let raw_json = "[\"REQ\",\"some-id\",{\"foo\": 3}]";
|
|
|
|
assert!(serde_json::from_str::<Subscription>(raw_json).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn author_filter() -> Result<()> {
|
|
|
|
let raw_json = "[\"REQ\",\"some-id\",{\"author\": \"test-author-id\"}]";
|
|
|
|
let s: Subscription = serde_json::from_str(raw_json)?;
|
|
|
|
assert_eq!(s.id, "some-id");
|
|
|
|
assert_eq!(s.filters.len(), 1);
|
|
|
|
let first_filter = s.filters.get(0).unwrap();
|
|
|
|
assert_eq!(first_filter.author, Some("test-author-id".to_owned()));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn interest_id_nomatch() -> Result<()> {
|
|
|
|
// subscription with a filter for ID
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"id":"abc"}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "abcde".to_owned(),
|
|
|
|
pubkey: "".to_owned(),
|
|
|
|
created_at: 0,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), false);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn interest_time_and_id() -> Result<()> {
|
|
|
|
// subscription with a filter for ID and time
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"id":"abc", "since": 1000}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "abc".to_owned(),
|
|
|
|
pubkey: "".to_owned(),
|
|
|
|
created_at: 50,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), false);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn interest_time_and_id2() -> Result<()> {
|
|
|
|
// subscription with a filter for ID and time
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"id":"abc", "since": 1000}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "abc".to_owned(),
|
|
|
|
pubkey: "".to_owned(),
|
|
|
|
created_at: 1001,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), true);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn interest_id() -> Result<()> {
|
|
|
|
// subscription with a filter for ID
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"id":"abc"}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "abc".to_owned(),
|
|
|
|
pubkey: "".to_owned(),
|
|
|
|
created_at: 0,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), true);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn authors_single() -> Result<()> {
|
|
|
|
// subscription with a filter for ID
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"authors":["abc"]}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "123".to_owned(),
|
|
|
|
pubkey: "abc".to_owned(),
|
|
|
|
created_at: 0,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), true);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
#[test]
|
2021-12-16 19:53:53 -05:00
|
|
|
|
2021-12-05 18:15:50 -05:00
|
|
|
fn authors_multi_pubkey() -> Result<()> {
|
|
|
|
// check for any of a set of authors, against the pubkey
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"authors":["abc", "bcd"]}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "123".to_owned(),
|
|
|
|
pubkey: "bcd".to_owned(),
|
|
|
|
created_at: 0,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), true);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn authors_multi_no_match() -> Result<()> {
|
|
|
|
// check for any of a set of authors, against the pubkey
|
|
|
|
let s: Subscription = serde_json::from_str(r#"["REQ","xyz",{"authors":["abc", "bcd"]}]"#)?;
|
|
|
|
let e = Event {
|
|
|
|
id: "123".to_owned(),
|
|
|
|
pubkey: "xyz".to_owned(),
|
|
|
|
created_at: 0,
|
|
|
|
kind: 0,
|
|
|
|
tags: Vec::new(),
|
|
|
|
content: "".to_owned(),
|
|
|
|
sig: "".to_owned(),
|
|
|
|
};
|
|
|
|
assert_eq!(s.interested_in_event(&e), false);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|