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}