Skip to main content

coven_protocol/
circle_activation.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::circle::{
6    AccessEnvelope, CircleAccessDisposition, CircleAccessLeaf, CircleBootstrapRef, CircleControl,
7    CircleControlCoord, CircleEpochCloseId, CircleId, CircleMetadata, PreparedAccessLeaf,
8    PreparedCircleAccess, PreparedCircleControl,
9};
10use crate::circle_roster::CircleMaterializedRoster;
11use crate::store_commit::{
12    CandidateFamilyId, CircleAccessObjectRef, CircleControlRef, CirclePackageRef, ObjectHash,
13    StoreBatchCommit, StoreBatchCommitRef, StoreDeviceRegistration, StoreDeviceRegistrationRef,
14    StreamActivation, StreamActivationId, VerifiedStoreBatchCommit,
15};
16use coven_keys::encryption::{EncryptionService, KeyFingerprint, MasterKeyring};
17
18/// The local device's own exclusion from a Circle epoch close, derived strictly
19/// from the verified successor outcome at materialization. It records the exact
20/// close and successor an excluded device must reset from. Never derived from
21/// unverified storage.
22/// Verified Circle activation state that contradicts itself, its control, or
23/// the commit that carries it. Produced by the activation values' own
24/// validation; workflow errors wrap it at the operation boundary.
25#[derive(Debug, thiserror::Error)]
26pub enum CircleStateError {
27    #[error("invalid Circle state: {0}")]
28    Invariant(String),
29    #[error("invalid Circle state protocol: {0}")]
30    Protocol(#[from] crate::store_commit::StoreProtocolError),
31    #[error("{operation}: {source}")]
32    Json {
33        operation: &'static str,
34        #[source]
35        source: serde_json::Error,
36    },
37    #[error("{operation} for Circle {circle_id}: {source}")]
38    Encryption {
39        operation: &'static str,
40        circle_id: crate::circle::CircleId,
41        #[source]
42        source: coven_keys::encryption::EncryptionError,
43    },
44}
45
46mod access;
47mod activations;
48mod current_state;
49
50pub use access::{
51    CircleEpochAccess, LocalCircleExclusion, VerifiedCircleAccess, VerifiedCircleActive,
52    VerifiedCircleImage, VerifiedCircleReference,
53};
54pub use activations::{
55    VerifiedCircleActivations, VerifiedStreamActivationPrefix, VerifiedStreamActivations,
56};
57#[cfg(any(test, feature = "test-utils"))]
58pub use current_state::CircleCurrentControl;
59pub use current_state::{CircleAuthoringState, CircleCurrentState};
60
61pub fn verify_control_context_for_verified_commit(
62    reference: &CircleControlRef,
63    control: &PreparedCircleControl,
64    verified: &VerifiedStoreBatchCommit,
65) -> Result<(), CircleStateError> {
66    verified.reference().verify_commit(verified.value())?;
67    let commit = verified.value();
68    let author = verified.author();
69    let device_matches = control.value.value.order.device_id == author.device_id.to_string();
70    if !control.verify()
71        || reference.circle_id() != control.value.circle_id
72        || reference.control() != &control.coord
73        || control.value.store_root_hash != commit.store_root_hash
74        || control.value.author_pubkey != author.author_pubkey
75        || !device_matches
76    {
77        return Err(CircleStateError::Invariant(
78            "circle control context differs from its Store reference and commit".to_string(),
79        ));
80    }
81    Ok(())
82}
83
84#[cfg(test)]
85mod derived_state_tests;