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