acir/circuit/
mod.rs

1//! Native structures for representing ACIR
2
3pub mod black_box_functions;
4pub mod brillig;
5pub mod opcodes;
6
7use crate::{
8    SerializationFormat,
9    circuit::opcodes::display_opcode,
10    native_types::{Expression, Witness},
11    serialization::{self, deserialize_any_format, serialize_with_format},
12};
13use acir_field::AcirField;
14pub use opcodes::Opcode;
15use thiserror::Error;
16
17use std::{io::prelude::*, num::ParseIntError, str::FromStr};
18
19use base64::Engine;
20use flate2::Compression;
21use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializationError};
22
23use std::collections::BTreeSet;
24
25use self::{brillig::BrilligBytecode, opcodes::BlockId};
26
27/// A program represented by multiple ACIR [circuit][Circuit]'s. The execution trace of these
28/// circuits is dictated by construction of the [crate::native_types::WitnessStack].
29#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
30#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
31pub struct Program<F: AcirField> {
32    pub functions: Vec<Circuit<F>>,
33    pub unconstrained_functions: Vec<BrilligBytecode<F>>,
34}
35
36/// Representation of a single ACIR circuit. The execution trace of this structure
37/// is dictated by the construction of a [crate::native_types::WitnessMap]
38#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
39#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
40pub struct Circuit<F: AcirField> {
41    /// Name of the function represented by this circuit.
42    #[serde(default)] // For backwards compatibility
43    pub function_name: String,
44    /// The current highest witness index in the circuit.
45    ///
46    /// This is tracked as an optimization so that when new witness values are created, incrementing this witness
47    /// results in a new unique witness index without needing to scan all opcodes to find the maximum witness index.
48    ///
49    /// Note that if the current witness index is 0, it might mean that there were no witnesses created at all,
50    /// or that there was exactly one witness.
51    pub current_witness_index: u32,
52    /// The circuit opcodes representing the relationship between witness values.
53    ///
54    /// The opcodes should be further converted into a backend-specific circuit representation.
55    /// When initial witness inputs are provided, these opcodes can also be used for generating an execution trace.
56    pub opcodes: Vec<Opcode<F>>,
57
58    /// The set of private inputs to the circuit.
59    pub private_parameters: BTreeSet<Witness>,
60    // ACIR distinguishes between the public inputs which are provided externally or calculated within the circuit and returned.
61    // The elements of these sets may not be mutually exclusive, i.e. a parameter may be returned from the circuit.
62    // All public inputs (parameters and return values) must be provided to the verifier at verification time.
63    /// The set of public inputs provided by the prover.
64    pub public_parameters: PublicInputs,
65    /// The set of public inputs calculated within the circuit.
66    pub return_values: PublicInputs,
67    /// Maps opcode locations to failed assertion payloads.
68    /// The data in the payload is embedded in the circuit to provide useful feedback to users
69    /// when a constraint in the circuit is not satisfied.
70    ///
71    // Note: This should be a BTreeMap, but serde-reflect is creating invalid
72    // c++ code at the moment when it is, due to OpcodeLocation needing a comparison
73    // implementation which is never generated.
74    pub assert_messages: Vec<(OpcodeLocation, AssertionPayload<F>)>,
75}
76
77/// Enumeration of either an [expression][Expression] or a [memory identifier][BlockId].
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
79#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
80pub enum ExpressionOrMemory<F> {
81    Expression(Expression<F>),
82    Memory(BlockId),
83}
84
85/// Payload tied to an assertion failure.
86/// This data allows users to specify feedback upon a constraint not being satisfied in the circuit.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
88#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
89pub struct AssertionPayload<F> {
90    /// Selector that maps a hash of either a constant string or an internal compiler error type
91    /// to an ABI type. The ABI type should then be used to appropriately resolve the payload data.
92    pub error_selector: u64,
93    /// The dynamic payload data.
94    ///
95    /// Upon fetching the appropriate ABI type from the `error_selector`, the values
96    /// in this payload can be decoded into the given ABI type.
97    /// The payload is expected to be empty in the case of a constant string
98    /// as the string can be contained entirely within the error type and ABI type.
99    pub payload: Vec<ExpressionOrMemory<F>>,
100}
101
102/// Value for differentiating error types. Used internally by an [AssertionPayload].
103#[derive(Debug, Copy, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
104pub struct ErrorSelector(u64);
105
106impl ErrorSelector {
107    pub fn new(integer: u64) -> Self {
108        ErrorSelector(integer)
109    }
110
111    pub fn as_u64(&self) -> u64 {
112        self.0
113    }
114}
115
116impl Serialize for ErrorSelector {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: Serializer,
120    {
121        self.0.to_string().serialize(serializer)
122    }
123}
124
125impl<'de> Deserialize<'de> for ErrorSelector {
126    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127    where
128        D: Deserializer<'de>,
129    {
130        let s: String = Deserialize::deserialize(deserializer)?;
131        let as_u64 = s.parse().map_err(serde::de::Error::custom)?;
132        Ok(ErrorSelector(as_u64))
133    }
134}
135
136#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
137#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
138/// Opcodes are locatable so that callers can
139/// map opcodes to debug information related to their context.
140pub enum OpcodeLocation {
141    Acir(usize),
142    // TODO(https://github.com/noir-lang/noir/issues/5792): We can not get rid of this enum field entirely just yet as this format is still
143    // used for resolving assert messages which is a breaking serialization change.
144    Brillig { acir_index: usize, brillig_index: usize },
145}
146
147/// Opcodes are locatable so that callers can
148/// map opcodes to debug information related to their context.
149#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
150pub struct AcirOpcodeLocation(usize);
151impl std::fmt::Display for AcirOpcodeLocation {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        write!(f, "{}", self.0)
154    }
155}
156
157impl AcirOpcodeLocation {
158    pub fn new(index: usize) -> Self {
159        AcirOpcodeLocation(index)
160    }
161    pub fn index(&self) -> usize {
162        self.0
163    }
164}
165/// Index of Brillig opcode within a list of Brillig opcodes.
166/// To be used by callers for resolving debug information.
167#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
168pub struct BrilligOpcodeLocation(pub usize);
169
170impl OpcodeLocation {
171    // Utility method to allow easily comparing a resolved Brillig location and a debug Brillig location.
172    // This method is useful when fetching Brillig debug locations as this does not need an ACIR index,
173    // and just need the Brillig index.
174    pub fn to_brillig_location(self) -> Option<BrilligOpcodeLocation> {
175        match self {
176            OpcodeLocation::Brillig { brillig_index, .. } => {
177                Some(BrilligOpcodeLocation(brillig_index))
178            }
179            OpcodeLocation::Acir(_) => None,
180        }
181    }
182}
183
184impl std::fmt::Display for OpcodeLocation {
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186        match self {
187            OpcodeLocation::Acir(index) => write!(f, "{index}"),
188            OpcodeLocation::Brillig { acir_index, brillig_index } => {
189                write!(f, "{acir_index}.{brillig_index}")
190            }
191        }
192    }
193}
194
195#[derive(Error, Debug)]
196pub enum OpcodeLocationFromStrError {
197    #[error("Invalid opcode location string: {0}")]
198    InvalidOpcodeLocationString(String),
199}
200
201/// The implementation of display and FromStr allows serializing and deserializing a OpcodeLocation to a string.
202/// This is useful when used as key in a map that has to be serialized to JSON/TOML, for example when mapping an opcode to its metadata.
203impl FromStr for OpcodeLocation {
204    type Err = OpcodeLocationFromStrError;
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        let parts: Vec<_> = s.split('.').collect();
207
208        if parts.is_empty() || parts.len() > 2 {
209            return Err(OpcodeLocationFromStrError::InvalidOpcodeLocationString(s.to_string()));
210        }
211
212        fn parse_components(parts: Vec<&str>) -> Result<OpcodeLocation, ParseIntError> {
213            match parts.len() {
214                1 => {
215                    let index = parts[0].parse()?;
216                    Ok(OpcodeLocation::Acir(index))
217                }
218                2 => {
219                    let acir_index = parts[0].parse()?;
220                    let brillig_index = parts[1].parse()?;
221                    Ok(OpcodeLocation::Brillig { acir_index, brillig_index })
222                }
223                _ => unreachable!("`OpcodeLocation` has too many components"),
224            }
225        }
226
227        parse_components(parts)
228            .map_err(|_| OpcodeLocationFromStrError::InvalidOpcodeLocationString(s.to_string()))
229    }
230}
231
232impl std::fmt::Display for BrilligOpcodeLocation {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        let index = self.0;
235        write!(f, "{index}")
236    }
237}
238
239impl<F: AcirField> Circuit<F> {
240    /// Returns all witnesses which are required to execute the circuit successfully.
241    pub fn circuit_arguments(&self) -> BTreeSet<Witness> {
242        self.private_parameters.union(&self.public_parameters.0).copied().collect()
243    }
244
245    /// Returns all public inputs. This includes those provided as parameters to the circuit and those
246    /// computed as return values.
247    pub fn public_inputs(&self) -> PublicInputs {
248        let public_inputs =
249            self.public_parameters.0.union(&self.return_values.0).copied().collect();
250        PublicInputs(public_inputs)
251    }
252}
253
254impl<F: Serialize + AcirField> Program<F> {
255    /// Compress a serialized [Program].
256    fn compress(buf: Vec<u8>) -> std::io::Result<Vec<u8>> {
257        let mut compressed: Vec<u8> = Vec::new();
258        // Compress the data, which should help with formats that uses field names.
259        let mut encoder = flate2::write::GzEncoder::new(&mut compressed, Compression::default());
260        encoder.write_all(&buf)?;
261        encoder.finish()?;
262        Ok(compressed)
263    }
264
265    /// Serialize and compress a [Program] into bytes, using the given format.
266    pub fn serialize_program_with_format(program: &Self, format: serialization::Format) -> Vec<u8> {
267        let program_bytes =
268            serialize_with_format(program, format).expect("expected circuit to be serializable");
269        Self::compress(program_bytes).expect("expected circuit to compress")
270    }
271
272    /// Serialize and compress a [Program] into bytes, using the format from the environment, or the default format.
273    pub fn serialize_program(program: &Self) -> Vec<u8> {
274        let format = SerializationFormat::from_env().expect("invalid format");
275        Self::serialize_program_with_format(program, format.unwrap_or_default())
276    }
277
278    /// Serialize, compress then base64 encode a [Program], using the format from the environment, or the default format,
279    pub fn serialize_program_base64<S>(program: &Self, s: S) -> Result<S::Ok, S::Error>
280    where
281        S: Serializer,
282    {
283        let program_bytes = Program::serialize_program(program);
284        let encoded_b64 = base64::engine::general_purpose::STANDARD.encode(program_bytes);
285        s.serialize_str(&encoded_b64)
286    }
287}
288
289impl<F: AcirField + for<'a> Deserialize<'a>> Program<F> {
290    /// Decompress and deserialize bytes into a [Program].
291    fn read<R: Read>(reader: R) -> std::io::Result<Self> {
292        let mut gz_decoder = flate2::read::GzDecoder::new(reader);
293        let mut buf = Vec::new();
294        gz_decoder.read_to_end(&mut buf)?;
295        let program = deserialize_any_format(&buf)?;
296        Ok(program)
297    }
298
299    /// Deserialize bytecode.
300    pub fn deserialize_program(serialized_circuit: &[u8]) -> std::io::Result<Self> {
301        Program::read(serialized_circuit)
302    }
303
304    /// Deserialize and base64 decode program
305    pub fn deserialize_program_base64<'de, D>(deserializer: D) -> Result<Self, D::Error>
306    where
307        D: Deserializer<'de>,
308    {
309        let bytecode_b64: String = Deserialize::deserialize(deserializer)?;
310        let program_bytes = base64::engine::general_purpose::STANDARD
311            .decode(bytecode_b64)
312            .map_err(D::Error::custom)?;
313        let circuit = Self::deserialize_program(&program_bytes).map_err(D::Error::custom)?;
314        Ok(circuit)
315    }
316}
317
318impl<F: AcirField> std::fmt::Display for Circuit<F> {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        let write_witness_indices =
321            |f: &mut std::fmt::Formatter<'_>, indices: &[u32]| -> Result<(), std::fmt::Error> {
322                write!(f, "[")?;
323                for (index, witness_index) in indices.iter().enumerate() {
324                    write!(f, "w{witness_index}")?;
325                    if index != indices.len() - 1 {
326                        write!(f, ", ")?;
327                    }
328                }
329                writeln!(f, "]")
330            };
331
332        write!(f, "private parameters: ")?;
333        write_witness_indices(
334            f,
335            &self
336                .private_parameters
337                .iter()
338                .map(|witness| witness.witness_index())
339                .collect::<Vec<_>>(),
340        )?;
341
342        write!(f, "public parameters: ")?;
343        write_witness_indices(f, &self.public_parameters.indices())?;
344
345        write!(f, "return values: ")?;
346        write_witness_indices(f, &self.return_values.indices())?;
347
348        for opcode in &self.opcodes {
349            display_opcode(opcode, Some(&self.return_values), f)?;
350            writeln!(f)?;
351        }
352        Ok(())
353    }
354}
355
356impl<F: AcirField> std::fmt::Debug for Circuit<F> {
357    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358        std::fmt::Display::fmt(self, f)
359    }
360}
361
362impl<F: AcirField> std::fmt::Display for Program<F> {
363    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364        for (func_index, function) in self.functions.iter().enumerate() {
365            writeln!(f, "func {func_index}")?;
366            writeln!(f, "{function}")?;
367        }
368        for (func_index, function) in self.unconstrained_functions.iter().enumerate() {
369            writeln!(f, "unconstrained func {func_index}: {}", function.function_name)?;
370            let width = function.bytecode.len().to_string().len();
371            for (index, opcode) in function.bytecode.iter().enumerate() {
372                writeln!(f, "{index:>width$}: {opcode}")?;
373            }
374        }
375        Ok(())
376    }
377}
378
379impl<F: AcirField> std::fmt::Debug for Program<F> {
380    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381        std::fmt::Display::fmt(self, f)
382    }
383}
384
385#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
386#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
387pub struct PublicInputs(pub BTreeSet<Witness>);
388
389impl PublicInputs {
390    /// Returns the witness index of each public input
391    pub fn indices(&self) -> Vec<u32> {
392        self.0.iter().map(|witness| witness.witness_index()).collect()
393    }
394
395    pub fn contains(&self, index: usize) -> bool {
396        self.0.contains(&Witness(index as u32))
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::{Circuit, Compression};
403    use crate::circuit::Program;
404    use acir_field::{AcirField, FieldElement};
405    use serde::{Deserialize, Serialize};
406
407    #[test]
408    fn serialization_roundtrip() {
409        let src = "
410        private parameters: []
411        public parameters: [w2, w12]
412        return values: [w4, w12]
413        BLACKBOX::AND lhs: w1, rhs: w2, output: w3, bits: 4
414        BLACKBOX::RANGE input: w1, bits: 8
415        ";
416        let circuit = Circuit::from_str(src).unwrap();
417        let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() };
418
419        fn read_write<F: Serialize + for<'a> Deserialize<'a> + AcirField>(
420            program: Program<F>,
421        ) -> (Program<F>, Program<F>) {
422            let bytes = Program::serialize_program(&program);
423            let got_program = Program::deserialize_program(&bytes).unwrap();
424            (program, got_program)
425        }
426
427        let (circ, got_circ) = read_write(program);
428        assert_eq!(circ, got_circ);
429    }
430
431    #[test]
432    fn test_serialize() {
433        let src = "
434        private parameters: []
435        public parameters: [w2]
436        return values: [w2]
437        ASSERT 0 = 8
438        BLACKBOX::RANGE input: w1, bits: 8
439        BLACKBOX::AND lhs: w1, rhs: w2, output: w3, bits: 4
440        BLACKBOX::KECCAKF1600 inputs: [w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25], outputs: [w26, w27, w28, w29, w30, w31, w32, w33, w34, w35, w36, w37, w38, w39, w40, w41, w42, w43, w44, w45, w46, w47, w48, w49, w50]
441        ";
442        let circuit = Circuit::from_str(src).unwrap();
443        let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() };
444
445        let json = serde_json::to_string_pretty(&program).unwrap();
446
447        let deserialized = serde_json::from_str(&json).unwrap();
448        assert_eq!(program, deserialized);
449    }
450
451    #[test]
452    fn does_not_panic_on_invalid_circuit() {
453        use std::io::Write;
454
455        let bad_circuit = "I'm not an ACIR circuit".as_bytes();
456
457        // We expect to load circuits as compressed artifacts so we compress the junk circuit.
458        let mut zipped_bad_circuit = Vec::new();
459        let mut encoder =
460            flate2::write::GzEncoder::new(&mut zipped_bad_circuit, Compression::default());
461        encoder.write_all(bad_circuit).unwrap();
462        encoder.finish().unwrap();
463
464        let deserialization_result: Result<Program<FieldElement>, _> =
465            Program::deserialize_program(&zipped_bad_circuit);
466        assert!(deserialization_result.is_err());
467    }
468
469    #[test]
470    fn circuit_display_snapshot() {
471        let src = "
472        private parameters: []
473        public parameters: [w2]
474        return values: [w2]
475        ASSERT 0 = 2*w1 + 8
476        BLACKBOX::RANGE input: w1, bits: 8
477        BLACKBOX::AND lhs: w1, rhs: w2, output: w3, bits: 4
478        BLACKBOX::KECCAKF1600 inputs: [w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25], outputs: [w26, w27, w28, w29, w30, w31, w32, w33, w34, w35, w36, w37, w38, w39, w40, w41, w42, w43, w44, w45, w46, w47, w48, w49, w50]
479        ";
480        let circuit = Circuit::from_str(src).unwrap();
481
482        // All witnesses are expected to be formatted as `w{witness_index}`.
483        insta::assert_snapshot!(
484            circuit.to_string(),
485            @r"
486        private parameters: []
487        public parameters: [w2]
488        return values: [w2]
489        ASSERT 0 = 2*w1 + 8
490        BLACKBOX::RANGE input: w1, bits: 8
491        BLACKBOX::AND lhs: w1, rhs: w2, output: w3, bits: 4
492        BLACKBOX::KECCAKF1600 inputs: [w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25], outputs: [w26, w27, w28, w29, w30, w31, w32, w33, w34, w35, w36, w37, w38, w39, w40, w41, w42, w43, w44, w45, w46, w47, w48, w49, w50]
493        "
494        );
495    }
496
497    /// Property based testing for serialization
498    mod props {
499        use acir_field::FieldElement;
500        use proptest::prelude::*;
501        use proptest::test_runner::{TestCaseResult, TestRunner};
502
503        use crate::circuit::Program;
504        use crate::native_types::{WitnessMap, WitnessStack};
505        use crate::serialization::*;
506
507        // It's not possible to set the maximum size of collections via `ProptestConfig`, only an env var,
508        // because e.g. the `VecStrategy` uses `Config::default().max_default_size_range`. On top of that,
509        // `Config::default()` reads a static `DEFAULT_CONFIG`, which gets the env vars only once at the
510        // beginning, so we can't override this on a test-by-test basis, unless we use `fork`,
511        // which is a feature that is currently disabled, because it doesn't work with Wasm.
512        // We could add it as a `dev-dependency` just for this crate, but when I tried it just crashed.
513        // For now using a const so it's obvious we can't set it to different values for different tests.
514        const MAX_SIZE_RANGE: usize = 5;
515        const SIZE_RANGE_KEY: &str = "PROPTEST_MAX_DEFAULT_SIZE_RANGE";
516
517        // Define a wrapper around field so we can implement `Arbitrary`.
518        // NB there are other methods like `arbitrary_field_elements` around the codebase,
519        // but for `proptest_derive::Arbitrary` we need `F: AcirField + Arbitrary`.
520        acir_field::field_wrapper!(TestField, FieldElement);
521
522        impl Arbitrary for TestField {
523            type Parameters = ();
524            type Strategy = BoxedStrategy<Self>;
525
526            fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
527                any::<u128>().prop_map(|v| Self(FieldElement::from(v))).boxed()
528            }
529        }
530
531        /// Override the maximum size of collections created by `proptest`.
532        #[allow(unsafe_code)]
533        fn run_with_max_size_range<T, F>(cases: u32, f: F)
534        where
535            T: Arbitrary,
536            F: Fn(T) -> TestCaseResult,
537        {
538            let orig_size_range = std::env::var(SIZE_RANGE_KEY).ok();
539            // The defaults are only read once. If they are already set, leave them be.
540            if orig_size_range.is_none() {
541                unsafe {
542                    std::env::set_var(SIZE_RANGE_KEY, MAX_SIZE_RANGE.to_string());
543                }
544            }
545
546            let mut runner = TestRunner::new(ProptestConfig { cases, ..Default::default() });
547            let result = runner.run(&any::<T>(), f);
548
549            // Restore the original.
550            unsafe {
551                std::env::set_var(SIZE_RANGE_KEY, orig_size_range.unwrap_or_default());
552            }
553
554            result.unwrap();
555        }
556
557        #[test]
558        fn prop_program_msgpack_roundtrip() {
559            run_with_max_size_range(100, |(program, compact): (Program<TestField>, bool)| {
560                let bz = msgpack_serialize(&program, compact)?;
561                let de = msgpack_deserialize(&bz)?;
562                prop_assert_eq!(program, de);
563                Ok(())
564            });
565        }
566
567        #[test]
568        fn prop_program_roundtrip() {
569            run_with_max_size_range(10, |program: Program<TestField>| {
570                let bz = Program::serialize_program(&program);
571                let de = Program::deserialize_program(&bz)?;
572                prop_assert_eq!(program, de);
573                Ok(())
574            });
575        }
576
577        #[test]
578        fn prop_witness_stack_msgpack_roundtrip() {
579            run_with_max_size_range(10, |(witness, compact): (WitnessStack<TestField>, bool)| {
580                let bz = msgpack_serialize(&witness, compact)?;
581                let de = msgpack_deserialize(&bz)?;
582                prop_assert_eq!(witness, de);
583                Ok(())
584            });
585        }
586
587        #[test]
588        fn prop_witness_stack_roundtrip() {
589            run_with_max_size_range(10, |witness: WitnessStack<TestField>| {
590                let bz = witness.serialize()?;
591                let de = WitnessStack::deserialize(bz.as_slice())?;
592                prop_assert_eq!(witness, de);
593                Ok(())
594            });
595        }
596
597        #[test]
598        fn prop_witness_map_msgpack_roundtrip() {
599            run_with_max_size_range(10, |(witness, compact): (WitnessMap<TestField>, bool)| {
600                let bz = msgpack_serialize(&witness, compact)?;
601                let de = msgpack_deserialize(&bz)?;
602                prop_assert_eq!(witness, de);
603                Ok(())
604            });
605        }
606
607        #[test]
608        fn prop_witness_map_roundtrip() {
609            run_with_max_size_range(10, |witness: WitnessMap<TestField>| {
610                let bz = witness.serialize()?;
611                let de = WitnessMap::deserialize(bz.as_slice())?;
612                prop_assert_eq!(witness, de);
613                Ok(())
614            });
615        }
616    }
617}