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}