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(
111 bytes[pos..pos + 4]
112 .try_into()
113 .map_err(|_| "truncated seal_id length")?,
114 ) as usize;
115 pos += 4;
116
117 if seal_id_len > MAX_SEAL_ID_SIZE {
118 return Err("seal_id exceeds maximum allowed size (1KB)");
119 }
120 if seal_id_len == 0 {
121 return Err("seal_id cannot be empty");
122 }
123 if bytes.len() < pos + seal_id_len {
124 return Err("truncated seal_id");
125 }
126 let seal_id = bytes[pos..pos + seal_id_len].to_vec();
127
128 Ok(Self { seal_id, nonce })
129 }
130}
131
132#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
139pub struct AnchorRef {
140 pub anchor_id: Vec<u8>,
142 pub block_height: u64,
144 pub metadata: Vec<u8>,
146}
147
148impl AnchorRef {
149 pub fn new(
159 anchor_id: Vec<u8>,
160 block_height: u64,
161 metadata: Vec<u8>,
162 ) -> Result<Self, &'static str> {
163 if anchor_id.len() > MAX_ANCHOR_ID_SIZE {
164 return Err("anchor_id exceeds maximum allowed size (1KB)");
165 }
166 if anchor_id.is_empty() {
167 return Err("anchor_id cannot be empty");
168 }
169 if metadata.len() > MAX_ANCHOR_METADATA_SIZE {
170 return Err("metadata exceeds maximum allowed size (4KB)");
171 }
172 Ok(Self {
173 anchor_id,
174 block_height,
175 metadata,
176 })
177 }
178
179 pub fn new_unchecked(anchor_id: Vec<u8>, block_height: u64, metadata: Vec<u8>) -> Self {
188 Self {
189 anchor_id,
190 block_height,
191 metadata,
192 }
193 }
194
195 pub fn to_vec(&self) -> Vec<u8> {
197 let mut out = Vec::with_capacity(8 + self.anchor_id.len() + self.metadata.len());
198 out.extend_from_slice(&self.block_height.to_le_bytes());
199 out.extend_from_slice(&self.anchor_id);
200 out.extend_from_slice(&self.metadata);
201 out
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_seal_ref_creation() {
211 let seal = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
212 assert_eq!(seal.seal_id, vec![1, 2, 3]);
213 assert_eq!(seal.nonce, Some(42));
214 }
215
216 #[test]
217 fn test_anchor_ref_creation() {
218 let anchor = AnchorRef::new(vec![4, 5, 6], 100, vec![7, 8]).unwrap();
219 assert_eq!(anchor.anchor_id, vec![4, 5, 6]);
220 assert_eq!(anchor.block_height, 100);
221 assert_eq!(anchor.metadata, vec![7, 8]);
222 }
223
224 #[test]
225 fn test_seal_ref_serialization() {
226 let seal = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
227 let bytes = seal.to_vec();
228 assert!(!bytes.is_empty());
229 }
230
231 #[test]
232 fn test_seal_ref_roundtrip() {
233 let seal1 = SealRef::new(vec![1, 2, 3], Some(42)).unwrap();
235 let bytes1 = seal1.to_vec();
236 let restored1 = SealRef::from_bytes(&bytes1).unwrap();
237 assert_eq!(restored1.seal_id, vec![1, 2, 3]);
238 assert_eq!(restored1.nonce, Some(42));
239
240 let seal2 = SealRef::new(vec![4, 5, 6], None).unwrap();
242 let bytes2 = seal2.to_vec();
243 let restored2 = SealRef::from_bytes(&bytes2).unwrap();
244 assert_eq!(restored2.seal_id, vec![4, 5, 6]);
245 assert_eq!(restored2.nonce, None);
246
247 let seal_none = SealRef::new(vec![1, 2, 3], None).unwrap();
249 let seal_zero = SealRef::new(vec![1, 2, 3], Some(0)).unwrap();
250 assert_ne!(seal_none.to_vec(), seal_zero.to_vec());
251 }
252
253 #[test]
254 fn test_seal_ref_from_bytes_errors() {
255 assert_eq!(SealRef::from_bytes(&[]), Err("empty bytes"));
257
258 assert_eq!(SealRef::from_bytes(&[5]), Err("invalid nonce flag"));
260
261 assert_eq!(SealRef::from_bytes(&[1, 0, 0]), Err("truncated nonce"));
263
264 assert_eq!(SealRef::from_bytes(&[0]), Err("truncated seal_id length"));
266
267 assert_eq!(
269 SealRef::from_bytes(&[0, 3, 0, 0, 0, 1]),
270 Err("truncated seal_id")
271 );
272
273 let mut large = vec![0, 0x01, 0x04, 0x00, 0x00]; large.extend(vec![0u8; 1025]);
276 assert_eq!(
277 SealRef::from_bytes(&large),
278 Err("seal_id exceeds maximum allowed size (1KB)")
279 );
280
281 assert_eq!(
283 SealRef::from_bytes(&[0, 0, 0, 0, 0]),
284 Err("seal_id cannot be empty")
285 );
286 }
287
288 #[test]
289 fn test_anchor_ref_serialization() {
290 let anchor = AnchorRef::new(vec![4, 5, 6], 100, vec![7, 8]).unwrap();
291 let bytes = anchor.to_vec();
292 assert!(!bytes.is_empty());
293 }
294
295 #[test]
296 fn test_seal_ref_empty_id() {
297 let result = SealRef::new(vec![], Some(42));
298 assert!(result.is_err());
299 assert_eq!(result.unwrap_err(), "seal_id cannot be empty");
300 }
301
302 #[test]
303 fn test_seal_ref_too_large() {
304 let large_id = vec![0u8; MAX_SEAL_ID_SIZE + 1];
305 let result = SealRef::new(large_id, Some(42));
306 assert!(result.is_err());
307 assert_eq!(
308 result.unwrap_err(),
309 "seal_id exceeds maximum allowed size (1KB)"
310 );
311 }
312
313 #[test]
314 fn test_seal_ref_at_max_size() {
315 let max_id = vec![0u8; MAX_SEAL_ID_SIZE];
316 let result = SealRef::new(max_id, Some(42));
317 assert!(result.is_ok());
318 }
319
320 #[test]
321 fn test_anchor_ref_empty_id() {
322 let result = AnchorRef::new(vec![], 100, vec![7, 8]);
323 assert!(result.is_err());
324 assert_eq!(result.unwrap_err(), "anchor_id cannot be empty");
325 }
326
327 #[test]
328 fn test_anchor_ref_id_too_large() {
329 let large_id = vec![0u8; MAX_ANCHOR_ID_SIZE + 1];
330 let result = AnchorRef::new(large_id, 100, vec![7, 8]);
331 assert!(result.is_err());
332 assert_eq!(
333 result.unwrap_err(),
334 "anchor_id exceeds maximum allowed size (1KB)"
335 );
336 }
337
338 #[test]
339 fn test_anchor_ref_metadata_too_large() {
340 let large_metadata = vec![0u8; MAX_ANCHOR_METADATA_SIZE + 1];
341 let result = AnchorRef::new(vec![1, 2, 3], 100, large_metadata);
342 assert!(result.is_err());
343 assert_eq!(
344 result.unwrap_err(),
345 "metadata exceeds maximum allowed size (4KB)"
346 );
347 }
348
349 #[test]
350 fn test_anchor_ref_at_max_sizes() {
351 let max_id = vec![0u8; MAX_ANCHOR_ID_SIZE];
352 let max_metadata = vec![0u8; MAX_ANCHOR_METADATA_SIZE];
353 let result = AnchorRef::new(max_id, 100, max_metadata);
354 assert!(result.is_ok());
355 }
356
357 #[test]
358 fn test_seal_ref_new_unchecked() {
359 let seal = SealRef::new_unchecked(vec![1, 2, 3], Some(42));
360 assert_eq!(seal.seal_id, vec![1, 2, 3]);
361 assert_eq!(seal.nonce, Some(42));
362 }
363
364 #[test]
365 fn test_anchor_ref_new_unchecked() {
366 let anchor = AnchorRef::new_unchecked(vec![4, 5, 6], 100, vec![7, 8]);
367 assert_eq!(anchor.anchor_id, vec![4, 5, 6]);
368 assert_eq!(anchor.block_height, 100);
369 assert_eq!(anchor.metadata, vec![7, 8]);
370 }
371}