Skip to main content

coven_replication/sync/store/commit_publication/operation/commit_plan/
plan.rs

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    /// This device's turn to author its own next Store commit, taken when the
46    /// position this plan's order extends was read. A plan is the live claim on
47    /// that position: keep it through publication, or transfer it back to the
48    /// operation that will continue publishing the persisted candidate.
49    _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}