1use alloc::boxed::Box;
2
3use crate::wire::{ConversationSequenceExhausted, SequenceAllocatingEnvelope, SequenceBudget};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum RecoverySequenceReserve {
12 #[default]
14 None,
15 DetachedCredentialRecovery,
17}
18
19impl RecoverySequenceReserve {
20 const fn claims(self) -> (u64, u64) {
21 match self {
22 Self::None => (0, 0),
23 Self::DetachedCredentialRecovery => (1, 1),
24 }
25 }
26}
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
34pub struct SequenceClaims {
35 live_members: u64,
36 binding_terminals: u64,
37 markers: u64,
38 recovery: RecoverySequenceReserve,
39}
40
41impl SequenceClaims {
42 #[must_use]
44 pub const fn new(
45 live_members: u64,
46 binding_terminals: u64,
47 markers: u64,
48 recovery: RecoverySequenceReserve,
49 ) -> Self {
50 Self {
51 live_members,
52 binding_terminals,
53 markers,
54 recovery,
55 }
56 }
57
58 #[must_use]
60 pub const fn live_members(self) -> u64 {
61 self.live_members
62 }
63
64 #[must_use]
66 pub const fn binding_terminals(self) -> u64 {
67 self.binding_terminals
68 }
69
70 #[must_use]
72 pub const fn markers(self) -> u64 {
73 self.markers
74 }
75
76 #[must_use]
78 pub const fn recovery(self) -> RecoverySequenceReserve {
79 self.recovery
80 }
81
82 #[must_use]
84 pub fn budget(self, high_watermark: u64) -> SequenceBudget {
85 let live_members = u128::from(self.live_members);
86 let exits = self.live_members;
87 let exits_wide = u128::from(exits);
88 let terminals = u128::from(self.binding_terminals);
89 let (rs, rt) = self.recovery.claims();
90 let replacement_terminals = u128::from(rt);
91 let other_live_members = if live_members == 0 {
92 0
93 } else {
94 live_members - 1
95 };
96
97 SequenceBudget {
98 high_watermark,
99 remaining: u64::MAX - high_watermark,
100 e: exits,
101 t: self.binding_terminals,
102 m: self.markers,
103 rs,
104 rt,
105 l_times_t: live_members * terminals,
106 l_times_rt: live_members * replacement_terminals,
107 l_other_times_e: other_live_members * exits_wide,
108 }
109 }
110
111 #[must_use]
117 pub fn checked_required_reserve(self) -> Option<u128> {
118 checked_required_reserve(&self.budget(0))
119 }
120}
121
122#[derive(Clone, Debug, PartialEq, Eq)]
124pub enum SequenceLedgerInvariantError {
125 ClaimsExceedRemaining {
127 budget: Box<SequenceBudget>,
129 required_reserve: Option<u128>,
131 },
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
148pub struct SequenceLedger {
149 high_watermark: u64,
150 claims: SequenceClaims,
151 required_reserve: u128,
152}
153
154impl SequenceLedger {
155 pub fn try_new(
162 high_watermark: u64,
163 claims: SequenceClaims,
164 ) -> Result<Self, SequenceLedgerInvariantError> {
165 let budget = claims.budget(high_watermark);
166 let required_reserve = checked_required_reserve(&budget);
167 let Some(required_reserve) = required_reserve else {
168 return Err(SequenceLedgerInvariantError::ClaimsExceedRemaining {
169 budget: Box::new(budget),
170 required_reserve,
171 });
172 };
173 if required_reserve > u128::from(budget.remaining) {
174 return Err(SequenceLedgerInvariantError::ClaimsExceedRemaining {
175 budget: Box::new(budget),
176 required_reserve: Some(required_reserve),
177 });
178 }
179 Ok(Self {
180 high_watermark,
181 claims,
182 required_reserve,
183 })
184 }
185
186 #[must_use]
188 pub const fn high_watermark(self) -> u64 {
189 self.high_watermark
190 }
191
192 #[must_use]
194 pub const fn claims(self) -> SequenceClaims {
195 self.claims
196 }
197
198 #[must_use]
200 pub fn budget(self) -> SequenceBudget {
201 self.claims.budget(self.high_watermark)
202 }
203
204 #[must_use]
206 pub const fn required_reserve(self) -> u128 {
207 self.required_reserve
208 }
209
210 #[cfg(test)]
221 pub(crate) fn plan_enrollment(
222 self,
223 new_markers: u64,
224 ) -> Result<ResultingSequenceState, SequenceAdmissionError> {
225 self.plan_enrollment_with_recovery_quartet(new_markers, false)
226 }
227
228 pub(crate) fn plan_enrollment_with_recovery_quartet(
234 self,
235 new_markers: u64,
236 endow_recovery_quartet: bool,
237 ) -> Result<ResultingSequenceState, SequenceAdmissionError> {
238 self.ensure_quartet_can_be_endowed(endow_recovery_quartet)?;
239 let high_watermark = self.checked_high_watermark(1)?;
240 let live_members = self.claims.live_members.checked_add(1).ok_or(
241 SequenceAdmissionError::LiveMemberClaimOverflow {
242 live_members: self.claims.live_members,
243 },
244 )?;
245 let binding_terminals = self.claims.binding_terminals.checked_add(1).ok_or(
246 SequenceAdmissionError::BindingTerminalClaimOverflow {
247 binding_terminals: self.claims.binding_terminals,
248 },
249 )?;
250 let markers = self.checked_markers(new_markers)?;
251 Ok(ResultingSequenceState {
252 high_watermark,
253 claims: SequenceClaims {
254 live_members,
255 binding_terminals,
256 markers,
257 recovery: if endow_recovery_quartet {
258 RecoverySequenceReserve::DetachedCredentialRecovery
259 } else {
260 self.claims.recovery
261 },
262 },
263 })
264 }
265
266 #[cfg(test)]
275 pub(crate) fn plan_detached_attach(
276 self,
277 new_markers: u64,
278 ) -> Result<ResultingSequenceState, SequenceAdmissionError> {
279 let high_watermark = self.checked_high_watermark(1)?;
280 let binding_terminals = self.claims.binding_terminals.checked_add(1).ok_or(
281 SequenceAdmissionError::BindingTerminalClaimOverflow {
282 binding_terminals: self.claims.binding_terminals,
283 },
284 )?;
285 let markers = self.checked_markers(new_markers)?;
286 Ok(ResultingSequenceState {
287 high_watermark,
288 claims: SequenceClaims {
289 binding_terminals,
290 markers,
291 ..self.claims
292 },
293 })
294 }
295
296 #[cfg(test)]
306 pub(crate) fn plan_supersession(
307 self,
308 new_markers: u64,
309 ) -> Result<ResultingSequenceState, SequenceAdmissionError> {
310 let high_watermark = self.checked_high_watermark(2)?;
311 let markers = self.checked_markers(new_markers)?;
312 Ok(ResultingSequenceState {
313 high_watermark,
314 claims: SequenceClaims {
315 markers,
316 ..self.claims
317 },
318 })
319 }
320
321 pub(crate) fn plan_ordinary_record(
330 self,
331 new_markers: u64,
332 ) -> Result<ResultingSequenceState, SequenceAdmissionError> {
333 let high_watermark = self.checked_high_watermark(1)?;
334 let markers = self.checked_markers(new_markers)?;
335 Ok(ResultingSequenceState {
336 high_watermark,
337 claims: SequenceClaims {
338 markers,
339 ..self.claims
340 },
341 })
342 }
343
344 const fn ensure_quartet_can_be_endowed(
345 self,
346 endow_recovery_quartet: bool,
347 ) -> Result<(), SequenceAdmissionError> {
348 if endow_recovery_quartet
349 && matches!(
350 self.claims.recovery,
351 RecoverySequenceReserve::DetachedCredentialRecovery
352 )
353 {
354 return Err(SequenceAdmissionError::RecoverySequenceReserveAlreadyPresent);
355 }
356 Ok(())
357 }
358
359 pub(in crate::lifecycle) fn apply_fenced_recovery(
373 self,
374 ) -> Result<Self, SequenceAdmissionError> {
375 if self.claims.recovery != RecoverySequenceReserve::DetachedCredentialRecovery {
376 return Err(SequenceAdmissionError::RecoverySequenceReserveMissing);
377 }
378 let high_watermark = self.checked_high_watermark(1)?;
379 let binding_terminals = self.claims.binding_terminals.checked_add(1).ok_or(
380 SequenceAdmissionError::BindingTerminalClaimOverflow {
381 binding_terminals: self.claims.binding_terminals,
382 },
383 )?;
384 let claims = SequenceClaims {
385 binding_terminals,
386 recovery: RecoverySequenceReserve::None,
387 ..self.claims
388 };
389 let budget = claims.budget(high_watermark);
390 let required_reserve = checked_required_reserve(&budget)
391 .ok_or(SequenceAdmissionError::RecoverySequenceInvariantViolation)?;
392 if required_reserve > u128::from(budget.remaining) {
393 return Err(SequenceAdmissionError::RecoverySequenceInvariantViolation);
394 }
395 Ok(Self {
396 high_watermark,
397 claims,
398 required_reserve,
399 })
400 }
401
402 fn checked_high_watermark(self, required_values: u64) -> Result<u64, SequenceAdmissionError> {
403 self.high_watermark.checked_add(required_values).ok_or(
404 SequenceAdmissionError::HighWatermarkOverflow {
405 high_watermark: self.high_watermark,
406 required_values,
407 },
408 )
409 }
410
411 fn checked_markers(self, new_markers: u64) -> Result<u64, SequenceAdmissionError> {
412 self.claims.markers.checked_add(new_markers).ok_or(
413 SequenceAdmissionError::MarkerClaimOverflow {
414 markers: self.claims.markers,
415 new_markers,
416 },
417 )
418 }
419}
420
421#[derive(Clone, Copy, Debug, PartialEq, Eq)]
426pub struct ResultingSequenceState {
427 high_watermark: u64,
428 claims: SequenceClaims,
429}
430
431#[derive(Clone, Copy, Debug, PartialEq, Eq)]
433pub struct SequenceAdmission {
434 resulting: SequenceLedger,
435}
436
437impl SequenceAdmission {
438 #[must_use]
440 pub const fn resulting(self) -> SequenceLedger {
441 self.resulting
442 }
443}
444
445#[derive(Clone, Debug, PartialEq, Eq)]
447pub enum SequenceAdmissionError {
448 Exhausted(Box<ConversationSequenceExhausted>),
450 HighWatermarkOverflow {
452 high_watermark: u64,
454 required_values: u64,
456 },
457 LiveMemberClaimOverflow {
459 live_members: u64,
461 },
462 BindingTerminalClaimOverflow {
464 binding_terminals: u64,
466 },
467 MarkerClaimOverflow {
469 markers: u64,
471 new_markers: u64,
473 },
474 RecoverySequenceReserveMissing,
476 RecoverySequenceReserveAlreadyPresent,
478 RecoverySequenceInvariantViolation,
480}
481
482pub fn admit_sequence(
493 request: SequenceAllocatingEnvelope,
494 resulting: ResultingSequenceState,
495) -> Result<SequenceAdmission, SequenceAdmissionError> {
496 let budget = resulting.claims.budget(resulting.high_watermark);
497 let Some(required_reserve) = checked_required_reserve(&budget) else {
498 return Err(SequenceAdmissionError::Exhausted(Box::new(
499 ConversationSequenceExhausted {
500 request,
501 sequence_budget: budget,
502 },
503 )));
504 };
505 if required_reserve > u128::from(budget.remaining) {
506 return Err(SequenceAdmissionError::Exhausted(Box::new(
507 ConversationSequenceExhausted {
508 request,
509 sequence_budget: budget,
510 },
511 )));
512 }
513
514 Ok(SequenceAdmission {
515 resulting: SequenceLedger {
516 high_watermark: resulting.high_watermark,
517 claims: resulting.claims,
518 required_reserve,
519 },
520 })
521}
522
523fn checked_required_reserve(budget: &SequenceBudget) -> Option<u128> {
524 let mut reserve = u128::from(budget.e);
525 reserve = reserve.checked_add(u128::from(budget.t))?;
526 reserve = reserve.checked_add(u128::from(budget.m))?;
527 reserve = reserve.checked_add(u128::from(budget.rs))?;
528 reserve = reserve.checked_add(u128::from(budget.rt))?;
529 reserve = reserve.checked_add(budget.l_times_t)?;
530 reserve = reserve.checked_add(budget.l_times_rt)?;
531 reserve.checked_add(budget.l_other_times_e)
532}