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