1use crate::membership::{
6 self, AuthorHead, MembershipEntry, MembershipEntryRef, MembershipHeadRef,
7 MergeMembershipHeadTransition,
8};
9use crate::objects::{ExactObjectRef, PreparedExactObject};
10use crate::store_commit::ObjectHash;
11
12pub fn prepare_exact_object(
21 object: &ExactObjectRef,
22 value: &impl serde::Serialize,
23) -> Result<PreparedExactObject, MembershipPreparationError> {
24 let bytes = serde_json::to_vec(value).map_err(MembershipPreparationError::Json)?;
25 PreparedExactObject::new(object.clone(), bytes).map_err(MembershipPreparationError::ExactObject)
26}
27
28fn binds_exact_object(object: &ExactObjectRef, value: &impl serde::Serialize) -> bool {
29 prepare_exact_object(object, value).is_ok()
30}
31
32#[derive(Debug, thiserror::Error)]
35pub enum MembershipPreparationError {
36 #[error("invalid prepared membership mutation: {0}")]
37 Invariant(String),
38 #[error("serialize prepared membership mutation: {0}")]
39 Json(#[source] serde_json::Error),
40 #[error("prepared membership exact object: {0}")]
41 ExactObject(#[source] crate::objects::StorageError),
42}
43
44#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
52#[serde(deny_unknown_fields)]
53pub struct PreparedMembershipPublication {
54 pub entry: MembershipEntry,
55 pub entry_ref: MembershipEntryRef,
56 pub head: AuthorHead,
57 pub head_ref: MembershipHeadRef,
58}
59
60impl PreparedMembershipPublication {
61 pub fn validate(&self) -> Result<(), MembershipPreparationError> {
62 PreparedMembershipTransition {
63 entry: self.entry.clone(),
64 entry_ref: self.entry_ref.clone(),
65 transition: membership::MergeMembershipHeadTransition {
66 body: self.head.body.clone(),
67 head_slot: self.head_ref.object.slot().clone(),
68 },
69 }
70 .validate()?;
71 let coord = self.entry.coord();
72 if self.entry_ref.coord != coord
73 || self.head.body.entry != self.entry_ref
74 || self.head.entry_coord() != coord
75 || self.head_ref.coord != coord
76 || self.head_ref.head_hash != self.head.head_hash()
77 || !binds_exact_object(&self.head_ref.object, &self.head)
78 {
79 return Err(MembershipPreparationError::Invariant(
80 "prepared membership publication does not bind one exact entry and head"
81 .to_string(),
82 ));
83 }
84 Ok(())
85 }
86
87 pub fn prepared_entry(&self) -> Result<PreparedExactObject, MembershipPreparationError> {
89 prepare_exact_object(&self.entry_ref.object, &self.entry)
90 }
91
92 pub fn prepared_head(&self) -> Result<PreparedExactObject, MembershipPreparationError> {
94 prepare_exact_object(&self.head_ref.object, &self.head)
95 }
96}
97
98#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
99#[serde(deny_unknown_fields)]
100pub struct PreparedMembershipTransition {
101 pub entry: MembershipEntry,
102 pub entry_ref: MembershipEntryRef,
103 pub transition: MergeMembershipHeadTransition,
104}
105
106impl PreparedMembershipTransition {
107 pub fn validate(&self) -> Result<(), MembershipPreparationError> {
108 let coord = self.entry.coord();
109 let next_sequence = coord.seq.checked_add(1).ok_or_else(|| {
110 MembershipPreparationError::Invariant("membership sequence is exhausted".to_string())
111 })?;
112 let entry_key = format!(
113 "{}.json",
114 crate::store_commit::membership_entry_semantic_prefix(
115 &coord.author_pubkey,
116 &coord.author_owner_grant,
117 coord.stream_id,
118 coord.seq,
119 coord.entry_hash,
120 )
121 );
122 let head_key = format!(
123 "{}.json",
124 crate::store_commit::membership_head_slot_prefix(
125 &coord.author_pubkey,
126 &coord.author_owner_grant,
127 coord.stream_id,
128 coord.seq,
129 )
130 );
131 let successor_key = format!(
132 "{}.json",
133 crate::store_commit::membership_head_slot_prefix(
134 &coord.author_pubkey,
135 &coord.author_owner_grant,
136 coord.stream_id,
137 next_sequence,
138 )
139 );
140 if self.entry_ref.coord != self.entry.coord()
141 || !binds_exact_object(&self.entry_ref.object, &self.entry)
142 || self.entry_ref.object.slot().logical_key() != entry_key
143 || self.transition.body.entry != self.entry_ref
144 || self.transition.body.resolutions != self.entry.resolution_dependencies
145 || self.transition.head_slot.logical_key() != head_key
146 || self.transition.body.successor.next_slot.logical_key() != successor_key
147 {
148 return Err(MembershipPreparationError::Invariant(
149 "prepared membership transition does not bind its exact entry".to_string(),
150 ));
151 }
152 Ok(())
153 }
154
155 pub fn prepared_entry(&self) -> Result<PreparedExactObject, MembershipPreparationError> {
157 prepare_exact_object(&self.entry_ref.object, &self.entry)
158 }
159}
160
161pub enum StoreMembershipJournalCompletion {
162 Mutation {
163 intent_hash: ObjectHash,
164 progress_bytes: Vec<u8>,
165 remote_objects: Vec<crate::remote_object::RemoteObjectRecord>,
166 },
167 RotationMutation {
168 intent_hash: ObjectHash,
169 progress_bytes: Vec<u8>,
170 generation: u64,
171 remote_objects: Vec<crate::remote_object::RemoteObjectRecord>,
172 },
173 OwnerPromotion {
174 transition: crate::owner_promotion_journal::OwnerPromotionJournalTransition,
175 remote_objects: Vec<crate::remote_object::RemoteObjectRecord>,
176 },
177}
178
179impl StoreMembershipJournalCompletion {
180 pub fn object_refs(&self) -> Vec<ExactObjectRef> {
181 let remote_objects = match self {
182 Self::Mutation { remote_objects, .. }
183 | Self::RotationMutation { remote_objects, .. }
184 | Self::OwnerPromotion { remote_objects, .. } => remote_objects,
185 };
186 remote_objects
187 .iter()
188 .map(|remote| remote.object().clone())
189 .collect()
190 }
191
192 pub fn remote_object(
193 &self,
194 object: &ExactObjectRef,
195 ) -> Result<crate::remote_object::RemoteObjectRecord, MembershipPreparationError> {
196 let remote_objects = match self {
197 Self::Mutation { remote_objects, .. }
198 | Self::RotationMutation { remote_objects, .. }
199 | Self::OwnerPromotion { remote_objects, .. } => remote_objects,
200 };
201 remote_objects
202 .iter()
203 .find(|remote| remote.object() == object)
204 .cloned()
205 .ok_or_else(|| {
206 MembershipPreparationError::Invariant(
207 "membership completion omits an exact activated object".to_string(),
208 )
209 })
210 }
211}