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,
52 pub opcodes: Vec<Opcode<F>>,
57
58 pub private_parameters: BTreeSet<Witness>,
60 pub public_parameters: PublicInputs,
65 pub return_values: PublicInputs,
67 pub assert_messages: Vec<(OpcodeLocation, AssertionPayload<F>)>,
75}
76
77#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
88#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
89pub struct AssertionPayload<F> {
90 pub error_selector: u64,
93 pub payload: Vec<ExpressionOrMemory<F>>,
100}
101
102#[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))]
138pub enum OpcodeLocation {
141 Acir(usize),
142 Brillig { acir_index: usize, brillig_index: usize },
145}
146
147#[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#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
168pub struct BrilligOpcodeLocation(pub usize);
169
170impl OpcodeLocation {
171 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
201impl 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 pub fn circuit_arguments(&self) -> BTreeSet<Witness> {
242 self.private_parameters.union(&self.public_parameters.0).copied().collect()
243 }
244
245 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 fn compress(buf: Vec<u8>) -> std::io::Result<Vec<u8>> {
257 let mut compressed: Vec<u8> = Vec::new();
258 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 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 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 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 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 pub fn deserialize_program(serialized_circuit: &[u8]) -> std::io::Result<Self> {
301 Program::read(serialized_circuit)
302 }
303
304 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 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 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 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 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 const MAX_SIZE_RANGE: usize = 5;
515 const SIZE_RANGE_KEY: &str = "PROPTEST_MAX_DEFAULT_SIZE_RANGE";
516
517 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 #[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 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 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}