1use crate::*;
2use crate::{RetainedReplayAuthority, RetainedReplayGenesisAuthority};
3use coven_protocol::store_commit::{
4 ResolvedStoreDeviceState, StoreAckRef, StoreDeviceRegistrationRef,
5};
6use rusqlite::OptionalExtension;
7
8use super::*;
9
10impl StoreSession<'_> {
11 fn membership_head_cursors(&mut self) -> Result<InitialStoreMembershipAuthority, DbError> {
12 InitialStoreMembershipAuthority::load_on(self.conn)
13 }
14
15 fn persist_membership_head_cursors(
16 &mut self,
17 head_refs: Vec<coven_protocol::membership::MembershipHeadRef>,
18 ) -> Result<(), DbError> {
19 let transaction = self.conn.unchecked_transaction().map_err(DbError::from)?;
20 InitialStoreMembershipAuthority { head_refs }.install_on(&transaction)?;
21 transaction.commit().map_err(DbError::from)
22 }
23
24 fn validated_store_owner(
25 &mut self,
26 expected_root: coven_protocol::store_commit::StoreRootRef,
27 ) -> Result<String, DbError> {
28 let records = crate::store::store_session::StoreRecords::new(self.conn, self.store_dir);
29 let (root, protocol_root) = self
30 .verified_store_authority
31 .root_authority_on(records)?
32 .ok_or(DbError::StoreRootHashMissing)?;
33 if root != expected_root {
34 return Err(DbError::Message(
35 "local Store root differs from the operation authority".to_string(),
36 ));
37 }
38 let owner = get_protocol_state_on(
39 self.conn,
40 coven_protocol::membership::OWNER_PUBKEY_STATE_KEY,
41 )?
42 .ok_or_else(|| DbError::Message("Store owner anchor is absent".to_string()))?;
43 if owner != protocol_root.descriptor.founder_pubkey {
44 return Err(DbError::Message(
45 "Store owner anchor differs from its signed root".to_string(),
46 ));
47 }
48 let baseline = self
49 .verified_store_authority
50 .retained_replay_baseline_on(records)?;
51 let owner_authority = match &baseline.authority {
52 RetainedReplayAuthority::Genesis(authority) => authority.clone(),
53 RetainedReplayAuthority::InstalledSnapshot(authority) => {
54 RetainedReplayGenesisAuthority {
55 store_root: authority.store_root.clone(),
56 founder_registration: authority.founder_registration.clone(),
57 }
58 }
59 };
60 if owner_authority.store_root != root {
61 return Err(DbError::Message(
62 "retained replay baseline belongs to another Store root".to_string(),
63 ));
64 }
65 let founder = self.verified_store_authority.activated_registration_on(
66 records,
67 &root,
68 &owner_authority.founder_registration,
69 )?;
70 let expected_genesis = ResolvedStoreDeviceState::founder(
71 &root,
72 owner_authority.founder_registration.clone(),
73 &protocol_root.descriptor.founder_pubkey,
74 protocol_root.descriptor.founder_grant.clone(),
75 &protocol_root.descriptor.founder_recovery,
76 )
77 .map_err(DbError::from)?;
78 let stored_genesis: ResolvedStoreDeviceState = serde_json::from_str(
79 &required_protocol_state_on(self.conn, STORE_DEVICE_GENESIS_STATE_KEY)?,
80 )
81 .map_err(|error| DbError::context("Store device genesis state", error))?;
82 if founder.author_pubkey != owner || stored_genesis != expected_genesis {
83 return Err(DbError::Message(
84 "Store device genesis differs from installed founder authority".to_string(),
85 ));
86 }
87 self.verified_store_authority
88 .remember_verified_owner_anchor(owner_authority)?;
89 Ok(owner)
90 }
91
92 fn install_store_owner_anchor(
93 &mut self,
94 anchor: crate::StoreOwnerAnchor,
95 membership: InitialStoreMembershipAuthority,
96 ) -> Result<(), DbError> {
97 if self.verified_store_authority.reuses_owner_anchor(&anchor)? {
98 return self.persist_membership_head_cursors(membership.head_refs);
99 }
100 let authority = anchor.authority().clone();
101 let tx = self.conn.unchecked_transaction().map_err(DbError::from)?;
102 let root_value =
103 install_store_root_authority_on(&tx, &authority.store_root, &anchor.root().bytes)?;
104 install_store_founder_state_on(
105 &tx,
106 &authority.store_root,
107 &authority.founder_registration,
108 &anchor.founder().value,
109 &anchor.founder().bytes,
110 anchor.genesis(),
111 )?;
112 set_protocol_state_on(
113 &tx,
114 coven_protocol::membership::OWNER_PUBKEY_STATE_KEY,
115 anchor.owner(),
116 )?;
117 membership.install_on(&tx)?;
118 let baseline = crate::store::store_session::StoreTransaction::new(&tx, self.store_dir)
119 .ensure_founder_replay_baseline(
120 self.schema_version,
121 self.sync_routing_hash,
122 authority.clone(),
123 )?;
124 tx.commit().map_err(DbError::from)?;
125 self.verified_store_authority.commit_installed_owner_anchor(
126 authority,
127 root_value,
128 anchor.founder().value.clone(),
129 baseline,
130 );
131 Ok(())
132 }
133
134 fn local_store_founder_graph(&mut self) -> Result<Option<Box<DurableFounderGraph>>, DbError> {
135 load_local_store_founder_graph_on(self.conn)
136 }
137
138 fn stage_store_founder_graph(
139 &mut self,
140 graph: Box<DurableFounderGraph>,
141 ) -> Result<(), DbError> {
142 let conn = self.conn;
143 let tx = conn.unchecked_transaction().map_err(DbError::from)?;
144 if let Some(existing) = load_local_store_founder_graph_on(&tx)? {
145 existing.validate()?;
146 if founder_graph_identity(&existing) == founder_graph_identity(&graph) {
147 return Ok(());
148 }
149 return Err(DbError::Message(
150 "local Store founder graph already owns different exact objects".to_string(),
151 ));
152 }
153 use coven_protocol::provider::{ExactProbeProgress, ProviderProbeJournalRecord};
154 use coven_protocol::store_creation::{
155 StoreCreationAttempt, STORE_CREATION_ATTEMPT_STATE_KEY,
156 };
157
158 let attempt_json =
159 crate::required_protocol_state_on(&tx, STORE_CREATION_ATTEMPT_STATE_KEY)?;
160 let attempt: StoreCreationAttempt = serde_json::from_str(&attempt_json)
161 .map_err(|error| DbError::context("parse Store creation attempt", error))?;
162 let StoreCreationAttempt::FounderGraphReserved(graph_reservation) = attempt else {
163 return Err(DbError::Message(
164 "Store creation attempt has not reserved the complete founder graph".to_string(),
165 ));
166 };
167 let reservation = &graph_reservation.descriptor;
168 let descriptor = &graph.root.value.descriptor;
169 let founder = &reservation.membership.founder;
170 let authority = &founder.root.authority;
171 if authority.creation_id != descriptor.creation_id
172 || authority.founder_grant != descriptor.founder_grant
173 || authority.provider_admin_grant != descriptor.founder_provider_admin.grant_id
174 || authority.binding.store != descriptor.provider
175 || authority.binding.device != descriptor.founder_provider_admin.provider
176 || authority.founder_pubkey != descriptor.founder_pubkey
177 || authority.schema_version != descriptor.schema_version
178 || authority.sync_routing_hash != descriptor.sync_routing_hash
179 || founder.root.root_slot != descriptor.root_slot
180 || reservation.current_publication_slot != descriptor.current_publication_slot
181 || founder.registration_slot != descriptor.founder_registration
182 || &reservation.recovery_slot != descriptor.founder_recovery.first_slot()
183 || descriptor.founder_membership.first_slot() != &reservation.membership.first_slot
184 {
185 return Err(DbError::Message(
186 "signed Store descriptor differs from its durable creation attempt".to_string(),
187 ));
188 }
189 if graph.registration.value.acknowledgements != graph_reservation.acknowledgements
190 || graph.initial_ack.value.last_sync != authority.founder_timestamp
191 || graph.initial_ack.value.successor.next_slot != graph_reservation.next_ack_slot
192 || graph.membership.entry.value.created_at != authority.founder_timestamp
193 || graph.membership.head.value.body.successor.next_slot
194 != graph_reservation.membership.next_head_slot
195 {
196 return Err(DbError::Message(
197 "signed founder graph differs from its durable slot reservation".to_string(),
198 ));
199 }
200
201 let exact_key = format!(
202 "provider_probe/{}",
203 hex::encode(authority.probes.exact_slots().as_bytes())
204 );
205 let exact_json = crate::required_protocol_state_on(&tx, &exact_key)?;
206 let exact: ProviderProbeJournalRecord = serde_json::from_str(&exact_json)
207 .map_err(|error| DbError::context("parse provider probe journal", error))?;
208 let ProviderProbeJournalRecord::Exact(exact) = exact else {
209 return Err(DbError::Message(
210 "Store creation exact probe id names another probe kind".to_string(),
211 ));
212 };
213 let ExactProbeProgress::ReceiptReady { receipt } = exact.progress else {
214 return Err(DbError::Message(
215 "Store creation exact probe has no terminal receipt".to_string(),
216 ));
217 };
218 if receipt != descriptor.founder_provider_admin.capability.exact_slots {
219 return Err(DbError::Message(
220 "signed Store descriptor differs from its terminal exact probe".to_string(),
221 ));
222 }
223 for key in [exact_key, STORE_CREATION_ATTEMPT_STATE_KEY.to_string()] {
224 let deleted = crate::delete_protocol_state_on(&tx, &key)?;
225 if deleted != 1 {
226 return Err(DbError::Message(
227 "Store creation journal disappeared during typed consumption".to_string(),
228 ));
229 }
230 }
231 tx.execute(
232 "INSERT INTO local_store_protocol_root \
233 (singleton, store_root_hash, store_protocol_root_bytes, prepared_object) \
234 VALUES (1, ?1, ?2, ?3)",
235 rusqlite::params![
236 graph.root.value.object_hash().to_string(),
237 graph.root.bytes,
238 serde_json::to_string(&graph.root.prepared).map_err(|error| {
239 DbError::context("serialize prepared Store root", error)
240 })?,
241 ],
242 )
243 .map_err(DbError::from)?;
244 tx.execute(
245 "INSERT INTO local_store_device_registration \
246 (singleton, device_id, registration_hash, registration_bytes, prepared_object, \
247 initial_ack_ref, initial_ack_bytes, initial_ack_prepared, state) \
248 VALUES (1, ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
249 rusqlite::params![
250 graph.registration.value.device_id.to_string(),
251 graph.registration.value.registration_hash().to_string(),
252 graph.registration.bytes,
253 serde_json::to_string(&graph.registration.prepared).map_err(|error| {
254 DbError::context("serialize prepared founder registration", error)
255 })?,
256 serde_json::to_string(&graph.initial_ack_ref).map_err(|error| {
257 DbError::context("serialize founder initial ack ref", error)
258 })?,
259 graph.initial_ack.bytes,
260 serde_json::to_string(&graph.initial_ack.prepared).map_err(|error| {
261 DbError::context("serialize founder initial ack object", error)
262 })?,
263 serde_json::to_string(&LocalDeviceRegistrationState::Prepared).map_err(
264 |error| DbError::context("serialize registration journal state", error)
265 )?,
266 ],
267 )
268 .map_err(DbError::from)?;
269 tx.execute(
270 "INSERT INTO local_store_founder_graph \
271 (singleton, membership_graph) VALUES (1, ?1)",
272 rusqlite::params![
273 serde_json::to_string(&DurableFounderMembershipJournal::from_graph(
274 &graph.membership,
275 ))
276 .map_err(|error| DbError::context("serialize founder membership graph", error))?,
277 ],
278 )
279 .map_err(DbError::from)?;
280 tx.commit().map_err(DbError::from)
281 }
282
283 fn complete_store_founder_graph(
284 &mut self,
285 expected_root: coven_protocol::store_commit::StoreRootRef,
286 expected_registration: StoreDeviceRegistrationRef,
287 expected_initial_ack: StoreAckRef,
288 expected_membership: FounderMembershipRefs,
289 current_publication: crate::ObservedStorePublication,
290 ) -> Result<(), DbError> {
291 let schema_version = self.schema_version;
292 let routing_hash = self.sync_routing_hash;
293 let verified_authority = &mut *self.verified_store_authority;
294 let tx = self.conn.unchecked_transaction().map_err(DbError::from)?;
295 let store_dir = self.store_dir;
296 let graph = load_local_store_founder_graph_on(&tx)?
297 .ok_or_else(|| DbError::Message("local Store founder graph is absent".to_string()))?;
298 let root = coven_protocol::store_commit::StoreRootRef {
299 store_root_id: graph.root.value.descriptor.store_root_id(),
300 store_root_hash: graph.root.value.object_hash(),
301 object: graph.root.prepared.reference().clone(),
302 };
303 let registration = StoreDeviceRegistrationRef::from_registration(
304 &graph.registration.value,
305 graph.registration.prepared.reference().clone(),
306 );
307 if root != expected_root
308 || registration != expected_registration
309 || graph.initial_ack_ref != expected_initial_ack
310 || graph.membership.entry_ref != expected_membership.entry
311 || graph.membership.head_ref != expected_membership.head
312 {
313 return Err(DbError::Message(
314 "verified founder graph differs from its durable exact references".to_string(),
315 ));
316 }
317 let founder_authority =
318 coven_protocol::store_commit::StoreDeviceRegistrationActivation::Founder {
319 root: root.clone(),
320 };
321 let device_genesis = ResolvedStoreDeviceState::founder(
322 &root,
323 registration.clone(),
324 &graph.root.value.descriptor.founder_pubkey,
325 graph.root.value.descriptor.founder_grant.clone(),
326 &graph.root.value.descriptor.founder_recovery,
327 )
328 .map_err(DbError::from)?;
329 let device_genesis_json = serde_json::to_string(&device_genesis)
330 .map_err(|error| DbError::context("serialize Store device genesis state", error))?;
331 let device_id = registration.device_id.to_string();
332 let registration_hash = registration.registration_hash.to_string();
333 match &graph.registration_state {
334 LocalDeviceRegistrationState::Prepared
335 | LocalDeviceRegistrationState::RegistrationPublished
336 | LocalDeviceRegistrationState::RegistrationActivated { .. } => {
337 return Err(DbError::Message(
338 "founder registration and initial acknowledgement are not exact-created"
339 .to_string(),
340 ));
341 }
342 LocalDeviceRegistrationState::Created => {}
343 LocalDeviceRegistrationState::Activated { authority } => {
344 if authority != &founder_authority {
345 return Err(DbError::Message(
346 "founder registration journal carries another activation authority"
347 .to_string(),
348 ));
349 }
350 let store_transaction =
351 crate::store::store_session::StoreTransaction::new(&tx, store_dir);
352 let installed = store_transaction.root_authority(verified_authority)?;
353 let installed_registration = store_transaction.activated_registration(
354 verified_authority,
355 &root,
356 ®istration,
357 )?;
358 let stored: Option<(String, String, String)> = tx
359 .query_row(
360 "SELECT registration_object, activation_authority, registration_hash \
361 FROM store_device_registration_activations WHERE device_id = ?1",
362 [&device_id],
363 |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
364 )
365 .optional()
366 .map_err(DbError::from)?;
367 let ack: Option<String> = tx
368 .query_row(
369 "SELECT ack_ref FROM published_store_acks WHERE singleton = 1",
370 [],
371 |row| row.get(0),
372 )
373 .optional()
374 .map_err(DbError::from)?;
375 let stored_device_genesis =
376 crate::get_protocol_state_on(&tx, STORE_DEVICE_GENESIS_STATE_KEY)?;
377 if installed
378 .as_ref()
379 .map(|(reference, value)| (reference, value))
380 != Some((&root, &graph.root.value))
381 || installed_registration != graph.registration.value
382 || stored
383 != Some((
384 serde_json::to_string(®istration).map_err(|error| {
385 DbError::context("serialize founder registration ref", error)
386 })?,
387 serde_json::to_string(&founder_authority).map_err(|error| {
388 DbError::context("serialize founder authority", error)
389 })?,
390 registration.registration_hash.to_string(),
391 ))
392 || ack
393 != Some(
394 serde_json::to_string(&graph.initial_ack_ref).map_err(|error| {
395 DbError::context("serialize founder ack ref", error)
396 })?,
397 )
398 || stored_device_genesis.as_deref() != Some(&device_genesis_json)
399 {
400 return Err(DbError::Message(
401 "activated founder journal differs from installed exact authority"
402 .to_string(),
403 ));
404 }
405 let publication =
406 super::observed_store_publication::load_store_current_publication_on(&tx)?;
407 if publication.record() != current_publication.record()
408 || publication.observed_version() != Some(current_publication.version())
409 {
410 return Err(DbError::Message(
411 "activated founder journal differs from its Store publication record"
412 .to_string(),
413 ));
414 }
415 let owner_authority = RetainedReplayGenesisAuthority {
416 store_root: root.clone(),
417 founder_registration: registration.clone(),
418 };
419 let baseline_matches = {
420 let baseline =
421 crate::store::store_session::StoreTransaction::new(&tx, store_dir)
422 .retained_replay_baseline(verified_authority)?;
423 baseline.schema_version == schema_version
424 && baseline.routing_hash == routing_hash
425 && baseline.authority
426 == RetainedReplayAuthority::Genesis(owner_authority.clone())
427 };
428 if !baseline_matches {
429 return Err(DbError::Message(
430 "activated founder state differs from its generation-zero replay baseline"
431 .to_string(),
432 ));
433 }
434 verified_authority.remember_verified_owner_anchor(owner_authority)?;
435 return Ok(());
436 }
437 }
438 let root_value = install_store_root_authority_on(&tx, &root, &graph.root.bytes)?;
439 current_publication
440 .record()
441 .verify_genesis(
442 root.store_root_hash,
443 &graph.root.value.descriptor.founder_pubkey,
444 )
445 .map_err(DbError::from)?;
446 super::observed_store_publication::install_genesis_store_publication_on(
447 &tx,
448 current_publication.record(),
449 current_publication.version(),
450 )?;
451 let activation = serde_json::to_string(
452 &coven_protocol::store_commit::StoreDeviceRegistrationActivation::Founder {
453 root: root.clone(),
454 },
455 )
456 .map_err(|error| DbError::context("serialize founder registration activation", error))?;
457 let journal_state = serde_json::to_string(&LocalDeviceRegistrationState::Activated {
458 authority: founder_authority,
459 })
460 .map_err(|error| DbError::context("serialize founder registration journal", error))?;
461 let updated = tx
462 .execute(
463 "UPDATE local_store_device_registration SET state = ?1 \
464 WHERE singleton = 1 AND device_id = ?2 AND registration_hash = ?3 \
465 AND initial_ack_ref = ?4 AND state = ?5",
466 rusqlite::params![
467 journal_state,
468 &device_id,
469 ®istration_hash,
470 serde_json::to_string(&graph.initial_ack_ref).map_err(|error| {
471 DbError::context("serialize founder initial ack ref", error)
472 })?,
473 serde_json::to_string(&LocalDeviceRegistrationState::Created).map_err(
474 |error| DbError::context("serialize created journal state", error)
475 )?,
476 ],
477 )
478 .map_err(DbError::from)?;
479 if updated != 1 {
480 return Err(DbError::Message(
481 "founder registration journal did not activate".to_string(),
482 ));
483 }
484 tx.execute(
485 "INSERT INTO store_device_registration_activations \
486 (device_id, registration_hash, author_pubkey, device_signing_pubkey, \
487 registration_bytes, registration_object, activation_authority) \
488 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
489 rusqlite::params![
490 &device_id,
491 ®istration_hash,
492 graph.registration.value.author_pubkey,
493 graph.registration.value.device_signing_pubkey,
494 graph.registration.bytes,
495 serde_json::to_string(®istration).map_err(|error| {
496 DbError::context("serialize founder registration ref", error)
497 })?,
498 activation,
499 ],
500 )
501 .map_err(DbError::from)?;
502 tx.execute(
503 "INSERT INTO published_store_acks \
504 (singleton, ack_ref, successor_slot) VALUES (1, ?1, ?2)",
505 rusqlite::params![
506 serde_json::to_string(&graph.initial_ack_ref).map_err(|error| {
507 DbError::context("serialize founder initial ack ref", error)
508 })?,
509 serde_json::to_string(&graph.initial_ack.value.successor.next_slot)
510 .map_err(|error| DbError::context("serialize founder ack successor", error))?,
511 ],
512 )
513 .map_err(DbError::from)?;
514 for (key, value) in [
515 (LOCAL_DEVICE_ID_STATE_KEY, device_id),
516 (STORE_DEVICE_GENESIS_STATE_KEY, device_genesis_json),
517 ] {
518 crate::set_protocol_state_on(&tx, key, &value)?;
519 }
520 crate::set_protocol_state_on(
521 &tx,
522 coven_protocol::membership::OWNER_PUBKEY_STATE_KEY,
523 &graph.root.value.descriptor.founder_pubkey,
524 )?;
525 crate::InitialStoreMembershipAuthority {
526 head_refs: vec![graph.membership.head_ref.clone()],
527 }
528 .install_on(&tx)?;
529 let baseline = crate::store::store_session::StoreTransaction::new(&tx, store_dir)
530 .install_generation_zero_replay_baseline(
531 schema_version,
532 routing_hash,
533 RetainedReplayGenesisAuthority {
534 store_root: root.clone(),
535 founder_registration: registration.clone(),
536 },
537 )?;
538 tx.commit().map_err(DbError::from)?;
539 verified_authority.commit_installed_owner_anchor(
540 RetainedReplayGenesisAuthority {
541 store_root: root,
542 founder_registration: registration,
543 },
544 root_value,
545 graph.registration.value,
546 baseline,
547 );
548 Ok(())
549 }
550}
551
552impl StoreDatabase {
553 pub async fn membership_head_cursors(
554 &self,
555 ) -> Result<crate::InitialStoreMembershipAuthority, DbError> {
556 self.call_store(|session| session.membership_head_cursors())
557 .await
558 }
559
560 pub async fn persist_membership_head_cursors(
561 &self,
562 head_refs: Vec<coven_protocol::membership::MembershipHeadRef>,
563 ) -> Result<(), DbError> {
564 self.call_store(move |session| session.persist_membership_head_cursors(head_refs))
565 .await
566 }
567
568 pub async fn local_store_root_ref(
569 &self,
570 ) -> Result<Option<coven_protocol::store_commit::StoreRootRef>, DbError> {
571 self.call_store(|session| {
572 session
573 .root_authority()
574 .map(|authority| authority.map(|(reference, _)| reference))
575 })
576 .await
577 }
578
579 pub async fn validated_store_owner(
580 &self,
581 expected_root: &coven_protocol::store_commit::StoreRootRef,
582 ) -> Result<String, DbError> {
583 let expected_root = expected_root.clone();
584 self.call_store(move |session| session.validated_store_owner(expected_root))
585 .await
586 }
587
588 pub async fn install_store_owner_anchor(
589 &self,
590 anchor: crate::StoreOwnerAnchor,
591 membership: InitialStoreMembershipAuthority,
592 ) -> Result<(), DbError> {
593 self.call_store(move |session| session.install_store_owner_anchor(anchor, membership))
594 .await
595 }
596
597 pub async fn local_store_founder_graph(
598 &self,
599 ) -> Result<Option<Box<DurableFounderGraph>>, DbError> {
600 self.call_store(|session| session.local_store_founder_graph())
601 .await
602 }
603
604 pub async fn stage_store_founder_graph(
605 &self,
606 graph: Box<DurableFounderGraph>,
607 ) -> Result<(), DbError> {
608 graph.validate()?;
609 self.call_store(move |session| session.stage_store_founder_graph(graph))
610 .await
611 }
612
613 pub async fn complete_store_founder_graph(
614 &self,
615 expected_root: coven_protocol::store_commit::StoreRootRef,
616 expected_registration: StoreDeviceRegistrationRef,
617 expected_initial_ack: StoreAckRef,
618 expected_membership: FounderMembershipRefs,
619 current_publication: crate::ObservedStorePublication,
620 ) -> Result<(), DbError> {
621 self.call_store(move |session| {
622 session.complete_store_founder_graph(
623 expected_root,
624 expected_registration,
625 expected_initial_ack,
626 expected_membership,
627 current_publication,
628 )
629 })
630 .await
631 }
632}