Skip to content

Commit

Permalink
database: add support to event deletion by coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Dec 4, 2023
1 parent 783ce1a commit 511b406
Showing 1 changed file with 124 additions and 13 deletions.
137 changes: 124 additions & 13 deletions crates/nostr-database/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl DatabaseIndexes {

if event.is_replaceable() {
let filter: Filter = Filter::new().author(event.pubkey).kind(event.kind);
for ev in self.internal_query(&index, filter).await {
for ev in self.internal_query(&index, filter, false).await {
if ev.created_at > event.created_at {
should_insert = false;
} else if ev.created_at <= event.created_at {
Expand All @@ -179,7 +179,7 @@ impl DatabaseIndexes {
.author(event.pubkey)
.kind(event.kind)
.identifier(identifier);
for ev in self.internal_query(&index, filter).await {
for ev in self.internal_query(&index, filter, false).await {
if ev.created_at >= event.created_at {
should_insert = false;
} else if ev.created_at < event.created_at {
Expand All @@ -191,16 +191,30 @@ impl DatabaseIndexes {
}
} else if event.kind == Kind::EventDeletion {
let mut deleted = self.deleted.write().await;
let ids = event.event_ids().copied();
let filter = Filter::new().ids(ids);
let pubkey_prefix: PublicKeyPrefix = PublicKeyPrefix::from(event.pubkey);
for ev in self.internal_query(&index, filter).await {

// Check `e` tags
let ids = event.event_ids().copied();
let filter: Filter = Filter::new().ids(ids);
for ev in self.internal_query(&index, filter, false).await {
if ev.pubkey == pubkey_prefix {
to_discard.insert(ev.event_id);
deleted.insert(ev.event_id);
}
}
// TODO: support event deletion by coordinate (`a` tag)

// Check `a` tags
for coordinate in event.coordinates() {
let coordinate_pubkey_prefix: PublicKeyPrefix =
PublicKeyPrefix::from(coordinate.pubkey);
if coordinate_pubkey_prefix == pubkey_prefix {
let filter: Filter = coordinate.into();
for ev in self.internal_query(&index, filter, false).await {
to_discard.insert(ev.event_id);
deleted.insert(ev.event_id);
}
}
}
}

// Remove events
Expand All @@ -223,19 +237,24 @@ impl DatabaseIndexes {
&self,
index: &'a BTreeSet<EventIndex>,
filter: Filter,
allow_empty_filter: bool,
) -> impl Iterator<Item = &'a EventIndex> {
let authors: HashSet<PublicKeyPrefix> = filter
.authors
.iter()
.map(|p| PublicKeyPrefix::from(*p))
.collect();
index.iter().filter(move |m| {
(filter.ids.is_empty() || filter.ids.contains(&m.event_id))
&& filter.since.map_or(true, |t| m.created_at >= t)
&& filter.until.map_or(true, |t| m.created_at <= t)
&& (filter.authors.is_empty() || authors.contains(&m.pubkey))
&& (filter.kinds.is_empty() || filter.kinds.contains(&m.kind))
&& m.filter_tags_match(&filter)
if (filter.is_empty() && allow_empty_filter) || !filter.is_empty() {
(filter.ids.is_empty() || filter.ids.contains(&m.event_id))
&& filter.since.map_or(true, |t| m.created_at >= t)
&& filter.until.map_or(true, |t| m.created_at <= t)
&& (filter.authors.is_empty() || authors.contains(&m.pubkey))
&& (filter.kinds.is_empty() || filter.kinds.contains(&m.kind))
&& m.filter_tags_match(&filter)
} else {
false
}
})
}

Expand All @@ -257,7 +276,7 @@ impl DatabaseIndexes {
}

let limit: Option<usize> = filter.limit;
let iter = self.internal_query(&index, filter).await;
let iter = self.internal_query(&index, filter, true).await;
if let Some(limit) = limit {
matching_ids.extend(iter.take(limit))
} else {
Expand All @@ -280,3 +299,95 @@ impl DatabaseIndexes {
index.clear();
}
}

#[cfg(test)]
mod tests {
use nostr::nips::nip01::Coordinate;
use nostr::secp256k1::SecretKey;
use nostr::{EventBuilder, FromBech32, Keys, Tag};

use super::*;

const SECRET_KEY_A: &str = "nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85";
const SECRET_KEY_B: &str = "nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99";

#[tokio::test]
async fn test_event_deletion() {
let indexes = DatabaseIndexes::new();

// Keys
let keys_a = Keys::new(SecretKey::from_bech32(SECRET_KEY_A).unwrap());
let keys_b = Keys::new(SecretKey::from_bech32(SECRET_KEY_B).unwrap());

// Build some events
let events = [
EventBuilder::new(
Kind::ParameterizedReplaceable(32122),
"Empty",
&[Tag::Identifier(String::from("abdefgh:12345678"))],
)
.to_event(&keys_a)
.unwrap(),
EventBuilder::new(
Kind::ParameterizedReplaceable(32122),
"",
&[Tag::Identifier(String::from("ijklmnop:87654321"))],
)
.to_event(&keys_a)
.unwrap(),
EventBuilder::new(
Kind::ParameterizedReplaceable(32122),
"",
&[Tag::Identifier(String::from("abdefgh:87654321"))],
)
.to_event(&keys_b)
.unwrap(),
EventBuilder::new(
Kind::ParameterizedReplaceable(32122),
"",
&[Tag::Identifier(String::from("abdefgh:12345678"))],
)
.to_event(&keys_b)
.unwrap(),
EventBuilder::new(
Kind::ParameterizedReplaceable(32122),
"Test",
&[Tag::Identifier(String::from("abdefgh:12345678"))],
)
.to_event(&keys_a)
.unwrap(),
];

for event in events.iter() {
indexes.index_event(event).await;
}

// Total events: 4

// Invalid event deletion (wrong signing keys)
let coordinate =
Coordinate::new(Kind::ParameterizedReplaceable(32122), keys_a.public_key());
let event = EventBuilder::delete([coordinate])
.to_event(&keys_b)
.unwrap();
indexes.index_event(&event).await;

// Total events: 5 (4 + 1)

// Delete valid event
let coordinate =
Coordinate::new(Kind::ParameterizedReplaceable(32122), keys_a.public_key())
.identifier("ijklmnop:87654321");
let event = EventBuilder::delete([coordinate])
.to_event(&keys_a)
.unwrap();
indexes.index_event(&event).await;

// Total events: 5 (5 - 1 + 1)

// Check total number of indexes
let filter = Filter::new();
let res = indexes.query([filter]).await;
assert_eq!(res.len(), 5);
}
}

0 comments on commit 511b406

Please sign in to comment.