1use std::collections::HashMap;
70
71use acir::{
72 AcirField, BlackBoxFunc,
73 brillig::ForeignCallResult,
74 circuit::{
75 AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation,
76 brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs},
77 opcodes::{AcirFunctionId, BlockId, FunctionInput, InvalidInputBitSize},
78 },
79 native_types::{Expression, Witness, WitnessMap},
80};
81use acvm_blackbox_solver::BlackBoxResolutionError;
82use brillig_vm::fuzzing::BranchToFeatureMap;
83use itertools::Itertools;
84
85use self::{arithmetic::ExpressionSolver, memory_op::MemoryOpSolver};
86use crate::BlackBoxFunctionSolver;
87
88use thiserror::Error;
89
90pub(crate) mod arithmetic;
92pub(crate) mod brillig;
94pub(crate) mod blackbox;
96pub(crate) mod memory_op;
97
98pub use self::brillig::{BrilligSolver, BrilligSolverStatus};
99pub use brillig::ForeignCallWaitInfo;
100use serde::{Deserialize, Serialize};
101
102#[derive(Debug, Clone, PartialEq)]
103pub enum ACVMStatus<F> {
104 Solved,
106
107 InProgress,
109
110 Failure(OpcodeResolutionError<F>),
113
114 RequiresForeignCall(ForeignCallWaitInfo<F>),
120
121 RequiresAcirCall(AcirCallWaitInfo<F>),
126}
127
128impl<F> std::fmt::Display for ACVMStatus<F> {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 match self {
131 ACVMStatus::Solved => write!(f, "Solved"),
132 ACVMStatus::InProgress => write!(f, "In progress"),
133 ACVMStatus::Failure(_) => write!(f, "Execution failure"),
134 ACVMStatus::RequiresForeignCall(_) => write!(f, "Waiting on foreign call"),
135 ACVMStatus::RequiresAcirCall(_) => write!(f, "Waiting on acir call"),
136 }
137 }
138}
139
140#[expect(clippy::large_enum_variant)]
141pub enum StepResult<'a, F, B: BlackBoxFunctionSolver<F>> {
142 Status(ACVMStatus<F>),
143 IntoBrillig(BrilligSolver<'a, F, B>),
144}
145
146#[derive(Clone, PartialEq, Eq, Debug, Error)]
155pub enum OpcodeNotSolvable<F> {
156 #[error("missing assignment for witness index {0}")]
157 MissingAssignment(u32),
158 #[error("Attempted to load uninitialized memory block")]
159 MissingMemoryBlock(u32),
160 #[error("expression has too many unknowns {0}")]
161 ExpressionHasTooManyUnknowns(Expression<F>),
162}
163
164#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
168pub enum ErrorLocation {
169 #[default]
170 Unresolved,
171 Resolved(OpcodeLocation),
172}
173
174impl std::fmt::Display for ErrorLocation {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 match self {
177 ErrorLocation::Unresolved => write!(f, "unresolved"),
178 ErrorLocation::Resolved(location) => {
179 write!(f, "{location}")
180 }
181 }
182 }
183}
184
185#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
188pub struct RawAssertionPayload<F> {
189 pub selector: ErrorSelector,
191 pub data: Vec<F>,
194}
195
196#[derive(Clone, PartialEq, Eq, Debug)]
200pub enum ResolvedAssertionPayload<F> {
201 String(String),
202 Raw(RawAssertionPayload<F>),
203}
204
205#[derive(Clone, PartialEq, Eq, Debug, Error)]
206pub enum OpcodeResolutionError<F> {
207 #[error("Cannot solve opcode: {0}")]
208 OpcodeNotSolvable(#[from] OpcodeNotSolvable<F>),
209 #[error("Cannot satisfy constraint")]
210 UnsatisfiedConstrain {
211 opcode_location: ErrorLocation,
212 payload: Option<ResolvedAssertionPayload<F>>,
213 },
214 #[error("Index out of bounds, array has size {array_size:?}, but index was {index:?}")]
215 IndexOutOfBounds {
216 opcode_location: ErrorLocation,
217 index: F,
218 array_size: u32,
219 payload: Option<ResolvedAssertionPayload<F>>,
226 },
227 #[error("Cannot solve opcode: {invalid_input_bit_size}")]
228 InvalidInputBitSize {
229 opcode_location: ErrorLocation,
230 invalid_input_bit_size: InvalidInputBitSize,
231 },
232 #[error("Failed to solve blackbox function: {0}, reason: {1}")]
233 BlackBoxFunctionFailed(BlackBoxFunc, String),
234 #[error("Failed to solve brillig function")]
235 BrilligFunctionFailed {
236 function_id: BrilligFunctionId,
237 call_stack: Vec<OpcodeLocation>,
238 payload: Option<ResolvedAssertionPayload<F>>,
239 },
240 #[error("Attempted to call `main` with a `Call` opcode")]
241 AcirMainCallAttempted { opcode_location: ErrorLocation },
242 #[error(
243 "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen"
244 )]
245 AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 },
246 #[error("Brillig function {function_id} returned data inconsistent with its call outputs")]
247 BrilligOutputsMismatch { function_id: BrilligFunctionId },
248 #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")]
249 PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F },
250 #[error("(--pedantic): Memory operations are expected to be 0 or 1, but found: {operation}")]
251 MemoryOperationLargerThanOne { opcode_location: ErrorLocation, operation: F },
252}
253
254impl<F> From<BlackBoxResolutionError> for OpcodeResolutionError<F> {
255 fn from(value: BlackBoxResolutionError) -> Self {
256 match value {
257 BlackBoxResolutionError::Failed(func, reason) => {
258 OpcodeResolutionError::BlackBoxFunctionFailed(func, reason)
259 }
260 BlackBoxResolutionError::AssertFailed(error) => {
261 OpcodeResolutionError::UnsatisfiedConstrain {
262 opcode_location: ErrorLocation::Unresolved,
263 payload: Some(ResolvedAssertionPayload::String(error)),
264 }
265 }
266 }
267 }
268}
269
270impl<F> From<InvalidInputBitSize> for OpcodeResolutionError<F> {
271 fn from(invalid_input_bit_size: InvalidInputBitSize) -> Self {
272 Self::InvalidInputBitSize {
273 opcode_location: ErrorLocation::Unresolved,
274 invalid_input_bit_size,
275 }
276 }
277}
278
279pub type ProfilingSamples = Vec<ProfilingSample>;
280
281#[derive(Default)]
282pub struct ProfilingSample {
283 pub call_stack: Vec<OpcodeLocation>,
284 pub brillig_function_id: Option<BrilligFunctionId>,
285}
286
287pub struct ACVM<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> {
288 status: ACVMStatus<F>,
289
290 backend: &'a B,
291
292 block_solvers: HashMap<BlockId, MemoryOpSolver<F>>,
294
295 opcodes: &'a [Opcode<F>],
297 instruction_pointer: usize,
299
300 witness_map: WitnessMap<F>,
303
304 brillig_solver: Option<BrilligSolver<'a, F, B>>,
305
306 acir_call_counter: usize,
309 acir_call_results: Vec<Vec<F>>,
312
313 unconstrained_functions: &'a [BrilligBytecode<F>],
315
316 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
317
318 profiling_active: bool,
319
320 profiling_samples: ProfilingSamples,
321
322 brillig_fuzzing_active: bool,
324
325 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
327
328 brillig_fuzzing_trace: Option<Vec<u32>>,
329}
330
331impl<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> ACVM<'a, F, B> {
332 pub fn new(
333 backend: &'a B,
334 opcodes: &'a [Opcode<F>],
335 initial_witness: WitnessMap<F>,
336 unconstrained_functions: &'a [BrilligBytecode<F>],
337 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
338 ) -> Self {
339 let status = if opcodes.is_empty() { ACVMStatus::Solved } else { ACVMStatus::InProgress };
340 ACVM {
341 status,
342 backend,
343 block_solvers: HashMap::default(),
344 opcodes,
345 instruction_pointer: 0,
346 witness_map: initial_witness,
347 brillig_solver: None,
348 acir_call_counter: 0,
349 acir_call_results: Vec::default(),
350 unconstrained_functions,
351 assertion_payloads,
352 profiling_active: false,
353 profiling_samples: Vec::new(),
354 brillig_fuzzing_active: false,
355 brillig_branch_to_feature_map: None,
356 brillig_fuzzing_trace: None,
357 }
358 }
359
360 pub fn with_profiler(&mut self, profiling_active: bool) {
362 self.profiling_active = profiling_active;
363 }
364
365 pub fn with_brillig_fuzzing(
367 &mut self,
368 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
369 ) {
370 self.brillig_fuzzing_active = brillig_branch_to_feature_map.is_some();
371 self.brillig_branch_to_feature_map = brillig_branch_to_feature_map;
372 }
373
374 pub fn get_brillig_fuzzing_trace(&self) -> Option<Vec<u32>> {
375 self.brillig_fuzzing_trace.clone()
376 }
377
378 pub fn witness_map(&self) -> &WitnessMap<F> {
382 &self.witness_map
383 }
384
385 pub fn overwrite_witness(&mut self, witness: Witness, value: F) -> Option<F> {
386 self.witness_map.insert(witness, value)
387 }
388
389 pub fn opcodes(&self) -> &[Opcode<F>] {
391 self.opcodes
392 }
393
394 pub fn instruction_pointer(&self) -> usize {
396 self.instruction_pointer
397 }
398
399 pub fn take_profiling_samples(&mut self) -> ProfilingSamples {
400 std::mem::take(&mut self.profiling_samples)
401 }
402
403 pub fn finalize(self) -> WitnessMap<F> {
405 if self.status != ACVMStatus::Solved {
406 panic!("ACVM execution is not complete: ({})", self.status);
407 }
408 self.witness_map
409 }
410
411 fn status(&mut self, status: ACVMStatus<F>) -> ACVMStatus<F> {
414 self.status = status.clone();
415 status
416 }
417
418 pub fn get_status(&self) -> &ACVMStatus<F> {
419 &self.status
420 }
421
422 fn fail(&mut self, error: OpcodeResolutionError<F>) -> ACVMStatus<F> {
425 self.status(ACVMStatus::Failure(error))
426 }
427
428 fn wait_for_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo<F>) -> ACVMStatus<F> {
431 self.status(ACVMStatus::RequiresForeignCall(foreign_call))
432 }
433
434 pub fn get_pending_foreign_call(&self) -> Option<&ForeignCallWaitInfo<F>> {
436 if let ACVMStatus::RequiresForeignCall(foreign_call) = &self.status {
437 Some(foreign_call)
438 } else {
439 None
440 }
441 }
442
443 pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult<F>) {
447 if !matches!(self.status, ACVMStatus::RequiresForeignCall(_)) {
448 panic!("ACVM is not expecting a foreign call response as no call was made");
449 }
450
451 let brillig_solver = self.brillig_solver.as_mut().expect("No active Brillig solver");
452 brillig_solver.resolve_pending_foreign_call(foreign_call_result);
453
454 self.status(ACVMStatus::InProgress);
456 }
457
458 fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo<F>) -> ACVMStatus<F> {
461 self.status(ACVMStatus::RequiresAcirCall(acir_call))
462 }
463
464 pub fn resolve_pending_acir_call(&mut self, call_result: Vec<F>) {
468 if !matches!(self.status, ACVMStatus::RequiresAcirCall(_)) {
469 panic!("ACVM is not expecting an ACIR call response as no call was made");
470 }
471
472 if self.acir_call_counter < self.acir_call_results.len() {
473 panic!("No unresolved ACIR calls");
474 }
475 self.acir_call_results.push(call_result);
476
477 self.status(ACVMStatus::InProgress);
479 }
480
481 pub fn solve(&mut self) -> ACVMStatus<F> {
488 while self.status == ACVMStatus::InProgress {
489 self.solve_opcode();
490 }
491 self.status.clone()
492 }
493
494 fn current_opcode(&self) -> &'a Opcode<F> {
495 &self.opcodes[self.instruction_pointer]
496 }
497
498 pub fn solve_opcode(&mut self) -> ACVMStatus<F> {
505 let resolution = match self.current_opcode() {
506 Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr),
507 Opcode::BlackBoxFuncCall(bb_func) => {
508 blackbox::solve(self.backend, &mut self.witness_map, bb_func)
509 }
510 Opcode::MemoryInit { block_id, init, .. } => {
511 self.solve_memory_init_opcode(*block_id, init)
512 }
513 Opcode::MemoryOp { block_id, op } => match self.block_solvers.get_mut(block_id) {
514 Some(solver) => solver.solve_memory_op(op, &mut self.witness_map),
515 None => Err(OpcodeResolutionError::OpcodeNotSolvable(
516 OpcodeNotSolvable::MissingMemoryBlock(block_id.as_u32()),
517 )),
518 },
519 Opcode::BrilligCall { id, inputs, outputs, predicate } => {
520 match self.solve_brillig_call_opcode(id, inputs, outputs, predicate) {
521 Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call),
522 res => res.map(|_| ()),
523 }
524 }
525 Opcode::Call { id, inputs, outputs, predicate } => {
526 match self.solve_call_opcode(id, inputs, outputs, predicate) {
527 Ok(Some(input_values)) => return self.wait_for_acir_call(input_values),
528 res => res.map(|_| ()),
529 }
530 }
531 };
532 self.handle_opcode_resolution(resolution)
533 }
534
535 fn handle_opcode_resolution(
538 &mut self,
539 resolution: Result<(), OpcodeResolutionError<F>>,
540 ) -> ACVMStatus<F> {
541 match resolution {
542 Ok(()) => {
543 self.instruction_pointer += 1;
544 if self.instruction_pointer == self.opcodes.len() {
545 self.status(ACVMStatus::Solved)
546 } else {
547 self.status(ACVMStatus::InProgress)
548 }
549 }
550 Err(mut error) => {
551 match &mut error {
552 OpcodeResolutionError::IndexOutOfBounds {
556 opcode_location: opcode_index,
557 payload: assertion_payload,
558 ..
559 } => {
560 let location = OpcodeLocation::Acir(self.instruction_pointer());
561 *opcode_index = ErrorLocation::Resolved(location);
562 *assertion_payload = self.extract_assertion_payload(location);
563 }
564 OpcodeResolutionError::UnsatisfiedConstrain {
565 opcode_location: opcode_index,
566 payload: assertion_payload,
567 } => {
568 let location = OpcodeLocation::Acir(self.instruction_pointer());
569 *opcode_index = ErrorLocation::Resolved(location);
570 *assertion_payload = self.extract_assertion_payload(location);
571 }
572 OpcodeResolutionError::InvalidInputBitSize {
573 opcode_location: opcode_index,
574 ..
575 } => {
576 let location = OpcodeLocation::Acir(self.instruction_pointer());
577 *opcode_index = ErrorLocation::Resolved(location);
578 }
579 _ => (),
581 }
582 self.fail(error)
583 }
584 }
585 }
586
587 fn extract_assertion_payload(
588 &self,
589 location: OpcodeLocation,
590 ) -> Option<ResolvedAssertionPayload<F>> {
591 let (_, assertion_descriptor) =
592 self.assertion_payloads.iter().find(|(loc, _)| location == *loc)?;
593 let mut fields = Vec::new();
594 for expr in &assertion_descriptor.payload {
595 match expr {
596 ExpressionOrMemory::Expression(expr) => {
597 let value = get_value(expr, &self.witness_map).ok()?;
598 fields.push(value);
599 }
600 ExpressionOrMemory::Memory(block_id) => {
601 let memory_block = self.block_solvers.get(block_id)?;
602 fields.extend(&memory_block.block_value);
603 }
604 }
605 }
606 let error_selector = ErrorSelector::new(assertion_descriptor.error_selector);
607
608 Some(ResolvedAssertionPayload::Raw(RawAssertionPayload {
609 selector: error_selector,
610 data: fields,
611 }))
612 }
613
614 fn solve_memory_init_opcode(
618 &mut self,
619 block_id: BlockId,
620 init: &[Witness],
621 ) -> Result<(), OpcodeResolutionError<F>> {
622 let solver = MemoryOpSolver::new(init, &self.witness_map)?;
623 if self.block_solvers.insert(block_id, solver).is_some() {
624 return Err(OpcodeResolutionError::UnsatisfiedConstrain {
625 opcode_location: ErrorLocation::Unresolved,
626 payload: None,
627 });
628 }
629 Ok(())
630 }
631
632 fn solve_brillig_call_opcode(
636 &mut self,
637 id: &BrilligFunctionId,
638 inputs: &'a [BrilligInputs<F>],
639 outputs: &[BrilligOutputs],
640 predicate: &Expression<F>,
641 ) -> Result<Option<ForeignCallWaitInfo<F>>, OpcodeResolutionError<F>> {
642 let opcode_location =
643 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
644 if id.as_usize() >= self.unconstrained_functions.len() {
645 return Err(OpcodeResolutionError::BrilligFunctionFailed {
646 function_id: *id,
647 call_stack: vec![OpcodeLocation::Acir(self.instruction_pointer())],
648 payload: None,
649 });
650 }
651 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
652 return BrilligSolver::<F, B>::zero_out_brillig_outputs(&mut self.witness_map, outputs)
653 .map(|_| None);
654 }
655
656 let mut solver: BrilligSolver<'_, F, B> = match self.brillig_solver.take() {
659 Some(solver) => solver,
660 None => BrilligSolver::new_call(
661 &self.witness_map,
662 &self.block_solvers,
663 inputs,
664 &self.unconstrained_functions[id.as_usize()].bytecode,
665 self.backend,
666 self.instruction_pointer,
667 *id,
668 self.profiling_active,
669 self.brillig_branch_to_feature_map,
670 )?,
671 };
672
673 let result = solver.solve().inspect_err(|_| {
675 if self.brillig_fuzzing_active {
676 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
677 }
678 })?;
679
680 match result {
681 BrilligSolverStatus::ForeignCallWait(foreign_call) => {
682 self.brillig_solver = Some(solver);
684 Ok(Some(foreign_call))
685 }
686 BrilligSolverStatus::InProgress => {
687 unreachable!("Brillig solver still in progress")
688 }
689 BrilligSolverStatus::Finished => {
690 if self.brillig_fuzzing_active {
691 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
692 }
693 if self.profiling_active {
695 let profiling_info =
696 solver.finalize_with_profiling(&mut self.witness_map, outputs)?;
697 profiling_info.into_iter().for_each(|sample| {
698 let mapped =
699 sample.call_stack.into_iter().map(|loc| OpcodeLocation::Brillig {
700 acir_index: self.instruction_pointer,
701 brillig_index: loc,
702 });
703 self.profiling_samples.push(ProfilingSample {
704 call_stack: std::iter::once(OpcodeLocation::Acir(
705 self.instruction_pointer,
706 ))
707 .chain(mapped)
708 .collect(),
709 brillig_function_id: Some(*id),
710 });
711 });
712 } else {
713 solver.finalize(&mut self.witness_map, outputs)?;
714 }
715
716 Ok(None)
717 }
718 }
719 }
720
721 pub fn step_into_brillig(&mut self) -> StepResult<'a, F, B> {
723 let Opcode::BrilligCall { id, inputs, outputs, predicate } = self.current_opcode() else {
724 return StepResult::Status(self.solve_opcode());
725 };
726
727 let opcode_location =
728 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
729 if id.as_usize() >= self.unconstrained_functions.len() {
730 return StepResult::Status(self.handle_opcode_resolution(Err(
731 OpcodeResolutionError::BrilligFunctionFailed {
732 function_id: *id,
733 call_stack: vec![OpcodeLocation::Acir(self.instruction_pointer())],
734 payload: None,
735 },
736 )));
737 }
738 let witness = &mut self.witness_map;
739 let should_skip = match is_predicate_false(witness, predicate, &opcode_location) {
740 Ok(result) => result,
741 Err(err) => return StepResult::Status(self.handle_opcode_resolution(Err(err))),
742 };
743 if should_skip {
744 let resolution = BrilligSolver::<F, B>::zero_out_brillig_outputs(witness, outputs);
745 return StepResult::Status(self.handle_opcode_resolution(resolution));
746 }
747
748 let solver = BrilligSolver::new_call(
749 witness,
750 &self.block_solvers,
751 inputs,
752 &self.unconstrained_functions[id.as_usize()].bytecode,
753 self.backend,
754 self.instruction_pointer,
755 *id,
756 self.profiling_active,
757 self.brillig_branch_to_feature_map,
758 );
759 match solver {
760 Ok(solver) => StepResult::IntoBrillig(solver),
761 Err(..) => StepResult::Status(self.handle_opcode_resolution(solver.map(|_| ()))),
762 }
763 }
764
765 pub fn finish_brillig_with_solver(&mut self, solver: BrilligSolver<'a, F, B>) -> ACVMStatus<F> {
767 if !matches!(self.current_opcode(), Opcode::BrilligCall { .. }) {
768 unreachable!("Not executing a Brillig/BrilligCall opcode");
769 }
770 self.brillig_solver = Some(solver);
771 self.solve_opcode()
772 }
773
774 pub fn solve_call_opcode(
780 &mut self,
781 id: &AcirFunctionId,
782 inputs: &[Witness],
783 outputs: &[Witness],
784 predicate: &Expression<F>,
785 ) -> Result<Option<AcirCallWaitInfo<F>>, OpcodeResolutionError<F>> {
786 let opcode_location =
787 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
788 if *id == AcirFunctionId::new(0) {
789 return Err(OpcodeResolutionError::AcirMainCallAttempted { opcode_location });
790 }
791
792 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
793 for output in outputs {
795 insert_value(output, F::zero(), &mut self.witness_map)?;
796 }
797 return Ok(None);
798 }
799
800 if self.acir_call_counter >= self.acir_call_results.len() {
801 let mut initial_witness = WitnessMap::default();
802 for (i, input_witness) in inputs.iter().enumerate() {
803 let input_value = *witness_to_value(&self.witness_map, *input_witness)?;
804 initial_witness.insert(Witness::new(i as u32), input_value);
805 }
806 return Ok(Some(AcirCallWaitInfo { id: *id, initial_witness }));
807 }
808
809 let result_values = &self.acir_call_results[self.acir_call_counter];
810 if outputs.len() != result_values.len() {
811 return Err(OpcodeResolutionError::AcirCallOutputsMismatch {
812 opcode_location,
813 results_size: result_values.len() as u32,
814 outputs_size: outputs.len() as u32,
815 });
816 }
817
818 for (output_witness, result_value) in outputs.iter().zip_eq(result_values) {
819 insert_value(output_witness, *result_value, &mut self.witness_map)?;
820 }
821
822 self.acir_call_counter += 1;
823 Ok(None)
824 }
825}
826
827pub fn witness_to_value<F>(
831 initial_witness: &WitnessMap<F>,
832 witness: Witness,
833) -> Result<&F, OpcodeResolutionError<F>> {
834 match initial_witness.get(&witness) {
835 Some(value) => Ok(value),
836 None => Err(OpcodeNotSolvable::MissingAssignment(witness.witness_index()).into()),
837 }
838}
839
840pub fn input_to_value<F: AcirField>(
841 initial_witness: &WitnessMap<F>,
842 input: FunctionInput<F>,
843) -> Result<F, OpcodeResolutionError<F>> {
844 match input {
845 FunctionInput::Witness(witness) => {
846 let initial_value = *witness_to_value(initial_witness, witness)?;
847 Ok(initial_value)
848 }
849 FunctionInput::Constant(value) => Ok(value),
850 }
851}
852
853pub fn check_bit_size<F: AcirField>(
854 value: F,
855 num_bits: u32,
856) -> Result<(), OpcodeResolutionError<F>> {
857 if value.num_bits() <= num_bits {
858 Ok(())
859 } else {
860 let value_num_bits = value.num_bits();
861 let value = value.to_string();
862 Err(OpcodeResolutionError::InvalidInputBitSize {
863 opcode_location: ErrorLocation::Unresolved,
864 invalid_input_bit_size: InvalidInputBitSize {
865 value,
866 value_num_bits,
867 max_bits: num_bits,
868 },
869 })
870 }
871}
872
873pub fn get_value<F: AcirField>(
876 expr: &Expression<F>,
877 initial_witness: &WitnessMap<F>,
878) -> Result<F, OpcodeResolutionError<F>> {
879 if let Some(&c) = expr.to_const() {
880 return Ok(c);
881 }
882 let expr = ExpressionSolver::evaluate(expr, initial_witness);
883 match expr.to_const() {
884 Some(value) => Ok(*value),
885 None => Err(OpcodeResolutionError::OpcodeNotSolvable(
886 OpcodeNotSolvable::MissingAssignment(any_witness_from_expression(&expr).unwrap().0),
887 )),
888 }
889}
890
891pub fn insert_value<F: AcirField>(
896 witness: &Witness,
897 value_to_insert: F,
898 initial_witness: &mut WitnessMap<F>,
899) -> Result<(), OpcodeResolutionError<F>> {
900 use std::collections::btree_map::Entry;
901 match initial_witness.entry(*witness) {
902 Entry::Vacant(e) => {
903 e.insert(value_to_insert);
904 Ok(())
905 }
906 Entry::Occupied(e) => {
907 if *e.get() != value_to_insert {
908 Err(OpcodeResolutionError::UnsatisfiedConstrain {
909 opcode_location: ErrorLocation::Unresolved,
910 payload: None,
911 })
912 } else {
913 Ok(())
914 }
915 }
916 }
917}
918
919fn any_witness_from_expression<F>(expr: &Expression<F>) -> Option<Witness> {
923 if expr.linear_combinations.is_empty() {
924 if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) }
925 } else {
926 Some(expr.linear_combinations[0].1)
927 }
928}
929
930pub(crate) fn is_predicate_false<F: AcirField>(
934 witness: &WitnessMap<F>,
935 predicate: &Expression<F>,
936 opcode_location: &ErrorLocation,
937) -> Result<bool, OpcodeResolutionError<F>> {
938 let pred_value = get_value(predicate, witness)?;
939 let predicate_is_false = pred_value.is_zero();
940
941 if !predicate_is_false && !pred_value.is_one() {
943 let opcode_location = *opcode_location;
944 return Err(OpcodeResolutionError::PredicateLargerThanOne { opcode_location, pred_value });
945 }
946
947 Ok(predicate_is_false)
948}
949
950#[derive(Debug, Clone, PartialEq)]
955pub struct AcirCallWaitInfo<F> {
956 pub id: AcirFunctionId,
958 pub initial_witness: WitnessMap<F>,
960}
961
962#[cfg(test)]
963mod tests {
964 use std::collections::BTreeMap;
965
966 use acir::{
967 FieldElement,
968 native_types::{Witness, WitnessMap},
969 parse_opcodes,
970 };
971
972 use crate::pwg::{ACVM, ACVMStatus, OpcodeNotSolvable, OpcodeResolutionError};
973
974 #[test]
975 fn solve_simple_circuit() {
976 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
977 (Witness(1), FieldElement::from(1u128)),
978 (Witness(2), FieldElement::from(1u128)),
979 (Witness(3), FieldElement::from(2u128)),
980 ]));
981 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
982
983 let src = "
984 BLACKBOX::RANGE input: w1, bits: 32
985 BLACKBOX::RANGE input: w2, bits: 32
986 BLACKBOX::RANGE input: w3, bits: 32
987 ASSERT w4 = 2*w1 - w2
988 ASSERT w5 = -w2*w4 + 1
989 ";
990 let opcodes = parse_opcodes(src).unwrap();
991
992 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
993 assert_eq!(acvm.solve(), ACVMStatus::Solved);
994 assert_eq!(acvm.witness_map()[&Witness(5)], FieldElement::from(0u128));
995 }
996
997 #[test]
998 fn insert_value_does_not_overwrite_on_conflict() {
999 use crate::pwg::insert_value;
1000
1001 let old_value = FieldElement::from(1u128);
1002 let new_value = FieldElement::from(2u128);
1003 let witness = Witness(0);
1004
1005 let mut witness_map = WitnessMap::new();
1006 insert_value(&witness, old_value, &mut witness_map).expect("first insert should succeed");
1007
1008 let result = insert_value(&witness, new_value, &mut witness_map);
1009 assert!(
1010 matches!(result, Err(OpcodeResolutionError::UnsatisfiedConstrain { .. })),
1011 "expected UnsatisfiedConstrain error on conflicting insert"
1012 );
1013 assert_eq!(witness_map[&witness], old_value, "map should still hold the original value");
1014 }
1015
1016 #[test]
1017 fn errors_on_memory_op_without_init() {
1018 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
1019 (Witness(0), FieldElement::from(0u128)),
1020 (Witness(1), FieldElement::from(0u128)),
1021 ]));
1022 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1023
1024 let src = "
1026 READ w1 = b0[w0]
1027 ";
1028 let opcodes = parse_opcodes(src).unwrap();
1029
1030 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1031 let status = acvm.solve();
1032 assert!(
1033 matches!(
1034 status,
1035 ACVMStatus::Failure(OpcodeResolutionError::OpcodeNotSolvable(
1036 OpcodeNotSolvable::MissingMemoryBlock(0)
1037 ))
1038 ),
1039 "expected MissingMemoryBlock(0) failure, got {status:?}",
1040 );
1041 }
1042
1043 #[test]
1044 fn errors_on_duplicate_memory_init() {
1045 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
1046 (Witness(1), FieldElement::from(1u128)),
1047 (Witness(2), FieldElement::from(2u128)),
1048 ]));
1049 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1050
1051 let src = "
1052 INIT b0 = [w1, w2]
1053 INIT b0 = [w1, w2]
1054 ";
1055 let opcodes = parse_opcodes(src).unwrap();
1056
1057 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1058 let status = acvm.solve();
1059 assert!(
1060 matches!(
1061 status,
1062 ACVMStatus::Failure(OpcodeResolutionError::UnsatisfiedConstrain { .. })
1063 ),
1064 "expected UnsatisfiedConstrain failure, got {status:?}",
1065 );
1066 assert_eq!(acvm.get_status(), &status, "status field should reflect the returned failure");
1067 }
1068
1069 #[test]
1070 fn errors_when_calling_function_zero() {
1071 let initial_witness =
1072 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1073 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1074
1075 let src = "
1076 CALL func: 0, predicate: 1, inputs: [w1], outputs: [w2]
1077 ";
1078 let opcodes = parse_opcodes(src).unwrap();
1079
1080 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1081 assert!(matches!(
1082 acvm.solve(),
1083 ACVMStatus::Failure(OpcodeResolutionError::AcirMainCallAttempted { .. })
1084 ));
1085 }
1086
1087 mod brillig_oob {
1088 use std::collections::BTreeMap;
1089
1090 use acir::{
1091 FieldElement,
1092 circuit::{
1093 OpcodeLocation,
1094 brillig::{BrilligBytecode, BrilligFunctionId},
1095 },
1096 native_types::{Witness, WitnessMap},
1097 parse_opcodes,
1098 };
1099
1100 use crate::pwg::{ACVM, ACVMStatus, OpcodeResolutionError, StepResult};
1101 use test_case::test_case;
1102
1103 #[test_case(0, 1, 0 ; "empty function table")]
1104 #[test_case(3, 1, 1 ; "id past end of table")]
1105 #[test_case(5, 0, 0 ; "false predicate does not bypass check")]
1106 fn brillig_call_with_out_of_bounds_id_fails(
1107 func_id: u32,
1108 predicate: u32,
1109 table_size: usize,
1110 ) {
1111 let initial_witness =
1112 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1113 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1114
1115 let src = format!(
1116 "BRILLIG CALL func: {func_id}, predicate: {predicate}, inputs: [w1], outputs: [w2]"
1117 );
1118 let opcodes = parse_opcodes(&src).unwrap();
1119
1120 let unconstrained_functions =
1121 vec![
1122 BrilligBytecode { function_name: "unused".to_string(), bytecode: vec![] };
1123 table_size
1124 ];
1125
1126 let mut acvm =
1127 ACVM::new(&backend, &opcodes, initial_witness, &unconstrained_functions, &[]);
1128 assert_eq!(
1129 acvm.solve(),
1130 ACVMStatus::Failure(OpcodeResolutionError::BrilligFunctionFailed {
1131 function_id: BrilligFunctionId::new(func_id),
1132 call_stack: vec![OpcodeLocation::Acir(0)],
1133 payload: None,
1134 }),
1135 );
1136 }
1137
1138 #[test]
1139 fn step_into_brillig_fails_on_out_of_bounds_id() {
1140 let initial_witness =
1141 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1142 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1143
1144 let src = "
1145 BRILLIG CALL func: 2, predicate: 1, inputs: [w1], outputs: [w2]
1146 ";
1147 let opcodes = parse_opcodes(src).unwrap();
1148
1149 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1150 let step = acvm.step_into_brillig();
1151 match step {
1152 StepResult::Status(ACVMStatus::Failure(
1153 OpcodeResolutionError::BrilligFunctionFailed {
1154 function_id,
1155 call_stack,
1156 payload,
1157 },
1158 )) => {
1159 assert_eq!(function_id, BrilligFunctionId::new(2));
1160 assert_eq!(call_stack, vec![OpcodeLocation::Acir(0)]);
1161 assert!(payload.is_none());
1162 }
1163 StepResult::Status(other) => {
1164 panic!("expected BrilligFunctionFailed, got status {other:?}")
1165 }
1166 StepResult::IntoBrillig(_) => {
1167 panic!("expected BrilligFunctionFailed, stepped into Brillig instead")
1168 }
1169 }
1170 }
1171 }
1172}