1pub 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#[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#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
39#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
40pub struct Circuit<F: AcirField> {
41 #[serde(default)] pub function_name: String,
44 pub current_witness_index: u32,
49 pub opcodes: Vec<Opcode<F>>,
54
55 pub private_parameters: BTreeSet<Witness>,
57 pub public_parameters: PublicInputs,
62 pub return_values: PublicInputs,
64 pub assert_messages: Vec<(OpcodeLocation, AssertionPayload<F>)>,
72}
73
74#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
85#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
86pub struct AssertionPayload<F> {
87 pub error_selector: u64,
90 pub payload: Vec<ExpressionOrMemory<F>>,
97}
98
99#[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))]
135pub enum OpcodeLocation {
138 Acir(usize),
139 Brillig { acir_index: usize, brillig_index: usize },
142}
143
144#[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#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
165pub struct BrilligOpcodeLocation(pub usize);
166
167impl OpcodeLocation {
168 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
198impl 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 pub fn circuit_arguments(&self) -> BTreeSet<Witness> {
239 self.private_parameters.union(&self.public_parameters.0).cloned().collect()
240 }
241
242 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 fn compress(buf: Vec<u8>) -> std::io::Result<Vec<u8>> {
254 let mut compressed: Vec<u8> = Vec::new();
255 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 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 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 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 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 pub fn deserialize_program(serialized_circuit: &[u8]) -> std::io::Result<Self> {
298 Program::read(serialized_circuit)
299 }
300
301 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 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 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 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 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 const MAX_SIZE_RANGE: usize = 5;
512 const SIZE_RANGE_KEY: &str = "PROPTEST_MAX_DEFAULT_SIZE_RANGE";
513
514 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 #[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 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 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}