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 pub(in crate::lifecycle) fn apply_fenced_recovery_finalizing_pending(
407 self,
408 ) -> Result<Self, SequenceAdmissionError> {
409 if self.claims.recovery != RecoverySequenceReserve::DetachedCredentialRecovery {
410 return Err(SequenceAdmissionError::RecoverySequenceReserveMissing);
411 }
412 if self.claims.binding_terminals == 0 {
413 return Err(SequenceAdmissionError::RecoverySequenceInvariantViolation);
414 }
415 let high_watermark = self.checked_high_watermark(2)?;
416 let claims = SequenceClaims {
417 recovery: RecoverySequenceReserve::None,
418 ..self.claims
419 };
420 let budget = claims.budget(high_watermark);
421 let required_reserve = checked_required_reserve(&budget)
422 .ok_or(SequenceAdmissionError::RecoverySequenceInvariantViolation)?;
423 if required_reserve > u128::from(budget.remaining) {
424 return Err(SequenceAdmissionError::RecoverySequenceInvariantViolation);
425 }
426 Ok(Self {
427 high_watermark,
428 claims,
429 required_reserve,
430 })
431 }
432
433 fn checked_high_watermark(self, required_values: u64) -> Result<u64, SequenceAdmissionError> {
434 self.high_watermark.checked_add(required_values).ok_or(
435 SequenceAdmissionError::HighWatermarkOverflow {
436 high_watermark: self.high_watermark,
437 required_values,
438 },
439 )
440 }
441
442 fn checked_markers(self, new_markers: u64) -> Result<u64, SequenceAdmissionError> {
443 self.claims.markers.checked_add(new_markers).ok_or(
444 SequenceAdmissionError::MarkerClaimOverflow {
445 markers: self.claims.markers,
446 new_markers,
447 },
448 )
449 }
450}
451
452#[derive(Clone, Copy, Debug, PartialEq, Eq)]
457pub struct ResultingSequenceState {
458 high_watermark: u64,
459 claims: SequenceClaims,
460}
461
462#[derive(Clone, Copy, Debug, PartialEq, Eq)]
464pub struct SequenceAdmission {
465 resulting: SequenceLedger,
466}
467
468impl SequenceAdmission {
469 #[must_use]
471 pub const fn resulting(self) -> SequenceLedger {
472 self.resulting
473 }
474}
475
476#[derive(Clone, Debug, PartialEq, Eq)]
478pub enum SequenceAdmissionError {
479 Exhausted(Box<ConversationSequenceExhausted>),
481 HighWatermarkOverflow {
483 high_watermark: u64,
485 required_values: u64,
487 },
488 LiveMemberClaimOverflow {
490 live_members: u64,
492 },
493 BindingTerminalClaimOverflow {
495 binding_terminals: u64,
497 },
498 MarkerClaimOverflow {
500 markers: u64,
502 new_markers: u64,
504 },
505 RecoverySequenceReserveMissing,
507 RecoverySequenceReserveAlreadyPresent,
509 RecoverySequenceInvariantViolation,
511}
512
513pub fn admit_sequence(
524 request: SequenceAllocatingEnvelope,
525 resulting: ResultingSequenceState,
526) -> Result<SequenceAdmission, SequenceAdmissionError> {
527 let budget = resulting.claims.budget(resulting.high_watermark);
528 let Some(required_reserve) = checked_required_reserve(&budget) else {
529 return Err(SequenceAdmissionError::Exhausted(Box::new(
530 ConversationSequenceExhausted {
531 request,
532 sequence_budget: budget,
533 },
534 )));
535 };
536 if required_reserve > u128::from(budget.remaining) {
537 return Err(SequenceAdmissionError::Exhausted(Box::new(
538 ConversationSequenceExhausted {
539 request,
540 sequence_budget: budget,
541 },
542 )));
543 }
544
545 Ok(SequenceAdmission {
546 resulting: SequenceLedger {
547 high_watermark: resulting.high_watermark,
548 claims: resulting.claims,
549 required_reserve,
550 },
551 })
552}
553
554fn checked_required_reserve(budget: &SequenceBudget) -> Option<u128> {
555 let mut reserve = u128::from(budget.e);
556 reserve = reserve.checked_add(u128::from(budget.t))?;
557 reserve = reserve.checked_add(u128::from(budget.m))?;
558 reserve = reserve.checked_add(u128::from(budget.rs))?;
559 reserve = reserve.checked_add(u128::from(budget.rt))?;
560 reserve = reserve.checked_add(budget.l_times_t)?;
561 reserve = reserve.checked_add(budget.l_times_rt)?;
562 reserve.checked_add(budget.l_other_times_e)
563}