1use std::{
5 borrow::Cow,
6 cmp,
7 ops::{Index, IndexMut},
8};
9
10use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
11use bytes::{Buf, BufMut};
12
13use crate::{consts, error::ConstraintSystemError, word::Word};
14
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
17pub struct ValueIndex(pub u32);
18
19impl ValueIndex {
20 pub const INVALID: ValueIndex = ValueIndex(u32::MAX);
22}
23
24impl Default for ValueIndex {
26 fn default() -> Self {
27 Self::INVALID
28 }
29}
30
31impl SerializeBytes for ValueIndex {
32 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
33 self.0.serialize(write_buf)
34 }
35}
36
37impl DeserializeBytes for ValueIndex {
38 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
39 where
40 Self: Sized,
41 {
42 Ok(ValueIndex(u32::deserialize(read_buf)?))
43 }
44}
45
46#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
50pub enum ShiftVariant {
51 Sll,
53 Slr,
55 Sar,
60 Rotr,
64 Sll32,
69 Srl32,
74 Sra32,
80 Rotr32,
86}
87
88impl SerializeBytes for ShiftVariant {
89 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
90 let index = match self {
91 ShiftVariant::Sll => 0u8,
92 ShiftVariant::Slr => 1u8,
93 ShiftVariant::Sar => 2u8,
94 ShiftVariant::Rotr => 3u8,
95 ShiftVariant::Sll32 => 4u8,
96 ShiftVariant::Srl32 => 5u8,
97 ShiftVariant::Sra32 => 6u8,
98 ShiftVariant::Rotr32 => 7u8,
99 };
100 index.serialize(write_buf)
101 }
102}
103
104impl DeserializeBytes for ShiftVariant {
105 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
106 where
107 Self: Sized,
108 {
109 let index = u8::deserialize(read_buf)?;
110 match index {
111 0 => Ok(ShiftVariant::Sll),
112 1 => Ok(ShiftVariant::Slr),
113 2 => Ok(ShiftVariant::Sar),
114 3 => Ok(ShiftVariant::Rotr),
115 4 => Ok(ShiftVariant::Sll32),
116 5 => Ok(ShiftVariant::Srl32),
117 6 => Ok(ShiftVariant::Sra32),
118 7 => Ok(ShiftVariant::Rotr32),
119 _ => Err(SerializationError::UnknownEnumVariant {
120 name: "ShiftVariant",
121 index,
122 }),
123 }
124 }
125}
126
127#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
134pub struct ShiftedValueIndex {
135 pub value_index: ValueIndex,
137 pub shift_variant: ShiftVariant,
139 pub amount: usize,
143}
144
145impl ShiftedValueIndex {
146 pub fn plain(value_index: ValueIndex) -> Self {
149 Self {
150 value_index,
151 shift_variant: ShiftVariant::Sll,
152 amount: 0,
153 }
154 }
155
156 pub fn sll(value_index: ValueIndex, amount: usize) -> Self {
161 assert!(amount < 64, "shift amount n={amount} out of range");
162 Self {
163 value_index,
164 shift_variant: ShiftVariant::Sll,
165 amount,
166 }
167 }
168
169 pub fn srl(value_index: ValueIndex, amount: usize) -> Self {
174 assert!(amount < 64, "shift amount n={amount} out of range");
175 Self {
176 value_index,
177 shift_variant: ShiftVariant::Slr,
178 amount,
179 }
180 }
181
182 pub fn sar(value_index: ValueIndex, amount: usize) -> Self {
190 assert!(amount < 64, "shift amount n={amount} out of range");
191 Self {
192 value_index,
193 shift_variant: ShiftVariant::Sar,
194 amount,
195 }
196 }
197
198 pub fn rotr(value_index: ValueIndex, amount: usize) -> Self {
205 assert!(amount < 64, "shift amount n={amount} out of range");
206 Self {
207 value_index,
208 shift_variant: ShiftVariant::Rotr,
209 amount,
210 }
211 }
212
213 pub fn sll32(value_index: ValueIndex, amount: usize) -> Self {
221 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
222 Self {
223 value_index,
224 shift_variant: ShiftVariant::Sll32,
225 amount,
226 }
227 }
228
229 pub fn srl32(value_index: ValueIndex, amount: usize) -> Self {
237 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
238 Self {
239 value_index,
240 shift_variant: ShiftVariant::Srl32,
241 amount,
242 }
243 }
244
245 pub fn sra32(value_index: ValueIndex, amount: usize) -> Self {
254 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
255 Self {
256 value_index,
257 shift_variant: ShiftVariant::Sra32,
258 amount,
259 }
260 }
261
262 pub fn rotr32(value_index: ValueIndex, amount: usize) -> Self {
271 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit rotate");
272 Self {
273 value_index,
274 shift_variant: ShiftVariant::Rotr32,
275 amount,
276 }
277 }
278}
279
280impl SerializeBytes for ShiftedValueIndex {
281 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
282 self.value_index.serialize(&mut write_buf)?;
283 self.shift_variant.serialize(&mut write_buf)?;
284 self.amount.serialize(write_buf)
285 }
286}
287
288impl DeserializeBytes for ShiftedValueIndex {
289 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
290 where
291 Self: Sized,
292 {
293 let value_index = ValueIndex::deserialize(&mut read_buf)?;
294 let shift_variant = ShiftVariant::deserialize(&mut read_buf)?;
295 let amount = usize::deserialize(read_buf)?;
296
297 if amount >= 64 {
299 return Err(SerializationError::InvalidConstruction {
300 name: "ShiftedValueIndex::amount",
301 });
302 }
303
304 Ok(ShiftedValueIndex {
305 value_index,
306 shift_variant,
307 amount,
308 })
309 }
310}
311
312pub type Operand = Vec<ShiftedValueIndex>;
326
327#[derive(Debug, Clone, Default)]
332pub struct AndConstraint {
333 pub a: Operand,
335 pub b: Operand,
337 pub c: Operand,
339}
340
341impl AndConstraint {
342 pub fn plain_abc(
344 a: impl IntoIterator<Item = ValueIndex>,
345 b: impl IntoIterator<Item = ValueIndex>,
346 c: impl IntoIterator<Item = ValueIndex>,
347 ) -> AndConstraint {
348 AndConstraint {
349 a: a.into_iter().map(ShiftedValueIndex::plain).collect(),
350 b: b.into_iter().map(ShiftedValueIndex::plain).collect(),
351 c: c.into_iter().map(ShiftedValueIndex::plain).collect(),
352 }
353 }
354
355 pub fn abc(
357 a: impl IntoIterator<Item = ShiftedValueIndex>,
358 b: impl IntoIterator<Item = ShiftedValueIndex>,
359 c: impl IntoIterator<Item = ShiftedValueIndex>,
360 ) -> AndConstraint {
361 AndConstraint {
362 a: a.into_iter().collect(),
363 b: b.into_iter().collect(),
364 c: c.into_iter().collect(),
365 }
366 }
367}
368
369impl SerializeBytes for AndConstraint {
370 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
371 self.a.serialize(&mut write_buf)?;
372 self.b.serialize(&mut write_buf)?;
373 self.c.serialize(write_buf)
374 }
375}
376
377impl DeserializeBytes for AndConstraint {
378 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
379 where
380 Self: Sized,
381 {
382 let a = Vec::<ShiftedValueIndex>::deserialize(&mut read_buf)?;
383 let b = Vec::<ShiftedValueIndex>::deserialize(&mut read_buf)?;
384 let c = Vec::<ShiftedValueIndex>::deserialize(read_buf)?;
385
386 Ok(AndConstraint { a, b, c })
387 }
388}
389
390#[derive(Debug, Clone, Default)]
395pub struct MulConstraint {
396 pub a: Operand,
398 pub b: Operand,
400 pub hi: Operand,
404 pub lo: Operand,
408}
409
410impl SerializeBytes for MulConstraint {
411 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
412 self.a.serialize(&mut write_buf)?;
413 self.b.serialize(&mut write_buf)?;
414 self.hi.serialize(&mut write_buf)?;
415 self.lo.serialize(write_buf)
416 }
417}
418
419impl DeserializeBytes for MulConstraint {
420 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
421 where
422 Self: Sized,
423 {
424 let a = Vec::<ShiftedValueIndex>::deserialize(&mut read_buf)?;
425 let b = Vec::<ShiftedValueIndex>::deserialize(&mut read_buf)?;
426 let hi = Vec::<ShiftedValueIndex>::deserialize(&mut read_buf)?;
427 let lo = Vec::<ShiftedValueIndex>::deserialize(read_buf)?;
428
429 Ok(MulConstraint { a, b, hi, lo })
430 }
431}
432
433#[derive(Debug, Clone)]
442pub struct ConstraintSystem {
443 pub value_vec_layout: ValueVecLayout,
445 pub constants: Vec<Word>,
450 pub and_constraints: Vec<AndConstraint>,
452 pub mul_constraints: Vec<MulConstraint>,
454}
455
456impl ConstraintSystem {
457 pub const SERIALIZATION_VERSION: u32 = 2;
459}
460
461impl ConstraintSystem {
462 pub fn new(
464 constants: Vec<Word>,
465 value_vec_layout: ValueVecLayout,
466 and_constraints: Vec<AndConstraint>,
467 mul_constraints: Vec<MulConstraint>,
468 ) -> Self {
469 assert_eq!(constants.len(), value_vec_layout.n_const);
470 ConstraintSystem {
471 constants,
472 value_vec_layout,
473 and_constraints,
474 mul_constraints,
475 }
476 }
477
478 pub fn validate(&self) -> Result<(), ConstraintSystemError> {
488 tracing::debug_span!("Validating constraint system");
489
490 self.value_vec_layout.validate()?;
492
493 for i in 0..self.and_constraints.len() {
494 validate_operand(&self.and_constraints[i].a, &self.value_vec_layout, "and", i, "a")?;
495 validate_operand(&self.and_constraints[i].b, &self.value_vec_layout, "and", i, "b")?;
496 validate_operand(&self.and_constraints[i].c, &self.value_vec_layout, "and", i, "c")?;
497 }
498 for i in 0..self.mul_constraints.len() {
499 validate_operand(&self.mul_constraints[i].a, &self.value_vec_layout, "mul", i, "a")?;
500 validate_operand(&self.mul_constraints[i].b, &self.value_vec_layout, "mul", i, "b")?;
501 validate_operand(&self.mul_constraints[i].lo, &self.value_vec_layout, "mul", i, "lo")?;
502 validate_operand(&self.mul_constraints[i].hi, &self.value_vec_layout, "mul", i, "hi")?;
503 }
504
505 return Ok(());
506
507 fn validate_operand(
508 operand: &Operand,
509 value_vec_layout: &ValueVecLayout,
510 constraint_type: &'static str,
511 constraint_index: usize,
512 operand_name: &'static str,
513 ) -> Result<(), ConstraintSystemError> {
514 for term in operand {
515 if term.amount == 0 && term.shift_variant != ShiftVariant::Sll {
517 return Err(ConstraintSystemError::NonCanonicalShift {
518 constraint_type,
519 constraint_index,
520 operand_name,
521 });
522 }
523 if term.amount >= 64 {
524 return Err(ConstraintSystemError::ShiftAmountTooLarge {
525 constraint_type,
526 constraint_index,
527 operand_name,
528 shift_amount: term.amount,
529 });
530 }
531 if value_vec_layout.is_committed_oob(term.value_index) {
533 return Err(ConstraintSystemError::OutOfRangeValueIndex {
534 constraint_type,
535 constraint_index,
536 operand_name,
537 value_index: term.value_index.0,
538 total_len: value_vec_layout.committed_total_len,
539 });
540 }
541 if value_vec_layout.is_padding(term.value_index) {
543 return Err(ConstraintSystemError::PaddingValueIndex {
544 constraint_type,
545 constraint_index,
546 operand_name,
547 });
548 }
549 }
550 Ok(())
551 }
552 }
553
554 pub fn validate_and_prepare(&mut self) -> Result<(), ConstraintSystemError> {
561 self.validate()?;
562
563 let and_target_size = self.and_constraints.len().max(1).next_power_of_two();
565 let mul_target_size =
566 cmp::max(consts::MIN_MUL_CONSTRAINTS, self.mul_constraints.len()).next_power_of_two();
567
568 self.and_constraints
569 .resize_with(and_target_size, AndConstraint::default);
570 self.mul_constraints
571 .resize_with(mul_target_size, MulConstraint::default);
572
573 Ok(())
574 }
575
576 #[cfg(test)]
577 fn add_and_constraint(&mut self, and_constraint: AndConstraint) {
578 self.and_constraints.push(and_constraint);
579 }
580
581 #[cfg(test)]
582 fn add_mul_constraint(&mut self, mul_constraint: MulConstraint) {
583 self.mul_constraints.push(mul_constraint);
584 }
585
586 pub fn n_and_constraints(&self) -> usize {
588 self.and_constraints.len()
589 }
590
591 pub fn n_mul_constraints(&self) -> usize {
593 self.mul_constraints.len()
594 }
595
596 pub fn value_vec_len(&self) -> usize {
598 self.value_vec_layout.committed_total_len
599 }
600
601 pub fn new_value_vec(&self) -> ValueVec {
603 ValueVec::new(self.value_vec_layout.clone())
604 }
605}
606
607impl SerializeBytes for ConstraintSystem {
608 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
609 Self::SERIALIZATION_VERSION.serialize(&mut write_buf)?;
610
611 self.value_vec_layout.serialize(&mut write_buf)?;
612 self.constants.serialize(&mut write_buf)?;
613 self.and_constraints.serialize(&mut write_buf)?;
614 self.mul_constraints.serialize(write_buf)
615 }
616}
617
618impl DeserializeBytes for ConstraintSystem {
619 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
620 where
621 Self: Sized,
622 {
623 let version = u32::deserialize(&mut read_buf)?;
624 if version != Self::SERIALIZATION_VERSION {
625 return Err(SerializationError::InvalidConstruction {
626 name: "ConstraintSystem::version",
627 });
628 }
629
630 let value_vec_layout = ValueVecLayout::deserialize(&mut read_buf)?;
631 let constants = Vec::<Word>::deserialize(&mut read_buf)?;
632 let and_constraints = Vec::<AndConstraint>::deserialize(&mut read_buf)?;
633 let mul_constraints = Vec::<MulConstraint>::deserialize(read_buf)?;
634
635 if constants.len() != value_vec_layout.n_const {
636 return Err(SerializationError::InvalidConstruction {
637 name: "ConstraintSystem::constants",
638 });
639 }
640
641 Ok(ConstraintSystem {
642 value_vec_layout,
643 constants,
644 and_constraints,
645 mul_constraints,
646 })
647 }
648}
649
650#[derive(Clone, Debug, PartialEq, Eq)]
652pub struct ValueVecLayout {
653 pub n_const: usize,
655 pub n_inout: usize,
657 pub n_witness: usize,
659 pub n_internal: usize,
663
664 pub offset_inout: usize,
666 pub offset_witness: usize,
671 pub committed_total_len: usize,
676 pub n_scratch: usize,
678}
679
680impl ValueVecLayout {
681 pub fn validate(&self) -> Result<(), ConstraintSystemError> {
689 if !self.committed_total_len.is_power_of_two() {
690 return Err(ConstraintSystemError::ValueVecLenNotPowerOfTwo);
691 }
692
693 if !self.offset_witness.is_power_of_two() {
694 return Err(ConstraintSystemError::PublicInputPowerOfTwo);
695 }
696
697 let pub_input_size = self.offset_witness;
698 if pub_input_size < consts::MIN_WORDS_PER_SEGMENT {
699 return Err(ConstraintSystemError::PublicInputTooShort { pub_input_size });
700 }
701
702 Ok(())
703 }
704
705 fn is_padding(&self, index: ValueIndex) -> bool {
707 let idx = index.0 as usize;
708
709 if idx >= self.n_const && idx < self.offset_inout {
711 return true;
712 }
713
714 let end_of_inout = self.offset_inout + self.n_inout;
716 if idx >= end_of_inout && idx < self.offset_witness {
717 return true;
718 }
719
720 let end_of_internal = self.offset_witness + self.n_witness + self.n_internal;
722 if idx >= end_of_internal && idx < self.committed_total_len {
723 return true;
724 }
725
726 false
727 }
728
729 fn is_committed_oob(&self, index: ValueIndex) -> bool {
731 index.0 as usize >= self.committed_total_len
732 }
733}
734
735impl SerializeBytes for ValueVecLayout {
736 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
737 self.n_const.serialize(&mut write_buf)?;
738 self.n_inout.serialize(&mut write_buf)?;
739 self.n_witness.serialize(&mut write_buf)?;
740 self.n_internal.serialize(&mut write_buf)?;
741 self.offset_inout.serialize(&mut write_buf)?;
742 self.offset_witness.serialize(&mut write_buf)?;
743 self.committed_total_len.serialize(&mut write_buf)?;
744 self.n_scratch.serialize(write_buf)
745 }
746}
747
748impl DeserializeBytes for ValueVecLayout {
749 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
750 where
751 Self: Sized,
752 {
753 let n_const = usize::deserialize(&mut read_buf)?;
754 let n_inout = usize::deserialize(&mut read_buf)?;
755 let n_witness = usize::deserialize(&mut read_buf)?;
756 let n_internal = usize::deserialize(&mut read_buf)?;
757 let offset_inout = usize::deserialize(&mut read_buf)?;
758 let offset_witness = usize::deserialize(&mut read_buf)?;
759 let committed_total_len = usize::deserialize(&mut read_buf)?;
760 let n_scratch = usize::deserialize(read_buf)?;
761
762 Ok(ValueVecLayout {
763 n_const,
764 n_inout,
765 n_witness,
766 n_internal,
767 offset_inout,
768 offset_witness,
769 committed_total_len,
770 n_scratch,
771 })
772 }
773}
774
775#[derive(Clone, Debug)]
784pub struct ValueVec {
785 layout: ValueVecLayout,
786 data: Vec<Word>,
787}
788
789impl ValueVec {
790 pub fn new(layout: ValueVecLayout) -> ValueVec {
794 let size = layout.committed_total_len + layout.n_scratch;
795 ValueVec {
796 layout,
797 data: vec![Word::ZERO; size],
798 }
799 }
800
801 pub fn new_from_data(
805 layout: ValueVecLayout,
806 public: Vec<Word>,
807 private: Vec<Word>,
808 ) -> Result<ValueVec, ConstraintSystemError> {
809 let committed_len = public.len() + private.len();
810 if committed_len != layout.committed_total_len {
811 return Err(ConstraintSystemError::ValueVecLenMismatch {
812 expected: layout.committed_total_len,
813 actual: committed_len,
814 });
815 }
816
817 let full_len = layout.committed_total_len + layout.n_scratch;
818 let mut data = public;
819 data.reserve(full_len);
820 data.extend_from_slice(&private);
821 data.resize(full_len, Word::ZERO);
822
823 Ok(ValueVec { layout, data })
824 }
825
826 pub fn size(&self) -> usize {
828 self.layout.committed_total_len
829 }
830
831 pub fn get(&self, index: usize) -> Word {
835 self.data[index]
836 }
837
838 pub fn set(&mut self, index: usize, value: Word) {
842 self.data[index] = value;
843 }
844
845 pub fn public(&self) -> &[Word] {
847 &self.data[..self.layout.offset_witness]
848 }
849
850 pub fn non_public(&self) -> &[Word] {
852 &self.data[self.layout.offset_witness..self.layout.committed_total_len]
853 }
854
855 pub fn witness(&self) -> &[Word] {
857 let start = self.layout.offset_witness;
858 let end = start + self.layout.n_witness;
859 &self.data[start..end]
860 }
861
862 pub fn combined_witness(&self) -> &[Word] {
864 let start = 0;
865 let end = self.layout.committed_total_len;
866 &self.data[start..end]
867 }
868}
869
870impl Index<ValueIndex> for ValueVec {
871 type Output = Word;
872
873 fn index(&self, index: ValueIndex) -> &Self::Output {
874 &self.data[index.0 as usize]
875 }
876}
877
878impl IndexMut<ValueIndex> for ValueVec {
879 fn index_mut(&mut self, index: ValueIndex) -> &mut Self::Output {
880 &mut self.data[index.0 as usize]
881 }
882}
883
884#[derive(Clone, Debug, PartialEq, Eq)]
890pub struct ValuesData<'a> {
891 data: Cow<'a, [Word]>,
892}
893
894impl<'a> ValuesData<'a> {
895 pub const SERIALIZATION_VERSION: u32 = 1;
897
898 pub fn borrowed(data: &'a [Word]) -> Self {
900 Self {
901 data: Cow::Borrowed(data),
902 }
903 }
904
905 pub fn owned(data: Vec<Word>) -> Self {
907 Self {
908 data: Cow::Owned(data),
909 }
910 }
911
912 pub fn as_slice(&self) -> &[Word] {
914 &self.data
915 }
916
917 pub fn len(&self) -> usize {
919 self.data.len()
920 }
921
922 pub fn is_empty(&self) -> bool {
924 self.data.is_empty()
925 }
926
927 pub fn into_owned(self) -> Vec<Word> {
929 self.data.into_owned()
930 }
931
932 pub fn to_owned(&self) -> ValuesData<'static> {
934 ValuesData {
935 data: Cow::Owned(self.data.to_vec()),
936 }
937 }
938}
939
940impl<'a> SerializeBytes for ValuesData<'a> {
941 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
942 Self::SERIALIZATION_VERSION.serialize(&mut write_buf)?;
943
944 self.data.as_ref().serialize(write_buf)
945 }
946}
947
948impl DeserializeBytes for ValuesData<'static> {
949 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
950 where
951 Self: Sized,
952 {
953 let version = u32::deserialize(&mut read_buf)?;
954 if version != Self::SERIALIZATION_VERSION {
955 return Err(SerializationError::InvalidConstruction {
956 name: "Witness::version",
957 });
958 }
959
960 let data = Vec::<Word>::deserialize(read_buf)?;
961
962 Ok(ValuesData::owned(data))
963 }
964}
965
966impl<'a> From<&'a [Word]> for ValuesData<'a> {
967 fn from(data: &'a [Word]) -> Self {
968 ValuesData::borrowed(data)
969 }
970}
971
972impl From<Vec<Word>> for ValuesData<'static> {
973 fn from(data: Vec<Word>) -> Self {
974 ValuesData::owned(data)
975 }
976}
977
978impl<'a> AsRef<[Word]> for ValuesData<'a> {
979 fn as_ref(&self) -> &[Word] {
980 self.as_slice()
981 }
982}
983
984impl<'a> std::ops::Deref for ValuesData<'a> {
985 type Target = [Word];
986
987 fn deref(&self) -> &Self::Target {
988 self.as_slice()
989 }
990}
991
992impl<'a> From<ValuesData<'a>> for Vec<Word> {
993 fn from(value: ValuesData<'a>) -> Self {
994 value.into_owned()
995 }
996}
997
998#[derive(Clone, Debug, PartialEq, Eq)]
1015pub struct Proof<'a> {
1016 data: Cow<'a, [u8]>,
1017 challenger_type: String,
1018}
1019
1020impl<'a> Proof<'a> {
1021 pub const SERIALIZATION_VERSION: u32 = 1;
1023
1024 pub fn borrowed(data: &'a [u8], challenger_type: String) -> Self {
1026 Self {
1027 data: Cow::Borrowed(data),
1028 challenger_type,
1029 }
1030 }
1031
1032 pub fn owned(data: Vec<u8>, challenger_type: String) -> Self {
1034 Self {
1035 data: Cow::Owned(data),
1036 challenger_type,
1037 }
1038 }
1039
1040 pub fn as_slice(&self) -> &[u8] {
1042 &self.data
1043 }
1044
1045 pub fn challenger_type(&self) -> &str {
1047 &self.challenger_type
1048 }
1049
1050 pub fn len(&self) -> usize {
1052 self.data.len()
1053 }
1054
1055 pub fn is_empty(&self) -> bool {
1057 self.data.is_empty()
1058 }
1059
1060 pub fn into_owned(self) -> (Vec<u8>, String) {
1062 (self.data.into_owned(), self.challenger_type)
1063 }
1064
1065 pub fn to_owned(&self) -> Proof<'static> {
1067 Proof {
1068 data: Cow::Owned(self.data.to_vec()),
1069 challenger_type: self.challenger_type.clone(),
1070 }
1071 }
1072}
1073
1074impl<'a> SerializeBytes for Proof<'a> {
1075 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
1076 Self::SERIALIZATION_VERSION.serialize(&mut write_buf)?;
1077
1078 self.challenger_type.serialize(&mut write_buf)?;
1079
1080 self.data.as_ref().serialize(write_buf)
1081 }
1082}
1083
1084impl DeserializeBytes for Proof<'static> {
1085 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
1086 where
1087 Self: Sized,
1088 {
1089 let version = u32::deserialize(&mut read_buf)?;
1090 if version != Self::SERIALIZATION_VERSION {
1091 return Err(SerializationError::InvalidConstruction {
1092 name: "Proof::version",
1093 });
1094 }
1095
1096 let challenger_type = String::deserialize(&mut read_buf)?;
1097 let data = Vec::<u8>::deserialize(read_buf)?;
1098
1099 Ok(Proof::owned(data, challenger_type))
1100 }
1101}
1102
1103impl<'a> From<(&'a [u8], String)> for Proof<'a> {
1104 fn from((data, challenger_type): (&'a [u8], String)) -> Self {
1105 Proof::borrowed(data, challenger_type)
1106 }
1107}
1108
1109impl From<(Vec<u8>, String)> for Proof<'static> {
1110 fn from((data, challenger_type): (Vec<u8>, String)) -> Self {
1111 Proof::owned(data, challenger_type)
1112 }
1113}
1114
1115impl<'a> AsRef<[u8]> for Proof<'a> {
1116 fn as_ref(&self) -> &[u8] {
1117 self.as_slice()
1118 }
1119}
1120
1121impl<'a> std::ops::Deref for Proof<'a> {
1122 type Target = [u8];
1123
1124 fn deref(&self) -> &Self::Target {
1125 self.as_slice()
1126 }
1127}
1128
1129#[cfg(test)]
1130mod serialization_tests {
1131 use rand::prelude::*;
1132
1133 use super::*;
1134
1135 pub(crate) fn create_test_constraint_system() -> ConstraintSystem {
1136 let constants = vec![
1137 Word::from_u64(1),
1138 Word::from_u64(42),
1139 Word::from_u64(0xDEADBEEF),
1140 ];
1141
1142 let value_vec_layout = ValueVecLayout {
1143 n_const: 3,
1144 n_inout: 2,
1145 n_witness: 10,
1146 n_internal: 3,
1147 offset_inout: 4, offset_witness: 8, committed_total_len: 16, n_scratch: 0,
1151 };
1152
1153 let and_constraints = vec![
1154 AndConstraint::plain_abc(
1155 vec![ValueIndex(0), ValueIndex(1)],
1156 vec![ValueIndex(2)],
1157 vec![ValueIndex(3), ValueIndex(4)],
1158 ),
1159 AndConstraint::abc(
1160 vec![ShiftedValueIndex::sll(ValueIndex(0), 5)],
1161 vec![ShiftedValueIndex::srl(ValueIndex(1), 10)],
1162 vec![ShiftedValueIndex::sar(ValueIndex(2), 15)],
1163 ),
1164 ];
1165
1166 let mul_constraints = vec![MulConstraint {
1167 a: vec![ShiftedValueIndex::plain(ValueIndex(0))],
1168 b: vec![ShiftedValueIndex::plain(ValueIndex(1))],
1169 hi: vec![ShiftedValueIndex::plain(ValueIndex(2))],
1170 lo: vec![ShiftedValueIndex::plain(ValueIndex(3))],
1171 }];
1172
1173 ConstraintSystem::new(constants, value_vec_layout, and_constraints, mul_constraints)
1174 }
1175
1176 #[test]
1177 fn test_word_serialization_round_trip() {
1178 let mut rng = StdRng::seed_from_u64(0);
1179 let word = Word::from_u64(rng.next_u64());
1180
1181 let mut buf = Vec::new();
1182 word.serialize(&mut buf).unwrap();
1183
1184 let deserialized = Word::deserialize(&mut buf.as_slice()).unwrap();
1185 assert_eq!(word, deserialized);
1186 }
1187
1188 #[test]
1189 fn test_shift_variant_serialization_round_trip() {
1190 let variants = [
1191 ShiftVariant::Sll,
1192 ShiftVariant::Slr,
1193 ShiftVariant::Sar,
1194 ShiftVariant::Rotr,
1195 ];
1196
1197 for variant in variants {
1198 let mut buf = Vec::new();
1199 variant.serialize(&mut buf).unwrap();
1200
1201 let deserialized = ShiftVariant::deserialize(&mut buf.as_slice()).unwrap();
1202 match (variant, deserialized) {
1203 (ShiftVariant::Sll, ShiftVariant::Sll)
1204 | (ShiftVariant::Slr, ShiftVariant::Slr)
1205 | (ShiftVariant::Sar, ShiftVariant::Sar)
1206 | (ShiftVariant::Rotr, ShiftVariant::Rotr) => {}
1207 _ => panic!("ShiftVariant round trip failed: {:?} != {:?}", variant, deserialized),
1208 }
1209 }
1210 }
1211
1212 #[test]
1213 fn test_shift_variant_unknown_variant() {
1214 let mut buf = Vec::new();
1216 255u8.serialize(&mut buf).unwrap();
1217
1218 let result = ShiftVariant::deserialize(&mut buf.as_slice());
1219 assert!(result.is_err());
1220 match result.unwrap_err() {
1221 SerializationError::UnknownEnumVariant { name, index } => {
1222 assert_eq!(name, "ShiftVariant");
1223 assert_eq!(index, 255);
1224 }
1225 _ => panic!("Expected UnknownEnumVariant error"),
1226 }
1227 }
1228
1229 #[test]
1230 fn test_value_index_serialization_round_trip() {
1231 let value_index = ValueIndex(12345);
1232
1233 let mut buf = Vec::new();
1234 value_index.serialize(&mut buf).unwrap();
1235
1236 let deserialized = ValueIndex::deserialize(&mut buf.as_slice()).unwrap();
1237 assert_eq!(value_index, deserialized);
1238 }
1239
1240 #[test]
1241 fn test_shifted_value_index_serialization_round_trip() {
1242 let shifted_value_index = ShiftedValueIndex::srl(ValueIndex(42), 23);
1243
1244 let mut buf = Vec::new();
1245 shifted_value_index.serialize(&mut buf).unwrap();
1246
1247 let deserialized = ShiftedValueIndex::deserialize(&mut buf.as_slice()).unwrap();
1248 assert_eq!(shifted_value_index.value_index, deserialized.value_index);
1249 assert_eq!(shifted_value_index.amount, deserialized.amount);
1250 match (shifted_value_index.shift_variant, deserialized.shift_variant) {
1251 (ShiftVariant::Slr, ShiftVariant::Slr) => {}
1252 _ => panic!("ShiftVariant mismatch"),
1253 }
1254 }
1255
1256 #[test]
1257 fn test_shifted_value_index_invalid_amount() {
1258 let mut buf = Vec::new();
1260 ValueIndex(0).serialize(&mut buf).unwrap();
1261 ShiftVariant::Sll.serialize(&mut buf).unwrap();
1262 64usize.serialize(&mut buf).unwrap(); let result = ShiftedValueIndex::deserialize(&mut buf.as_slice());
1265 assert!(result.is_err());
1266 match result.unwrap_err() {
1267 SerializationError::InvalidConstruction { name } => {
1268 assert_eq!(name, "ShiftedValueIndex::amount");
1269 }
1270 _ => panic!("Expected InvalidConstruction error"),
1271 }
1272 }
1273
1274 #[test]
1275 fn test_and_constraint_serialization_round_trip() {
1276 let constraint = AndConstraint::abc(
1277 vec![ShiftedValueIndex::sll(ValueIndex(1), 5)],
1278 vec![ShiftedValueIndex::srl(ValueIndex(2), 10)],
1279 vec![
1280 ShiftedValueIndex::sar(ValueIndex(3), 15),
1281 ShiftedValueIndex::plain(ValueIndex(4)),
1282 ],
1283 );
1284
1285 let mut buf = Vec::new();
1286 constraint.serialize(&mut buf).unwrap();
1287
1288 let deserialized = AndConstraint::deserialize(&mut buf.as_slice()).unwrap();
1289 assert_eq!(constraint.a.len(), deserialized.a.len());
1290 assert_eq!(constraint.b.len(), deserialized.b.len());
1291 assert_eq!(constraint.c.len(), deserialized.c.len());
1292
1293 for (orig, deser) in constraint.a.iter().zip(deserialized.a.iter()) {
1294 assert_eq!(orig.value_index, deser.value_index);
1295 assert_eq!(orig.amount, deser.amount);
1296 }
1297 }
1298
1299 #[test]
1300 fn test_mul_constraint_serialization_round_trip() {
1301 let constraint = MulConstraint {
1302 a: vec![ShiftedValueIndex::plain(ValueIndex(0))],
1303 b: vec![ShiftedValueIndex::srl(ValueIndex(1), 32)],
1304 hi: vec![ShiftedValueIndex::plain(ValueIndex(2))],
1305 lo: vec![ShiftedValueIndex::plain(ValueIndex(3))],
1306 };
1307
1308 let mut buf = Vec::new();
1309 constraint.serialize(&mut buf).unwrap();
1310
1311 let deserialized = MulConstraint::deserialize(&mut buf.as_slice()).unwrap();
1312 assert_eq!(constraint.a.len(), deserialized.a.len());
1313 assert_eq!(constraint.b.len(), deserialized.b.len());
1314 assert_eq!(constraint.hi.len(), deserialized.hi.len());
1315 assert_eq!(constraint.lo.len(), deserialized.lo.len());
1316 }
1317
1318 #[test]
1319 fn test_value_vec_layout_serialization_round_trip() {
1320 let layout = ValueVecLayout {
1321 n_const: 5,
1322 n_inout: 3,
1323 n_witness: 12,
1324 n_internal: 7,
1325 offset_inout: 8,
1326 offset_witness: 16,
1327 committed_total_len: 32,
1328 n_scratch: 0,
1329 };
1330
1331 let mut buf = Vec::new();
1332 layout.serialize(&mut buf).unwrap();
1333
1334 let deserialized = ValueVecLayout::deserialize(&mut buf.as_slice()).unwrap();
1335 assert_eq!(layout, deserialized);
1336 }
1337
1338 #[test]
1339 fn test_constraint_system_serialization_round_trip() {
1340 let original = create_test_constraint_system();
1341
1342 let mut buf = Vec::new();
1343 original.serialize(&mut buf).unwrap();
1344
1345 let deserialized = ConstraintSystem::deserialize(&mut buf.as_slice()).unwrap();
1346
1347 assert_eq!(ConstraintSystem::SERIALIZATION_VERSION, 2);
1349
1350 assert_eq!(original.value_vec_layout, deserialized.value_vec_layout);
1352
1353 assert_eq!(original.constants.len(), deserialized.constants.len());
1355 for (orig, deser) in original.constants.iter().zip(deserialized.constants.iter()) {
1356 assert_eq!(orig, deser);
1357 }
1358
1359 assert_eq!(original.and_constraints.len(), deserialized.and_constraints.len());
1361
1362 assert_eq!(original.mul_constraints.len(), deserialized.mul_constraints.len());
1364 }
1365
1366 #[test]
1367 fn test_constraint_system_version_mismatch() {
1368 let mut buf = Vec::new();
1370 999u32.serialize(&mut buf).unwrap(); let result = ConstraintSystem::deserialize(&mut buf.as_slice());
1373 assert!(result.is_err());
1374 match result.unwrap_err() {
1375 SerializationError::InvalidConstruction { name } => {
1376 assert_eq!(name, "ConstraintSystem::version");
1377 }
1378 _ => panic!("Expected InvalidConstruction error"),
1379 }
1380 }
1381
1382 #[test]
1383 fn test_constraint_system_constants_length_mismatch() {
1384 let value_vec_layout = ValueVecLayout {
1386 n_const: 5, n_inout: 2,
1388 n_witness: 10,
1389 n_internal: 3,
1390 offset_inout: 8,
1391 offset_witness: 16,
1392 committed_total_len: 32,
1393 n_scratch: 0,
1394 };
1395
1396 let constants = vec![Word::from_u64(1), Word::from_u64(2)]; let and_constraints: Vec<AndConstraint> = vec![];
1398 let mul_constraints: Vec<MulConstraint> = vec![];
1399
1400 let mut buf = Vec::new();
1402 ConstraintSystem::SERIALIZATION_VERSION
1403 .serialize(&mut buf)
1404 .unwrap();
1405 value_vec_layout.serialize(&mut buf).unwrap();
1406 constants.serialize(&mut buf).unwrap();
1407 and_constraints.serialize(&mut buf).unwrap();
1408 mul_constraints.serialize(&mut buf).unwrap();
1409
1410 let result = ConstraintSystem::deserialize(&mut buf.as_slice());
1411 assert!(result.is_err());
1412 match result.unwrap_err() {
1413 SerializationError::InvalidConstruction { name } => {
1414 assert_eq!(name, "ConstraintSystem::constants");
1415 }
1416 _ => panic!("Expected InvalidConstruction error"),
1417 }
1418 }
1419
1420 #[test]
1421 fn test_serialization_with_different_sources() {
1422 let original = create_test_constraint_system();
1423
1424 let mut vec_buf = Vec::new();
1426 original.serialize(&mut vec_buf).unwrap();
1427 let deserialized1 = ConstraintSystem::deserialize(&mut vec_buf.as_slice()).unwrap();
1428 assert_eq!(original.constants.len(), deserialized1.constants.len());
1429
1430 let mut bytes_buf = bytes::BytesMut::new();
1432 original.serialize(&mut bytes_buf).unwrap();
1433 let deserialized2 = ConstraintSystem::deserialize(bytes_buf.freeze()).unwrap();
1434 assert_eq!(original.constants.len(), deserialized2.constants.len());
1435 }
1436
1437 #[test]
1441 #[ignore] fn create_reference_binary_file() {
1443 let constraint_system = create_test_constraint_system();
1444
1445 let mut buf = Vec::new();
1447 constraint_system.serialize(&mut buf).unwrap();
1448
1449 let test_data_path = std::path::Path::new("test_data/constraint_system_v2.bin");
1451
1452 if let Some(parent) = test_data_path.parent() {
1454 std::fs::create_dir_all(parent).unwrap();
1455 }
1456
1457 std::fs::write(test_data_path, &buf).unwrap();
1458
1459 println!("Created reference binary file at: {:?}", test_data_path);
1460 println!("Binary data length: {} bytes", buf.len());
1461 }
1462
1463 #[test]
1466 fn test_deserialize_from_reference_binary_file() {
1467 let binary_data = include_bytes!("../test_data/constraint_system_v2.bin");
1470
1471 let deserialized = ConstraintSystem::deserialize(&mut binary_data.as_slice()).unwrap();
1472
1473 assert_eq!(deserialized.value_vec_layout.n_const, 3);
1474 assert_eq!(deserialized.value_vec_layout.n_inout, 2);
1475 assert_eq!(deserialized.value_vec_layout.n_witness, 10);
1476 assert_eq!(deserialized.value_vec_layout.n_internal, 3);
1477 assert_eq!(deserialized.value_vec_layout.offset_inout, 4);
1478 assert_eq!(deserialized.value_vec_layout.offset_witness, 8);
1479 assert_eq!(deserialized.value_vec_layout.committed_total_len, 16);
1480 assert_eq!(deserialized.value_vec_layout.n_scratch, 0);
1481
1482 assert_eq!(deserialized.constants.len(), 3);
1483 assert_eq!(deserialized.constants[0].as_u64(), 1);
1484 assert_eq!(deserialized.constants[1].as_u64(), 42);
1485 assert_eq!(deserialized.constants[2].as_u64(), 0xDEADBEEF);
1486
1487 assert_eq!(deserialized.and_constraints.len(), 2);
1488 assert_eq!(deserialized.mul_constraints.len(), 1);
1489
1490 let version_bytes = &binary_data[0..4]; let expected_version_bytes = 2u32.to_le_bytes(); assert_eq!(
1496 version_bytes, expected_version_bytes,
1497 "Binary file version mismatch. If you made breaking changes, increment ConstraintSystem::SERIALIZATION_VERSION"
1498 );
1499 }
1500
1501 #[test]
1502 fn test_witness_serialization_round_trip_owned() {
1503 let data = vec![
1504 Word::from_u64(1),
1505 Word::from_u64(42),
1506 Word::from_u64(0xDEADBEEF),
1507 Word::from_u64(0x1234567890ABCDEF),
1508 ];
1509 let witness = ValuesData::owned(data.clone());
1510
1511 let mut buf = Vec::new();
1512 witness.serialize(&mut buf).unwrap();
1513
1514 let deserialized = ValuesData::deserialize(&mut buf.as_slice()).unwrap();
1515 assert_eq!(witness, deserialized);
1516 assert_eq!(deserialized.as_slice(), data.as_slice());
1517 }
1518
1519 #[test]
1520 fn test_witness_serialization_round_trip_borrowed() {
1521 let data = vec![Word::from_u64(123), Word::from_u64(456)];
1522 let witness = ValuesData::borrowed(&data);
1523
1524 let mut buf = Vec::new();
1525 witness.serialize(&mut buf).unwrap();
1526
1527 let deserialized = ValuesData::deserialize(&mut buf.as_slice()).unwrap();
1528 assert_eq!(witness, deserialized);
1529 assert_eq!(deserialized.as_slice(), data.as_slice());
1530 }
1531
1532 #[test]
1533 fn test_witness_version_mismatch() {
1534 let mut buf = Vec::new();
1535 999u32.serialize(&mut buf).unwrap(); vec![Word::from_u64(1)].serialize(&mut buf).unwrap(); let result = ValuesData::deserialize(&mut buf.as_slice());
1539 assert!(result.is_err());
1540 match result.unwrap_err() {
1541 SerializationError::InvalidConstruction { name } => {
1542 assert_eq!(name, "Witness::version");
1543 }
1544 _ => panic!("Expected version mismatch error"),
1545 }
1546 }
1547
1548 #[test]
1551 #[ignore] fn create_witness_reference_binary_file() {
1553 let data = vec![
1554 Word::from_u64(1),
1555 Word::from_u64(42),
1556 Word::from_u64(0xDEADBEEF),
1557 Word::from_u64(0x1234567890ABCDEF),
1558 ];
1559 let witness = ValuesData::owned(data);
1560
1561 let mut buf = Vec::new();
1562 witness.serialize(&mut buf).unwrap();
1563
1564 let test_data_path = std::path::Path::new("verifier/core/test_data/witness_v1.bin");
1565
1566 if let Some(parent) = test_data_path.parent() {
1567 std::fs::create_dir_all(parent).unwrap();
1568 }
1569
1570 std::fs::write(test_data_path, &buf).unwrap();
1571
1572 println!("Created Witness reference binary file at: {:?}", test_data_path);
1573 println!("Binary data length: {} bytes", buf.len());
1574 }
1575
1576 #[test]
1580 fn test_witness_deserialize_from_reference_binary_file() {
1581 let binary_data = include_bytes!("../test_data/witness_v1.bin");
1582
1583 let deserialized = ValuesData::deserialize(&mut binary_data.as_slice()).unwrap();
1584
1585 assert_eq!(deserialized.len(), 4);
1586 assert_eq!(deserialized.as_slice()[0].as_u64(), 1);
1587 assert_eq!(deserialized.as_slice()[1].as_u64(), 42);
1588 assert_eq!(deserialized.as_slice()[2].as_u64(), 0xDEADBEEF);
1589 assert_eq!(deserialized.as_slice()[3].as_u64(), 0x1234567890ABCDEF);
1590
1591 let version_bytes = &binary_data[0..4]; let expected_version_bytes = 1u32.to_le_bytes(); assert_eq!(
1597 version_bytes, expected_version_bytes,
1598 "WitnessData binary file version mismatch. If you made breaking changes, increment WitnessData::SERIALIZATION_VERSION"
1599 );
1600 }
1601
1602 #[test]
1603 fn test_proof_serialization_round_trip_owned() {
1604 let transcript_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
1605 let challenger_type = "HasherChallenger<Sha256>".to_string();
1606 let proof = Proof::owned(transcript_data.clone(), challenger_type.clone());
1607
1608 let mut buf = Vec::new();
1609 proof.serialize(&mut buf).unwrap();
1610
1611 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
1612 assert_eq!(proof, deserialized);
1613 assert_eq!(deserialized.as_slice(), transcript_data.as_slice());
1614 assert_eq!(deserialized.challenger_type(), &challenger_type);
1615 }
1616
1617 #[test]
1618 fn test_proof_serialization_round_trip_borrowed() {
1619 let transcript_data = vec![0xAA, 0xBB, 0xCC, 0xDD];
1620 let challenger_type = "TestChallenger".to_string();
1621 let proof = Proof::borrowed(&transcript_data, challenger_type.clone());
1622
1623 let mut buf = Vec::new();
1624 proof.serialize(&mut buf).unwrap();
1625
1626 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
1627 assert_eq!(proof, deserialized);
1628 assert_eq!(deserialized.as_slice(), transcript_data.as_slice());
1629 assert_eq!(deserialized.challenger_type(), &challenger_type);
1630 }
1631
1632 #[test]
1633 fn test_proof_empty_transcript() {
1634 let proof = Proof::owned(vec![], "EmptyProof".to_string());
1635 assert!(proof.is_empty());
1636 assert_eq!(proof.len(), 0);
1637
1638 let mut buf = Vec::new();
1639 proof.serialize(&mut buf).unwrap();
1640
1641 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
1642 assert_eq!(proof, deserialized);
1643 assert!(deserialized.is_empty());
1644 }
1645
1646 #[test]
1647 fn test_proof_large_transcript() {
1648 let mut rng = StdRng::seed_from_u64(12345);
1649 let mut large_data = vec![0u8; 10000];
1650 rng.fill_bytes(&mut large_data);
1651
1652 let challenger_type = "LargeProofChallenger".to_string();
1653 let proof = Proof::owned(large_data.clone(), challenger_type.clone());
1654
1655 let mut buf = Vec::new();
1656 proof.serialize(&mut buf).unwrap();
1657
1658 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
1659 assert_eq!(proof, deserialized);
1660 assert_eq!(deserialized.len(), 10000);
1661 assert_eq!(deserialized.challenger_type(), &challenger_type);
1662 }
1663
1664 #[test]
1665 fn test_proof_version_mismatch() {
1666 let mut buf = Vec::new();
1667 999u32.serialize(&mut buf).unwrap(); "TestChallenger".serialize(&mut buf).unwrap(); vec![0xAAu8].serialize(&mut buf).unwrap(); let result = Proof::deserialize(&mut buf.as_slice());
1672 assert!(result.is_err());
1673 match result.unwrap_err() {
1674 SerializationError::InvalidConstruction { name } => {
1675 assert_eq!(name, "Proof::version");
1676 }
1677 _ => panic!("Expected version mismatch error"),
1678 }
1679 }
1680
1681 #[test]
1682 fn test_proof_into_owned() {
1683 let original_data = vec![1, 2, 3, 4, 5];
1684 let original_challenger = "TestChallenger".to_string();
1685 let proof = Proof::owned(original_data.clone(), original_challenger.clone());
1686
1687 let (data, challenger_type) = proof.into_owned();
1688 assert_eq!(data, original_data);
1689 assert_eq!(challenger_type, original_challenger);
1690 }
1691
1692 #[test]
1693 fn test_proof_to_owned() {
1694 let data = vec![0xFF, 0xEE, 0xDD];
1695 let challenger_type = "BorrowedChallenger".to_string();
1696 let borrowed_proof = Proof::borrowed(&data, challenger_type.clone());
1697
1698 let owned_proof = borrowed_proof.to_owned();
1699 assert_eq!(owned_proof.as_slice(), data);
1700 assert_eq!(owned_proof.challenger_type(), &challenger_type);
1701 drop(data); assert_eq!(owned_proof.len(), 3);
1704 }
1705
1706 #[test]
1707 fn test_proof_different_challenger_types() {
1708 let data = vec![0x42];
1709 let challengers = vec![
1710 "HasherChallenger<Sha256>".to_string(),
1711 "HasherChallenger<Blake2b>".to_string(),
1712 "CustomChallenger".to_string(),
1713 "".to_string(), ];
1715
1716 for challenger_type in challengers {
1717 let proof = Proof::owned(data.clone(), challenger_type.clone());
1718 let mut buf = Vec::new();
1719 proof.serialize(&mut buf).unwrap();
1720
1721 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
1722 assert_eq!(deserialized.challenger_type(), &challenger_type);
1723 }
1724 }
1725
1726 #[test]
1727 fn test_proof_serialization_with_different_sources() {
1728 let transcript_data = vec![0x11, 0x22, 0x33, 0x44];
1729 let challenger_type = "MultiSourceChallenger".to_string();
1730 let original = Proof::owned(transcript_data, challenger_type);
1731
1732 let mut vec_buf = Vec::new();
1734 original.serialize(&mut vec_buf).unwrap();
1735 let deserialized1 = Proof::deserialize(&mut vec_buf.as_slice()).unwrap();
1736 assert_eq!(original, deserialized1);
1737
1738 let mut bytes_buf = bytes::BytesMut::new();
1740 original.serialize(&mut bytes_buf).unwrap();
1741 let deserialized2 = Proof::deserialize(bytes_buf.freeze()).unwrap();
1742 assert_eq!(original, deserialized2);
1743 }
1744
1745 #[test]
1748 #[ignore] fn create_proof_reference_binary_file() {
1750 let transcript_data = vec![
1751 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
1752 0x32, 0x10, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE,
1753 ];
1754 let challenger_type = "HasherChallenger<Sha256>".to_string();
1755 let proof = Proof::owned(transcript_data, challenger_type);
1756
1757 let mut buf = Vec::new();
1758 proof.serialize(&mut buf).unwrap();
1759
1760 let test_data_path = std::path::Path::new("verifier/core/test_data/proof_v1.bin");
1761
1762 if let Some(parent) = test_data_path.parent() {
1763 std::fs::create_dir_all(parent).unwrap();
1764 }
1765
1766 std::fs::write(test_data_path, &buf).unwrap();
1767
1768 println!("Created Proof reference binary file at: {:?}", test_data_path);
1769 println!("Binary data length: {} bytes", buf.len());
1770 }
1771
1772 #[test]
1776 fn test_proof_deserialize_from_reference_binary_file() {
1777 let binary_data = include_bytes!("../test_data/proof_v1.bin");
1778
1779 let deserialized = Proof::deserialize(&mut binary_data.as_slice()).unwrap();
1780
1781 assert_eq!(deserialized.len(), 24); assert_eq!(deserialized.challenger_type(), "HasherChallenger<Sha256>");
1783
1784 let expected_data = vec![
1785 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
1786 0x32, 0x10, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE,
1787 ];
1788 assert_eq!(deserialized.as_slice(), expected_data);
1789
1790 let version_bytes = &binary_data[0..4]; let expected_version_bytes = 1u32.to_le_bytes(); assert_eq!(
1796 version_bytes, expected_version_bytes,
1797 "Proof binary file version mismatch. If you made breaking changes, increment Proof::SERIALIZATION_VERSION"
1798 );
1799 }
1800
1801 #[test]
1802 fn split_values_vec_and_combine() {
1803 let values = ValueVec::new(ValueVecLayout {
1804 n_const: 2,
1805 n_inout: 2,
1806 n_witness: 2,
1807 n_internal: 2,
1808 offset_inout: 2,
1809 offset_witness: 4,
1810 committed_total_len: 8,
1811 n_scratch: 0,
1812 });
1813
1814 let public = values.public();
1815 let non_public = values.non_public();
1816 let combined =
1817 ValueVec::new_from_data(values.layout.clone(), public.to_vec(), non_public.to_vec())
1818 .unwrap();
1819 assert_eq!(combined.combined_witness(), values.combined_witness());
1820 }
1821
1822 #[test]
1823 fn test_roundtrip_cs_and_witnesses_reconstruct_valuevec_with_scratch() {
1824 let layout = ValueVecLayout {
1826 n_const: 2,
1827 n_inout: 3,
1828 n_witness: 4,
1829 n_internal: 3,
1830 offset_inout: 4, offset_witness: 8, committed_total_len: 16,
1833 n_scratch: 5, };
1835
1836 let constants = vec![Word::from_u64(11), Word::from_u64(22)];
1837 let cs = ConstraintSystem::new(constants, layout.clone(), vec![], vec![]);
1838
1839 let mut values = cs.new_value_vec();
1841 let full_len = layout.committed_total_len + layout.n_scratch;
1842 for i in 0..full_len {
1843 let val = Word::from_u64(0xA5A5_5A5A ^ (i as u64 * 0x9E37_79B9));
1845 values.set(i, val);
1846 }
1847
1848 let public_data = ValuesData::from(values.public());
1850 let non_public_data = ValuesData::from(values.non_public());
1851
1852 let mut buf_cs = Vec::new();
1853 cs.serialize(&mut buf_cs).unwrap();
1854
1855 let mut buf_pub = Vec::new();
1856 public_data.serialize(&mut buf_pub).unwrap();
1857
1858 let mut buf_non_pub = Vec::new();
1859 non_public_data.serialize(&mut buf_non_pub).unwrap();
1860
1861 let cs2 = ConstraintSystem::deserialize(&mut buf_cs.as_slice()).unwrap();
1863 let pub2 = ValuesData::deserialize(&mut buf_pub.as_slice()).unwrap();
1864 let non_pub2 = ValuesData::deserialize(&mut buf_non_pub.as_slice()).unwrap();
1865
1866 let reconstructed = ValueVec::new_from_data(
1868 cs2.value_vec_layout.clone(),
1869 pub2.into_owned(),
1870 non_pub2.into_owned(),
1871 )
1872 .unwrap();
1873
1874 assert_eq!(reconstructed.combined_witness(), values.combined_witness());
1876
1877 let scratch_start = layout.committed_total_len;
1879 let scratch_end = scratch_start + layout.n_scratch;
1880 for i in scratch_start..scratch_end {
1881 assert_eq!(reconstructed.get(i), Word::ZERO, "scratch index {i} should be zero");
1882 }
1883 }
1884
1885 #[test]
1886 fn test_is_padding_comprehensive() {
1887 let layout = ValueVecLayout {
1889 n_const: 2, n_inout: 3, n_witness: 5, n_internal: 10, offset_inout: 4, offset_witness: 16, committed_total_len: 64, n_scratch: 0,
1897 };
1898
1899 assert!(!layout.is_padding(ValueIndex(0)), "index 0 should be constant");
1901 assert!(!layout.is_padding(ValueIndex(1)), "index 1 should be constant");
1902
1903 assert!(
1905 layout.is_padding(ValueIndex(2)),
1906 "index 2 should be padding between const and inout"
1907 );
1908 assert!(
1909 layout.is_padding(ValueIndex(3)),
1910 "index 3 should be padding between const and inout"
1911 );
1912
1913 assert!(!layout.is_padding(ValueIndex(4)), "index 4 should be inout");
1915 assert!(!layout.is_padding(ValueIndex(5)), "index 5 should be inout");
1916 assert!(!layout.is_padding(ValueIndex(6)), "index 6 should be inout");
1917
1918 for i in 7..16 {
1920 assert!(
1921 layout.is_padding(ValueIndex(i)),
1922 "index {} should be padding between inout and witness",
1923 i
1924 );
1925 }
1926
1927 for i in 16..21 {
1929 assert!(!layout.is_padding(ValueIndex(i)), "index {} should be witness", i);
1930 }
1931
1932 for i in 21..31 {
1934 assert!(!layout.is_padding(ValueIndex(i)), "index {} should be internal", i);
1935 }
1936
1937 for i in 31..64 {
1939 assert!(
1940 layout.is_padding(ValueIndex(i)),
1941 "index {} should be padding after internal",
1942 i
1943 );
1944 }
1945 }
1946
1947 #[test]
1948 fn test_is_padding_minimal_layout() {
1949 let layout = ValueVecLayout {
1951 n_const: 4, n_inout: 4, n_witness: 4, n_internal: 4, offset_inout: 4, offset_witness: 8, committed_total_len: 16, n_scratch: 0,
1959 };
1960
1961 for i in 0..16 {
1963 assert!(
1964 !layout.is_padding(ValueIndex(i)),
1965 "index {} should not be padding in minimal layout",
1966 i
1967 );
1968 }
1969 }
1970
1971 #[test]
1972 fn test_is_padding_public_section_min_size() {
1973 let layout = ValueVecLayout {
1975 n_const: 1, n_inout: 1, n_witness: 2, n_internal: 2, offset_inout: 4, offset_witness: 8, committed_total_len: 16, n_scratch: 0,
1983 };
1984
1985 assert!(!layout.is_padding(ValueIndex(0)), "index 0 should be constant");
1987
1988 assert!(layout.is_padding(ValueIndex(1)), "index 1 should be padding");
1990 assert!(layout.is_padding(ValueIndex(2)), "index 2 should be padding");
1991 assert!(layout.is_padding(ValueIndex(3)), "index 3 should be padding");
1992
1993 assert!(!layout.is_padding(ValueIndex(4)), "index 4 should be inout");
1995
1996 assert!(layout.is_padding(ValueIndex(5)), "index 5 should be padding");
1998 assert!(layout.is_padding(ValueIndex(6)), "index 6 should be padding");
1999 assert!(layout.is_padding(ValueIndex(7)), "index 7 should be padding");
2000
2001 assert!(!layout.is_padding(ValueIndex(8)), "index 8 should be witness");
2003 assert!(!layout.is_padding(ValueIndex(9)), "index 9 should be witness");
2004
2005 assert!(!layout.is_padding(ValueIndex(10)), "index 10 should be internal");
2007 assert!(!layout.is_padding(ValueIndex(11)), "index 11 should be internal");
2008
2009 for i in 12..16 {
2011 assert!(layout.is_padding(ValueIndex(i)), "index {} should be end padding", i);
2012 }
2013 }
2014
2015 #[test]
2016 fn test_is_padding_boundary_conditions() {
2017 let layout = ValueVecLayout {
2018 n_const: 2,
2019 n_inout: 2,
2020 n_witness: 4,
2021 n_internal: 4,
2022 offset_inout: 4,
2023 offset_witness: 8,
2024 committed_total_len: 16,
2025 n_scratch: 0,
2026 };
2027
2028 assert!(!layout.is_padding(ValueIndex(1)), "last constant should not be padding");
2030 assert!(layout.is_padding(ValueIndex(2)), "first padding after const should be padding");
2031
2032 assert!(layout.is_padding(ValueIndex(3)), "last padding before inout should be padding");
2033 assert!(!layout.is_padding(ValueIndex(4)), "first inout should not be padding");
2034
2035 assert!(!layout.is_padding(ValueIndex(5)), "last inout should not be padding");
2036 assert!(layout.is_padding(ValueIndex(6)), "first padding after inout should be padding");
2037
2038 assert!(layout.is_padding(ValueIndex(7)), "last padding before witness should be padding");
2039 assert!(!layout.is_padding(ValueIndex(8)), "first witness should not be padding");
2040
2041 assert!(!layout.is_padding(ValueIndex(11)), "last witness should not be padding");
2042 assert!(!layout.is_padding(ValueIndex(12)), "first internal should not be padding");
2043
2044 assert!(!layout.is_padding(ValueIndex(15)), "last internal should not be padding");
2045 }
2047
2048 #[test]
2049 fn test_validate_rejects_padding_references() {
2050 let mut cs = ConstraintSystem::new(
2051 vec![Word::from_u64(1)],
2052 ValueVecLayout {
2053 n_const: 1,
2054 n_inout: 1,
2055 n_witness: 2,
2056 n_internal: 2,
2057 offset_inout: 4,
2058 offset_witness: 8,
2059 committed_total_len: 16,
2060 n_scratch: 0,
2061 },
2062 vec![],
2063 vec![],
2064 );
2065
2066 cs.add_and_constraint(AndConstraint::plain_abc(
2068 vec![ValueIndex(0)], vec![ValueIndex(2)], vec![ValueIndex(8)], ));
2072
2073 let result = cs.validate_and_prepare();
2074 assert!(result.is_err(), "Should reject constraint referencing padding");
2075
2076 match result.unwrap_err() {
2077 ConstraintSystemError::PaddingValueIndex {
2078 constraint_type, ..
2079 } => {
2080 assert_eq!(constraint_type, "and");
2081 }
2082 other => panic!("Expected PaddingValueIndex error, got: {:?}", other),
2083 }
2084 }
2085
2086 #[test]
2087 fn test_validate_accepts_non_padding_references() {
2088 let mut cs = ConstraintSystem::new(
2089 vec![Word::from_u64(1), Word::from_u64(2)],
2090 ValueVecLayout {
2091 n_const: 2,
2092 n_inout: 2,
2093 n_witness: 4,
2094 n_internal: 4,
2095 offset_inout: 2,
2096 offset_witness: 4,
2097 committed_total_len: 16,
2098 n_scratch: 0,
2099 },
2100 vec![],
2101 vec![],
2102 );
2103
2104 cs.add_and_constraint(AndConstraint::plain_abc(
2106 vec![ValueIndex(0), ValueIndex(1)], vec![ValueIndex(2), ValueIndex(3)], vec![ValueIndex(4), ValueIndex(5)], ));
2110
2111 cs.add_mul_constraint(MulConstraint {
2112 a: vec![ShiftedValueIndex::plain(ValueIndex(6))], b: vec![ShiftedValueIndex::plain(ValueIndex(7))], hi: vec![ShiftedValueIndex::plain(ValueIndex(8))], lo: vec![ShiftedValueIndex::plain(ValueIndex(9))], });
2117
2118 let result = cs.validate_and_prepare();
2119 assert!(
2120 result.is_ok(),
2121 "Should accept constraints with only valid references: {:?}",
2122 result
2123 );
2124 }
2125
2126 #[test]
2127 fn test_is_padding_matches_compiler_requirements() {
2128 let layout1 = ValueVecLayout {
2136 n_const: 1,
2137 n_inout: 1,
2138 n_witness: 4,
2139 n_internal: 4,
2140 offset_inout: 1, offset_witness: 8, committed_total_len: 16,
2143 n_scratch: 0,
2144 };
2145
2146 assert!(!layout1.is_padding(ValueIndex(0)), "const should not be padding");
2148 assert!(!layout1.is_padding(ValueIndex(1)), "inout should not be padding");
2149 for i in 2..8 {
2150 assert!(
2151 layout1.is_padding(ValueIndex(i)),
2152 "index {} should be padding to meet MIN_WORDS_PER_SEGMENT",
2153 i
2154 );
2155 }
2156
2157 let layout2 = ValueVecLayout {
2159 n_const: 4,
2160 n_inout: 4,
2161 n_witness: 8,
2162 n_internal: 0,
2163 offset_inout: 4,
2164 offset_witness: 8, committed_total_len: 16,
2166 n_scratch: 0,
2167 };
2168
2169 for i in 0..8 {
2171 assert!(!layout2.is_padding(ValueIndex(i)), "index {} should not be padding", i);
2172 }
2173
2174 let layout3 = ValueVecLayout {
2177 n_const: 5,
2178 n_inout: 5,
2179 n_witness: 16,
2180 n_internal: 0,
2181 offset_inout: 5,
2182 offset_witness: 16, committed_total_len: 32,
2184 n_scratch: 0,
2185 };
2186
2187 for i in 0..5 {
2189 assert!(!layout3.is_padding(ValueIndex(i)), "const {} should not be padding", i);
2190 }
2191 for i in 5..10 {
2192 assert!(!layout3.is_padding(ValueIndex(i)), "inout {} should not be padding", i);
2193 }
2194 for i in 10..16 {
2195 assert!(
2196 layout3.is_padding(ValueIndex(i)),
2197 "index {} should be padding for power-of-2 alignment",
2198 i
2199 );
2200 }
2201
2202 let layout4 = ValueVecLayout {
2204 n_const: 2, n_inout: 2, n_witness: 4, n_internal: 4, offset_inout: 8, offset_witness: 16, committed_total_len: 32, n_scratch: 0,
2212 };
2213
2214 assert!(!layout4.is_padding(ValueIndex(0)));
2216 assert!(!layout4.is_padding(ValueIndex(1)));
2217
2218 for i in 2..8 {
2220 assert!(layout4.is_padding(ValueIndex(i)), "padding between const and inout at {}", i);
2221 }
2222
2223 assert!(!layout4.is_padding(ValueIndex(8)));
2225 assert!(!layout4.is_padding(ValueIndex(9)));
2226
2227 for i in 10..16 {
2229 assert!(
2230 layout4.is_padding(ValueIndex(i)),
2231 "padding between inout and witness at {}",
2232 i
2233 );
2234 }
2235
2236 for i in 16..20 {
2238 assert!(!layout4.is_padding(ValueIndex(i)), "witness at {}", i);
2239 }
2240
2241 for i in 20..24 {
2243 assert!(!layout4.is_padding(ValueIndex(i)), "internal at {}", i);
2244 }
2245
2246 for i in 24..32 {
2248 assert!(layout4.is_padding(ValueIndex(i)), "padding after internal at {}", i);
2249 }
2250 }
2251
2252 #[test]
2253 fn test_validate_rejects_out_of_range_indices() {
2254 let mut cs = ConstraintSystem::new(
2255 vec![Word::from_u64(1)],
2256 ValueVecLayout {
2257 n_const: 1,
2258 n_inout: 1,
2259 n_witness: 2,
2260 n_internal: 2,
2261 offset_inout: 4,
2262 offset_witness: 8,
2263 committed_total_len: 16,
2264 n_scratch: 0,
2265 },
2266 vec![],
2267 vec![],
2268 );
2269
2270 cs.add_and_constraint(AndConstraint::plain_abc(
2272 vec![ValueIndex(0)], vec![ValueIndex(16)], vec![ValueIndex(8)], ));
2276
2277 let result = cs.validate_and_prepare();
2278 assert!(result.is_err(), "Should reject constraint with out-of-range index");
2279
2280 match result.unwrap_err() {
2281 ConstraintSystemError::OutOfRangeValueIndex {
2282 constraint_type,
2283 operand_name,
2284 value_index,
2285 total_len,
2286 ..
2287 } => {
2288 assert_eq!(constraint_type, "and");
2289 assert_eq!(operand_name, "b");
2290 assert_eq!(value_index, 16);
2291 assert_eq!(total_len, 16);
2292 }
2293 other => panic!("Expected OutOfRangeValueIndex error, got: {:?}", other),
2294 }
2295 }
2296
2297 #[test]
2298 fn test_validate_rejects_out_of_range_in_mul_constraint() {
2299 let mut cs = ConstraintSystem::new(
2300 vec![Word::from_u64(1), Word::from_u64(2)],
2301 ValueVecLayout {
2302 n_const: 2,
2303 n_inout: 2,
2304 n_witness: 4,
2305 n_internal: 4,
2306 offset_inout: 2,
2307 offset_witness: 4,
2308 committed_total_len: 16,
2309 n_scratch: 0,
2310 },
2311 vec![],
2312 vec![],
2313 );
2314
2315 cs.add_mul_constraint(MulConstraint {
2317 a: vec![ShiftedValueIndex::plain(ValueIndex(0))], b: vec![ShiftedValueIndex::plain(ValueIndex(1))], hi: vec![ShiftedValueIndex::plain(ValueIndex(100))], lo: vec![ShiftedValueIndex::plain(ValueIndex(3))], });
2322
2323 let result = cs.validate_and_prepare();
2324 assert!(result.is_err(), "Should reject MUL constraint with out-of-range index");
2325
2326 match result.unwrap_err() {
2327 ConstraintSystemError::OutOfRangeValueIndex {
2328 constraint_type,
2329 operand_name,
2330 value_index,
2331 total_len,
2332 ..
2333 } => {
2334 assert_eq!(constraint_type, "mul");
2335 assert_eq!(operand_name, "hi");
2336 assert_eq!(value_index, 100);
2337 assert_eq!(total_len, 16);
2338 }
2339 other => panic!("Expected OutOfRangeValueIndex error, got: {:?}", other),
2340 }
2341 }
2342
2343 #[test]
2344 fn test_validate_checks_out_of_range_before_padding() {
2345 let mut cs = ConstraintSystem::new(
2349 vec![Word::from_u64(1)],
2350 ValueVecLayout {
2351 n_const: 1,
2352 n_inout: 1,
2353 n_witness: 2,
2354 n_internal: 2,
2355 offset_inout: 4,
2356 offset_witness: 8,
2357 committed_total_len: 16,
2358 n_scratch: 0,
2359 },
2360 vec![],
2361 vec![],
2362 );
2363
2364 cs.add_and_constraint(AndConstraint::plain_abc(
2367 vec![ValueIndex(0)],
2368 vec![ValueIndex(20)], vec![ValueIndex(8)],
2370 ));
2371
2372 let result = cs.validate_and_prepare();
2373 assert!(result.is_err());
2374
2375 match result.unwrap_err() {
2377 ConstraintSystemError::OutOfRangeValueIndex { .. } => {
2378 }
2380 other => panic!(
2381 "Expected OutOfRangeValueIndex to be detected before padding check, got: {:?}",
2382 other
2383 ),
2384 }
2385 }
2386}