diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-21 03:40:19 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-21 03:40:19 +0000 |
| commit | 8f7f0d87f238c490a6f1c6fdef3333e8d2f9e7af (patch) | |
| tree | 112d1652f66d96b55443478c8a85167c51b14249 | |
| parent | bdb45df8f38a427fa3062215edd1a85e15080eca (diff) | |
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
| -rw-r--r-- | src/nostr/builder.rs | 86 |
1 files changed, 63 insertions, 23 deletions
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 { | |||
| 169 | } | 169 | } |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | /// Check if any accepted event references this event ID (forward reference) | 172 | /// Check if any accepted event references this event (forward reference) |
| 173 | /// Checks all reference tag types: e, E, q, Q (event ID references only, not addressable) | ||
| 174 | /// | 173 | /// |
| 175 | /// Note: Must check each tag type separately as custom_tag chaining creates AND not OR | 174 | /// For addressable events (kind >= 30000): Only checks addressable reference tags (a, A, q) |
| 175 | /// For regular events: Only checks event ID reference tags (e, E, q) | ||
| 176 | /// | ||
| 177 | /// This optimization recognizes that addressable events won't be referenced by ID, | ||
| 178 | /// and regular events won't be referenced by coordinate. | ||
| 176 | async fn is_referenced_by_accepted( | 179 | async fn is_referenced_by_accepted( |
| 177 | database: &Arc<MemoryDatabase>, | 180 | database: &Arc<MemoryDatabase>, |
| 178 | event_id: &EventId, | 181 | event: &Event, |
| 179 | ) -> Result<bool, String> { | 182 | ) -> Result<bool, String> { |
| 180 | let event_id_hex = event_id.to_hex(); | 183 | // Check if this is an addressable event (parameterized replaceable) |
| 181 | 184 | let is_addressable = event.kind.as_u16() >= 30000 && event.kind.as_u16() < 40000; | |
| 182 | // Check each tag type that can reference event IDs | 185 | |
| 183 | // Note: 'a' and 'A' tags use addressable format (kind:pubkey:d), not event IDs | 186 | if is_addressable { |
| 184 | let tag_types = [ | 187 | // For addressable events, build the coordinate string (handles empty identifier) |
| 185 | SingleLetterTag::lowercase(Alphabet::E), // 'e' - standard event reference | 188 | let identifier = event.tags.iter() |
| 186 | SingleLetterTag::uppercase(Alphabet::E), // 'E' - NIP-22 root event reference | 189 | .find_map(|tag| { |
| 187 | SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote reference | 190 | let tag_vec = tag.clone().to_vec(); |
| 188 | SingleLetterTag::uppercase(Alphabet::Q), // 'Q' - uppercase quote (if used) | 191 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { |
| 189 | ]; | 192 | Some(tag_vec[1].clone()) |
| 190 | 193 | } else { | |
| 191 | for tag_type in &tag_types { | 194 | None |
| 192 | let filter = Filter::new().custom_tag(tag_type.clone(), event_id_hex.clone()); | 195 | } |
| 196 | }) | ||
| 197 | .unwrap_or_default(); // Empty string if no 'd' tag | ||
| 193 | 198 | ||
| 194 | match database.query(filter).await { | 199 | let address = format!("{}:{}:{}", event.kind.as_u16(), event.pubkey.to_hex(), identifier); |
| 195 | Ok(events) => { | 200 | |
| 196 | if !events.is_empty() { | 201 | // Check addressable reference tags: a, A, q (with address format) |
| 197 | return Ok(true); | 202 | let addressable_tags = [ |
| 203 | SingleLetterTag::lowercase(Alphabet::A), // 'a' - addressable event reference | ||
| 204 | SingleLetterTag::uppercase(Alphabet::A), // 'A' - uppercase addressable reference | ||
| 205 | SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote (can be address or ID) | ||
| 206 | ]; | ||
| 207 | |||
| 208 | for tag_type in &addressable_tags { | ||
| 209 | let filter = Filter::new().custom_tag(tag_type.clone(), address.clone()); | ||
| 210 | |||
| 211 | match database.query(filter).await { | ||
| 212 | Ok(events) => { | ||
| 213 | if !events.is_empty() { | ||
| 214 | return Ok(true); | ||
| 215 | } | ||
| 198 | } | 216 | } |
| 217 | Err(e) => return Err(format!("Database query failed: {}", e)), | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } else { | ||
| 221 | // For regular events, check event ID reference tags: e, E, q (with hex ID) | ||
| 222 | let event_id_hex = event.id.to_hex(); | ||
| 223 | |||
| 224 | let event_id_tags = [ | ||
| 225 | SingleLetterTag::lowercase(Alphabet::E), // 'e' - standard event reference | ||
| 226 | SingleLetterTag::uppercase(Alphabet::E), // 'E' - NIP-22 root event reference | ||
| 227 | SingleLetterTag::lowercase(Alphabet::Q), // 'q' - quote reference | ||
| 228 | ]; | ||
| 229 | |||
| 230 | for tag_type in &event_id_tags { | ||
| 231 | let filter = Filter::new().custom_tag(tag_type.clone(), event_id_hex.clone()); | ||
| 232 | |||
| 233 | match database.query(filter).await { | ||
| 234 | Ok(events) => { | ||
| 235 | if !events.is_empty() { | ||
| 236 | return Ok(true); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | Err(e) => return Err(format!("Database query failed: {}", e)), | ||
| 199 | } | 240 | } |
| 200 | Err(e) => return Err(format!("Database query failed: {}", e)), | ||
| 201 | } | 241 | } |
| 202 | } | 242 | } |
| 203 | 243 | ||
| @@ -304,7 +344,7 @@ impl WritePolicy for Nip34WritePolicy { | |||
| 304 | } | 344 | } |
| 305 | 345 | ||
| 306 | // Check 3: Is this event referenced by an accepted event? (forward reference) | 346 | // Check 3: Is this event referenced by an accepted event? (forward reference) |
| 307 | match Self::is_referenced_by_accepted(&database, &event.id).await { | 347 | match Self::is_referenced_by_accepted(&database, event).await { |
| 308 | Ok(true) => { | 348 | Ok(true) => { |
| 309 | tracing::debug!( | 349 | tracing::debug!( |
| 310 | "Accepted event {}: referenced by accepted event", | 350 | "Accepted event {}: referenced by accepted event", |