1use super::*;
2
3impl ProtocolEngine {
4 pub fn load_or_create_for_local_device(
5 storage: Arc<dyn StorageAdapter>,
6 owner_pubkey: PublicKey,
7 device_keys: &Keys,
8 ) -> anyhow::Result<Self> {
9 let local_owner = ndr_owner(owner_pubkey);
10 let local_device = ndr_device(device_keys.public_key());
11 let device_secret = device_keys.secret_key().to_secret_bytes();
12 let mut engine = Self::load_persisted_state(
13 Arc::clone(&storage),
14 owner_pubkey,
15 local_owner,
16 local_device,
17 device_secret,
18 false,
19 )?
20 .unwrap_or_else(|| Self {
21 owner_pubkey,
22 local_owner,
23 local_device,
24 local_device_secret: device_secret,
25 storage,
26 session_manager: SessionManager::new(local_owner, device_secret),
27 group_manager: GroupEventManager::new(local_owner),
28 pending_inbound: Vec::new(),
29 pending_inbound_retry_deadline: std::cell::Cell::new(None),
30 failed_inbound_receives: BTreeMap::new(),
31 pending_group_fanouts: Vec::new(),
32 pending_group_fanout_retry_deadline: std::cell::Cell::new(None),
33 ready_group_publications: BTreeMap::new(),
34 pending_local_sibling_sends: Vec::new(),
35 held_direct_chats: BTreeSet::new(),
36 pending_remote_sends: Vec::new(),
37 pending_group_pairwise_payloads: Vec::new(),
38 pending_group_sender_key_messages: PendingGroupMessages::default(),
39 pending_group_sender_key_repairs: Vec::new(),
40 processed_group_sender_key_messages: ProtocolGroupReplayCache::default(),
41 group_sender_key_retry: std::cell::RefCell::new(ProtocolGroupSenderKeyRetry::default()),
42 group_retry_pacing: std::cell::RefCell::new(GroupRetryPacing::default()),
43 answered_group_sender_key_repairs: Vec::new(),
44 pending_decrypted_deliveries: Vec::new(),
45 group_roster_fact_histories: BTreeMap::new(),
46 handshake_proof_cache: std::cell::RefCell::new(HandshakeProofCache::default()),
47 known_message_author_cache: std::cell::RefCell::new(None),
48 known_message_author_cache_build_count: std::cell::Cell::new(0),
49 verified_app_keys_owners: BTreeSet::new(),
50 invite_owner_app_keys_evidence: BTreeMap::new(),
51 processed_private_invite_response_ids: Vec::new(),
52 group_invite_uses: BTreeMap::new(),
53 local_app_keys_observed: false,
54 local_owner_authenticated: false,
55 subscription_generation: 0,
56 batch_depth: std::cell::Cell::new(0),
57 batch_persist_dirty: std::cell::Cell::new(false),
58 checkpoint_layout: std::cell::RefCell::new(ProtocolCheckpointLayout::default()),
59 });
60
61 let local_invite = if let Some(invite) = engine.session_manager.snapshot().local_invite {
62 let invite = normalize_local_invite_owner(invite, owner_pubkey);
63 engine.session_manager.replace_local_invite(invite.clone());
64 invite
65 } else {
66 let device_id = device_keys.public_key().to_hex();
67 let invite = load_or_create_local_invite(
68 engine.storage.as_ref(),
69 device_keys.public_key(),
70 &device_id,
71 owner_pubkey,
72 )?;
73 engine.session_manager.replace_local_invite(invite.clone());
74 invite
75 };
76 engine.finish_local_device_startup(local_invite.created_at)?;
77 Ok(engine)
78 }
79
80 pub(super) fn load_persisted_state(
81 storage: Arc<dyn StorageAdapter>,
82 owner_pubkey: PublicKey,
83 local_owner: NdrOwnerPubkey,
84 local_device: NdrDevicePubkey,
85 device_secret: [u8; 32],
86 notification_preview: bool,
87 ) -> anyhow::Result<Option<Self>> {
88 let Some(raw) = storage.get(PROTOCOL_ENGINE_STATE_KEY)? else {
89 return Ok(None);
90 };
91 let mut state = if notification_preview {
92 serde_json::from_str::<NotificationPreviewPersistedState>(&raw)
93 .map(|preview| preview.state)
94 } else {
95 serde_json::from_str::<ProtocolEnginePersistedState>(&raw)
96 }
97 .map_err(|_| {
98 anyhow::anyhow!("Saved chat state could not be read. Existing data has been kept.")
99 })?;
100 anyhow::ensure!(
101 state.version == PROTOCOL_ENGINE_STATE_VERSION,
102 "Saved chat state uses an unsupported version. Existing data has been kept."
103 );
104 quarantine_legacy_group_ingress(storage.as_ref(), &raw, &mut state)?;
105 sanitize_invite_owner_persisted_state(&mut state);
106 restore_signed_peer_evidence_for_session_manager(&mut state);
107 quarantine_unverified_owner_rosters(
108 &mut state.session_manager,
109 &state.verified_app_keys_owners,
110 );
111 let checkpoint_layout = if notification_preview {
112 ProtocolCheckpointLayout::default()
113 } else {
114 ProtocolCheckpointLayout::from_json(&raw)
115 };
116 drop(raw);
117 let session_manager = SessionManager::from_snapshot(state.session_manager, device_secret)?;
118 let group_manager = GroupEventManager::from_snapshot(state.group_manager)?;
119 let mut answered_group_sender_key_repairs = state.answered_group_sender_key_repairs;
120 let excess = answered_group_sender_key_repairs
121 .len()
122 .saturating_sub(ANSWERED_GROUP_SENDER_KEY_REPAIR_LIMIT);
123 if excess > 0 {
124 answered_group_sender_key_repairs.drain(0..excess);
125 }
126
127 Ok(Some(Self {
128 owner_pubkey,
129 local_owner,
130 local_device,
131 local_device_secret: device_secret,
132 storage,
133 session_manager,
134 group_manager,
135 pending_inbound: state.pending_inbound,
136 pending_inbound_retry_deadline: std::cell::Cell::new(None),
137 failed_inbound_receives: BTreeMap::new(),
138 pending_group_fanouts: state.pending_group_fanouts,
139 pending_group_fanout_retry_deadline: std::cell::Cell::new(None),
140 ready_group_publications: state.ready_group_publications,
141 pending_local_sibling_sends: state.pending_local_sibling_sends,
142 held_direct_chats: BTreeSet::new(),
143 pending_remote_sends: state.pending_remote_sends,
144 pending_group_pairwise_payloads: state.pending_group_pairwise_payloads,
145 pending_group_sender_key_messages: state.pending_group_sender_key_messages,
146 pending_group_sender_key_repairs: state.pending_group_sender_key_repairs,
147 processed_group_sender_key_messages: state.processed_group_sender_key_messages,
148 group_sender_key_retry: std::cell::RefCell::new(ProtocolGroupSenderKeyRetry::default()),
149 group_retry_pacing: std::cell::RefCell::new(GroupRetryPacing::default()),
150 answered_group_sender_key_repairs,
151 pending_decrypted_deliveries: state.pending_decrypted_deliveries,
152 group_roster_fact_histories: state.group_roster_fact_histories,
153 handshake_proof_cache: std::cell::RefCell::new(HandshakeProofCache::default()),
154 known_message_author_cache: std::cell::RefCell::new(None),
155 known_message_author_cache_build_count: std::cell::Cell::new(0),
156 local_app_keys_observed: state.verified_app_keys_owners.contains(&local_owner),
157 verified_app_keys_owners: state.verified_app_keys_owners,
158 invite_owner_app_keys_evidence: state.invite_owner_app_keys_evidence,
159 processed_private_invite_response_ids: state.processed_private_invite_response_ids,
160 group_invite_uses: state.group_invite_uses,
161 local_owner_authenticated: false,
162 subscription_generation: state.subscription_generation,
163 batch_depth: std::cell::Cell::new(0),
164 batch_persist_dirty: std::cell::Cell::new(false),
165 checkpoint_layout: std::cell::RefCell::new(checkpoint_layout),
166 }))
167 }
168
169 fn finish_local_device_startup(
170 &mut self,
171 local_invite_created_at: NdrUnixSeconds,
172 ) -> anyhow::Result<()> {
173 self.ensure_local_roster(local_invite_created_at);
174 self.discard_pending_messages_for_other_devices()?;
175 self.hydrate_pending_inbound_metadata();
176 self.prune_superseded_local_group_sync_fanouts();
180 self.prune_pending_group_sender_key_work_for_inactive_local_groups();
181 self.persist()
182 }
183
184 pub(super) fn prune_superseded_local_group_sync_fanouts(&mut self) -> bool {
185 let original_len = self.pending_group_fanouts.len();
186 if original_len < 2 {
187 return false;
188 }
189
190 let mut seen_groups = HashSet::new();
191 let mut compacted = Vec::with_capacity(original_len);
192 for pending in std::mem::take(&mut self.pending_group_fanouts)
193 .into_iter()
194 .rev()
195 {
196 let keep = if pending.inner_event_id.is_none()
197 && matches!(
198 pending.fanout.as_ref(),
199 GroupPendingFanout::LocalSiblings { .. }
200 ) {
201 seen_groups.insert(pending.group_id.clone())
202 } else {
203 true
204 };
205 if keep {
206 compacted.push(pending);
207 }
208 }
209 compacted.reverse();
210 self.pending_group_fanouts = compacted;
211 let changed = self.pending_group_fanouts.len() != original_len;
212 if changed {
213 self.invalidate_pending_group_fanout_retry_deadline();
214 }
215 changed
216 }
217
218 fn hydrate_pending_inbound_metadata(&mut self) {
219 let metadata = self
220 .pending_inbound
221 .iter()
222 .map(|pending| {
223 self.pending_inbound_metadata_for_event(
224 &pending.event,
225 pending.envelope.as_ref(),
226 None,
227 )
228 })
229 .collect::<Vec<_>>();
230 for (pending, metadata) in self.pending_inbound.iter_mut().zip(metadata) {
231 apply_pending_inbound_metadata(pending, metadata);
232 }
233 }
234
235 pub fn debug_snapshot(&self) -> ProtocolEngineDebugSnapshot {
236 let pending_group_sender_key_retry_count = self.pending_group_sender_key_retry_count();
237 let pending_group_sender_key_unmapped_count = self
238 .pending_group_sender_key_messages
239 .len()
240 .saturating_sub(pending_group_sender_key_retry_count);
241 let known_message_author_pubkeys = self
242 .known_message_author_pubkeys()
243 .into_iter()
244 .map(|pubkey| pubkey.to_hex())
245 .collect::<Vec<_>>();
246 let known_group_sender_key_author_pubkeys = self
247 .known_group_sender_event_pubkeys()
248 .into_iter()
249 .map(|pubkey| pubkey.to_hex())
250 .collect::<Vec<_>>();
251 ProtocolEngineDebugSnapshot {
252 known_message_author_count: known_message_author_pubkeys.len(),
253 known_message_author_pubkeys,
254 known_group_sender_key_author_count: known_group_sender_key_author_pubkeys.len(),
255 known_group_sender_key_author_pubkeys,
256 pending_inbound_count: self.pending_inbound.len(),
257 pending_group_fanout_count: self.pending_group_fanouts.len(),
258 pending_group_pairwise_payload_count: self.pending_group_pairwise_payloads.len(),
259 pending_group_sender_key_message_count: self.pending_group_sender_key_messages.len(),
260 pending_group_sender_key_retry_count,
261 pending_group_sender_key_unmapped_count,
262 pending_group_sender_key_repair_count: self.pending_group_sender_key_repairs.len(),
263 pending_group_sender_key_repair_last_requested_at_secs: self
264 .pending_group_sender_key_repairs
265 .iter()
266 .map(|repair| repair.last_requested_at_secs)
267 .max()
268 .unwrap_or_default(),
269 pending_group_sender_key_repair_next_retry_at_secs: self
270 .pending_group_sender_key_repairs
271 .iter()
272 .map(Self::pending_group_sender_key_repair_due_at_secs)
273 .min()
274 .unwrap_or_default(),
275 pending_group_sender_key_repair_max_request_count: self
276 .pending_group_sender_key_repairs
277 .iter()
278 .map(|repair| repair.request_count)
279 .max()
280 .unwrap_or_default(),
281 pending_group_fanout_targets: self.queued_group_targets(),
282 subscription_generation: self.subscription_generation,
283 }
284 }
285
286 pub fn is_known_local_owner_device(&self, device_pubkey: PublicKey) -> bool {
287 let device_pubkey = ndr_device(device_pubkey);
288 if device_pubkey == self.local_device {
289 return true;
290 }
291 if !self.verified_app_keys_owners.contains(&self.local_owner) {
292 return false;
293 }
294 self.session_manager
295 .snapshot()
296 .users
297 .into_iter()
298 .find(|user| user.owner_pubkey == self.local_owner)
299 .is_some_and(|user| {
300 user.roster
301 .as_ref()
302 .is_some_and(|roster| roster.get_device(&device_pubkey).is_some())
303 && user.devices.iter().any(|device| {
304 device.device_pubkey == device_pubkey
305 && device.authorized
306 && !device.is_stale
307 })
308 })
309 }
310
311 pub fn owner_hint_for_device(
312 &self,
313 device_pubkey: PublicKey,
314 ) -> Option<ProtocolDeviceOwnerHint> {
315 let device = ndr_device(device_pubkey);
316 let provisional_owner = ndr_owner(device_pubkey);
317 let mut claimed_owner = None;
318 for user in self.session_manager.snapshot().users {
319 for record in user.devices {
320 if record.device_pubkey != device {
321 continue;
322 }
323 if user.owner_pubkey != provisional_owner {
324 if self.verified_app_keys_owners.contains(&user.owner_pubkey)
325 && user.roster.as_ref().is_some_and(|roster| {
326 roster.get_device(&record.device_pubkey).is_some()
327 })
328 {
329 return public_owner(user.owner_pubkey).ok().map(|owner| {
330 ProtocolDeviceOwnerHint {
331 owner,
332 verified: true,
333 }
334 });
335 }
336 if claimed_owner.is_none() {
337 claimed_owner = Some(user.owner_pubkey);
338 }
339 continue;
340 }
341 if claimed_owner.is_none() {
342 claimed_owner = record.claimed_owner_pubkey;
343 }
344 }
345 }
346 claimed_owner
347 .and_then(|owner| public_owner(owner).ok())
348 .map(|owner| ProtocolDeviceOwnerHint {
349 owner,
350 verified: false,
351 })
352 }
353
354 pub(super) fn verified_roster_owner_for_device(
355 &self,
356 device_pubkey: NdrDevicePubkey,
357 ) -> Option<NdrOwnerPubkey> {
358 let provisional_owner = NdrOwnerPubkey::from_bytes(device_pubkey.to_bytes());
359 let mut provisional_match = None;
360 for user in self.session_manager.snapshot().users {
361 let Some(roster) = user.roster.as_ref() else {
362 continue;
363 };
364 if roster.get_device(&device_pubkey).is_none() {
365 continue;
366 }
367 if user.owner_pubkey == self.local_owner {
368 continue;
369 }
370 if user.owner_pubkey != provisional_owner {
371 if self.verified_app_keys_owners.contains(&user.owner_pubkey) {
372 return Some(user.owner_pubkey);
373 }
374 continue;
375 }
376 provisional_match = Some(user.owner_pubkey);
377 }
378 provisional_match
379 }
380
381 pub fn has_pending_inbound_direct_events(&self) -> bool {
382 !self.pending_inbound.is_empty()
383 }
384
385 pub fn has_pending_retry_work(&self) -> bool {
386 !self.pending_decrypted_deliveries.is_empty()
387 || !self.pending_inbound.is_empty()
388 || !self.pending_group_fanouts.is_empty()
389 || !self.ready_group_publications.is_empty()
390 || !self.pending_local_sibling_sends.is_empty()
391 || !self.pending_remote_sends.is_empty()
392 || !self.pending_group_pairwise_payloads.is_empty()
393 || self.has_ready_group_sender_key_retry_work()
394 || !self.pending_group_sender_key_repairs.is_empty()
395 }
396
397 pub fn has_ready_group_sender_key_retry_work(&self) -> bool {
400 self.refresh_group_sender_key_retry_inputs();
401 !self.group_sender_key_retry.borrow().ready.is_empty()
402 }
403
404 fn pending_group_sender_key_retry_count(&self) -> usize {
405 self.pending_group_sender_key_messages
406 .iter()
407 .filter(|pending| {
408 self.group_manager
409 .group_id_for_sender_event_pubkey(pending.sender_event_pubkey)
410 .is_some()
411 })
412 .count()
413 }
414
415 pub fn has_pending_inbound_direct_event_id(&self, event_id: &str) -> bool {
416 self.pending_inbound.iter().any(|pending| {
417 let pending_event_id = if pending.event_id.is_empty() {
418 pending.event.id.to_string()
419 } else {
420 pending.event_id.clone()
421 };
422 pending_event_id == event_id
423 })
424 }
425
426 pub fn queued_owner_claim_targets(&self) -> Vec<String> {
427 let mut targets = self.pending_inbound_owner_claim_targets();
428 targets.extend(self.pending_group_pairwise_owner_claim_targets());
429 targets.sort();
430 targets.dedup();
431 targets
432 }
433
434 pub fn direct_send_readiness(&self, peer_pubkey: PublicKey) -> DirectSendReadiness {
435 if (self.local_owner_authenticated && self.local_handshake_owner_proof().is_none())
439 || (!self.local_app_keys_observed && !self.has_authoritative_local_roster())
440 {
441 return DirectSendReadiness::MissingLocalAppKeys;
442 }
443
444 let peer_owner = ndr_owner(peer_pubkey);
445 let Some(roster) = self.session_manager.roster(peer_owner) else {
446 return DirectSendReadiness::MissingPeerAppKeys;
447 };
448 let peer_devices = roster.devices();
449 if peer_devices.is_empty() {
450 return DirectSendReadiness::MissingPeerAppKeys;
451 }
452 if peer_owner == self.local_owner {
453 if matches!(peer_devices, [device] if device.device_pubkey == self.local_device)
456 || peer_devices.iter().any(|device| {
457 device.device_pubkey != self.local_device
458 && self
459 .session_manager
460 .can_send_to_device(peer_owner, device.device_pubkey)
461 })
462 {
463 return DirectSendReadiness::Ready;
464 }
465 return DirectSendReadiness::MissingLocalSiblingInviteOrSession;
466 }
467 if !peer_devices.iter().any(|device| {
468 self.session_manager
469 .can_send_to_device(peer_owner, device.device_pubkey)
470 }) {
471 return DirectSendReadiness::MissingPeerInviteOrSession;
472 }
473
474 DirectSendReadiness::Ready
478 }
479
480 pub fn authenticate_local_owner_for_sending(
481 &mut self,
482 owner_keys: &Keys,
483 ) -> anyhow::Result<()> {
484 if owner_keys.public_key() != self.owner_pubkey {
485 anyhow::bail!("local owner key does not match protocol owner");
486 }
487 self.local_owner_authenticated = true;
488 Ok(())
489 }
490
491 pub fn local_invite(&self) -> Option<Invite> {
492 self.session_manager.snapshot().local_invite
493 }
494
495 pub fn local_invite_response_pubkey(&self) -> Option<PublicKey> {
496 self.local_invite()?
497 .inviter_ephemeral_public_key
498 .to_nostr()
499 .ok()
500 }
501
502 pub fn known_message_author_pubkeys(&self) -> Vec<PublicKey> {
503 self.with_known_message_author_cache(|cache| cache.pubkeys.clone())
504 }
505
506 pub fn message_author_pubkeys_filtered<F>(&self, accept_owner: F) -> Vec<PublicKey>
513 where
514 F: Fn(PublicKey) -> bool,
515 {
516 self.message_session_projection(accept_owner).0
517 }
518
519 fn message_session_projection<F>(&self, accept_owner: F) -> (Vec<PublicKey>, HashSet<PublicKey>)
520 where
521 F: Fn(PublicKey) -> bool,
522 {
523 let mut authors = HashSet::new();
524 let mut usable_session_owners = HashSet::new();
525 for user in self.session_manager.snapshot().users {
526 let Ok(owner) = PublicKey::parse(&user.owner_pubkey.to_string()) else {
527 continue;
528 };
529 if !accept_owner(owner) {
530 continue;
531 }
532 let roster = user.roster.clone();
533 for device in user.devices {
534 if !device.authorized
535 || device.is_stale
536 || !self.owner_device_binding_is_verified_in_roster(
537 user.owner_pubkey,
538 device.device_pubkey,
539 roster.as_ref(),
540 )
541 {
542 continue;
543 }
544 for session in device
545 .active_session
546 .iter()
547 .chain(&device.inactive_sessions)
548 {
549 collect_expected_sender_pubkeys(session, &mut authors);
550 if session.their_next_nostr_public_key.is_some()
552 && session.our_current_nostr_key.is_some()
553 {
554 usable_session_owners.insert(owner);
555 }
556 }
557 }
558 }
559 let mut authors = authors.into_iter().collect::<Vec<_>>();
560 authors.sort_unstable();
561 (authors, usable_session_owners)
562 }
563
564 pub fn has_usable_direct_session_for_owner(&self, owner: PublicKey) -> bool {
567 self.with_known_message_author_cache(|cache| cache.usable_session_owners.contains(&owner))
568 }
569
570 pub fn is_known_message_author(&self, author: PublicKey) -> bool {
571 self.with_known_message_author_cache(|cache| cache.pubkey_set.contains(&author))
572 }
573
574 fn with_known_message_author_cache<T>(
575 &self,
576 read: impl FnOnce(&KnownMessageAuthorCache) -> T,
577 ) -> T {
578 let mut cached = self.known_message_author_cache.borrow_mut();
579 let cache = cached.get_or_insert_with(|| self.build_known_message_author_cache());
580 read(cache)
581 }
582
583 fn build_known_message_author_cache(&self) -> KnownMessageAuthorCache {
584 self.known_message_author_cache_build_count
585 .set(self.known_message_author_cache_build_count.get() + 1);
586
587 let (pubkeys, usable_session_owners) = self.message_session_projection(|_| true);
588 KnownMessageAuthorCache {
589 pubkey_set: pubkeys.iter().copied().collect(),
590 pubkeys,
591 usable_session_owners,
592 }
593 }
594
595 pub(super) fn invalidate_known_message_author_cache(&self) {
596 self.known_message_author_cache.borrow_mut().take();
597 }
598
599 pub fn known_group_sender_event_pubkeys(&self) -> Vec<PublicKey> {
600 let mut authors = self
601 .group_manager
602 .known_sender_event_pubkeys()
603 .into_iter()
604 .filter_map(|pubkey| public_device(pubkey).ok())
605 .collect::<Vec<_>>();
606 authors.sort_by_key(|pubkey| pubkey.to_hex());
607 authors.dedup();
608 authors
609 }
610
611 pub fn group_sender_event_pubkeys_for_owner(&self, owner: PublicKey) -> Vec<PublicKey> {
612 self.group_manager
613 .snapshot()
614 .sender_keys
615 .into_iter()
616 .filter(|record| record.sender_owner == ndr_owner(owner))
617 .filter_map(|record| public_device(record.sender_event_pubkey).ok())
618 .collect()
619 }
620
621 pub fn group_sender_event_pubkeys_for_group(&self, group_id: &str) -> Vec<PublicKey> {
622 self.group_manager
623 .known_sender_event_pubkeys()
624 .into_iter()
625 .filter(|author| {
626 self.group_manager
627 .group_id_for_sender_event_pubkey(*author)
628 .as_deref()
629 == Some(group_id)
630 })
631 .filter_map(|author| public_device(author).ok())
632 .collect()
633 }
634
635 pub fn is_known_group_sender_event_author(&self, author: PublicKey) -> bool {
636 self.group_manager
637 .group_id_for_sender_event_pubkey(ndr_device(author))
638 .is_some()
639 }
640
641 pub fn is_potential_group_sender_key_event(&self, event: &Event) -> bool {
642 parse_group_sender_key_message_event_unchecked(event).is_ok_and(|parsed| {
643 self.group_manager
644 .group_id_for_sender_event_pubkey(parsed.sender_event_pubkey)
645 .is_some()
646 })
647 }
648
649 pub fn is_group_sender_key_candidate_with_local_group_context(&self, event: &Event) -> bool {
650 if !protocol_event_has_tag(event, "header")
651 || self.group_manager.snapshot().groups.is_empty()
652 {
653 return false;
654 }
655 parse_group_sender_key_message_event_unchecked(event).is_ok()
656 }
657
658 pub fn header_message_sender_has_verified_owner(&self, event: &Event) -> bool {
659 if !protocol_event_has_tag(event, "header") {
660 return false;
661 }
662 let Ok(envelope) = parse_message_event(event) else {
663 return false;
664 };
665 let Ok(sender) = public_device(envelope.sender) else {
666 return false;
667 };
668 self.owner_hint_for_device(sender)
669 .is_some_and(|hint| hint.verified)
670 }
671
672 pub fn header_message_sender_has_tracked_session(&self, event: &Event) -> bool {
673 if !protocol_event_has_tag(event, "header") {
674 return false;
675 }
676 let Ok(envelope) = parse_message_event(event) else {
677 return false;
678 };
679 !matches!(
680 self.resolve_message_sender_owner(&envelope),
681 ProtocolSenderOwnerResolution::ProvisionalDeviceOwner { .. }
682 )
683 }
684
685 pub fn known_device_identity_pubkeys_for_owner(
686 &self,
687 owner_pubkey: PublicKey,
688 ) -> Vec<PublicKey> {
689 self.known_device_identity_pubkeys_for_owner_with_snapshot(
690 &self.session_manager.snapshot(),
691 owner_pubkey,
692 )
693 }
694
695 pub fn known_device_identity_pubkeys_for_owner_with_snapshot(
696 &self,
697 snapshot: &SessionManagerSnapshot,
698 owner_pubkey: PublicKey,
699 ) -> Vec<PublicKey> {
700 let owner = ndr_owner(owner_pubkey);
701 let mut devices = snapshot
702 .users
703 .iter()
704 .find(|user| user.owner_pubkey == owner)
705 .and_then(|user| {
706 user.roster.as_ref().map(|roster| {
707 roster
708 .devices()
709 .iter()
710 .filter(|device| {
711 self.owner_device_binding_is_verified_in_roster(
712 owner,
713 device.device_pubkey,
714 Some(roster),
715 )
716 })
717 .filter_map(|device| public_device(device.device_pubkey).ok())
718 .collect::<Vec<_>>()
719 })
720 })
721 .unwrap_or_default();
722 devices.sort_by_key(|pubkey| pubkey.to_hex());
723 devices.dedup();
724 devices
725 }
726
727 pub fn message_author_pubkeys_for_owner(&self, owner_pubkey: PublicKey) -> Vec<PublicKey> {
728 let mut authors = HashSet::new();
729 let owner = ndr_owner(owner_pubkey);
730 for user in self.session_manager.snapshot().users {
731 if user.owner_pubkey != owner {
732 continue;
733 }
734 let roster = user.roster.clone();
735 for device in user.devices {
736 if !device.authorized
737 || device.is_stale
738 || !self.owner_device_binding_is_verified_in_roster(
739 owner,
740 device.device_pubkey,
741 roster.as_ref(),
742 )
743 {
744 continue;
745 }
746 if let Some(session) = device.active_session.as_ref() {
747 collect_expected_sender_pubkeys(session, &mut authors);
748 }
749 for session in &device.inactive_sessions {
750 collect_expected_sender_pubkeys(session, &mut authors);
751 }
752 }
753 }
754 let mut authors = authors.into_iter().collect::<Vec<_>>();
755 authors.sort_by_key(|pubkey| pubkey.to_hex());
756 authors
757 }
758
759 pub fn session_manager_snapshot(&self) -> SessionManagerSnapshot {
765 self.session_manager.snapshot()
766 }
767
768 pub fn message_session_debug_snapshots_with_snapshot(
769 snapshot: &SessionManagerSnapshot,
770 owner_pubkey: PublicKey,
771 ) -> Vec<ProtocolMessageSessionDebugSnapshot> {
772 let owner = ndr_owner(owner_pubkey);
773 snapshot
774 .users
775 .iter()
776 .filter(|user| user.owner_pubkey == owner)
777 .flat_map(|user| user.devices.iter())
778 .flat_map(|device| {
779 device
780 .active_session
781 .iter()
782 .chain(device.inactive_sessions.iter())
783 })
784 .map(|state| {
785 let mut tracked = HashSet::new();
786 collect_expected_sender_pubkeys(state, &mut tracked);
787 let mut tracked_sender_pubkeys = tracked.into_iter().collect::<Vec<_>>();
788 tracked_sender_pubkeys.sort_by_key(|pubkey| pubkey.to_hex());
789 ProtocolMessageSessionDebugSnapshot {
790 has_receiving_capability: state.receiving_chain_key.is_some()
791 || state.their_current_nostr_public_key.is_some(),
792 state: state.clone(),
793 tracked_sender_pubkeys,
794 }
795 })
796 .collect()
797 }
798
799 pub fn verified_message_session_snapshots_for_owner(
800 &self,
801 owner_pubkey: PublicKey,
802 ) -> Vec<ProtocolMessageSessionDebugSnapshot> {
803 let owner = ndr_owner(owner_pubkey);
804 let mut snapshot = self.session_manager.snapshot();
805 for user in &mut snapshot.users {
806 if user.owner_pubkey != owner {
807 continue;
808 }
809 let roster = user.roster.clone();
810 user.devices.retain(|device| {
811 device.authorized
812 && !device.is_stale
813 && self.owner_device_binding_is_verified_in_roster(
814 owner,
815 device.device_pubkey,
816 roster.as_ref(),
817 )
818 });
819 }
820 Self::message_session_debug_snapshots_with_snapshot(&snapshot, owner_pubkey)
821 }
822
823 pub fn active_session_count_for_owner_with_snapshot(
824 snapshot: &SessionManagerSnapshot,
825 owner_pubkey: PublicKey,
826 ) -> usize {
827 let owner = ndr_owner(owner_pubkey);
828 snapshot
829 .users
830 .iter()
831 .filter(|user| user.owner_pubkey == owner)
832 .flat_map(|user| user.devices.iter())
833 .filter(|device| device.active_session.is_some())
834 .count()
835 }
836
837 pub fn active_session_count_for_owner(&self, owner_pubkey: PublicKey) -> usize {
838 let owner = ndr_owner(owner_pubkey);
839 self.session_manager
840 .snapshot()
841 .users
842 .into_iter()
843 .filter(|user| user.owner_pubkey == owner)
844 .map(|user| {
845 let roster = user.roster;
846 user.devices
847 .into_iter()
848 .filter(|device| {
849 device.active_session.is_some()
850 && device.authorized
851 && !device.is_stale
852 && self.owner_device_binding_is_verified_in_roster(
853 owner,
854 device.device_pubkey,
855 roster.as_ref(),
856 )
857 })
858 .count()
859 })
860 .sum()
861 }
862
863 pub fn active_roster_session_count_for_owner(&self, owner_pubkey: PublicKey) -> usize {
864 self.active_roster_session_count_for_owner_with_snapshot(
865 &self.session_manager.snapshot(),
866 owner_pubkey,
867 )
868 }
869
870 pub fn active_roster_session_count_for_owner_with_snapshot(
871 &self,
872 snapshot: &SessionManagerSnapshot,
873 owner_pubkey: PublicKey,
874 ) -> usize {
875 let owner = ndr_owner(owner_pubkey);
876 snapshot
877 .users
878 .iter()
879 .find(|user| user.owner_pubkey == owner)
880 .and_then(|user| {
881 let roster = user.roster.as_ref()?;
882 Some(
883 user.devices
884 .iter()
885 .filter(|device| {
886 device.active_session.is_some()
887 && device.authorized
888 && !device.is_stale
889 && self.owner_device_binding_is_verified_in_roster(
890 owner,
891 device.device_pubkey,
892 Some(roster),
893 )
894 && roster
895 .devices()
896 .iter()
897 .any(|entry| entry.device_pubkey == device.device_pubkey)
898 })
899 .count(),
900 )
901 })
902 .unwrap_or_default()
903 }
904
905 pub fn owner_device_binding_is_verified(
906 &self,
907 owner_pubkey: PublicKey,
908 device_pubkey: PublicKey,
909 ) -> bool {
910 self.has_verified_device_owner_claim(ndr_owner(owner_pubkey), ndr_device(device_pubkey))
911 }
912
913 pub(super) fn owner_device_binding_is_verified_in_roster(
914 &self,
915 owner: NdrOwnerPubkey,
916 device: NdrDevicePubkey,
917 roster: Option<&DeviceRoster>,
918 ) -> bool {
919 owner == provisional_owner_from_sender_pubkey(device)
920 || (self.verified_app_keys_owners.contains(&owner)
921 && roster.is_some_and(|roster| roster.get_device(&device).is_some()))
922 }
923
924 pub fn known_verified_peer_owner_pubkeys(&self) -> Vec<PublicKey> {
925 let mut owners = self
926 .session_manager
927 .snapshot()
928 .users
929 .into_iter()
930 .filter(|user| {
931 let roster = user.roster.as_ref();
932 user.devices.iter().any(|device| {
933 self.owner_device_binding_is_verified_in_roster(
934 user.owner_pubkey,
935 device.device_pubkey,
936 roster,
937 )
938 })
939 })
940 .filter_map(|user| public_owner(user.owner_pubkey).ok())
941 .collect::<Vec<_>>();
942 owners.sort_by_key(|owner| owner.to_hex());
943 owners.dedup();
944 owners
945 }
946
947 pub fn has_delivery_blocking_message_work(&self, message_id: &str) -> bool {
948 self.pending_group_fanouts
949 .iter()
950 .any(|pending| pending.inner_event_id.as_deref() == Some(message_id))
951 }
952}