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: StoreOperationMembershipAuthority,
103        request: OwnerPromotionRequest,
104        signer: &UserKeypair,
105    ) -> Result<Self, StoreProtocolError> {
106        let membership_authority = membership_authority.into_commit_authority();
107        validate_commit_envelope(
108            store_root_hash,
109            &coord,
110            &author_registration,
111            author,
112            &order,
113            &membership_state,
114            &device_state,
115            Some(&membership_authority),
116            signer,
117        )?;
118        validate_owner_promotion_request_for_commit(
119            &request,
120            store_root_hash,
121            &author_registration,
122            author,
123            &membership_state,
124            &device_state,
125        )?;
126        Self::finish_signed_body(
127            store_root_hash,
128            write_id,
129            author_registration,
130            order,
131            publication_base,
132            membership_state,
133            device_state,
134            Some(membership_authority),
135            StoreCommitBody::OwnerPromotionRequest {
136                request: Box::new(request),
137            },
138            signer,
139        )
140    }
141
142    #[allow(clippy::too_many_arguments)]
143    pub fn signed_with_candidate_abandonment(
144        store_root_hash: ObjectHash,
145        write_id: WriteId,
146        coord: StoreCommitCoord,
147        author_registration: StoreDeviceRegistrationRef,
148        author: &StoreDeviceRegistration,
149        order: StoreCommitOrder,
150        publication_base: StorePublicationBase,
151        membership_state: StoreMembershipStateRef,
152        device_state: StoreDeviceStateRef,
153        mut manifests: Vec<CandidateCleanupManifest>,
154        signer: &UserKeypair,
155    ) -> Result<Self, StoreProtocolError> {
156        validate_commit_envelope(
157            store_root_hash,
158            &coord,
159            &author_registration,
160            author,
161            &order,
162            &membership_state,
163            &device_state,
164            None,
165            signer,
166        )?;
167        manifests.sort();
168        validate_candidate_abandonment(
169            &manifests,
170            store_root_hash,
171            &author_registration,
172            &coord,
173            &order,
174            author,
175        )?;
176        Self::finish_signed_body(
177            store_root_hash,
178            write_id,
179            author_registration,
180            order,
181            publication_base,
182            membership_state,
183            device_state,
184            None,
185            StoreCommitBody::AbandonCandidates { manifests },
186            signer,
187        )
188    }
189
190    #[allow(clippy::too_many_arguments)]
191    pub fn signed_operations(
192        store_root_hash: ObjectHash,
193        write_id: WriteId,
194        coord: StoreCommitCoord,
195        author_registration: StoreDeviceRegistrationRef,
196        author: &StoreDeviceRegistration,
197        order: StoreCommitOrder,
198        publication_base: StorePublicationBase,
199        membership_state: StoreMembershipStateRef,
200        device_state: StoreDeviceStateRef,
201        membership_authority: StoreOperationMembershipAuthority,
202        input: StoreCommitOperationsInput<'_>,
203        signer: &UserKeypair,
204    ) -> Result<Self, StoreProtocolError> {
205        let membership_authority = membership_authority.into_commit_authority();
206        validate_commit_envelope(
207            store_root_hash,
208            &coord,
209            &author_registration,
210            author,
211            &order,
212            &membership_state,
213            &device_state,
214            Some(&membership_authority),
215            signer,
216        )?;
217        let StoreCommitOperationsInput {
218            acknowledgement,
219            circle_acknowledgements,
220            control,
221            device_join_attempt_decisions,
222            provider_access_grants,
223            device_registrations,
224            device_exclusion_proposals,
225            device_exclusion_outcomes,
226            stream_activations,
227            circle_controls,
228            store_package,
229            circle_packages,
230        } = input;
231        validate_control(
232            &author_registration,
233            &author.author_pubkey,
234            &membership_state,
235            control.as_ref(),
236        )?;
237        validate_commit_acknowledgement(&acknowledgement, &author_registration)?;
238        validate_commit_circle_acknowledgements(&circle_acknowledgements, &author_registration)?;
239        let stream_id = commit_stream_id(&coord);
240        let seq = order.seq();
241        let candidate_family =
242            CandidateFamilyId::derive(store_root_hash, &author_registration, &write_id, &order);
243        let store_package = store_package
244            .map(|input| {
245                if input.candidate_family != candidate_family {
246                    return Err(StoreProtocolError::Malformed(
247                        "Store package candidate family differs from its commit".to_string(),
248                    ));
249                }
250                let semantic_prefix = package_semantic_prefix(
251                    candidate_family,
252                    &stream_id,
253                    seq,
254                    ObjectHash::digest(input.bytes),
255                );
256                package_ref(&semantic_prefix, &input)
257            })
258            .transpose()?;
259        validate_device_join_attempt_decision_refs(&device_join_attempt_decisions)?;
260        validate_provider_access_refs(&provider_access_grants)?;
261        validate_device_registration_refs(&device_registrations)?;
262        validate_device_exclusion_refs(&device_exclusion_proposals, &device_exclusion_outcomes)?;
263        validate_stream_activations(
264            store_root_hash,
265            &author_registration,
266            control.as_ref(),
267            &stream_activations,
268        )?;
269        let mut seen_circles = BTreeSet::new();
270        let circle_packages = circle_packages
271            .iter()
272            .map(|input| {
273                if !seen_circles.insert(input.circle_id) {
274                    return Err(StoreProtocolError::DuplicateCirclePackage(input.circle_id));
275                }
276                validate_circle_control_coord(&input.control)?;
277                if input.package.candidate_family != candidate_family {
278                    return Err(StoreProtocolError::Malformed(
279                        "Circle package candidate family differs from its commit".to_string(),
280                    ));
281                }
282                let semantic_prefix = circle_package_semantic_prefix(
283                    input.circle_id,
284                    candidate_family,
285                    &stream_id,
286                    seq,
287                    ObjectHash::digest(input.package.bytes),
288                );
289                let package = package_ref(&semantic_prefix, &input.package)?;
290                Ok(CirclePackageRef {
291                    circle_id: input.circle_id,
292                    control: input.control.clone(),
293                    package,
294                    key_fingerprint: input.key_fingerprint,
295                })
296            })
297            .collect::<Result<Vec<_>, StoreProtocolError>>()?;
298        validate_circle_control_refs(&circle_controls)?;
299        let operations = StoreCommitOperations {
300            acknowledgement,
301            circle_acknowledgements,
302            control,
303            device_join_attempt_decisions,
304            provider_access_grants,
305            device_registrations,
306            device_exclusion_proposals,
307            device_exclusion_outcomes,
308            stream_activations,
309            circle_controls,
310            store_package,
311            circle_packages,
312        };
313        if operations.is_empty() {
314            return Err(StoreProtocolError::EmptyBatch);
315        }
316        Self::finish_signed_body(
317            store_root_hash,
318            write_id,
319            author_registration,
320            order,
321            publication_base,
322            membership_state,
323            device_state,
324            Some(membership_authority),
325            StoreCommitBody::Operations(operations),
326            signer,
327        )
328    }
329
330    #[allow(clippy::too_many_arguments)]
331    fn finish_signed_body(
332        store_root_hash: ObjectHash,
333        write_id: WriteId,
334        author_registration: StoreDeviceRegistrationRef,
335        order: StoreCommitOrder,
336        publication_base: StorePublicationBase,
337        membership_state: StoreMembershipStateRef,
338        device_state: StoreDeviceStateRef,
339        membership_authority: Option<MembershipGrantCreationAuthority>,
340        body: StoreCommitBody,
341        signer: &UserKeypair,
342    ) -> Result<Self, StoreProtocolError> {
343        publication_base.validate_for_store(store_root_hash)?;
344        let family =
345            CandidateFamilyId::derive(store_root_hash, &author_registration, &write_id, &order);
346        validate_commit_body(store_root_hash, &body, &author_registration)?;
347        let candidate_objects = candidate_manifest(family, &body)?;
348        Ok(Signed::sign(
349            StoreBatchCommitBody {
350                store_root_hash,
351                write_id,
352                author_registration,
353                order,
354                publication_base,
355                membership_state,
356                device_state,
357                membership_authority,
358                candidate_objects,
359                body,
360            },
361            signer,
362        ))
363    }
364}