coven_replication/sync/store/merge_conflict/
abandonment.rs1use crate::sync::store::StoreError;
2use coven_protocol::objects::PreparedExactObject;
3use coven_protocol::store_commit::{
4 ObjectHash, StoreBatchCommitDeletionTarget, StoreDeviceHead, StoreDeviceRegistration,
5 VerifiedStoreBatchCommit,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MergeCandidateAbandonment {
10 NotRequired,
11 Abandoned,
12 CandidateActivated,
13}
14
15#[derive(Debug, Clone)]
28pub struct VerifiedMergeWinner {
29 store_root_hash: ObjectHash,
30 expected_slot: coven_protocol::objects::ObjectSlot,
31 expected: StoreDeviceHead,
32 expected_commit: Box<VerifiedStoreBatchCommit>,
33 winner: StoreDeviceHead,
34 winner_prepared: PreparedExactObject,
35 winner_commit: Box<VerifiedStoreBatchCommit>,
36}
37
38impl VerifiedMergeWinner {
39 pub(crate) fn from_verified_parts(
40 store_root_hash: ObjectHash,
41 expected_slot: coven_protocol::objects::ObjectSlot,
42 expected: StoreDeviceHead,
43 expected_commit: VerifiedStoreBatchCommit,
44 winner: StoreDeviceHead,
45 winner_prepared: PreparedExactObject,
46 winner_commit: VerifiedStoreBatchCommit,
47 ) -> Self {
48 Self {
49 store_root_hash,
50 expected_slot,
51 expected,
52 expected_commit: Box::new(expected_commit),
53 winner,
54 winner_prepared,
55 winner_commit: Box::new(winner_commit),
56 }
57 }
58
59 pub(crate) fn verified_nonactivation(
60 &self,
61 candidate: StoreBatchCommitDeletionTarget,
62 author: &StoreDeviceRegistration,
63 ) -> Result<
64 coven_protocol::remote_object::VerifiedCandidateNonactivation,
65 coven_protocol::remote_object::RemoteObjectRecordError,
66 > {
67 let commit = candidate
68 .verify_nonactivation_candidate(self.store_root_hash, author)
69 .map_err(
70 coven_protocol::remote_object::RemoteObjectRecordError::InvalidProofProtocol,
71 )?;
72 let reference = commit.reference().clone();
73 if self.expected.store_root_hash != self.store_root_hash
74 || commit.store_root_hash != self.store_root_hash
75 || self.expected.author_registration != commit.author_registration
76 || self.expected.commit.coord != reference.coord
77 || self.expected_commit.author_registration != commit.author_registration
78 || self.expected_commit.order.predecessor() != commit.order.predecessor()
79 || self.winner.store_root_hash != self.store_root_hash
80 || self.winner.author_registration != self.expected.author_registration
81 || self.winner.commit.coord != self.expected.commit.coord
82 || self.winner.successor.activation != self.expected.successor.activation
83 || self.winner.successor.predecessor != self.expected.successor.predecessor
84 || self.winner_prepared.reference().slot() != &self.expected_slot
85 || self.winner.commit == reference
86 || self.winner.commit != *self.winner_commit.reference()
87 {
88 return Err(
89 coven_protocol::remote_object::RemoteObjectRecordError::InvalidProof(
90 "Merge winner observation is not bound to the losing candidate's exact activation point"
91 .to_string(),
92 ),
93 );
94 }
95 coven_protocol::remote_object::VerifiedCandidateNonactivation::from_verified_merge_winner(
96 candidate,
97 coven_protocol::store_commit::StoreDeviceHeadRef {
98 head_hash: self.winner.head_hash(),
99 object: self.winner_prepared.reference().clone(),
100 },
101 self.winner.commit.clone(),
102 )
103 }
104
105 pub(crate) fn verified_nonactivations(
106 &self,
107 targets: impl IntoIterator<Item = StoreBatchCommitDeletionTarget>,
108 author: &StoreDeviceRegistration,
109 ) -> Result<Vec<coven_protocol::remote_object::VerifiedCandidateNonactivation>, StoreError>
110 {
111 let mut nonactivations = Vec::new();
112 for target in targets {
113 if target.coord == self.winner.commit.coord
114 && target.object == self.winner.commit.object
115 && target.canonical_signed_bytes == self.winner_commit.to_bytes()
116 {
117 continue;
118 }
119 nonactivations.push(
120 self.verified_nonactivation(target, author)
121 .map_err(StoreError::from)?,
122 );
123 }
124 Ok(nonactivations)
125 }
126
127 pub(crate) fn winner(&self) -> &StoreDeviceHead {
128 &self.winner
129 }
130
131 pub(crate) fn winner_prepared(&self) -> &PreparedExactObject {
132 &self.winner_prepared
133 }
134
135 pub(crate) fn into_head(self) -> (StoreDeviceHead, PreparedExactObject) {
136 (self.winner, self.winner_prepared)
137 }
138
139 #[cfg(test)]
140 pub(crate) fn winner_commit(&self) -> &VerifiedStoreBatchCommit {
141 &self.winner_commit
142 }
143
144 #[cfg(test)]
145 pub(crate) fn winner_mut_for_test(&mut self) -> &mut StoreDeviceHead {
146 &mut self.winner
147 }
148
149 #[cfg(test)]
150 pub(crate) fn set_expected_slot_for_test(
151 &mut self,
152 expected_slot: coven_protocol::objects::ObjectSlot,
153 ) {
154 self.expected_slot = expected_slot;
155 }
156}
157
158pub enum ExcludedCandidateHeadObservation {
159 AuthorExclusion,
160 MergeWinner(VerifiedMergeWinner),
161}