1use alloc::vec::Vec;
7use serde::{Deserialize, Serialize};
8
9pub const MAX_SEAL_ID_SIZE: usize = 1024;
11
12pub const MAX_ANCHOR_ID_SIZE: usize = 1024;
14
15pub const MAX_ANCHOR_METADATA_SIZE: usize = 4096;
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub struct SealRef {
27 pub seal_id: Vec<u8>,
29 pub nonce: Option<u64>,
31}
32
33impl SealRef {
34 pub fn new(seal_id: Vec<u8>, nonce: Option<u64>) -> Result<Self, &'static str> {
43 if seal_id.len() > MAX_SEAL_ID_SIZE {
44 return Err("seal_id exceeds maximum allowed size (1KB)");
45 }
46 if seal_id.is_empty() {
47 return Err("seal_id cannot be empty");
48 }
49 Ok(Self { seal_id, nonce })
50 }
51
52 pub fn new_unchecked(seal_id: Vec<u8>, nonce: Option<u64>) -> Self {
58 Self { seal_id, nonce }
59 }
60
61 pub fn to_vec(&self) -> Vec<u8> {
66 let mut out = Vec::with_capacity(9 + self.seal_id.len());
67 if let Some(nonce) = self.nonce {
68 out.push(1);
69 out.extend_from_slice(&nonce.to_le_bytes());
70 } else {
71 out.push(0);
72 }
73 out.extend_from_slice(&(self.seal_id.len() as u32).to_le_bytes());
74 out.extend_from_slice(&self.seal_id);
75 out
76 }
77
78 pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
83 if bytes.is_empty() {
84 return Err("empty bytes");
85 }
86 let mut pos = 0;
87
88 let nonce = match bytes[pos] {
89 0 => {
90 pos += 1;
91 None
92 }
93 1 => {
94 pos += 1;
95 if bytes.len() < pos + 8 {
96 return Err("truncated nonce");
97 }
98 let nonce_bytes: [u8; 8] = bytes[pos..pos + 8]
99 .try_into()
100 .map_err(|_| "truncated nonce")?;
101 pos += 8;
102 Some(u64::from_le_bytes(nonce_bytes))
103 }
104 _ => return Err("invalid nonce flag"),
105 };
106
107 if bytes.len() < pos + 4 {
108 return Err("truncated seal_id length");
109 }
110 let seal_id_len = u32::from_le_bytes(bytes[pos..pos + 4].try_into().map_err(|_| "truncated seal_id length")?) as usize;
111 pos += 4;
112
113 if seal_id_len > MAX_SEAL_ID_SIZE {
114 return Err("seal_id exceeds maximum allowed size (1KB)");
115 }
116 if seal_id_len == 0 {
117 return Err("seal_id cannot be empty");
118 }
119 if bytes.len() < pos + seal_id_len {
120 return Err("truncated seal_id");
121 }
122 let seal_id = bytes[pos..pos + seal_id_len].to_vec();
123
124 Ok(Self { seal_id, nonce })
125 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
135pub struct AnchorRef {
136 pub anchor_id: Vec<u8>,
138 pub block_height: u64,
140 pub metadata: Vec<u8>,
142}
143
144impl AnchorRef {
145 pub fn new(
155 anchor_id: Vec<u8>,
156 block_height: u64,
157 metadata: Vec<u8>,
158 ) -> Result<Self, &'static str> {
159 if anchor_id.len() > MAX_ANCHOR_ID_SIZE {
160 return Err("anchor_id exceeds maximum allowed size (1KB)");
161 }
162 if anchor_id.is_empty() {
163 return Err("anchor_id cannot be empty");
164 }
165 if metadata.len() > MAX_ANCHOR_METADATA_SIZE {
166 return Err("metadata exceeds maximum allowed size (4KB)");
167 }
168 Ok(Self {
169 anchor_id,
170 block_height,
171 metadata,
172 })
173 }
174
175 pub fn new_unchecked(anchor_id: Vec<u8>, block_height: u64, metadata: Vec<u8>) -> Self {
184 Self {
185 anchor_id,
186 block_height,
187 metadata,
188 }
189 }
190
191 pub fn to_vec(&self) -> Vec<u8> {
193 let mut out = Vec::with_capacity(8 + self.anchor_id.len() + self.metadata.len());
194 out.extend_from_slice(&self.block_height.to_le_bytes());
195 out.extend_from_slice(&self.anchor_id);
196 out.extend_from_slice(&self.metadata);
197 out
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_seal_ref_creation() {
207 let seal = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
208 assert_eq!(seal.seal_id, vec![1, 2, 3]);
209 assert_eq!(seal.nonce, Some(42));
210 }
211
212 #[test]
213 fn test_anchor_ref_creation() {
214 let anchor = AnchorRef::new(vec![4, 5, 6], 100, vec![7, 8]).unwrap();
215 assert_eq!(anchor.anchor_id, vec![4, 5, 6]);
216 assert_eq!(anchor.block_height, 100);
217 assert_eq!(anchor.metadata, vec![7, 8]);
218 }
219
220 #[test]
221 fn test_seal_ref_serialization() {
222 let seal = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
223 let bytes = seal.to_vec();
224 assert!(!bytes.is_empty());
225 }
226
227 #[test]
228 fn test_seal_ref_roundtrip() {
229 let seal1 = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
231 let bytes1 = seal1.to_vec();
232 let restored1 = SealRef::from_bytes(&bytes1).unwrap();
233 assert_eq!(restored1.seal_id, vec![1, 2, 3]);
234 assert_eq!(restored1.nonce, Some(42));
235
236 let seal2 = SealRef::new(vec![4, 5, 6], None).unwrap();
238 let bytes2 = seal2.to_vec();
239 let restored2 = SealRef::from_bytes(&bytes2).unwrap();
240 assert_eq!(restored2.seal_id, vec![4, 5, 6]);
241 assert_eq!(restored2.nonce, None);
242
243 let seal_none = SealRef::new(vec![1, 2, 3], None).unwrap();
245 let seal_zero = SealRef::new(vec![1, 2, 3], Some(0)).unwrap();
246 assert_ne!(seal_none.to_vec(), seal_zero.to_vec());
247 }
248
249 #[test]
250 fn test_seal_ref_from_bytes_errors() {
251 assert_eq!(SealRef::from_bytes(&[]), Err("empty bytes"));
253
254 assert_eq!(SealRef::from_bytes(&[5]), Err("invalid nonce flag"));
256
257 assert_eq!(SealRef::from_bytes(&[1, 0, 0]), Err("truncated nonce"));
259
260 assert_eq!(SealRef::from_bytes(&[0]), Err("truncated seal_id length"));
262
263 assert_eq!(SealRef::from_bytes(&[0, 3, 0, 0, 0, 1]), Err("truncated seal_id"));
265
266 let mut large = vec![0, 0x01, 0x04, 0x00, 0x00]; large.extend(vec![0u8; 1025]);
269 assert_eq!(
270 SealRef::from_bytes(&large),
271 Err("seal_id exceeds maximum allowed size (1KB)")
272 );
273
274 assert_eq!(SealRef::from_bytes(&[0, 0, 0, 0, 0]), Err("seal_id cannot be empty"));
276 }
277
278 #[test]
279 fn test_anchor_ref_serialization() {
280 let anchor = AnchorRef::new(vec![4, 5, 6], 100, vec![7, 8]).unwrap();
281 let bytes = anchor.to_vec();
282 assert!(!bytes.is_empty());
283 }
284
285 #[test]
286 fn test_seal_ref_empty_id() {
287 let result = SealRef::new(vec![], Some(42));
288 assert!(result.is_err());
289 assert_eq!(result.unwrap_err(), "seal_id cannot be empty");
290 }
291
292 #[test]
293 fn test_seal_ref_too_large() {
294 let large_id = vec![0u8; MAX_SEAL_ID_SIZE + 1];
295 let result = SealRef::new(large_id, Some(42));
296 assert!(result.is_err());
297 assert_eq!(
298 result.unwrap_err(),
299 "seal_id exceeds maximum allowed size (1KB)"
300 );
301 }
302
303 #[test]
304 fn test_seal_ref_at_max_size() {
305 let max_id = vec![0u8; MAX_SEAL_ID_SIZE];
306 let result = SealRef::new(max_id, Some(42));
307 assert!(result.is_ok());
308 }
309
310 #[test]
311 fn test_anchor_ref_empty_id() {
312 let result = AnchorRef::new(vec![], 100, vec![7, 8]);
313 assert!(result.is_err());
314 assert_eq!(result.unwrap_err(), "anchor_id cannot be empty");
315 }
316
317 #[test]
318 fn test_anchor_ref_id_too_large() {
319 let large_id = vec![0u8; MAX_ANCHOR_ID_SIZE + 1];
320 let result = AnchorRef::new(large_id, 100, vec![7, 8]);
321 assert!(result.is_err());
322 assert_eq!(
323 result.unwrap_err(),
324 "anchor_id exceeds maximum allowed size (1KB)"
325 );
326 }
327
328 #[test]
329 fn test_anchor_ref_metadata_too_large() {
330 let large_metadata = vec![0u8; MAX_ANCHOR_METADATA_SIZE + 1];
331 let result = AnchorRef::new(vec![1, 2, 3], 100, large_metadata);
332 assert!(result.is_err());
333 assert_eq!(
334 result.unwrap_err(),
335 "metadata exceeds maximum allowed size (4KB)"
336 );
337 }
338
339 #[test]
340 fn test_anchor_ref_at_max_sizes() {
341 let max_id = vec![0u8; MAX_ANCHOR_ID_SIZE];
342 let max_metadata = vec![0u8; MAX_ANCHOR_METADATA_SIZE];
343 let result = AnchorRef::new(max_id, 100, max_metadata);
344 assert!(result.is_ok());
345 }
346
347 #[test]
348 fn test_seal_ref_new_unchecked() {
349 let seal = SealRef::new_unchecked(vec![1, 2, 3], Some(42));
350 assert_eq!(seal.seal_id, vec![1, 2, 3]);
351 assert_eq!(seal.nonce, Some(42));
352 }
353
354 #[test]
355 fn test_anchor_ref_new_unchecked() {
356 let anchor = AnchorRef::new_unchecked(vec![4, 5, 6], 100, vec![7, 8]);
357 assert_eq!(anchor.anchor_id, vec![4, 5, 6]);
358 assert_eq!(anchor.block_height, 100);
359 assert_eq!(anchor.metadata, vec![7, 8]);
360 }
361}