1use super::*;
2
3pub(crate) enum StoreOperationBatch {
4 Acknowledgement {
5 reference: super::store_commit::StoreAckRef,
6 value: super::store_commit::StoreAck,
7 circle_acknowledgements: Vec<CircleAckActivation>,
8 },
9
10 ProviderAccessGrant(super::provider::StoreMemberProviderAccessGrantRef),
11 Attempt(coven_protocol::store_commit::DeviceJoinAttemptId),
12 SamePrincipalDeviceJoin {
13 attempt_id: coven_protocol::store_commit::DeviceJoinAttemptId,
14 registration: Box<ActivatedStoreDeviceRegistration>,
15 },
16 Abandonment(coven_protocol::store_commit::DeviceJoinAbandonmentRef),
17 JoinActivation {
18 registration: Box<ActivatedStoreDeviceRegistration>,
19 },
20 DeviceExclusionProposal(super::store_commit::RetainedStoreDeviceExclusionProposal),
21 DeviceExclusionOutcome(super::store_commit::RetainedStoreDeviceExclusionOutcome),
22 ReclaimAuthorization(Box<coven_protocol::reclaim::ReclaimAuthorizationRef>),
23 ReclaimReceipt(Box<coven_protocol::reclaim::ReclaimReceiptRef>),
24 OwnerPromotionRequest(super::store_commit::OwnerPromotionRequest),
25 MergeMembershipActivation {
26 transition: super::membership::MergeMembershipHeadTransition,
27 stream_activations: Vec<super::store_commit::StreamActivation>,
28 },
29}
30
31pub struct StoreOperationPlanCommon {
32 _authorship: coven_database::OwnStreamAuthorship,
37 writer: std::sync::Arc<LocalStoreWriter>,
38 root: StoreRootRef,
39 coord: StoreCommitCoord,
40 order: StoreCommitOrder,
41 membership_state: super::circle_control::StoreMembershipStateRef,
42 device_state: super::store_commit::StoreDeviceStateRef,
43 membership_authority: StoreOperationMembershipAuthority,
44 owner_grant: Option<super::membership::MembershipGrantId>,
45}
46
47pub struct StoreOperationCommitPlan {
48 common: StoreOperationPlanCommon,
49 membership: MembershipChain,
50 predecessor_state: super::store_commit::ResolvedStoreDeviceState,
51}
52
53impl std::ops::Deref for StoreOperationCommitPlan {
54 type Target = StoreOperationPlanCommon;
55
56 fn deref(&self) -> &Self::Target {
57 &self.common
58 }
59}
60
61impl StoreOperationPlanCommon {
62 #[allow(clippy::too_many_arguments)]
63 pub(crate) fn new(
64 authorship: coven_database::OwnStreamAuthorship,
65 writer: std::sync::Arc<LocalStoreWriter>,
66 root: StoreRootRef,
67 coord: StoreCommitCoord,
68 order: StoreCommitOrder,
69 membership_state: super::circle_control::StoreMembershipStateRef,
70 device_state: super::store_commit::StoreDeviceStateRef,
71 membership_authority: StoreOperationMembershipAuthority,
72 owner_grant: Option<super::membership::MembershipGrantId>,
73 ) -> Self {
74 Self {
75 _authorship: authorship,
76 writer,
77 root,
78 coord,
79 order,
80 membership_state,
81 device_state,
82 membership_authority,
83 owner_grant,
84 }
85 }
86
87 pub(crate) fn validate_acknowledgement(
88 &self,
89 acknowledgement: &super::store_commit::StoreAck,
90 ) -> Result<(), StoreError> {
91 let predecessor_cut = self.order.predecessor_cut().map_err(StoreError::from)?;
92 if !self
93 .writer
94 .is_authored_by_registration(&acknowledgement.registration)
95 || acknowledgement.store_cut != predecessor_cut
96 || acknowledgement.device_state != self.device_state
97 {
98 return Err(StoreError::InvalidOutbound(
99 "Store acknowledgement differs from its operation commit predecessor".to_string(),
100 ));
101 }
102 Ok(())
103 }
104
105 fn sign_batch(
106 &self,
107 write_id: coven_protocol::write::WriteId,
108 batch: StoreOperationBatch,
109 ) -> Result<(StoreBatchCommit, Option<ActivatedStoreDeviceRegistration>), StoreError> {
110 self.writer.sign_operation_batch(
111 write_id,
112 StoreOperationSigningContext {
113 root: self.root.clone(),
114 coord: self.coord.clone(),
115 order: self.order.clone(),
116 membership_state: self.membership_state.clone(),
117 device_state: self.device_state.clone(),
118 membership_authority: self.membership_authority.clone(),
119 },
120 batch,
121 )
122 }
123}
124
125impl StoreOperationCommitPlan {
126 pub(crate) fn new(
127 common: StoreOperationPlanCommon,
128 membership: MembershipChain,
129 predecessor_state: super::store_commit::ResolvedStoreDeviceState,
130 ) -> Self {
131 Self {
132 common,
133 membership,
134 predecessor_state,
135 }
136 }
137
138 pub(crate) fn membership(&self) -> &MembershipChain {
139 &self.membership
140 }
141
142 pub(crate) fn predecessor_state(&self) -> &super::store_commit::ResolvedStoreDeviceState {
143 &self.predecessor_state
144 }
145
146 pub(crate) fn sign_batch(
147 &self,
148 write_id: coven_protocol::write::WriteId,
149 batch: StoreOperationBatch,
150 ) -> Result<(StoreBatchCommit, Option<ActivatedStoreDeviceRegistration>), StoreError> {
151 self.common.sign_batch(write_id, batch)
152 }
153
154 pub(crate) fn validate_acknowledgement(
155 &self,
156 acknowledgement: &super::store_commit::StoreAck,
157 ) -> Result<(), StoreError> {
158 self.common.validate_acknowledgement(acknowledgement)
159 }
160
161 #[allow(clippy::too_many_arguments)]
162 pub(crate) fn sign_owner_promotion_request(
163 &self,
164 promotion_id: super::store_commit::OwnerPromotionId,
165 member_registration: super::store_commit::StoreDeviceRegistrationRef,
166 member_pubkey: String,
167 member_grant: super::membership::MembershipGrantId,
168 finalization: super::store_commit::OwnerPromotionFinalization,
169 ) -> Result<super::store_commit::OwnerPromotionRequest, StoreError> {
170 let promoter_owner_grant = self.owner_grant.clone().ok_or_else(|| {
171 StoreError::InvalidOutbound(
172 "Owner-promotion request author has no active Owner grant".to_string(),
173 )
174 })?;
175 self.writer.sign_owner_promotion_request(
176 promotion_id,
177 &self.root,
178 promoter_owner_grant,
179 member_pubkey,
180 member_grant,
181 member_registration,
182 self.membership_state.clone(),
183 self.device_state.clone(),
184 finalization,
185 )
186 }
187
188 pub(crate) fn predecessor_cut(&self) -> Result<StoreHistoryCut, StoreError> {
189 self.order.predecessor_cut().map_err(StoreError::from)
190 }
191
192 pub(crate) fn membership_state(&self) -> &super::circle_control::StoreMembershipStateRef {
193 &self.membership_state
194 }
195
196 #[cfg(test)]
197 pub(crate) fn membership_authority(&self) -> &StoreOperationMembershipAuthority {
198 &self.membership_authority
199 }
200
201 pub(crate) fn device_state(&self) -> &super::store_commit::StoreDeviceStateRef {
202 &self.device_state
203 }
204
205 pub(crate) fn root(&self) -> &StoreRootRef {
206 &self.root
207 }
208
209 pub(crate) fn coord(&self) -> &StoreCommitCoord {
210 &self.coord
211 }
212
213 pub(crate) fn device_id(&self) -> &super::store_commit::StoreDeviceId {
214 self.writer.device_id()
215 }
216
217 pub(crate) fn author_pubkey(&self) -> String {
218 self.writer.author_pubkey()
219 }
220
221 pub(crate) fn is_local_registration(
222 &self,
223 registration: &super::store_commit::StoreDeviceRegistrationRef,
224 ) -> bool {
225 self.writer.is_authored_by_registration(registration)
226 }
227
228 pub(crate) fn retain_device_exclusion_proposal(
229 &self,
230 reference: super::store_commit::StoreDeviceExclusionProposalRef,
231 proposal: &super::store_commit::StoreDeviceExclusionProposal,
232 target: &super::store_commit::StoreDeviceRegistration,
233 ) -> Result<super::store_commit::RetainedStoreDeviceExclusionProposal, StoreError> {
234 self.writer
235 .retain_device_exclusion_proposal(reference, proposal, target)
236 .map_err(StoreError::from)
237 }
238
239 pub(crate) fn retain_device_exclusion_outcome(
240 &self,
241 reference: &super::store_commit::StoreDeviceExclusionOutcomeRef,
242 proposal: super::store_commit::RetainedStoreDeviceExclusionProposal,
243 outcome: &super::store_commit::StoreDeviceExclusionOutcome,
244 ) -> Result<super::store_commit::RetainedStoreDeviceExclusionOutcome, StoreError> {
245 self.writer
246 .retain_device_exclusion_outcome(reference, proposal, outcome)
247 .map_err(StoreError::from)
248 }
249
250 pub(crate) fn announcement_activation_id(
251 &self,
252 ) -> Result<super::store_commit::StreamActivationId, StoreError> {
253 self.writer
254 .announcement_activation_id()
255 .map_err(StoreError::from)
256 }
257
258 pub(crate) fn verify_prepared_commit(
259 &self,
260 bytes: &[u8],
261 object: coven_protocol::objects::ExactObjectRef,
262 ) -> Result<super::store_commit::VerifiedStoreBatchCommit, StoreError> {
263 self.writer
264 .verify_prepared_commit(bytes, self.root.store_root_hash, self.coord.clone(), object)
265 .map_err(StoreError::from)
266 }
267
268 pub(crate) async fn retain_acknowledgement(
269 &self,
270 history: &AuthorizedStoreHistory<'_>,
271 activating_commit: &super::store_commit::StoreBatchCommitRef,
272 activating_commit_value: &super::store_commit::StoreBatchCommit,
273 reference: super::store_commit::StoreAckRef,
274 value: super::store_commit::StoreAck,
275 ) -> Result<super::store_commit::RetainedVerifiedActivatedAck, pull::StorePullError> {
276 self.writer
277 .retain_acknowledgement(
278 history,
279 activating_commit,
280 activating_commit_value,
281 reference,
282 value,
283 )
284 .await
285 }
286
287 pub(crate) fn owner_grant(&self) -> Option<&super::membership::MembershipGrantId> {
288 self.owner_grant.as_ref()
289 }
290
291 pub(crate) fn effective_provider_admin_grant(
292 &self,
293 state: &coven_protocol::provider::ProviderAdminState,
294 ) -> Option<coven_protocol::provider::ProviderAdminGrantId> {
295 self.writer.effective_provider_admin_grant(state)
296 }
297
298 pub(crate) fn sign_reclaim_evidence(
299 &self,
300 claim: coven_protocol::reclaim::ReclaimClaim,
301 ) -> Result<coven_protocol::reclaim::ReclaimEvidence, StoreError> {
302 self.writer
303 .sign_reclaim_evidence(self.root.store_root_hash, claim)
304 .map_err(StoreError::from)
305 }
306
307 pub(crate) fn sign_reclaim_authorization(
308 &self,
309 target: coven_protocol::reclaim::ReclaimTarget,
310 evidence: coven_protocol::reclaim::ReclaimEvidenceRef,
311 authority: coven_protocol::reclaim::StoreReclaimAuthority,
312 ) -> coven_protocol::reclaim::ReclaimAuthorization {
313 self.writer.sign_reclaim_authorization(
314 self.root.store_root_hash,
315 target,
316 evidence,
317 authority,
318 )
319 }
320
321 #[allow(clippy::too_many_arguments)]
322 pub(crate) fn sign_device_exclusion_proposal(
323 &self,
324 proposal_id: super::store_commit::StoreDeviceExclusionProposalId,
325 target: super::store_commit::StoreDeviceRegistrationRef,
326 target_registration: &super::store_commit::StoreDeviceRegistration,
327 outcome_slot: coven_protocol::objects::ObjectSlot,
328 owner_grant: super::membership::MembershipGrantId,
329 ) -> Result<super::store_commit::StoreDeviceExclusionProposal, StoreError> {
330 self.writer.sign_device_exclusion_proposal(
331 self.root.store_root_hash,
332 proposal_id,
333 target,
334 target_registration,
335 self.device_state.clone(),
336 outcome_slot,
337 owner_grant,
338 )
339 }
340
341 pub(crate) fn sign_device_exclusion_cancellation(
342 &self,
343 proposal: super::store_commit::StoreDeviceExclusionProposalRef,
344 proposal_value: &super::store_commit::StoreDeviceExclusionProposal,
345 owner_grant: super::membership::MembershipGrantId,
346 ) -> Result<super::store_commit::StoreDeviceExclusionCancellation, StoreError> {
347 self.writer
348 .sign_device_exclusion_cancellation(proposal, proposal_value, owner_grant)
349 }
350
351 #[allow(clippy::too_many_arguments)]
352 pub(crate) fn sign_device_exclusion(
353 &self,
354 proposal: super::store_commit::StoreDeviceExclusionProposalRef,
355 proposal_value: &super::store_commit::StoreDeviceExclusionProposal,
356 target: super::store_commit::StoreDeviceRegistrationRef,
357 target_registration: &super::store_commit::StoreDeviceRegistration,
358 proof: super::store_commit::StoreDeviceExclusionProof,
359 owner_grant: super::membership::MembershipGrantId,
360 ) -> Result<super::store_commit::StoreDeviceExclusion, StoreError> {
361 self.writer.sign_device_exclusion(
362 proposal,
363 proposal_value,
364 target,
365 target_registration,
366 proof,
367 owner_grant,
368 )
369 }
370
371 pub(crate) fn sign_device_head(
372 &self,
373 commit: super::store_commit::StoreBatchCommitRef,
374 successor: super::store_commit::SuccessorLink,
375 ) -> Result<super::store_commit::StoreDeviceHead, StoreError> {
376 self.writer
377 .sign_device_head(self.root.store_root_hash, commit, successor)
378 }
379
380 pub(crate) fn sign_reclaim_receipt(
381 &self,
382 authorization: coven_protocol::reclaim::ReclaimAuthorizationRef,
383 provider_admin_grant: coven_protocol::provider::ProviderAdminGrantId,
384 ) -> Result<coven_protocol::reclaim::ReclaimReceipt, StoreError> {
385 self.writer.sign_reclaim_receipt(
386 self.root.store_root_hash,
387 authorization,
388 self.membership_state.clone(),
389 provider_admin_grant,
390 )
391 }
392
393 #[cfg(any(test, feature = "test-utils"))]
394 pub(crate) fn local_registration_reference_for_test(
395 &self,
396 ) -> super::store_commit::StoreDeviceRegistrationRef {
397 self.writer.registration_reference_for_test()
398 }
399}