iris_chat_protocol/protocol_engine/
engine_app_keys.rs1use super::*;
2
3impl ProtocolEngine {
4 pub fn ingest_app_keys_snapshot(
5 &mut self,
6 owner_pubkey: PublicKey,
7 app_keys: AppKeys,
8 created_at: u64,
9 ) -> anyhow::Result<ProtocolRetryBatch> {
10 Ok(self
11 .ingest_app_keys_snapshot_inner(owner_pubkey, app_keys, created_at, None)?
12 .retry_batch)
13 }
14
15 pub fn ingest_app_keys_event(&mut self, event: &Event) -> anyhow::Result<ProtocolRetryBatch> {
16 Ok(self.ingest_app_keys_event_with_outcome(event)?.retry_batch)
17 }
18
19 pub fn ingest_app_keys_event_with_outcome(
20 &mut self,
21 event: &Event,
22 ) -> anyhow::Result<ProtocolMetadataIngestionResult> {
23 let app_keys = AppKeys::from_event(event)?;
24 self.ingest_app_keys_snapshot_inner(
25 event.pubkey,
26 app_keys,
27 event.created_at.as_secs(),
28 Some(event),
29 )
30 }
31
32 fn ingest_app_keys_snapshot_inner(
33 &mut self,
34 owner_pubkey: PublicKey,
35 app_keys: AppKeys,
36 created_at: u64,
37 source_event: Option<&Event>,
38 ) -> anyhow::Result<ProtocolMetadataIngestionResult> {
39 let owner = ndr_owner(owner_pubkey);
40 let now = unix_now();
44 let session_change_possible = match source_event {
45 Some(event) if owner_pubkey != self.owner_pubkey => self
46 .session_manager
47 .peer_app_keys_event_would_change(event, NdrUnixSeconds(now.get()))?,
48 _ => true,
49 };
50 let previous_session = session_change_possible.then(|| self.session_manager.clone());
51 let previous_evidence = self.invite_owner_app_keys_evidence.get(&owner).cloned();
52 let previously_verified = self.verified_app_keys_owners.contains(&owner);
53 let previously_observed = self.local_app_keys_observed;
54 let roster = DeviceRoster::new(
55 NdrUnixSeconds(created_at),
56 app_keys
57 .get_all_devices()
58 .into_iter()
59 .map(|entry| {
60 AuthorizedDevice::new(
61 ndr_device(entry.identity_pubkey),
62 NdrUnixSeconds(entry.created_at),
63 )
64 })
65 .collect(),
66 );
67 let mut roster_changed = if owner_pubkey == self.owner_pubkey {
68 if should_replace_provisional_local_roster(
69 &self.session_manager.snapshot(),
70 self.owner_pubkey,
71 self.local_device,
72 &roster,
73 ) {
74 !matches!(
75 self.session_manager.replace_local_roster(roster),
76 nostr_double_ratchet::RosterSnapshotDecision::Stale
77 )
78 } else {
79 !matches!(
80 self.session_manager.apply_local_roster(roster),
81 nostr_double_ratchet::RosterSnapshotDecision::Stale
82 )
83 }
84 } else if let Some(event) = source_event {
85 if session_change_possible {
86 self.session_manager
87 .observe_peer_app_keys_event(event.clone(), NdrUnixSeconds(now.get()))?
88 } else {
89 false
90 }
91 } else {
92 !matches!(
93 self.session_manager.observe_peer_roster(owner, roster),
94 nostr_double_ratchet::RosterSnapshotDecision::Stale
95 )
96 };
97 if roster_changed && (owner_pubkey == self.owner_pubkey || source_event.is_none()) {
98 roster_changed = self.session_manager.snapshot()
102 != previous_session
103 .as_ref()
104 .expect("local and projected roster checkpoint")
105 .snapshot();
106 }
107 let exact_source_is_valid = source_event.is_some_and(|event| {
108 app_keys_event_is_acceptable_for_owner(owner_pubkey, event, now.get())
109 });
110 let exact_evidence_changed = if exact_source_is_valid {
111 let event = source_event.expect("validated AppKeys source event");
112 self.update_invite_owner_app_keys_evidence(owner, event, &app_keys)
113 } else {
114 false
115 };
116 let provenance_changed = if owner_pubkey == self.owner_pubkey || exact_source_is_valid {
119 self.verified_app_keys_owners.insert(owner)
120 } else {
121 false
122 };
123 let local_observation_changed =
124 owner_pubkey == self.owner_pubkey && !self.local_app_keys_observed;
125 if owner_pubkey == self.owner_pubkey {
126 self.local_app_keys_observed = true;
127 }
128 if !roster_changed
129 && !exact_evidence_changed
130 && !provenance_changed
131 && !local_observation_changed
132 {
133 return Ok(ProtocolMetadataIngestionResult::default());
134 }
135 let mut checkpoint = self.state_checkpoint();
139 if let Some(previous_session) = previous_session {
140 checkpoint.session_manager = previous_session;
141 }
142 checkpoint.local_app_keys_observed = previously_observed;
143 if !previously_verified {
144 checkpoint.verified_app_keys_owners.remove(&owner);
145 }
146 match previous_evidence {
147 Some(evidence) => {
148 checkpoint
149 .invite_owner_app_keys_evidence
150 .insert(owner, evidence);
151 }
152 None => {
153 checkpoint.invite_owner_app_keys_evidence.remove(&owner);
154 }
155 }
156 self.invalidate_known_message_author_cache();
157 if owner == self.local_owner && roster_changed {
158 if let Some(roster) = self.session_manager.roster(owner) {
159 let source_authorized = roster.get_device(&self.local_device).is_some();
162 for pending in &mut self.pending_local_sibling_sends {
163 if let Some(eligible) = &mut pending.eligible_devices {
164 eligible.retain(|device| {
165 source_authorized && roster.get_device(device).is_some()
166 });
167 }
168 }
169 }
170 }
171 self.wake_pending_protocol_for_owner(owner);
172 if let Err(error) = self.persist() {
173 self.restore_checkpoint(checkpoint);
174 self.invalidate_known_message_author_cache();
175 return Err(error);
176 }
177 Ok(ProtocolMetadataIngestionResult {
178 changed: true,
179 retry_batch: self.retry_pending_protocol(NdrUnixSeconds(now.get()))?,
180 })
181 }
182}