ic_memory/ledger/
payload.rs1const LEDGER_PAYLOAD_MAGIC: &[u8; 8] = b"ICMEMLED";
2const LEDGER_PAYLOAD_HEADER_LEN: usize = 8 + 8;
3
4#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct LedgerPayloadEnvelope {
18 payload: Vec<u8>,
19}
20
21impl LedgerPayloadEnvelope {
22 #[must_use]
24 pub const fn current(payload: Vec<u8>) -> Self {
25 Self { payload }
26 }
27
28 #[must_use]
35 pub fn encode(&self) -> Vec<u8> {
36 self.try_encode()
37 .expect("payload length does not fit in the ledger envelope")
38 }
39
40 pub fn try_encode(&self) -> Result<Vec<u8>, LedgerPayloadEnvelopeError> {
42 let total_len = LEDGER_PAYLOAD_HEADER_LEN
43 .checked_add(self.payload.len())
44 .ok_or(LedgerPayloadEnvelopeError::PayloadLengthOverflow {
45 len: self.payload.len(),
46 })?;
47 let payload_len = u64::try_from(self.payload.len()).map_err(|_| {
48 LedgerPayloadEnvelopeError::PayloadLengthOverflow {
49 len: self.payload.len(),
50 }
51 })?;
52
53 let mut bytes = Vec::with_capacity(total_len);
54 bytes.extend_from_slice(LEDGER_PAYLOAD_MAGIC);
55 bytes.extend_from_slice(&payload_len.to_le_bytes());
56 bytes.extend_from_slice(&self.payload);
57 Ok(bytes)
58 }
59
60 pub fn decode(bytes: &[u8]) -> Result<Self, LedgerPayloadEnvelopeError> {
62 if bytes.len() < LEDGER_PAYLOAD_HEADER_LEN {
63 return Err(LedgerPayloadEnvelopeError::Truncated {
64 actual: bytes.len(),
65 minimum: LEDGER_PAYLOAD_HEADER_LEN,
66 });
67 }
68
69 let Some(magic) = bytes.get(0..8).and_then(|bytes| bytes.try_into().ok()) else {
70 return Err(LedgerPayloadEnvelopeError::Truncated {
71 actual: bytes.len(),
72 minimum: LEDGER_PAYLOAD_HEADER_LEN,
73 });
74 };
75 if &magic != LEDGER_PAYLOAD_MAGIC {
76 return Err(LedgerPayloadEnvelopeError::BadMagic { found: magic });
77 }
78
79 let Some(payload_len) = bytes.get(8..16).and_then(|bytes| bytes.try_into().ok()) else {
80 return Err(LedgerPayloadEnvelopeError::Truncated {
81 actual: bytes.len(),
82 minimum: LEDGER_PAYLOAD_HEADER_LEN,
83 });
84 };
85 let payload_len = u64::from_le_bytes(payload_len);
86 let payload_len = usize::try_from(payload_len)
87 .map_err(|_| LedgerPayloadEnvelopeError::PayloadTooLarge { len: payload_len })?;
88 let expected_len = LEDGER_PAYLOAD_HEADER_LEN
89 .checked_add(payload_len)
90 .ok_or(LedgerPayloadEnvelopeError::PayloadLengthOverflow { len: payload_len })?;
91 if bytes.len() != expected_len {
92 return Err(LedgerPayloadEnvelopeError::LengthMismatch {
93 declared: payload_len,
94 actual: bytes.len().saturating_sub(LEDGER_PAYLOAD_HEADER_LEN),
95 });
96 }
97
98 Ok(Self {
99 payload: bytes[LEDGER_PAYLOAD_HEADER_LEN..].to_vec(),
100 })
101 }
102
103 #[must_use]
105 pub fn payload(&self) -> &[u8] {
106 &self.payload
107 }
108}
109
110#[non_exhaustive]
115#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
116pub enum LedgerPayloadEnvelopeError {
117 #[error("ledger payload envelope is truncated: {actual} bytes, need at least {minimum}")]
119 Truncated {
120 actual: usize,
122 minimum: usize,
124 },
125 #[error("ledger payload envelope has bad magic {found:?}")]
127 BadMagic {
128 found: [u8; 8],
130 },
131 #[error("ledger payload envelope length {len} is too large")]
133 PayloadTooLarge {
134 len: u64,
136 },
137 #[error("ledger payload envelope length {len} overflows total length")]
139 PayloadLengthOverflow {
140 len: usize,
142 },
143 #[error("ledger payload envelope declared {declared} payload bytes but contained {actual}")]
145 LengthMismatch {
146 declared: usize,
148 actual: usize,
150 },
151}