Skip to main content

coven_protocol/store_commit/
batch_commit.rs

1use super::identifiers::commit_stream_id;
2use super::operation_refs::{
3    validate_commit_acknowledgement, validate_commit_circle_acknowledgements,
4    validate_device_exclusion_refs, validate_device_join_attempt_decision_refs,
5    validate_device_registration_refs, validate_provider_access_refs,
6};
7use super::validation::{
8    validate_commit_order, validate_commit_predecessor_states, validate_membership_authority,
9    validate_operation_membership_authority,
10};
11use super::*;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(deny_unknown_fields)]
15pub struct StoreBatchCommitBody {
16    pub store_root_hash: ObjectHash,
17    pub write_id: WriteId,
18    pub author_registration: StoreDeviceRegistrationRef,
19    pub order: StoreCommitOrder,
20    pub publication_base: StorePublicationBase,
21    pub membership_state: StoreMembershipStateRef,
22    pub device_state: StoreDeviceStateRef,
23    pub membership_authority: Option<MembershipGrantCreationAuthority>,
24    pub candidate_objects: CandidateObjectManifest,
25    pub body: StoreCommitBody,
26}
27
28impl SignedBody for StoreBatchCommitBody {
29    const DOMAIN: &'static [u8] = COMMIT_DOMAIN;
30}
31
32pub type StoreBatchCommit = Signed<StoreBatchCommitBody>;
33
34mod authoring;
35mod validation;
36pub(super) use validation::candidate_manifest;
37#[cfg(test)]
38pub(super) use validation::validate_stream_activations;
39
40impl StoreBatchCommit {
41    pub(crate) fn verified_candidate_objects(
42        &self,
43    ) -> Result<&CandidateObjectManifest, StoreProtocolError> {
44        let expected = candidate_manifest(self.candidate_family(), &self.body)?;
45        if self.candidate_objects != expected {
46            return Err(StoreProtocolError::Malformed(
47                "candidate object manifest differs from exact commit body graph".to_string(),
48            ));
49        }
50        Ok(&self.candidate_objects)
51    }
52
53    pub fn seq(&self) -> u64 {
54        self.order.seq()
55    }
56
57    pub fn publication_base(&self) -> &StorePublicationBase {
58        &self.publication_base
59    }
60
61    pub fn candidate_family(&self) -> CandidateFamilyId {
62        CandidateFamilyId::derive(
63            self.store_root_hash,
64            &self.author_registration,
65            &self.write_id,
66            &self.order,
67        )
68    }
69
70    pub fn operations(&self) -> Option<&StoreCommitOperations> {
71        match &self.body {
72            StoreCommitBody::Operations(operations) => Some(operations),
73            StoreCommitBody::ReclaimAuthorization { .. }
74            | StoreCommitBody::ReclaimReceipt { .. }
75            | StoreCommitBody::OwnerPromotionRequest { .. }
76            | StoreCommitBody::AbandonCandidates { .. } => None,
77        }
78    }
79
80    pub fn control(&self) -> Option<&StoreControl> {
81        self.operations()
82            .and_then(|operations| operations.control.as_ref())
83    }
84
85    pub fn acknowledgement(&self) -> Option<&StoreAckRef> {
86        self.operations()
87            .and_then(|operations| operations.acknowledgement.as_ref())
88    }
89
90    pub fn circle_acknowledgements(&self) -> &[CircleAckRef] {
91        self.operations().map_or(&[], |operations| {
92            operations.circle_acknowledgements.as_slice()
93        })
94    }
95
96    pub fn retained_operation_objects(&self) -> Result<Vec<ExactObjectRef>, StoreProtocolError> {
97        let objects = self
98            .acknowledgement()
99            .map(|reference| reference.object.clone())
100            .into_iter()
101            .chain(
102                self.circle_acknowledgements()
103                    .iter()
104                    .map(|reference| reference.object.clone()),
105            )
106            .chain(
107                self.device_exclusion_proposals()
108                    .iter()
109                    .map(|reference| reference.object.clone()),
110            )
111            .chain(
112                self.device_exclusion_outcomes()
113                    .iter()
114                    .map(|reference| reference.object().clone()),
115            )
116            .chain(
117                self.reclaim_authorization()
118                    .into_iter()
119                    .flat_map(|reference| {
120                        [reference.evidence.object.clone(), reference.object.clone()]
121                    }),
122            )
123            .chain(
124                self.reclaim_receipt()
125                    .map(|reference| reference.object.clone()),
126            )
127            .collect::<Vec<_>>();
128        if objects
129            .iter()
130            .collect::<std::collections::BTreeSet<_>>()
131            .len()
132            != objects.len()
133        {
134            return Err(StoreProtocolError::Malformed(
135                "Store operation publication repeats a retained authority object".to_string(),
136            ));
137        }
138        Ok(objects)
139    }
140
141    pub fn abandoned_candidates(&self) -> &[CandidateCleanupManifest] {
142        match &self.body {
143            StoreCommitBody::AbandonCandidates { manifests } => manifests,
144            StoreCommitBody::Operations(_)
145            | StoreCommitBody::ReclaimAuthorization { .. }
146            | StoreCommitBody::ReclaimReceipt { .. }
147            | StoreCommitBody::OwnerPromotionRequest { .. } => &[],
148        }
149    }
150
151    pub fn reclaim_authorization(&self) -> Option<&crate::reclaim::ReclaimAuthorizationRef> {
152        match &self.body {
153            StoreCommitBody::ReclaimAuthorization { authorization } => Some(authorization.as_ref()),
154            StoreCommitBody::Operations(_)
155            | StoreCommitBody::ReclaimReceipt { .. }
156            | StoreCommitBody::OwnerPromotionRequest { .. }
157            | StoreCommitBody::AbandonCandidates { .. } => None,
158        }
159    }
160
161    pub fn reclaim_receipt(&self) -> Option<&crate::reclaim::ReclaimReceiptRef> {
162        match &self.body {
163            StoreCommitBody::ReclaimReceipt { receipt } => Some(receipt.as_ref()),
164            StoreCommitBody::Operations(_)
165            | StoreCommitBody::ReclaimAuthorization { .. }
166            | StoreCommitBody::OwnerPromotionRequest { .. }
167            | StoreCommitBody::AbandonCandidates { .. } => None,
168        }
169    }
170
171    pub fn device_join_attempt_decisions(&self) -> &[DeviceJoinAttemptDecisionRef] {
172        self.operations().map_or(&[], |operations| {
173            operations.device_join_attempt_decisions.as_slice()
174        })
175    }
176
177    pub fn provider_access_grants(&self) -> &[crate::provider::StoreMemberProviderAccessGrantRef] {
178        self.operations().map_or(&[], |operations| {
179            operations.provider_access_grants.as_slice()
180        })
181    }
182
183    pub fn device_registrations(&self) -> &[ActivatedStoreDeviceRegistrationRef] {
184        match &self.body {
185            StoreCommitBody::Operations(operations) => operations.device_registrations.as_slice(),
186            StoreCommitBody::ReclaimAuthorization { .. }
187            | StoreCommitBody::ReclaimReceipt { .. }
188            | StoreCommitBody::OwnerPromotionRequest { .. } => &[],
189            StoreCommitBody::AbandonCandidates { .. } => &[],
190        }
191    }
192
193    pub fn device_exclusion_proposals(&self) -> &[StoreDeviceExclusionProposalRef] {
194        self.operations().map_or(&[], |operations| {
195            operations.device_exclusion_proposals.as_slice()
196        })
197    }
198
199    pub fn device_exclusion_outcomes(&self) -> &[StoreDeviceExclusionOutcomeRef] {
200        self.operations().map_or(&[], |operations| {
201            operations.device_exclusion_outcomes.as_slice()
202        })
203    }
204
205    pub fn stream_activations(&self) -> &[StreamActivation] {
206        self.operations()
207            .map_or(&[], |operations| operations.stream_activations.as_slice())
208    }
209
210    pub fn owner_promotion_request(&self) -> Option<&OwnerPromotionRequest> {
211        match &self.body {
212            StoreCommitBody::OwnerPromotionRequest { request } => Some(request),
213            StoreCommitBody::Operations(_)
214            | StoreCommitBody::ReclaimAuthorization { .. }
215            | StoreCommitBody::ReclaimReceipt { .. }
216            | StoreCommitBody::AbandonCandidates { .. } => None,
217        }
218    }
219
220    pub fn circle_controls(&self) -> &[CircleControlRef] {
221        self.operations()
222            .map_or(&[], |operations| operations.circle_controls.as_slice())
223    }
224
225    pub fn store_package(&self) -> Option<&StorePackageRef> {
226        self.operations()
227            .and_then(|operations| operations.store_package.as_ref())
228    }
229
230    pub fn circle_packages(&self) -> &[CirclePackageRef] {
231        self.operations()
232            .map_or(&[], |operations| operations.circle_packages.as_slice())
233    }
234}