nostr-rs-relay

My dev fork of nostr-rs-relay
git clone git://jb55.com/nostr-rs-relay
Log | Files | Refs | README | LICENSE

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:
MREADME.md | 2+-
Msrc/db.rs | 23+++++++++++++++++++++++
Msrc/error.rs | 8++++++++
Msrc/event.rs | 36++++++++++++++++++++++++++++++++++++
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(),