From 8f7f0d87f238c490a6f1c6fdef3333e8d2f9e7af Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 21 Nov 2025 03:40:19 +0000 Subject: refactor: optimize is_referenced_by_accepted for addressable events - Remove uppercase 'Q' tag (not in Nostr spec) - Add support for addressable references in 'q' tags - Optimize queries based on event type: - Addressable events (kind >= 30000): only check a, A, q with coordinates - Regular events: only check e, E, q with event IDs - Handle addressable events without 'd' tag (empty identifier) - Reduce query count from up to 6 to maximum 3 per event type --- src/nostr/builder.rs | 86 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index 73d3511..61b36d5 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs @@ -169,35 +169,75 @@ impl Nip34WritePolicy { } } - /// Check if any accepted event references this event ID (forward reference) - /// Checks all reference tag types: e, E, q, Q (event ID references only, not addressable) + /// Check if any accepted event references this event (forward reference) /// - /// Note: Must check each tag type separately as custom_tag chaining creates AND not OR + /// For addressable events (kind >= 30000): Only checks addressable reference tags (a, A, q) + /// For regular events: Only checks event ID reference tags (e, E, q) + /// + /// This optimization recognizes that addressable events won't be referenced by ID, + /// and regular events won't be referenced by coordinate. async fn is_referenced_by_accepted( database: &Arc, - event_id: &EventId, + event: &Event, ) -> Result { - let event_id_hex = event_id.to_hex(); - - // Check each tag type that can reference event IDs - // Note: 'a' and 'A' tags use addressable format (kind:pubkey:d), not event IDs - let tag_types = [ - SingleLetterTag::lowercase(Alphabet::E), // 'e' - standard event reference - SingleLetterTag::uppercase(Alphabet::E), // 'E' - NIP-22 root event reference - SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote reference - SingleLetterTag::uppercase(Alphabet::Q), // 'Q' - uppercase quote (if used) - ]; - - for tag_type in &tag_types { - let filter = Filter::new().custom_tag(tag_type.clone(), event_id_hex.clone()); + // Check if this is an addressable event (parameterized replaceable) + let is_addressable = event.kind.as_u16() >= 30000 && event.kind.as_u16() < 40000; + + if is_addressable { + // For addressable events, build the coordinate string (handles empty identifier) + let identifier = event.tags.iter() + .find_map(|tag| { + let tag_vec = tag.clone().to_vec(); + if tag_vec.len() >= 2 && tag_vec[0] == "d" { + Some(tag_vec[1].clone()) + } else { + None + } + }) + .unwrap_or_default(); // Empty string if no 'd' tag - match database.query(filter).await { - Ok(events) => { - if !events.is_empty() { - return Ok(true); + let address = format!("{}:{}:{}", event.kind.as_u16(), event.pubkey.to_hex(), identifier); + + // Check addressable reference tags: a, A, q (with address format) + let addressable_tags = [ + SingleLetterTag::lowercase(Alphabet::A), // 'a' - addressable event reference + SingleLetterTag::uppercase(Alphabet::A), // 'A' - uppercase addressable reference + SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote (can be address or ID) + ]; + + for tag_type in &addressable_tags { + let filter = Filter::new().custom_tag(tag_type.clone(), address.clone()); + + match database.query(filter).await { + Ok(events) => { + if !events.is_empty() { + return Ok(true); + } } + Err(e) => return Err(format!("Database query failed: {}", e)), + } + } + } else { + // For regular events, check event ID reference tags: e, E, q (with hex ID) + let event_id_hex = event.id.to_hex(); + + let event_id_tags = [ + SingleLetterTag::lowercase(Alphabet::E), // 'e' - standard event reference + SingleLetterTag::uppercase(Alphabet::E), // 'E' - NIP-22 root event reference + SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote reference + ]; + + for tag_type in &event_id_tags { + let filter = Filter::new().custom_tag(tag_type.clone(), event_id_hex.clone()); + + match database.query(filter).await { + Ok(events) => { + if !events.is_empty() { + return Ok(true); + } + } + Err(e) => return Err(format!("Database query failed: {}", e)), } - Err(e) => return Err(format!("Database query failed: {}", e)), } } @@ -304,7 +344,7 @@ impl WritePolicy for Nip34WritePolicy { } // Check 3: Is this event referenced by an accepted event? (forward reference) - match Self::is_referenced_by_accepted(&database, &event.id).await { + match Self::is_referenced_by_accepted(&database, event).await { Ok(true) => { tracing::debug!( "Accepted event {}: referenced by accepted event", -- cgit v1.2.3