upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/nostr
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-21 03:40:19 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-21 03:40:19 +0000
commit8f7f0d87f238c490a6f1c6fdef3333e8d2f9e7af (patch)
tree112d1652f66d96b55443478c8a85167c51b14249 /src/nostr
parentbdb45df8f38a427fa3062215edd1a85e15080eca (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
Diffstat (limited to 'src/nostr')
-rw-r--r--src/nostr/builder.rs86
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",