1use alloc::{
15 collections::{BTreeMap, BTreeSet},
16 format,
17 sync::Arc,
18 vec::Vec,
19};
20
21use super::{
22 DataChunk, DeferredError, DeferredState, Digest, MAX_DEFERRED_ELEMENTS, Node, NodeType,
23 PrecompileError, PrecompileRegistry, TRUE_DIGEST, Tag,
24};
25use crate::{
26 Felt, ZERO,
27 serde::{
28 BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
29 SliceReader,
30 },
31};
32
33const TRUE_INDEX: u32 = 0;
38
39const MAX_WIRE_ENTRIES: usize = MAX_DEFERRED_ELEMENTS / Tag::FELT_LEN;
40
41fn reserve_wire_elements(
42 remaining_elements: &mut usize,
43 requested_elements: usize,
44) -> Result<(), DeserializationError> {
45 *remaining_elements = remaining_elements.checked_sub(requested_elements).ok_or_else(|| {
46 DeserializationError::InvalidValue(format!(
47 "deferred wire exceeds the {MAX_DEFERRED_ELEMENTS} element limit"
48 ))
49 })?;
50 Ok(())
51}
52
53fn reserve_wire_payload(
54 remaining_elements: &mut usize,
55 payload_count: usize,
56) -> Result<(), DeserializationError> {
57 let payload_elements =
58 payload_count.checked_mul(Node::DATA_CHUNK_FELT_LEN).ok_or_else(|| {
59 DeserializationError::InvalidValue("deferred wire element count overflow".into())
60 })?;
61 reserve_wire_elements(remaining_elements, payload_elements)
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
73pub(crate) enum WireEntry {
74 Data { tag: Tag, chunks: Vec<DataChunk> },
79 Join { tag: Tag, lhs: u32, rhs: u32 },
81 PairList { tag: Tag, pairs: Vec<(u32, u32)> },
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Default)]
94pub struct DeferredStateWire {
95 entries: Vec<WireEntry>,
96}
97
98impl DeferredStateWire {
99 pub(crate) fn from_state(state: &DeferredState) -> Result<Self, IntegrityError> {
101 let mut build = WireEncoder::default();
102 build.visit_state_digest(state, state.root())?;
103 Ok(Self { entries: build.entries })
104 }
105
106 pub(crate) fn rehydrate(
108 &self,
109 precompiles: Arc<PrecompileRegistry>,
110 ) -> Result<DeferredState, IntegrityError> {
111 self.validate_element_limit()?;
112 let (entries, root) = WireDecoder::new(self, precompiles.as_ref())?.decode()?;
113 let mut state = DeferredState::new(Arc::clone(&precompiles))?;
114
115 for (digest, node) in entries {
119 let registered = state.register(node)?;
120 if registered != digest {
121 return Err(IntegrityError::InvalidStructure);
122 }
123 }
124
125 state.root = root;
126
127 if state.to_wire()? != *self {
131 return Err(IntegrityError::InvalidStructure);
132 }
133
134 if state.evaluate_digest(root)? != TRUE_DIGEST {
135 return Err(IntegrityError::RootNotTrue);
136 }
137
138 Ok(state)
139 }
140
141 fn validate_element_limit(&self) -> Result<(), IntegrityError> {
142 let mut remaining_elements = MAX_DEFERRED_ELEMENTS;
143 for entry in &self.entries {
144 let payload_count = match entry {
145 WireEntry::Data { chunks, .. } => chunks.len(),
146 WireEntry::Join { .. } => 1,
147 WireEntry::PairList { pairs, .. } => pairs.len(),
148 };
149 let payload_elements = payload_count
150 .checked_mul(Node::DATA_CHUNK_FELT_LEN)
151 .and_then(|elements| Tag::FELT_LEN.checked_add(elements))
152 .ok_or(IntegrityError::InvalidStructure)?;
153 remaining_elements = remaining_elements.checked_sub(payload_elements).ok_or(
154 IntegrityError::DeferredStateTooLarge {
155 num_elements: payload_elements,
156 max: remaining_elements,
157 },
158 )?;
159 }
160 Ok(())
161 }
162}
163
164#[derive(Debug, thiserror::Error)]
174pub enum IntegrityError {
175 #[error("invalid or non-canonical deferred wire/state structure")]
177 InvalidStructure,
178 #[error("deferred root failed evaluation: {0}")]
180 EvaluationFailed(#[source] PrecompileError),
181 #[error("deferred root evaluated to a non-TRUE canonical form")]
183 RootNotTrue,
184 #[error("deferred insertion requires {num_elements} elements but only {max} remain")]
186 DeferredStateTooLarge { num_elements: usize, max: usize },
187}
188
189impl From<PrecompileError> for IntegrityError {
190 fn from(err: PrecompileError) -> Self {
191 if let PrecompileError::Other(DeferredError::DeferredStateTooLarge { num_elements, max }) =
192 err.root()
193 {
194 Self::DeferredStateTooLarge { num_elements: *num_elements, max: *max }
195 } else {
196 Self::EvaluationFailed(err)
197 }
198 }
199}
200
201struct WireDecoder<'a> {
205 wire: &'a DeferredStateWire,
206 precompiles: &'a PrecompileRegistry,
207 entries: Vec<(Digest, Node)>,
208 index_to_digest: Vec<Digest>,
209 seen_digests: BTreeSet<Digest>,
210}
211
212impl<'a> WireDecoder<'a> {
213 fn new(
214 wire: &'a DeferredStateWire,
215 precompiles: &'a PrecompileRegistry,
216 ) -> Result<Self, IntegrityError> {
217 let total_nodes =
218 1usize.checked_add(wire.entries.len()).ok_or(IntegrityError::InvalidStructure)?;
219
220 let mut index_to_digest = Vec::with_capacity(total_nodes);
221 let mut seen_digests = BTreeSet::new();
222 index_to_digest.push(TRUE_DIGEST);
223 seen_digests.insert(TRUE_DIGEST);
224
225 Ok(Self {
226 wire,
227 precompiles,
228 entries: Vec::with_capacity(wire.entries.len()),
229 index_to_digest,
230 seen_digests,
231 })
232 }
233
234 fn decode(mut self) -> Result<(Vec<(Digest, Node)>, Digest), IntegrityError> {
235 for entry in &self.wire.entries {
236 let node = match entry {
237 WireEntry::Data { tag, chunks } => self.decode_data_entry(*tag, chunks)?,
238 WireEntry::Join { tag, lhs, rhs } => self.decode_join_entry(*tag, *lhs, *rhs)?,
239 WireEntry::PairList { tag, pairs } => self.decode_pair_list_entry(*tag, pairs)?,
240 };
241 self.push_entry(node)?;
242 }
243
244 let root = *self.index_to_digest.last().expect("digest table is seeded with TRUE_DIGEST");
245 Ok((self.entries, root))
246 }
247
248 fn decode_data_entry(&self, tag: Tag, chunks: &[DataChunk]) -> Result<Node, IntegrityError> {
249 let node_type = self
252 .precompiles
253 .decode_node_type(tag)
254 .map_err(|_| IntegrityError::InvalidStructure)?;
255 let NodeType::Data = node_type else {
256 return Err(IntegrityError::InvalidStructure);
257 };
258 let node = if tag == Tag::CHUNKS {
259 Node::chunks(chunks.to_vec()).map_err(|_| IntegrityError::InvalidStructure)?
260 } else {
261 Node::try_data(tag, chunks.to_vec()).map_err(|_| IntegrityError::InvalidStructure)?
262 };
263 node_type.validate_node(&node).map_err(|_| IntegrityError::InvalidStructure)?;
264 Ok(node)
265 }
266
267 fn decode_join_entry(&self, tag: Tag, lhs: u32, rhs: u32) -> Result<Node, IntegrityError> {
268 let lhs = self.resolve_index(lhs)?;
269 let rhs = self.resolve_index(rhs)?;
270 let node = if tag == Tag::AND {
271 Node::and(lhs, rhs)
272 } else {
273 Node::join(tag, lhs, rhs).map_err(|_| IntegrityError::InvalidStructure)?
274 };
275 let node_type = self
276 .precompiles
277 .decode_node_type(node.tag())
278 .map_err(|_| IntegrityError::InvalidStructure)?;
279 node_type.validate_node(&node).map_err(|_| IntegrityError::InvalidStructure)?;
280 match node_type {
281 NodeType::Join => Ok(node),
282 NodeType::True | NodeType::Data | NodeType::PairList => {
283 Err(IntegrityError::InvalidStructure)
284 },
285 }
286 }
287
288 fn decode_pair_list_entry(
289 &self,
290 tag: Tag,
291 pairs: &[(u32, u32)],
292 ) -> Result<Node, IntegrityError> {
293 let node_type = self
294 .precompiles
295 .decode_node_type(tag)
296 .map_err(|_| IntegrityError::InvalidStructure)?;
297 let NodeType::PairList = node_type else {
298 return Err(IntegrityError::InvalidStructure);
299 };
300
301 let pairs = pairs
302 .iter()
303 .map(|(lhs, rhs)| Ok((self.resolve_index(*lhs)?, self.resolve_index(*rhs)?)))
304 .collect::<Result<Vec<_>, IntegrityError>>()?;
305 let node = Node::try_pair_list(tag, pairs).map_err(|_| IntegrityError::InvalidStructure)?;
306 node_type.validate_node(&node).map_err(|_| IntegrityError::InvalidStructure)?;
307 Ok(node)
308 }
309
310 fn resolve_index(&self, idx: u32) -> Result<Digest, IntegrityError> {
311 self.index_to_digest
312 .get(idx as usize)
313 .copied()
314 .ok_or(IntegrityError::InvalidStructure)
315 }
316
317 fn push_entry(&mut self, node: Node) -> Result<(), IntegrityError> {
318 if node.is_true() {
319 return Err(IntegrityError::InvalidStructure);
320 }
321
322 let digest = node.digest();
323 if !self.seen_digests.insert(digest) {
324 return Err(IntegrityError::InvalidStructure);
325 }
326
327 let index = self.index_to_digest.len();
328 if index > u32::MAX as usize {
329 return Err(IntegrityError::InvalidStructure);
330 }
331
332 self.entries.push((digest, node));
333 self.index_to_digest.push(digest);
334 Ok(())
335 }
336}
337
338#[derive(Default)]
343struct WireEncoder {
344 seen: BTreeSet<Digest>,
345 by_digest: BTreeMap<Digest, u32>,
346 entries: Vec<WireEntry>,
347}
348
349impl WireEncoder {
350 fn visit_state_digest(
351 &mut self,
352 state: &DeferredState,
353 digest: Digest,
354 ) -> Result<(), IntegrityError> {
355 let mut pending = Vec::new();
356 pending.push(WireEncodeStep::Visit(digest));
357
358 while let Some(step) = pending.pop() {
359 match step {
360 WireEncodeStep::Visit(digest) => {
361 self.schedule_digest(state, digest, &mut pending)?
362 },
363 WireEncodeStep::Emit(digest) => {
364 let entry = self.entry_for_digest(state, digest)?;
365 self.push_entry(digest, entry)?;
366 },
367 }
368 }
369
370 Ok(())
371 }
372
373 fn schedule_digest(
374 &mut self,
375 state: &DeferredState,
376 digest: Digest,
377 pending: &mut Vec<WireEncodeStep>,
378 ) -> Result<(), IntegrityError> {
379 if digest == TRUE_DIGEST || !self.seen.insert(digest) {
380 return Ok(());
381 }
382
383 let node = self.validated_node(state, digest)?;
384 pending.push(WireEncodeStep::Emit(digest));
385
386 match self.node_type(state, node)? {
387 NodeType::Data => {},
388 NodeType::Join => {
389 let (lhs, rhs) =
390 node.payload().as_join().map_err(|_| IntegrityError::InvalidStructure)?;
391 pending.push(WireEncodeStep::Visit(rhs));
392 pending.push(WireEncodeStep::Visit(lhs));
393 },
394 NodeType::PairList => {
395 let pairs =
396 node.payload().as_pair_list().map_err(|_| IntegrityError::InvalidStructure)?;
397 for (lhs, rhs) in pairs.iter().rev() {
398 pending.push(WireEncodeStep::Visit(*rhs));
399 pending.push(WireEncodeStep::Visit(*lhs));
400 }
401 },
402 NodeType::True => return Err(IntegrityError::InvalidStructure),
403 };
404
405 Ok(())
406 }
407
408 fn entry_for_digest(
409 &self,
410 state: &DeferredState,
411 digest: Digest,
412 ) -> Result<WireEntry, IntegrityError> {
413 let node = self.validated_node(state, digest)?;
414
415 Ok(match self.node_type(state, node)? {
416 NodeType::Data => WireEntry::Data {
417 tag: node.tag(),
418 chunks: node
419 .payload()
420 .as_data()
421 .map_err(|_| IntegrityError::InvalidStructure)?
422 .to_vec(),
423 },
424 NodeType::Join => {
425 let (lhs, rhs) =
426 node.payload().as_join().map_err(|_| IntegrityError::InvalidStructure)?;
427 let lhs = self.index_for(lhs)?;
428 let rhs = self.index_for(rhs)?;
429 WireEntry::Join { tag: node.tag(), lhs, rhs }
430 },
431 NodeType::PairList => {
432 let pairs =
433 node.payload().as_pair_list().map_err(|_| IntegrityError::InvalidStructure)?;
434 let pairs = pairs
435 .iter()
436 .map(|(lhs, rhs)| Ok((self.index_for(*lhs)?, self.index_for(*rhs)?)))
437 .collect::<Result<Vec<_>, IntegrityError>>()?;
438 WireEntry::PairList { tag: node.tag(), pairs }
439 },
440 NodeType::True => return Err(IntegrityError::InvalidStructure),
441 })
442 }
443
444 fn validated_node<'a>(
445 &self,
446 state: &'a DeferredState,
447 digest: Digest,
448 ) -> Result<&'a Node, IntegrityError> {
449 let node = state.get_node(&digest).ok_or(IntegrityError::InvalidStructure)?;
450 self.node_type(state, node)?
451 .validate_node(node)
452 .map_err(|_| IntegrityError::InvalidStructure)?;
453 Ok(node)
454 }
455
456 fn node_type(&self, state: &DeferredState, node: &Node) -> Result<NodeType, IntegrityError> {
457 state
458 .registry()
459 .decode_node_type(node.tag())
460 .map_err(|_| IntegrityError::InvalidStructure)
461 }
462
463 fn index_for(&self, digest: Digest) -> Result<u32, IntegrityError> {
464 if digest == TRUE_DIGEST {
465 return Ok(TRUE_INDEX);
466 }
467 self.by_digest.get(&digest).copied().ok_or(IntegrityError::InvalidStructure)
468 }
469
470 fn push_entry(&mut self, digest: Digest, entry: WireEntry) -> Result<(), IntegrityError> {
471 let next_index =
472 self.entries.len().checked_add(1).ok_or(IntegrityError::InvalidStructure)?;
473 let next_index = u32::try_from(next_index).map_err(|_| IntegrityError::InvalidStructure)?;
474 self.entries.push(entry);
475 self.by_digest.insert(digest, next_index);
476 Ok(())
477 }
478}
479
480enum WireEncodeStep {
481 Visit(Digest),
482 Emit(Digest),
483}
484
485impl Serializable for WireEntry {
489 fn write_into<W: ByteWriter>(&self, target: &mut W) {
490 match self {
491 Self::Data { tag, chunks } => {
492 target.write_u8(0);
493 tag.write_into(target);
494 target.write_usize(chunks.len());
495 for chunk in chunks {
496 for felt in chunk {
497 felt.write_into(target);
498 }
499 }
500 },
501 Self::Join { tag, lhs, rhs } => {
502 target.write_u8(1);
503 tag.write_into(target);
504 target.write_u32(*lhs);
505 target.write_u32(*rhs);
506 },
507 Self::PairList { tag, pairs } => {
508 target.write_u8(2);
509 tag.write_into(target);
510 target.write_usize(pairs.len());
511 for (lhs, rhs) in pairs {
512 target.write_u32(*lhs);
513 target.write_u32(*rhs);
514 }
515 },
516 }
517 }
518}
519
520impl Deserializable for WireEntry {
521 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
522 let mut remaining_elements = MAX_DEFERRED_ELEMENTS;
523 read_wire_entry(source, &mut remaining_elements)
524 }
525
526 fn min_serialized_size() -> usize {
527 1
528 }
529}
530
531struct WirePair((u32, u32));
532
533impl Deserializable for WirePair {
534 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
535 Ok(Self((source.read_u32()?, source.read_u32()?)))
536 }
537
538 fn min_serialized_size() -> usize {
539 u32::min_serialized_size() * 2
540 }
541}
542
543struct WireDataChunk(DataChunk);
544
545impl Deserializable for WireDataChunk {
546 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
547 let mut chunk = [ZERO; Node::DATA_CHUNK_FELT_LEN];
548 for felt in &mut chunk {
549 *felt = Felt::read_from(source)?;
550 }
551 Ok(Self(chunk))
552 }
553
554 fn min_serialized_size() -> usize {
555 Node::DATA_CHUNK_FELT_LEN * Felt::min_serialized_size()
556 }
557}
558
559impl Serializable for DeferredStateWire {
560 fn write_into<W: ByteWriter>(&self, target: &mut W) {
561 target.write_usize(self.entries.len());
562 for entry in &self.entries {
563 entry.write_into(target);
564 }
565 }
566}
567
568impl Deserializable for DeferredStateWire {
569 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
570 let entry_count = source.read_usize()?;
571 if entry_count > MAX_WIRE_ENTRIES {
572 return Err(DeserializationError::InvalidValue(format!(
573 "deferred wire contains {entry_count} entries, maximum is {MAX_WIRE_ENTRIES}"
574 )));
575 }
576
577 let mut remaining_elements = MAX_DEFERRED_ELEMENTS;
578 let mut entries = Vec::with_capacity(entry_count);
579 for _ in 0..entry_count {
580 entries.push(read_wire_entry(source, &mut remaining_elements)?);
581 }
582 Ok(Self { entries })
583 }
584
585 fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
586 let mut reader = BudgetedReader::new(SliceReader::new(bytes), bytes.len());
587 Self::read_from(&mut reader)
588 }
589
590 fn min_serialized_size() -> usize {
591 usize::min_serialized_size()
592 }
593}
594
595fn read_wire_entry<R: ByteReader>(
596 source: &mut R,
597 remaining_elements: &mut usize,
598) -> Result<WireEntry, DeserializationError> {
599 let discriminant = source.read_u8()?;
600 match discriminant {
601 0 => {
602 reserve_wire_elements(remaining_elements, Tag::FELT_LEN)?;
603 let tag = Tag::read_from(source)?;
604 let chunk_count = source.read_usize()?;
605 reserve_wire_payload(remaining_elements, chunk_count)?;
606 let chunks = source
607 .read_many_iter::<WireDataChunk>(chunk_count)?
608 .map(|chunk| chunk.map(|chunk| chunk.0))
609 .collect::<Result<_, _>>()?;
610 Ok(WireEntry::Data { tag, chunks })
611 },
612 1 => {
613 reserve_wire_elements(remaining_elements, Tag::FELT_LEN + Node::DATA_CHUNK_FELT_LEN)?;
614 let tag = Tag::read_from(source)?;
615 let lhs = source.read_u32()?;
616 let rhs = source.read_u32()?;
617 Ok(WireEntry::Join { tag, lhs, rhs })
618 },
619 2 => {
620 reserve_wire_elements(remaining_elements, Tag::FELT_LEN)?;
621 let tag = Tag::read_from(source)?;
622 let pair_count = source.read_usize()?;
623 reserve_wire_payload(remaining_elements, pair_count)?;
624 let pairs = source
625 .read_many_iter::<WirePair>(pair_count)?
626 .map(|pair| pair.map(|pair| pair.0))
627 .collect::<Result<_, _>>()?;
628 Ok(WireEntry::PairList { tag, pairs })
629 },
630 other => Err(DeserializationError::InvalidValue(format!(
631 "invalid deferred wire entry discriminant: {other}"
632 ))),
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use alloc::vec::Vec;
639
640 use super::*;
641 use crate::{
642 Felt,
643 deferred::{DeferredContext, Payload, Precompile, precompile_id},
644 serde::{ByteWriter, Serializable},
645 };
646
647 #[derive(Debug, Clone, Copy)]
648 struct PairListFixture;
649
650 impl PairListFixture {
651 const NAME: &'static str = "wire-pair-list-fixture";
652
653 fn tag() -> Tag {
654 Tag::precompile(precompile_id(Self::NAME), [ZERO; 3])
655 .expect("fixture id is precompile-owned")
656 }
657 }
658
659 impl Precompile for PairListFixture {
660 fn name(&self) -> &'static str {
661 Self::NAME
662 }
663
664 fn id(&self) -> Felt {
665 precompile_id(Self::NAME)
666 }
667
668 fn decode(&self, args: [Felt; 3]) -> Option<NodeType> {
669 (args == [ZERO; 3]).then_some(NodeType::PairList)
670 }
671
672 fn evaluate(
673 &self,
674 _args: [Felt; 3],
675 _payload: &Payload,
676 _context: &mut DeferredContext<'_>,
677 ) -> Result<Node, PrecompileError> {
678 Ok(Node::TRUE)
679 }
680 }
681
682 fn felts(seed: u64) -> [Felt; 8] {
683 core::array::from_fn(|i| Felt::new_unchecked(seed + i as u64))
684 }
685
686 fn tag(seed: u64) -> Tag {
687 Tag::from_word(felts(seed)[..4].try_into().unwrap())
688 }
689
690 fn wire(entries: Vec<WireEntry>) -> DeferredStateWire {
691 DeferredStateWire { entries }
692 }
693
694 fn assert_wire_round_trips(wire: DeferredStateWire) {
695 let decoded = DeferredStateWire::read_from_bytes(&wire.to_bytes()).unwrap();
696 assert_eq!(decoded, wire);
697 }
698
699 #[test]
700 fn empty_wire_vector_decodes_with_exact_canonical_budget() {
701 let wires = alloc::vec![DeferredStateWire::default(), DeferredStateWire::default()];
702 let bytes = wires.to_bytes();
703
704 assert_eq!(DeferredStateWire::min_serialized_size(), 1);
705 assert_eq!(bytes.len(), 3);
706 assert_eq!(
707 Vec::<DeferredStateWire>::read_from_bytes_with_budget(&bytes, bytes.len()).unwrap(),
708 wires
709 );
710 }
711
712 #[test]
713 fn wire_decoder_accepts_exact_framework_chunks_data() {
714 let registry = PrecompileRegistry::new();
715 let chunks = alloc::vec![felts(10), felts(20)];
716 let wire = wire(alloc::vec![WireEntry::Data { tag: Tag::CHUNKS, chunks: chunks.clone() }]);
717 let node = Node::chunks(chunks).unwrap();
718
719 let (entries, root) = WireDecoder::new(&wire, ®istry).unwrap().decode().unwrap();
720
721 assert_eq!(entries, alloc::vec![(node.digest(), node.clone())]);
722 assert_eq!(root, node.digest());
723 }
724
725 #[test]
726 fn empty_wire_rehydrates_with_the_fixed_deferred_element_limit() {
727 let state = DeferredState::from_wire(
728 Arc::new(PrecompileRegistry::new()),
729 &DeferredStateWire::default(),
730 )
731 .unwrap();
732
733 assert_eq!(state.num_elements(), 0);
734 assert_eq!(state.remaining_elements(), MAX_DEFERRED_ELEMENTS);
735 }
736
737 #[test]
738 fn in_memory_wire_element_limit_accepts_exact_and_rejects_next_entry() {
739 let join = WireEntry::Join {
740 tag: Tag::AND,
741 lhs: TRUE_INDEX,
742 rhs: TRUE_INDEX,
743 };
744 let join_elements = Tag::FELT_LEN + Node::DATA_CHUNK_FELT_LEN;
745 let join_count = MAX_DEFERRED_ELEMENTS / join_elements;
746 let mut entries = alloc::vec![join; join_count];
747 entries.push(WireEntry::Data { tag: Tag::CHUNKS, chunks: Vec::new() });
748 let mut wire = DeferredStateWire { entries };
749
750 wire.validate_element_limit().unwrap();
751
752 wire.entries.push(WireEntry::Data { tag: Tag::CHUNKS, chunks: Vec::new() });
753 assert!(matches!(
754 wire.validate_element_limit(),
755 Err(IntegrityError::DeferredStateTooLarge { .. })
756 ));
757 }
758
759 #[test]
760 fn rehydration_rejects_empty_data_and_pair_list_entries() {
761 let empty_data =
762 wire(alloc::vec![WireEntry::Data { tag: Tag::CHUNKS, chunks: Vec::new() }]);
763 assert!(matches!(
764 DeferredState::from_wire(Arc::new(PrecompileRegistry::new()), &empty_data),
765 Err(IntegrityError::InvalidStructure)
766 ));
767
768 let empty_pairs = wire(alloc::vec![WireEntry::PairList {
769 tag: PairListFixture::tag(),
770 pairs: Vec::new(),
771 }]);
772 assert!(matches!(
773 DeferredState::from_wire(
774 Arc::new(PrecompileRegistry::new().with_precompile(PairListFixture)),
775 &empty_pairs,
776 ),
777 Err(IntegrityError::InvalidStructure)
778 ));
779 }
780
781 #[test]
782 fn wire_decoder_rejects_malformed_framework_chunks_data() {
783 let registry = PrecompileRegistry::new();
784 let malformed = Tag::from_word([Tag::CHUNKS.id(), Felt::new_unchecked(1), ZERO, ZERO]);
785 let wire = wire(alloc::vec![WireEntry::Data {
786 tag: malformed,
787 chunks: alloc::vec![felts(10)],
788 }]);
789
790 assert!(matches!(
791 WireDecoder::new(&wire, ®istry).unwrap().decode(),
792 Err(IntegrityError::InvalidStructure)
793 ));
794 }
795
796 #[test]
798 fn wire_serialize_round_trip_all_entries() {
799 assert_wire_round_trips(wire(alloc::vec![
800 WireEntry::Data {
801 tag: tag(1),
802 chunks: alloc::vec![felts(10)]
803 },
804 WireEntry::Data {
805 tag: tag(2),
806 chunks: alloc::vec![felts(20), felts(30)],
807 },
808 WireEntry::Join { tag: tag(3), lhs: 1, rhs: TRUE_INDEX },
809 WireEntry::PairList {
810 tag: tag(5),
811 pairs: alloc::vec![(1, 2), (TRUE_INDEX, 3)],
812 },
813 ]));
814 assert_wire_round_trips(DeferredStateWire::default());
815 }
816
817 #[test]
818 fn wire_encoder_omits_nodes_unreachable_from_root() {
819 let mut state = DeferredState::default();
820 let orphan = state.register(Node::chunks(alloc::vec![[ZERO; 8]]).unwrap()).unwrap();
821 state.log_statement(TRUE_DIGEST).unwrap();
822
823 assert!(state.get_node(&orphan).is_some());
824 assert_eq!(
825 state.to_wire().unwrap().entries,
826 alloc::vec![WireEntry::Join {
827 tag: Tag::AND,
828 lhs: TRUE_INDEX,
829 rhs: TRUE_INDEX,
830 }]
831 );
832 }
833
834 #[test]
835 fn wire_encoder_handles_deep_roots_iteratively() {
836 let mut state = DeferredState::default();
837 for _ in 0..4_096 {
838 state.log_statement(TRUE_DIGEST).unwrap();
839 }
840
841 let root = state.root();
842 let wire = state.to_wire().unwrap();
843
844 assert_eq!(wire.entries.len(), 4_096);
845 assert_eq!(
846 wire.entries.last(),
847 Some(&WireEntry::Join {
848 tag: Tag::AND,
849 lhs: 4_095,
850 rhs: TRUE_INDEX,
851 })
852 );
853 assert_eq!(
854 DeferredState::from_wire(Arc::new(PrecompileRegistry::new()), &wire)
855 .unwrap()
856 .root(),
857 root
858 );
859 }
860
861 fn encoded_entry_count(entry_count: usize) -> Vec<u8> {
862 let mut bytes = Vec::new();
863 bytes.write_usize(entry_count);
864 bytes
865 }
866
867 #[test]
868 fn wire_rejects_over_budget_entry_count() {
869 assert!(
870 DeferredStateWire::read_from_bytes(&encoded_entry_count(MAX_WIRE_ENTRIES + 1)).is_err()
871 );
872 }
873
874 #[test]
875 fn wire_element_budget_accepts_exact_limit_and_rejects_one_more() {
876 let mut exact = MAX_DEFERRED_ELEMENTS;
877 reserve_wire_elements(&mut exact, MAX_DEFERRED_ELEMENTS).unwrap();
878 assert_eq!(exact, 0);
879
880 let mut oversized = MAX_DEFERRED_ELEMENTS;
881 assert!(reserve_wire_elements(&mut oversized, MAX_DEFERRED_ELEMENTS + 1).is_err());
882 }
883
884 #[test]
885 fn wire_rejects_over_budget_data_chunk_count() {
886 let mut bytes = Vec::new();
887 bytes.write_usize(1);
888 bytes.write_u8(0); tag(1).write_into(&mut bytes);
890 bytes.write_usize(usize::MAX);
891
892 assert!(DeferredStateWire::read_from_bytes(&bytes).is_err());
893 }
894
895 #[test]
896 fn wire_rejects_over_budget_pair_count() {
897 let mut bytes = Vec::new();
898 bytes.write_usize(1);
899 bytes.write_u8(2); tag(1).write_into(&mut bytes);
901 bytes.write_usize(usize::MAX);
902
903 assert!(DeferredStateWire::read_from_bytes(&bytes).is_err());
904 }
905}