mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-22 00:59:07 -05:00
feat(NIP-33): parameterized replaceable events
This commit is contained in:
parent
e2869e8fad
commit
c8f7420334
135
src/event.rs
135
src/event.rs
|
@ -130,6 +130,33 @@ impl Event {
|
|||
self.kind == 0 || self.kind == 3 || self.kind == 41 || (self.kind >= 10000 && self.kind < 20000)
|
||||
}
|
||||
|
||||
/// Should this event be replaced with newer timestamps from same author, for distinct `d` tag values?
|
||||
#[must_use] pub fn is_param_replaceable(&self) -> bool {
|
||||
self.kind >= 30000 && self.kind < 40000
|
||||
}
|
||||
|
||||
/// What is the replaceable `d` tag value?
|
||||
|
||||
/// Should this event be replaced with newer timestamps from same author, for distinct `d` tag values?
|
||||
#[must_use] pub fn distinct_param(&self) -> Option<String> {
|
||||
if self.is_param_replaceable() {
|
||||
let default = "".to_string();
|
||||
let dvals:Vec<&String> = self.tags
|
||||
.iter()
|
||||
.filter(|x| x.len() >= 1)
|
||||
.filter(|x| x.get(0).unwrap() == "d")
|
||||
.map(|x| x.get(1).unwrap_or_else(|| &default)).take(1)
|
||||
.collect();
|
||||
let dval_first = dvals.get(0);
|
||||
match dval_first {
|
||||
Some(_) => {dval_first.map(|x| x.to_string())},
|
||||
None => Some(default)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Pull a NIP-05 Name out of the event, if one exists
|
||||
#[must_use] pub fn get_nip05_addr(&self) -> Option<nip05::Nip05Name> {
|
||||
if self.is_kind_metadata() {
|
||||
|
@ -509,6 +536,19 @@ mod tests {
|
|||
assert_eq!(c, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ephemeral_event() {
|
||||
let mut event = Event::simple_event();
|
||||
event.kind=20000;
|
||||
assert!(event.is_ephemeral());
|
||||
event.kind=29999;
|
||||
assert!(event.is_ephemeral());
|
||||
event.kind=30000;
|
||||
assert!(!event.is_ephemeral());
|
||||
event.kind=19999;
|
||||
assert!(!event.is_ephemeral());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replaceable_event() {
|
||||
let mut event = Event::simple_event();
|
||||
|
@ -516,9 +556,102 @@ mod tests {
|
|||
assert!(event.is_replaceable());
|
||||
event.kind=3;
|
||||
assert!(event.is_replaceable());
|
||||
event.kind=12000;
|
||||
event.kind=10000;
|
||||
assert!(event.is_replaceable());
|
||||
event.kind=19999;
|
||||
assert!(event.is_replaceable());
|
||||
event.kind=20000;
|
||||
assert!(!event.is_replaceable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_event() {
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
assert!(event.is_param_replaceable());
|
||||
event.kind = 39999;
|
||||
assert!(event.is_param_replaceable());
|
||||
event.kind = 29999;
|
||||
assert!(!event.is_param_replaceable());
|
||||
event.kind = 40000;
|
||||
assert!(!event.is_param_replaceable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_1() {
|
||||
// NIP case #1: "tags":[["d",""]]
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["d".to_owned(), "".to_owned()]];
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_2() {
|
||||
// NIP case #2: "tags":[]: implicit d tag with empty value
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_3() {
|
||||
// NIP case #3: "tags":[["d"]]: implicit empty value ""
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["d".to_owned()]];
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_4() {
|
||||
// NIP case #4: "tags":[["d",""],["d","not empty"]]: only first d tag is considered
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["d".to_owned(), "".to_string()],
|
||||
vec!["d".to_owned(), "not empty".to_string()]
|
||||
];
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_4b() {
|
||||
// Variation of #4 with
|
||||
// NIP case #4: "tags":[["d","not empty"],["d",""]]: only first d tag is considered
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["d".to_owned(), "not empty".to_string()],
|
||||
vec!["d".to_owned(), "".to_string()]
|
||||
];
|
||||
assert_eq!(event.distinct_param(), Some("not empty".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_5() {
|
||||
// NIP case #5: "tags":[["d"],["d","some value"]]: only first d tag is considered
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["d".to_owned()],
|
||||
vec!["d".to_owned(), "second value".to_string()],
|
||||
vec!["d".to_owned(), "third value".to_string()]
|
||||
];
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_replaceable_value_case_6() {
|
||||
// NIP case #6: "tags":[["e"]]: same as no tags
|
||||
let mut event = Event::simple_event();
|
||||
event.kind = 30000;
|
||||
event.tags = vec![
|
||||
vec!["e".to_owned()],
|
||||
];
|
||||
assert_eq!(event.distinct_param(), Some("".to_string()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -115,6 +115,25 @@ impl SqliteRepo {
|
|||
return Ok(0);
|
||||
}
|
||||
}
|
||||
// check for parameterized replaceable events that would be hidden; don't insert these either.
|
||||
if let Some(d_tag) = e.distinct_param() {
|
||||
let repl_count;
|
||||
if is_lower_hex(&d_tag) && (d_tag.len() % 2 == 0) {
|
||||
repl_count = tx.query_row(
|
||||
"SELECT e.id FROM event e LEFT JOIN tag t ON e.id=t.event_id WHERE e.author=? AND e.kind=? AND t.name='d' AND t.value_hex=? AND e.created_at >= ? LIMIT 1;",
|
||||
params![pubkey_blob, e.kind, hex::decode(d_tag).ok(), e.created_at],|row| row.get::<usize, usize>(0));
|
||||
} else {
|
||||
repl_count = tx.query_row(
|
||||
"SELECT e.id FROM event e LEFT JOIN tag t ON e.id=t.event_id WHERE e.author=? AND e.kind=? AND t.name='d' AND t.value=? AND e.created_at >= ? LIMIT 1;",
|
||||
params![pubkey_blob, e.kind, d_tag, e.created_at],|row| row.get::<usize, usize>(0));
|
||||
}
|
||||
// if any rows were returned, then some newer event with
|
||||
// the same author/kind/tag value exist, and we can ignore
|
||||
// this event.
|
||||
if repl_count.ok().is_some() {
|
||||
return Ok(0)
|
||||
}
|
||||
}
|
||||
// ignore if the event hash is a duplicate.
|
||||
let mut ins_count = tx.execute(
|
||||
"INSERT OR IGNORE INTO event (event_hash, created_at, kind, author, delegated_by, content, first_seen, hidden) VALUES (?1, ?2, ?3, ?4, ?5, ?6, strftime('%s','now'), FALSE);",
|
||||
|
@ -174,6 +193,27 @@ impl SqliteRepo {
|
|||
);
|
||||
}
|
||||
}
|
||||
// if this event is parameterized replaceable, remove other events.
|
||||
if let Some(d_tag) = e.distinct_param() {
|
||||
let update_count;
|
||||
if is_lower_hex(&d_tag) && (d_tag.len() % 2 == 0) {
|
||||
update_count = tx.execute(
|
||||
"DELETE FROM event WHERE kind=? AND author=? AND id NOT IN (SELECT e.id FROM event e LEFT JOIN tag t ON e.id=t.event_id WHERE e.kind=? AND e.author=? AND t.name='d' AND t.value_hex=? ORDER BY created_at DESC LIMIT 1);",
|
||||
params![e.kind, pubkey_blob, e.kind, pubkey_blob, hex::decode(d_tag).ok()])?;
|
||||
} else {
|
||||
update_count = tx.execute(
|
||||
"DELETE FROM event WHERE kind=? AND author=? AND id NOT IN (SELECT e.id FROM event e LEFT JOIN tag t ON e.id=t.event_id WHERE e.kind=? AND e.author=? AND t.name='d' AND t.value=? ORDER BY created_at DESC LIMIT 1);",
|
||||
params![e.kind, pubkey_blob, e.kind, pubkey_blob, d_tag])?;
|
||||
}
|
||||
if update_count > 0 {
|
||||
info!(
|
||||
"removed {} older parameterized replaceable kind {} events for author: {:?}",
|
||||
update_count,
|
||||
e.kind,
|
||||
e.get_author_prefix()
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
|
|
Loading…
Reference in New Issue
Block a user