Skip to main content

liminal_protocol/lifecycle/checkpoint_codec/
owner_tokens.rs

1use super::{
2    Reader, Writer, read_committed_terminal, read_ordinary_authority, write_committed_terminal,
3    write_epoch, write_ordinary_authority, write_wide,
4};
5use crate::lifecycle::{
6    BindingOrigin, CapsuleDecodeError, ClosureState, FencedAttachCommit,
7    OrdinaryBindingAuthorityRestore, OrdinaryBindingFateRestore, SealedBindingFateToken,
8    StorageRestoreError, StoredEdge,
9};
10use alloc::vec::Vec;
11
12/// Canonical owner framing or the original native provenance refusal.
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum OwnerCheckpointRestoreError {
15    /// Canonical framing could not be decoded.
16    Framing(CapsuleDecodeError),
17    /// Native origin or death provenance failed validation.
18    Provenance(StorageRestoreError),
19    /// The named saved field differs from its validated source.
20    Field(&'static str),
21}
22
23pub(in crate::lifecycle) fn encode_ordinary_fate(fate: &OrdinaryBindingFateRestore) -> Vec<u8> {
24    let mut w = Writer::default();
25    write_ordinary_authority(&mut w, fate.authority);
26    write_committed_terminal(&mut w, fate.terminal);
27    w.put_u64(fate.resulting_floor);
28    w.bytes
29}
30
31pub(in crate::lifecycle) fn decode_ordinary_fate(
32    body: &[u8],
33) -> Result<OrdinaryBindingFateRestore, OwnerCheckpointRestoreError> {
34    let read = || -> Result<_, CapsuleDecodeError> {
35        let mut r = Reader::new(body, 8);
36        let fate = OrdinaryBindingFateRestore {
37            authority: read_ordinary_authority(&mut r)?,
38            terminal: read_committed_terminal(&mut r)?,
39            resulting_floor: r.take_u64()?,
40        };
41        r.finish()?;
42        Ok(fate)
43    };
44    read().map_err(OwnerCheckpointRestoreError::Framing)
45}
46
47pub(in crate::lifecycle) fn encode_fate_token(
48    token: &SealedBindingFateToken,
49) -> Result<Vec<u8>, OwnerCheckpointRestoreError> {
50    let mut w = Writer::default();
51    w.put_u64(token.cursor);
52    match (&token.ordinary, &token.recovered) {
53        (Some(ordinary), None) if ordinary.through_seq() == token.cursor => {
54            w.put_u8(1);
55            write_ordinary_authority(
56                &mut w,
57                OrdinaryBindingAuthorityRestore {
58                    binding: ordinary.binding(),
59                    through_seq: ordinary.through_seq(),
60                },
61            );
62        }
63        (None, Some(proof)) => {
64            w.put_u8(2);
65            w.put_u64(proof.conversation_id());
66            w.put_u64(proof.participant_id());
67            w.put_u64(proof.marker_delivery_seq());
68            write_epoch(&mut w, proof.prior_binding_epoch());
69            write_epoch(&mut w, proof.new_binding_epoch());
70            match proof.next_state() {
71                ClosureState::Clear => w.put_u8(1),
72                ClosureState::Owed {
73                    debt,
74                    edge: StoredEdge::ObserverProjection(edge),
75                } => {
76                    w.put_u8(2);
77                    write_wide(&mut w, debt.value());
78                    w.put_u64(edge.through_seq());
79                }
80                ClosureState::Owed {
81                    debt,
82                    edge: StoredEdge::PhysicalCompaction(edge),
83                } => {
84                    w.put_u8(3);
85                    write_wide(&mut w, debt.value());
86                    w.put_u64(edge.from_floor());
87                    w.put_u64(edge.through_seq());
88                }
89                ClosureState::Owed { .. } => {
90                    return Err(OwnerCheckpointRestoreError::Field("sealed_fate.successor"));
91                }
92            }
93        }
94        _ => {
95            return Err(OwnerCheckpointRestoreError::Field(
96                "sealed_fate.class_or_cursor",
97            ));
98        }
99    }
100    Ok(w.bytes)
101}
102
103pub(in crate::lifecycle) fn restore_fate_token(
104    body: &[u8],
105    origin: &BindingOrigin,
106    cursor: u64,
107    recovered: Option<FencedAttachCommit>,
108) -> Result<SealedBindingFateToken, OwnerCheckpointRestoreError> {
109    let mut r = Reader::new(body, 7);
110    let saved_cursor = r.take_u64().map_err(OwnerCheckpointRestoreError::Framing)?;
111    if saved_cursor != cursor {
112        return Err(OwnerCheckpointRestoreError::Field("sealed_fate.cursor"));
113    }
114    match r.take_u8().map_err(OwnerCheckpointRestoreError::Framing)? {
115        1 => {
116            if recovered.is_some() {
117                return Err(OwnerCheckpointRestoreError::Field(
118                    "sealed_fate.unexpected_recovered_proof",
119                ));
120            }
121            let ordinary = read_ordinary_authority(&mut r)
122                .map_err(OwnerCheckpointRestoreError::Framing)?
123                .restore_with_origin(origin)
124                .map_err(OwnerCheckpointRestoreError::Provenance)?;
125            r.finish().map_err(OwnerCheckpointRestoreError::Framing)?;
126            if ordinary.through_seq() != cursor {
127                return Err(OwnerCheckpointRestoreError::Field(
128                    "sealed_fate.ordinary_cursor",
129                ));
130            }
131            Ok(SealedBindingFateToken {
132                ordinary: Some(ordinary),
133                recovered: None,
134                cursor,
135            })
136        }
137        2 => {
138            let proof = recovered.ok_or(OwnerCheckpointRestoreError::Field(
139                "sealed_fate.missing_validated_fenced_proof",
140            ))?;
141            let saved_origin = (
142                origin.conversation_id(),
143                origin.participant_id(),
144                origin.binding_epoch(),
145                origin.recovered_marker(),
146            );
147            let proof_origin = (
148                proof.conversation_id(),
149                proof.participant_id(),
150                proof.new_binding_epoch(),
151                Some((proof.marker_delivery_seq(), proof.prior_binding_epoch())),
152            );
153            if saved_origin != proof_origin {
154                return Err(OwnerCheckpointRestoreError::Field(
155                    "sealed_fate.recovered_origin",
156                ));
157            }
158            let token = SealedBindingFateToken {
159                ordinary: None,
160                recovered: Some(proof),
161                cursor,
162            };
163            if encode_fate_token(&token)? != body {
164                return Err(OwnerCheckpointRestoreError::Field(
165                    "sealed_fate.recovered_source",
166                ));
167            }
168            Ok(token)
169        }
170        _ => Err(OwnerCheckpointRestoreError::Field("sealed_fate.class")),
171    }
172}