iris_chat_protocol/protocol_engine/
engine_state_helpers.rs1use super::*;
2
3pub(super) fn restore_signed_peer_evidence_for_session_manager(
9 state: &mut ProtocolEnginePersistedState,
10) {
11 for (owner, evidence) in &state.invite_owner_app_keys_evidence {
12 if *owner == state.session_manager.local_owner_pubkey {
13 continue;
14 }
15 let ProtocolAppKeysEvidence::Verified(event) = evidence else {
16 continue;
17 };
18 if !state
19 .session_manager
20 .verified_peer_app_keys_events
21 .iter()
22 .any(|existing| existing.id == event.id)
23 {
24 state
25 .session_manager
26 .verified_peer_app_keys_events
27 .push(event.as_ref().clone());
28 }
29 }
30}
31
32pub(super) fn quarantine_unverified_owner_rosters(
33 snapshot: &mut SessionManagerSnapshot,
34 verified_app_keys_owners: &BTreeSet<NdrOwnerPubkey>,
35) {
36 for user in &mut snapshot.users {
37 if verified_app_keys_owners.contains(&user.owner_pubkey) {
38 continue;
39 }
40
41 let exact_self_owned_singleton = user.roster.as_ref().is_some_and(|roster| {
45 roster.devices().len() == 1
46 && provisional_owner_from_sender_pubkey(roster.devices()[0].device_pubkey)
47 == user.owner_pubkey
48 });
49 if !exact_self_owned_singleton {
50 user.roster = None;
51 }
52
53 for device in &mut user.devices {
54 device.authorized =
57 provisional_owner_from_sender_pubkey(device.device_pubkey) == user.owner_pubkey;
58 if !device.authorized {
59 device.is_stale = false;
60 device.stale_since = None;
61 }
62 }
63 }
64}
65
66pub(super) fn load_or_create_local_invite(
67 storage: &dyn StorageAdapter,
68 device_pubkey: PublicKey,
69 device_id: &str,
70 owner_pubkey: PublicKey,
71) -> anyhow::Result<Invite> {
72 let storage_key = format!("device-invite/{device_id}");
73 if let Some(serialized) = storage.get(&storage_key)? {
74 if let Ok(invite) = Invite::deserialize(&serialized) {
75 return Ok(normalize_local_invite_owner(invite, owner_pubkey));
76 }
77 }
78
79 let mut invite = Invite::create_new(device_pubkey, Some(device_id.to_string()), None)?;
80 invite = normalize_local_invite_owner(invite, owner_pubkey);
81 storage.put(&storage_key, invite.serialize()?)?;
82 Ok(invite)
83}
84
85pub(super) fn normalize_local_invite_owner(mut invite: Invite, owner_pubkey: PublicKey) -> Invite {
86 if invite.metadata_privacy_version != nostr_double_ratchet::METADATA_PRIVACY_VERSION {
87 invite.metadata_privacy_version = nostr_double_ratchet::METADATA_PRIVACY_VERSION;
88 invite.created_at = NdrUnixSeconds(
89 unix_now()
90 .get()
91 .max(invite.created_at.get().saturating_add(1)),
92 );
93 }
94 invite.inviter_owner_pubkey = Some(ndr_owner(owner_pubkey));
95 invite.owner_public_key = Some(owner_pubkey);
96 invite
97}
98
99pub(super) fn user_record_snapshot(
100 snapshot: &SessionManagerSnapshot,
101 owner: NdrOwnerPubkey,
102) -> Option<&UserRecordSnapshot> {
103 snapshot
104 .users
105 .iter()
106 .find(|user| user.owner_pubkey == owner)
107}
108
109pub(super) fn roster_device_pubkeys(user: &UserRecordSnapshot) -> Option<Vec<NdrDevicePubkey>> {
110 user.roster.as_ref().map(|roster| {
111 roster
112 .devices()
113 .iter()
114 .map(|entry| entry.device_pubkey)
115 .collect()
116 })
117}
118
119impl ProtocolEngine {
120 pub fn authorization_events(&self) -> Vec<Event> {
122 let mut events: BTreeMap<nostr::EventId, Event> = BTreeMap::new();
123 for evidence in self.invite_owner_app_keys_evidence.values() {
124 if let ProtocolAppKeysEvidence::Verified(event) = evidence {
125 events.insert(event.id, event.as_ref().clone());
126 }
127 }
128 for history in self.group_roster_fact_histories.values() {
129 for event in &history.events {
130 events.insert(event.id, event.clone());
131 }
132 }
133 events.into_values().collect()
134 }
135}
136
137impl ProtocolEngine {
138 pub(super) fn with_group_fanout_checkpoint<T>(
139 &mut self,
140 operation: impl FnOnce(&mut Self, &mut GroupFanoutRetryUndo) -> anyhow::Result<T>,
141 ) -> anyhow::Result<T> {
142 let checkpoint = (
147 self.session_manager.clone(),
148 self.ready_group_publications.clone(),
149 self.batch_persist_dirty.get(),
150 );
151 let mut undo = GroupFanoutRetryUndo::default();
152 let result = operation(self, &mut undo);
153 if result.is_err() {
154 self.invalidate_pending_group_fanout_retry_deadline();
155 undo.restore(&mut self.pending_group_fanouts);
156 self.session_manager = checkpoint.0;
157 self.ready_group_publications = checkpoint.1;
158 self.batch_persist_dirty.set(checkpoint.2);
159 }
160 self.invalidate_known_message_author_cache();
161 result
162 }
163
164 pub(super) fn state_checkpoint(&self) -> ProtocolEngineCheckpoint {
165 ProtocolEngineCheckpoint {
166 session_manager: self.session_manager.clone(),
167 group_manager: self.group_manager.clone(),
168 pending_inbound: self.pending_inbound.clone(),
169 pending_group_fanouts: self.pending_group_fanouts.clone(),
170 ready_group_publications: self.ready_group_publications.clone(),
171 pending_local_sibling_sends: self.pending_local_sibling_sends.clone(),
172 pending_remote_sends: self.pending_remote_sends.clone(),
173 pending_group_pairwise_payloads: self.pending_group_pairwise_payloads.clone(),
174 pending_group_sender_key_messages: self.pending_group_sender_key_messages.clone(),
175 pending_group_sender_key_repairs: self.pending_group_sender_key_repairs.clone(),
176 processed_group_sender_key_messages: self.processed_group_sender_key_messages.clone(),
177 group_sender_key_retry: self.group_sender_key_retry.borrow().clone(),
178 answered_group_sender_key_repairs: self.answered_group_sender_key_repairs.clone(),
179 pending_decrypted_deliveries: self.pending_decrypted_deliveries.clone(),
180 group_roster_fact_histories: self.group_roster_fact_histories.clone(),
181 verified_app_keys_owners: self.verified_app_keys_owners.clone(),
182 invite_owner_app_keys_evidence: self.invite_owner_app_keys_evidence.clone(),
183 processed_private_invite_response_ids: self
184 .processed_private_invite_response_ids
185 .clone(),
186 group_invite_uses: self.group_invite_uses.clone(),
187 local_app_keys_observed: self.local_app_keys_observed,
188 subscription_generation: self.subscription_generation,
189 }
190 }
191
192 pub(super) fn restore_checkpoint(&mut self, checkpoint: ProtocolEngineCheckpoint) {
193 self.group_invite_uses = checkpoint.group_invite_uses;
194 self.session_manager = checkpoint.session_manager;
195 self.group_manager = checkpoint.group_manager;
196 self.invalidate_pending_inbound_retry_deadline();
197 self.pending_inbound = checkpoint.pending_inbound;
198 self.invalidate_pending_group_fanout_retry_deadline();
199 self.pending_group_fanouts = checkpoint.pending_group_fanouts;
200 self.ready_group_publications = checkpoint.ready_group_publications;
201 self.pending_local_sibling_sends = checkpoint.pending_local_sibling_sends;
202 self.pending_remote_sends = checkpoint.pending_remote_sends;
203 self.pending_group_pairwise_payloads = checkpoint.pending_group_pairwise_payloads;
204 self.pending_group_sender_key_messages = checkpoint.pending_group_sender_key_messages;
205 self.pending_group_sender_key_repairs = checkpoint.pending_group_sender_key_repairs;
206 self.processed_group_sender_key_messages = checkpoint.processed_group_sender_key_messages;
207 self.group_sender_key_retry
208 .replace(checkpoint.group_sender_key_retry);
209 self.answered_group_sender_key_repairs = checkpoint.answered_group_sender_key_repairs;
210 self.pending_decrypted_deliveries = checkpoint.pending_decrypted_deliveries;
211 self.group_roster_fact_histories = checkpoint.group_roster_fact_histories;
212 self.verified_app_keys_owners = checkpoint.verified_app_keys_owners;
213 self.invite_owner_app_keys_evidence = checkpoint.invite_owner_app_keys_evidence;
214 self.processed_private_invite_response_ids =
215 checkpoint.processed_private_invite_response_ids;
216 self.local_app_keys_observed = checkpoint.local_app_keys_observed;
217 self.subscription_generation = checkpoint.subscription_generation;
218 self.invalidate_known_message_author_cache();
219 }
220
221 pub(super) fn with_state_checkpoint<T>(
222 &mut self,
223 operation: impl FnOnce(&mut Self) -> anyhow::Result<T>,
224 ) -> anyhow::Result<T> {
225 let checkpoint = self.state_checkpoint();
226 match operation(self) {
227 Ok(value) => {
228 self.invalidate_known_message_author_cache();
229 Ok(value)
230 }
231 Err(error) => {
232 self.restore_checkpoint(checkpoint);
233 Err(error)
234 }
235 }
236 }
237}