Skip to main content

coven_protocol/store_commit/batch_commit/
authoring.rs

1use super::validation::*;
2use super::*;
3
4impl StoreBatchCommit {
5    #[allow(clippy::too_many_arguments)]
6    pub fn signed_reclaim_authorization(
7        store_root_hash: ObjectHash,
8        write_id: WriteId,
9        coord: StoreCommitCoord,
10        author_registration: StoreDeviceRegistrationRef,
11        author: &StoreDeviceRegistration,
12        order: StoreCommitOrder,
13        publication_base: StorePublicationBase,
14        membership_state: StoreMembershipStateRef,
15        device_state: StoreDeviceStateRef,
16        authorization: crate::reclaim::ReclaimAuthorizationRef,
17        signer: &UserKeypair,
18    ) -> Result<Self, StoreProtocolError> {
19        validate_commit_envelope(
20            store_root_hash,
21            &coord,
22            &author_registration,
23            author,
24            &order,
25            &membership_state,
26            &device_state,
27            None,
28            signer,
29        )?;
30        Self::finish_signed_body(
31            store_root_hash,
32            write_id,
33            author_registration,
34            order,
35            publication_base,
36            membership_state,
37            device_state,
38            None,
39            StoreCommitBody::ReclaimAuthorization {
40                authorization: Box::new(authorization),
41            },
42            signer,
43        )
44    }
45
46    #[allow(clippy::too_many_arguments)]
47    pub fn signed_reclaim_receipt(
48        store_root_hash: ObjectHash,
49        write_id: WriteId,
50        coord: StoreCommitCoord,
51        author_registration: StoreDeviceRegistrationRef,
52        author: &StoreDeviceRegistration,
53        order: StoreCommitOrder,
54        publication_base: StorePublicationBase,
55        membership_state: StoreMembershipStateRef,
56        device_state: StoreDeviceStateRef,
57        receipt: crate::reclaim::ReclaimReceiptRef,
58        signer: &UserKeypair,
59    ) -> Result<Self, StoreProtocolError> {
60        validate_commit_envelope(
61            store_root_hash,
62            &coord,
63            &author_registration,
64            author,
65            &order,
66            &membership_state,
67            &device_state,
68            None,
69            signer,
70        )?;
71        Self::finish_signed_body(
72            store_root_hash,
73            write_id,
74            author_registration,
75            order,
76            publication_base,
77            membership_state,
78            device_state,
79            None,
80            StoreCommitBody::ReclaimReceipt {
81                receipt: Box::new(receipt),
82            },
83            signer,
84        )
85    }
86
87    pub fn merge_dependencies(&self) -> &BTreeMap<AuthorStreamId, StoreBatchCommitRef> {
88        &self.order.dependencies
89    }
90
91    #[allow(clippy::too_many_arguments)]
92    pub fn signed_with_owner_promotion_request(
93        store_root_hash: ObjectHash,
94        write_id: WriteId,
95        coord: StoreCommitCoord,
96        author_registration: StoreDeviceRegistrationRef,
97        author: &StoreDeviceRegistration,
98        order: StoreCommitOrder,
99        publication_base: StorePublicationBase,
100        membership_state: StoreMembershipStateRef,
101        device_state: StoreDeviceStateRef,
102        membership_authority: MembershipCoord,
103        request: OwnerPromotionRequest,
104        signer: &UserKeypair,
105    ) -> Result<Self, StoreProtocolError> {
106        validate_commit_envelope(
107            store_root_hash,
108            &coord,
109            &author_registration,
110            author,
111            &order,
112            &membership_state,
113            &device_state,
114            Some(&membership_authority),
115            signer,
116        )?;
117        validate_owner_promotion_request_for_commit(
118            &request,
119            store_root_hash,
120            &author_registration,
121            author,
122            &membership_state,
123            &device_state,
124        )?;
125        Self::finish_signed_body(
126            store_root_hash,
127            write_id,
128            author_registration,
129            order,
130            publication_base,
131            membership_state,
132            device_state,
133            Some(membership_authority),
134            StoreCommitBody::OwnerPromotionRequest {
135                request: Box::new(request),
136            },
137            signer,
138        )
139    }
140
141    #[allow(clippy::too_many_arguments)]
142    pub fn signed_with_candidate_abandonment(
143        store_root_hash: ObjectHash,
144        write_id: WriteId,
145        coord: StoreCommitCoord,
146        author_registration: StoreDeviceRegistrationRef,
147        author: &StoreDeviceRegistration,
148        order: StoreCommitOrder,
149        publication_base: StorePublicationBase,
150        membership_state: StoreMembershipStateRef,
151        device_state: StoreDeviceStateRef,
152        mut manifests: Vec<CandidateCleanupManifest>,
153        signer: &UserKeypair,
154    ) -> Result<Self, StoreProtocolError> {
155        validate_commit_envelope(
156            store_root_hash,
157            &coord,
158            &author_registration,
159            author,
160            &order,
161            &membership_state,
162            &device_state,
163            None,
164            signer,
165        )?;
166        manifests.sort();
167        validate_candidate_abandonment(
168            &manifests,
169            store_root_hash,
170            &author_registration,
171            &coord,
172            &order,
173            author,
174        )?;
175        Self::finish_signed_body(
176            store_root_hash,
177            write_id,
178            author_registration,
179            order,
180            publication_base,
181            membership_state,
182            device_state,
183            None,
184            StoreCommitBody::AbandonCandidates { manifests },
185            signer,
186        )
187    }
188
189    #[allow(clippy::too_many_arguments)]
190    pub fn signed_operations(
191        store_root_hash: ObjectHash,
192        write_id: WriteId,
193        coord: StoreCommitCoord,
194        author_registration: StoreDeviceRegistrationRef,
195        author: &StoreDeviceRegistration,
196        order: StoreCommitOrder,
197        publication_base: StorePublicationBase,
198        membership_state: StoreMembershipStateRef,
199        device_state: StoreDeviceStateRef,
200        membership_authority: MembershipCoord,
201        input: StoreCommitOperationsInput<'_>,
202        signer: &UserKeypair,
203    ) -> Result<Self, StoreProtocolError> {
204        validate_commit_envelope(
205            store_root_hash,
206            &coord,
207            &author_registration,
208            author,
209            &order,
210            &membership_state,
211            &device_state,
212            Some(&membership_authority),
213            signer,
214        )?;
215        let StoreCommitOperationsInput {
216            acknowledgement,
217            circle_acknowledgements,
218            control,
219            device_join_attempt_decisions,
220            provider_access_grants,
221            device_registrations,
222            device_exclusion_proposals,
223            device_exclusion_outcomes,
224            stream_activations,
225            circle_controls,
226            store_package,
227            circle_packages,
228        } = input;
229        validate_control(
230            &author_registration,
231            &author.author_pubkey,
232            &membership_state,
233            control.as_ref(),
234        )?;
235        validate_commit_acknowledgement(&acknowledgement, &author_registration)?;
236        validate_commit_circle_acknowledgements(&circle_acknowledgements, &author_registration)?;
237        let stream_id = commit_stream_id(&coord);
238        let seq = order.seq();
239        let candidate_family =
240            CandidateFamilyId::derive(store_root_hash, &author_registration, &write_id, &order);
241        let store_package = store_package
242            .map(|input| {
243                if input.candidate_family != candidate_family {
244                    return Err(StoreProtocolError::Malformed(
245                        "Store package candidate family differs from its commit".to_string(),
246                    ));
247                }
248                let semantic_prefix = package_semantic_prefix(
249                    candidate_family,
250                    &stream_id,
251                    seq,
252                    ObjectHash::digest(input.bytes),
253                );
254                package_ref(&semantic_prefix, &input)
255            })
256            .transpose()?;
257        validate_device_join_attempt_decision_refs(&device_join_attempt_decisions)?;
258        validate_provider_access_refs(&provider_access_grants)?;
259        validate_device_registration_refs(&device_registrations)?;
260        validate_device_exclusion_refs(&device_exclusion_proposals, &device_exclusion_outcomes)?;
261        validate_stream_activations(
262            store_root_hash,
263            &author_registration,
264            control.as_ref(),
265            &stream_activations,
266        )?;
267        let mut seen_circles = BTreeSet::new();
268        let circle_packages = circle_packages
269            .iter()
270            .map(|input| {
271                if !seen_circles.insert(input.circle_id) {
272                    return Err(StoreProtocolError::DuplicateCirclePackage(input.circle_id));
273                }
274                validate_circle_control_coord(&input.control)?;
275                if input.package.candidate_family != candidate_family {
276                    return Err(StoreProtocolError::Malformed(
277                        "Circle package candidate family differs from its commit".to_string(),
278                    ));
279                }
280                let semantic_prefix = circle_package_semantic_prefix(
281                    input.circle_id,
282                    candidate_family,
283                    &stream_id,
284                    seq,
285                    ObjectHash::digest(input.package.bytes),
286                );
287                let package = package_ref(&semantic_prefix, &input.package)?;
288                Ok(CirclePackageRef {
289                    circle_id: input.circle_id,
290                    control: input.control.clone(),
291                    package,
292                    key_fingerprint: input.key_fingerprint,
293                })
294            })
295            .collect::<Result<Vec<_>, StoreProtocolError>>()?;
296        validate_circle_control_refs(&circle_controls)?;
297        let operations = StoreCommitOperations {
298            acknowledgement,
299            circle_acknowledgements,
300            control,
301            device_join_attempt_decisions,
302            provider_access_grants,
303            device_registrations,
304            device_exclusion_proposals,
305            device_exclusion_outcomes,
306            stream_activations,
307            circle_controls,
308            store_package,
309            circle_packages,
310        };
311        if operations.is_empty() {
312            return Err(StoreProtocolError::EmptyBatch);
313        }
314        Self::finish_signed_body(
315            store_root_hash,
316            write_id,
317            author_registration,
318            order,
319            publication_base,
320            membership_state,
321            device_state,
322            Some(membership_authority),
323            StoreCommitBody::Operations(operations),
324            signer,
325        )
326    }
327
328    #[allow(clippy::too_many_arguments)]
329    fn finish_signed_body(
330        store_root_hash: ObjectHash,
331        write_id: WriteId,
332        author_registration: StoreDeviceRegistrationRef,
333        order: StoreCommitOrder,
334        publication_base: StorePublicationBase,
335        membership_state: StoreMembershipStateRef,
336        device_state: StoreDeviceStateRef,
337        membership_authority: Option<MembershipCoord>,
338        body: StoreCommitBody,
339        signer: &UserKeypair,
340    ) -> Result<Self, StoreProtocolError> {
341        publication_base.validate_for_store(store_root_hash)?;
342        let family =
343            CandidateFamilyId::derive(store_root_hash, &author_registration, &write_id, &order);
344        validate_commit_body(store_root_hash, &body, &author_registration)?;
345        let candidate_objects = candidate_manifest(family, &body)?;
346        Ok(Signed::sign(
347            StoreBatchCommitBody {
348                store_root_hash,
349                write_id,
350                author_registration,
351                order,
352                publication_base,
353                membership_state,
354                device_state,
355                membership_authority,
356                candidate_objects,
357                body,
358            },
359            signer,
360        ))
361    }
362}