|
1 | 1 | use crate::{
|
2 | 2 | note::NoteRef,
|
3 | 3 | notecache::{CachedNote, NoteCache},
|
| 4 | + subman::{SubConstraint, SubSpec, SubSpecBuilder}, |
4 | 5 | Result,
|
5 | 6 | };
|
6 | 7 |
|
7 | 8 | use enostr::{Filter, NoteId, Pubkey};
|
8 | 9 | use nostrdb::{BlockType, Mention, Ndb, Note, NoteKey, Transaction};
|
9 |
| -use std::collections::{HashMap, HashSet}; |
| 10 | +use std::collections::{BTreeMap, HashMap, HashSet}; |
10 | 11 | use std::time::{Duration, Instant};
|
11 | 12 | use tracing::error;
|
12 | 13 |
|
@@ -118,13 +119,72 @@ impl UnknownIds {
|
118 | 119 | &mut self.ids
|
119 | 120 | }
|
120 | 121 |
|
| 122 | + pub fn numids(&self) -> usize { |
| 123 | + self.ids.len() |
| 124 | + } |
| 125 | + |
121 | 126 | pub fn clear(&mut self) {
|
122 | 127 | self.ids = HashMap::default();
|
123 | 128 | }
|
124 | 129 |
|
125 |
| - pub fn filter(&self) -> Option<Vec<Filter>> { |
126 |
| - let ids: Vec<&UnknownId> = self.ids.keys().collect(); |
127 |
| - get_unknown_ids_filter(&ids) |
| 130 | + pub fn generate_resolution_requests(&self) -> Vec<SubSpec> { |
| 131 | + // 1. resolve as many ids per request as possible |
| 132 | + // 2. each request only has one filter (https://github.com/nostr-protocol/nips/pull/1645) |
| 133 | + // 3. each request is limited to MAX_CHUNK_IDS |
| 134 | + // 4. use relay hints when available |
| 135 | + |
| 136 | + // Collect the unknown ids by relay |
| 137 | + let mut ids_by_relay: BTreeMap<RelayUrl, (Vec<Pubkey>, Vec<NoteId>)> = BTreeMap::new(); |
| 138 | + for (unknown_id, relay_hints) in self.ids.iter() { |
| 139 | + // 1. use default relays (empty RelayUrl) if no hints are available |
| 140 | + // 2. query the default relays even when hints are available |
| 141 | + for relay in std::iter::once("".to_string()).chain(relay_hints.iter().cloned()) { |
| 142 | + match unknown_id { |
| 143 | + UnknownId::Pubkey(pk) => { |
| 144 | + ids_by_relay |
| 145 | + .entry(relay) |
| 146 | + .or_insert_with(|| (Vec::new(), Vec::new())) |
| 147 | + .0 |
| 148 | + .push(*pk); |
| 149 | + } |
| 150 | + UnknownId::Id(nid) => { |
| 151 | + ids_by_relay |
| 152 | + .entry(relay) |
| 153 | + .or_insert_with(|| (Vec::new(), Vec::new())) |
| 154 | + .1 |
| 155 | + .push(*nid); |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + const MAX_CHUNK_IDS: usize = 500; |
| 162 | + |
| 163 | + let mut subspecs = vec![]; |
| 164 | + for (relay, (pubkeys, noteids)) in ids_by_relay { |
| 165 | + // make a template SubSpecBuilder w/ the common parts |
| 166 | + let mut ssb = SubSpecBuilder::new().constraint(SubConstraint::OneShot); |
| 167 | + if !relay.is_empty() { |
| 168 | + ssb = ssb.constraint(SubConstraint::AllowedRelays(vec![relay])); |
| 169 | + } |
| 170 | + for chunk in pubkeys.chunks(MAX_CHUNK_IDS) { |
| 171 | + let pks: Vec<&[u8; 32]> = chunk.iter().map(|pk| pk.bytes()).collect(); |
| 172 | + subspecs.push( |
| 173 | + ssb.clone() |
| 174 | + .filters(vec![Filter::new().authors(pks).kinds([0]).build()]) |
| 175 | + .build(), |
| 176 | + ); |
| 177 | + } |
| 178 | + for chunk in noteids.chunks(MAX_CHUNK_IDS) { |
| 179 | + let nids: Vec<&[u8; 32]> = chunk.iter().map(|nid| nid.bytes()).collect(); |
| 180 | + subspecs.push( |
| 181 | + ssb.clone() |
| 182 | + .filters(vec![Filter::new().ids(nids).build()]) |
| 183 | + .build(), |
| 184 | + ); |
| 185 | + } |
| 186 | + } |
| 187 | + subspecs |
128 | 188 | }
|
129 | 189 |
|
130 | 190 | /// We've updated some unknown ids, update the last_updated time to now
|
@@ -350,31 +410,3 @@ pub fn get_unknown_note_ids<'a>(
|
350 | 410 |
|
351 | 411 | Ok(())
|
352 | 412 | }
|
353 |
| - |
354 |
| -fn get_unknown_ids_filter(ids: &[&UnknownId]) -> Option<Vec<Filter>> { |
355 |
| - if ids.is_empty() { |
356 |
| - return None; |
357 |
| - } |
358 |
| - |
359 |
| - let ids = &ids[0..500.min(ids.len())]; |
360 |
| - let mut filters: Vec<Filter> = vec![]; |
361 |
| - |
362 |
| - let pks: Vec<&[u8; 32]> = ids |
363 |
| - .iter() |
364 |
| - .flat_map(|id| id.is_pubkey().map(|pk| pk.bytes())) |
365 |
| - .collect(); |
366 |
| - if !pks.is_empty() { |
367 |
| - let pk_filter = Filter::new().authors(pks).kinds([0]).build(); |
368 |
| - filters.push(pk_filter); |
369 |
| - } |
370 |
| - |
371 |
| - let note_ids: Vec<&[u8; 32]> = ids |
372 |
| - .iter() |
373 |
| - .flat_map(|id| id.is_id().map(|id| id.bytes())) |
374 |
| - .collect(); |
375 |
| - if !note_ids.is_empty() { |
376 |
| - filters.push(Filter::new().ids(note_ids).build()); |
377 |
| - } |
378 |
| - |
379 |
| - Some(filters) |
380 |
| -} |
|
0 commit comments