commit 1d499cf12bdd3789df7647e59838e5843751e3d7
parent ed3a6b9692316c99ad382870e6e1cd8ebdc1c6c7
Author: Greg Heartsfield <scsibug@imap.cc>
Date: Sun, 27 Feb 2022 11:34:10 -0600
feat: handle NIP-09 for deletion events
Diffstat:
4 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
@@ -18,7 +18,7 @@ NIPs with a relay-specific implementation are listed here.
- [x] NIP-02: Hide old contact list events
- [ ] NIP-03: OpenTimestamps
- [x] NIP-05: Mapping Nostr keys to DNS identifiers
-- [ ] NIP-09: Event deletion
+- [x] NIP-09: Event deletion
- [x] NIP-11: Relay information document
- [x] NIP-12: Generic tag search (_experimental_)
diff --git a/src/db.rs b/src/db.rs
@@ -332,6 +332,29 @@ pub fn write_event(conn: &mut PooledConnection, e: &Event) -> Result<usize> {
);
}
}
+ // if this event is a deletion, hide the referenced events from the same author.
+ if e.kind == 5 {
+ let event_candidates = e.tag_values_by_name("e");
+ let mut params: Vec<Box<dyn ToSql>> = vec![];
+ // first parameter will be author
+ params.push(Box::new(hex::decode(&e.pubkey)?));
+ event_candidates
+ .iter()
+ .filter(|x| is_hex(x) && x.len() == 64)
+ .filter_map(|x| hex::decode(x).ok())
+ .for_each(|x| params.push(Box::new(x)));
+ let query = format!(
+ "UPDATE event SET hidden=TRUE WHERE author=?, event_hash IN ({})",
+ repeat_vars(params.len() - 1)
+ );
+ let mut stmt = tx.prepare(&query)?;
+ let update_count = stmt.execute(rusqlite::params_from_iter(params))?;
+ info!(
+ "hid {} deleted events for author {:?}",
+ update_count,
+ e.get_author_prefix()
+ );
+ }
tx.commit()?;
Ok(ins_count)
}
diff --git a/src/error.rs b/src/error.rs
@@ -48,6 +48,8 @@ pub enum Error {
JoinError,
#[error("Hyper Client error")]
HyperError(hyper::Error),
+ #[error("Hex encoding error")]
+ HexError(hex::FromHexError),
#[error("Unknown/Undocumented")]
UnknownError,
}
@@ -58,6 +60,12 @@ pub enum Error {
// }
//}
+impl From<hex::FromHexError> for Error {
+ fn from(h: hex::FromHexError) -> Self {
+ Error::HexError(h)
+ }
+}
+
impl From<hyper::Error> for Error {
fn from(h: hyper::Error) -> Self {
Error::HyperError(h)
diff --git a/src/event.rs b/src/event.rs
@@ -124,6 +124,16 @@ impl Event {
self.pubkey.chars().take(8).collect()
}
+ /// Retrieve tag values
+ pub fn tag_values_by_name(&self, tag_name: &str) -> Vec<String> {
+ self.tags
+ .iter()
+ .filter(|x| x.len() > 1)
+ .filter(|x| x.get(0).unwrap() == tag_name)
+ .map(|x| x.get(1).unwrap().to_owned())
+ .collect()
+ }
+
/// Check if this event has a valid signature.
fn is_valid(&self) -> bool {
// TODO: return a Result with a reason for invalid events
@@ -336,6 +346,32 @@ mod tests {
}
#[test]
+ fn event_tag_select() {
+ let e = Event {
+ id: "999".to_owned(),
+ pubkey: "012345".to_owned(),
+ created_at: 501234,
+ kind: 1,
+ tags: vec![
+ vec!["j".to_owned(), "abc".to_owned()],
+ vec!["e".to_owned(), "foo".to_owned()],
+ vec!["e".to_owned(), "bar".to_owned()],
+ vec!["e".to_owned(), "baz".to_owned()],
+ vec![
+ "p".to_owned(),
+ "aaaa".to_owned(),
+ "ws://example.com".to_owned(),
+ ],
+ ],
+ content: "this is a test".to_owned(),
+ sig: "abcde".to_owned(),
+ tagidx: None,
+ };
+ let v = e.tag_values_by_name("e");
+ assert_eq!(v, vec!["foo", "bar", "baz"]);
+ }
+
+ #[test]
fn event_canonical_with_tags() {
let e = Event {
id: "999".to_owned(),