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;
83
84use self::{arithmetic::ExpressionSolver, memory_op::MemoryOpSolver};
85use crate::BlackBoxFunctionSolver;
86
87use thiserror::Error;
88
89pub(crate) mod arithmetic;
91pub(crate) mod brillig;
93pub(crate) mod blackbox;
95pub(crate) mod memory_op;
96
97pub use self::brillig::{BrilligSolver, BrilligSolverStatus};
98pub use brillig::ForeignCallWaitInfo;
99use serde::{Deserialize, Serialize};
100
101#[derive(Debug, Clone, PartialEq)]
102pub enum ACVMStatus<F> {
103 Solved,
105
106 InProgress,
108
109 Failure(OpcodeResolutionError<F>),
112
113 RequiresForeignCall(ForeignCallWaitInfo<F>),
119
120 RequiresAcirCall(AcirCallWaitInfo<F>),
125}
126
127impl<F> std::fmt::Display for ACVMStatus<F> {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 match self {
130 ACVMStatus::Solved => write!(f, "Solved"),
131 ACVMStatus::InProgress => write!(f, "In progress"),
132 ACVMStatus::Failure(_) => write!(f, "Execution failure"),
133 ACVMStatus::RequiresForeignCall(_) => write!(f, "Waiting on foreign call"),
134 ACVMStatus::RequiresAcirCall(_) => write!(f, "Waiting on acir call"),
135 }
136 }
137}
138
139#[expect(clippy::large_enum_variant)]
140pub enum StepResult<'a, F, B: BlackBoxFunctionSolver<F>> {
141 Status(ACVMStatus<F>),
142 IntoBrillig(BrilligSolver<'a, F, B>),
143}
144
145#[derive(Clone, PartialEq, Eq, Debug, Error)]
154pub enum OpcodeNotSolvable<F> {
155 #[error("missing assignment for witness index {0}")]
156 MissingAssignment(u32),
157 #[error("Attempted to load uninitialized memory block")]
158 MissingMemoryBlock(u32),
159 #[error("expression has too many unknowns {0}")]
160 ExpressionHasTooManyUnknowns(Expression<F>),
161}
162
163#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
167pub enum ErrorLocation {
168 #[default]
169 Unresolved,
170 Resolved(OpcodeLocation),
171}
172
173impl std::fmt::Display for ErrorLocation {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 match self {
176 ErrorLocation::Unresolved => write!(f, "unresolved"),
177 ErrorLocation::Resolved(location) => {
178 write!(f, "{location}")
179 }
180 }
181 }
182}
183
184#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
187pub struct RawAssertionPayload<F> {
188 pub selector: ErrorSelector,
190 pub data: Vec<F>,
193}
194
195#[derive(Clone, PartialEq, Eq, Debug)]
199pub enum ResolvedAssertionPayload<F> {
200 String(String),
201 Raw(RawAssertionPayload<F>),
202}
203
204#[derive(Clone, PartialEq, Eq, Debug, Error)]
205pub enum OpcodeResolutionError<F> {
206 #[error("Cannot solve opcode: {0}")]
207 OpcodeNotSolvable(#[from] OpcodeNotSolvable<F>),
208 #[error("Cannot satisfy constraint")]
209 UnsatisfiedConstrain {
210 opcode_location: ErrorLocation,
211 payload: Option<ResolvedAssertionPayload<F>>,
212 },
213 #[error("Index out of bounds, array has size {array_size:?}, but index was {index:?}")]
214 IndexOutOfBounds { opcode_location: ErrorLocation, index: F, array_size: u32 },
215 #[error("Cannot solve opcode: {invalid_input_bit_size}")]
216 InvalidInputBitSize {
217 opcode_location: ErrorLocation,
218 invalid_input_bit_size: InvalidInputBitSize,
219 },
220 #[error("Failed to solve blackbox function: {0}, reason: {1}")]
221 BlackBoxFunctionFailed(BlackBoxFunc, String),
222 #[error("Failed to solve brillig function")]
223 BrilligFunctionFailed {
224 function_id: BrilligFunctionId,
225 call_stack: Vec<OpcodeLocation>,
226 payload: Option<ResolvedAssertionPayload<F>>,
227 },
228 #[error("Attempted to call `main` with a `Call` opcode")]
229 AcirMainCallAttempted { opcode_location: ErrorLocation },
230 #[error(
231 "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen"
232 )]
233 AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 },
234 #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")]
235 PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F },
236 #[error("(--pedantic): Memory operations are expected to be 0 or 1, but found: {operation}")]
237 MemoryOperationLargerThanOne { opcode_location: ErrorLocation, operation: F },
238}
239
240impl<F> From<BlackBoxResolutionError> for OpcodeResolutionError<F> {
241 fn from(value: BlackBoxResolutionError) -> Self {
242 match value {
243 BlackBoxResolutionError::Failed(func, reason) => {
244 OpcodeResolutionError::BlackBoxFunctionFailed(func, reason)
245 }
246 BlackBoxResolutionError::AssertFailed(error) => {
247 OpcodeResolutionError::UnsatisfiedConstrain {
248 opcode_location: ErrorLocation::Unresolved,
249 payload: Some(ResolvedAssertionPayload::String(error)),
250 }
251 }
252 }
253 }
254}
255
256impl<F> From<InvalidInputBitSize> for OpcodeResolutionError<F> {
257 fn from(invalid_input_bit_size: InvalidInputBitSize) -> Self {
258 Self::InvalidInputBitSize {
259 opcode_location: ErrorLocation::Unresolved,
260 invalid_input_bit_size,
261 }
262 }
263}
264
265pub type ProfilingSamples = Vec<ProfilingSample>;
266
267#[derive(Default)]
268pub struct ProfilingSample {
269 pub call_stack: Vec<OpcodeLocation>,
270 pub brillig_function_id: Option<BrilligFunctionId>,
271}
272
273pub struct ACVM<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> {
274 status: ACVMStatus<F>,
275
276 backend: &'a B,
277
278 block_solvers: HashMap<BlockId, MemoryOpSolver<F>>,
280
281 opcodes: &'a [Opcode<F>],
283 instruction_pointer: usize,
285
286 witness_map: WitnessMap<F>,
289
290 brillig_solver: Option<BrilligSolver<'a, F, B>>,
291
292 acir_call_counter: usize,
295 acir_call_results: Vec<Vec<F>>,
298
299 unconstrained_functions: &'a [BrilligBytecode<F>],
301
302 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
303
304 profiling_active: bool,
305
306 profiling_samples: ProfilingSamples,
307
308 brillig_fuzzing_active: bool,
310
311 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
313
314 brillig_fuzzing_trace: Option<Vec<u32>>,
315}
316
317impl<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> ACVM<'a, F, B> {
318 pub fn new(
319 backend: &'a B,
320 opcodes: &'a [Opcode<F>],
321 initial_witness: WitnessMap<F>,
322 unconstrained_functions: &'a [BrilligBytecode<F>],
323 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
324 ) -> Self {
325 let status = if opcodes.is_empty() { ACVMStatus::Solved } else { ACVMStatus::InProgress };
326 ACVM {
327 status,
328 backend,
329 block_solvers: HashMap::default(),
330 opcodes,
331 instruction_pointer: 0,
332 witness_map: initial_witness,
333 brillig_solver: None,
334 acir_call_counter: 0,
335 acir_call_results: Vec::default(),
336 unconstrained_functions,
337 assertion_payloads,
338 profiling_active: false,
339 profiling_samples: Vec::new(),
340 brillig_fuzzing_active: false,
341 brillig_branch_to_feature_map: None,
342 brillig_fuzzing_trace: None,
343 }
344 }
345
346 pub fn with_profiler(&mut self, profiling_active: bool) {
348 self.profiling_active = profiling_active;
349 }
350
351 pub fn with_brillig_fuzzing(
353 &mut self,
354 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
355 ) {
356 self.brillig_fuzzing_active = brillig_branch_to_feature_map.is_some();
357 self.brillig_branch_to_feature_map = brillig_branch_to_feature_map;
358 }
359
360 pub fn get_brillig_fuzzing_trace(&self) -> Option<Vec<u32>> {
361 self.brillig_fuzzing_trace.clone()
362 }
363
364 pub fn witness_map(&self) -> &WitnessMap<F> {
368 &self.witness_map
369 }
370
371 pub fn overwrite_witness(&mut self, witness: Witness, value: F) -> Option<F> {
372 self.witness_map.insert(witness, value)
373 }
374
375 pub fn opcodes(&self) -> &[Opcode<F>] {
377 self.opcodes
378 }
379
380 pub fn instruction_pointer(&self) -> usize {
382 self.instruction_pointer
383 }
384
385 pub fn take_profiling_samples(&mut self) -> ProfilingSamples {
386 std::mem::take(&mut self.profiling_samples)
387 }
388
389 pub fn finalize(self) -> WitnessMap<F> {
391 if self.status != ACVMStatus::Solved {
392 panic!("ACVM execution is not complete: ({})", self.status);
393 }
394 self.witness_map
395 }
396
397 fn status(&mut self, status: ACVMStatus<F>) -> ACVMStatus<F> {
400 self.status = status.clone();
401 status
402 }
403
404 pub fn get_status(&self) -> &ACVMStatus<F> {
405 &self.status
406 }
407
408 fn fail(&mut self, error: OpcodeResolutionError<F>) -> ACVMStatus<F> {
411 self.status(ACVMStatus::Failure(error))
412 }
413
414 fn wait_for_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo<F>) -> ACVMStatus<F> {
417 self.status(ACVMStatus::RequiresForeignCall(foreign_call))
418 }
419
420 pub fn get_pending_foreign_call(&self) -> Option<&ForeignCallWaitInfo<F>> {
422 if let ACVMStatus::RequiresForeignCall(foreign_call) = &self.status {
423 Some(foreign_call)
424 } else {
425 None
426 }
427 }
428
429 pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult<F>) {
433 if !matches!(self.status, ACVMStatus::RequiresForeignCall(_)) {
434 panic!("ACVM is not expecting a foreign call response as no call was made");
435 }
436
437 let brillig_solver = self.brillig_solver.as_mut().expect("No active Brillig solver");
438 brillig_solver.resolve_pending_foreign_call(foreign_call_result);
439
440 self.status(ACVMStatus::InProgress);
442 }
443
444 fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo<F>) -> ACVMStatus<F> {
447 self.status(ACVMStatus::RequiresAcirCall(acir_call))
448 }
449
450 pub fn resolve_pending_acir_call(&mut self, call_result: Vec<F>) {
454 if !matches!(self.status, ACVMStatus::RequiresAcirCall(_)) {
455 panic!("ACVM is not expecting an ACIR call response as no call was made");
456 }
457
458 if self.acir_call_counter < self.acir_call_results.len() {
459 panic!("No unresolved ACIR calls");
460 }
461 self.acir_call_results.push(call_result);
462
463 self.status(ACVMStatus::InProgress);
465 }
466
467 pub fn solve(&mut self) -> ACVMStatus<F> {
474 while self.status == ACVMStatus::InProgress {
475 self.solve_opcode();
476 }
477 self.status.clone()
478 }
479
480 fn current_opcode(&self) -> &'a Opcode<F> {
481 &self.opcodes[self.instruction_pointer]
482 }
483
484 pub fn solve_opcode(&mut self) -> ACVMStatus<F> {
491 let resolution = match self.current_opcode() {
492 Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr),
493 Opcode::BlackBoxFuncCall(bb_func) => {
494 blackbox::solve(self.backend, &mut self.witness_map, bb_func)
495 }
496 Opcode::MemoryInit { block_id, init, .. } => {
497 MemoryOpSolver::new(init, &self.witness_map).map(|solver| {
498 let existing_block_id = self.block_solvers.insert(*block_id, solver);
499 assert!(existing_block_id.is_none(), "Memory block already initialized");
500 })
501 }
502 Opcode::MemoryOp { block_id, op } => {
503 let solver = self
504 .block_solvers
505 .get_mut(block_id)
506 .expect("Memory block should have been initialized before use");
507 solver.solve_memory_op(op, &mut self.witness_map)
508 }
509 Opcode::BrilligCall { id, inputs, outputs, predicate } => {
510 match self.solve_brillig_call_opcode(id, inputs, outputs, predicate) {
511 Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call),
512 res => res.map(|_| ()),
513 }
514 }
515 Opcode::Call { id, inputs, outputs, predicate } => {
516 match self.solve_call_opcode(id, inputs, outputs, predicate) {
517 Ok(Some(input_values)) => return self.wait_for_acir_call(input_values),
518 res => res.map(|_| ()),
519 }
520 }
521 };
522 self.handle_opcode_resolution(resolution)
523 }
524
525 fn handle_opcode_resolution(
528 &mut self,
529 resolution: Result<(), OpcodeResolutionError<F>>,
530 ) -> ACVMStatus<F> {
531 match resolution {
532 Ok(()) => {
533 self.instruction_pointer += 1;
534 if self.instruction_pointer == self.opcodes.len() {
535 self.status(ACVMStatus::Solved)
536 } else {
537 self.status(ACVMStatus::InProgress)
538 }
539 }
540 Err(mut error) => {
541 match &mut error {
542 OpcodeResolutionError::IndexOutOfBounds {
546 opcode_location: opcode_index,
547 ..
548 } => {
549 *opcode_index = ErrorLocation::Resolved(OpcodeLocation::Acir(
550 self.instruction_pointer(),
551 ));
552 }
553 OpcodeResolutionError::UnsatisfiedConstrain {
554 opcode_location: opcode_index,
555 payload: assertion_payload,
556 } => {
557 let location = OpcodeLocation::Acir(self.instruction_pointer());
558 *opcode_index = ErrorLocation::Resolved(location);
559 *assertion_payload = self.extract_assertion_payload(location);
560 }
561 OpcodeResolutionError::InvalidInputBitSize {
562 opcode_location: opcode_index,
563 ..
564 } => {
565 let location = OpcodeLocation::Acir(self.instruction_pointer());
566 *opcode_index = ErrorLocation::Resolved(location);
567 }
568 _ => (),
570 }
571 self.fail(error)
572 }
573 }
574 }
575
576 fn extract_assertion_payload(
577 &self,
578 location: OpcodeLocation,
579 ) -> Option<ResolvedAssertionPayload<F>> {
580 let (_, assertion_descriptor) =
581 self.assertion_payloads.iter().find(|(loc, _)| location == *loc)?;
582 let mut fields = Vec::new();
583 for expr in &assertion_descriptor.payload {
584 match expr {
585 ExpressionOrMemory::Expression(expr) => {
586 let value = get_value(expr, &self.witness_map).ok()?;
587 fields.push(value);
588 }
589 ExpressionOrMemory::Memory(block_id) => {
590 let memory_block = self.block_solvers.get(block_id)?;
591 fields.extend(&memory_block.block_value);
592 }
593 }
594 }
595 let error_selector = ErrorSelector::new(assertion_descriptor.error_selector);
596
597 Some(ResolvedAssertionPayload::Raw(RawAssertionPayload {
598 selector: error_selector,
599 data: fields,
600 }))
601 }
602
603 fn solve_brillig_call_opcode(
607 &mut self,
608 id: &BrilligFunctionId,
609 inputs: &'a [BrilligInputs<F>],
610 outputs: &[BrilligOutputs],
611 predicate: &Expression<F>,
612 ) -> Result<Option<ForeignCallWaitInfo<F>>, OpcodeResolutionError<F>> {
613 let opcode_location =
614 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
615 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
616 return BrilligSolver::<F, B>::zero_out_brillig_outputs(&mut self.witness_map, outputs)
617 .map(|_| None);
618 }
619
620 let mut solver: BrilligSolver<'_, F, B> = match self.brillig_solver.take() {
623 Some(solver) => solver,
624 None => BrilligSolver::new_call(
625 &self.witness_map,
626 &self.block_solvers,
627 inputs,
628 &self.unconstrained_functions[id.as_usize()].bytecode,
629 self.backend,
630 self.instruction_pointer,
631 *id,
632 self.profiling_active,
633 self.brillig_branch_to_feature_map,
634 )?,
635 };
636
637 let result = solver.solve().inspect_err(|_| {
639 if self.brillig_fuzzing_active {
640 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
641 }
642 })?;
643
644 match result {
645 BrilligSolverStatus::ForeignCallWait(foreign_call) => {
646 self.brillig_solver = Some(solver);
648 Ok(Some(foreign_call))
649 }
650 BrilligSolverStatus::InProgress => {
651 unreachable!("Brillig solver still in progress")
652 }
653 BrilligSolverStatus::Finished => {
654 if self.brillig_fuzzing_active {
655 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
656 }
657 if self.profiling_active {
659 let profiling_info =
660 solver.finalize_with_profiling(&mut self.witness_map, outputs)?;
661 profiling_info.into_iter().for_each(|sample| {
662 let mapped =
663 sample.call_stack.into_iter().map(|loc| OpcodeLocation::Brillig {
664 acir_index: self.instruction_pointer,
665 brillig_index: loc,
666 });
667 self.profiling_samples.push(ProfilingSample {
668 call_stack: std::iter::once(OpcodeLocation::Acir(
669 self.instruction_pointer,
670 ))
671 .chain(mapped)
672 .collect(),
673 brillig_function_id: Some(*id),
674 });
675 });
676 } else {
677 solver.finalize(&mut self.witness_map, outputs)?;
678 }
679
680 Ok(None)
681 }
682 }
683 }
684
685 pub fn step_into_brillig(&mut self) -> StepResult<'a, F, B> {
687 let Opcode::BrilligCall { id, inputs, outputs, predicate } = self.current_opcode() else {
688 return StepResult::Status(self.solve_opcode());
689 };
690
691 let opcode_location =
692 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
693 let witness = &mut self.witness_map;
694 let should_skip = match is_predicate_false(witness, predicate, &opcode_location) {
695 Ok(result) => result,
696 Err(err) => return StepResult::Status(self.handle_opcode_resolution(Err(err))),
697 };
698 if should_skip {
699 let resolution = BrilligSolver::<F, B>::zero_out_brillig_outputs(witness, outputs);
700 return StepResult::Status(self.handle_opcode_resolution(resolution));
701 }
702
703 let solver = BrilligSolver::new_call(
704 witness,
705 &self.block_solvers,
706 inputs,
707 &self.unconstrained_functions[id.as_usize()].bytecode,
708 self.backend,
709 self.instruction_pointer,
710 *id,
711 self.profiling_active,
712 self.brillig_branch_to_feature_map,
713 );
714 match solver {
715 Ok(solver) => StepResult::IntoBrillig(solver),
716 Err(..) => StepResult::Status(self.handle_opcode_resolution(solver.map(|_| ()))),
717 }
718 }
719
720 pub fn finish_brillig_with_solver(&mut self, solver: BrilligSolver<'a, F, B>) -> ACVMStatus<F> {
722 if !matches!(self.current_opcode(), Opcode::BrilligCall { .. }) {
723 unreachable!("Not executing a Brillig/BrilligCall opcode");
724 }
725 self.brillig_solver = Some(solver);
726 self.solve_opcode()
727 }
728
729 pub fn solve_call_opcode(
735 &mut self,
736 id: &AcirFunctionId,
737 inputs: &[Witness],
738 outputs: &[Witness],
739 predicate: &Expression<F>,
740 ) -> Result<Option<AcirCallWaitInfo<F>>, OpcodeResolutionError<F>> {
741 let opcode_location =
742 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
743 if *id == AcirFunctionId(0) {
744 return Err(OpcodeResolutionError::AcirMainCallAttempted { opcode_location });
745 }
746
747 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
748 for output in outputs {
750 insert_value(output, F::zero(), &mut self.witness_map)?;
751 }
752 return Ok(None);
753 }
754
755 if self.acir_call_counter >= self.acir_call_results.len() {
756 let mut initial_witness = WitnessMap::default();
757 for (i, input_witness) in inputs.iter().enumerate() {
758 let input_value = *witness_to_value(&self.witness_map, *input_witness)?;
759 initial_witness.insert(Witness(i as u32), input_value);
760 }
761 return Ok(Some(AcirCallWaitInfo { id: *id, initial_witness }));
762 }
763
764 let result_values = &self.acir_call_results[self.acir_call_counter];
765 if outputs.len() != result_values.len() {
766 return Err(OpcodeResolutionError::AcirCallOutputsMismatch {
767 opcode_location,
768 results_size: result_values.len() as u32,
769 outputs_size: outputs.len() as u32,
770 });
771 }
772
773 for (output_witness, result_value) in outputs.iter().zip(result_values) {
774 insert_value(output_witness, *result_value, &mut self.witness_map)?;
775 }
776
777 self.acir_call_counter += 1;
778 Ok(None)
779 }
780}
781
782pub fn witness_to_value<F>(
786 initial_witness: &WitnessMap<F>,
787 witness: Witness,
788) -> Result<&F, OpcodeResolutionError<F>> {
789 match initial_witness.get(&witness) {
790 Some(value) => Ok(value),
791 None => Err(OpcodeNotSolvable::MissingAssignment(witness.0).into()),
792 }
793}
794
795pub fn input_to_value<F: AcirField>(
796 initial_witness: &WitnessMap<F>,
797 input: FunctionInput<F>,
798) -> Result<F, OpcodeResolutionError<F>> {
799 match input {
800 FunctionInput::Witness(witness) => {
801 let initial_value = *witness_to_value(initial_witness, witness)?;
802 Ok(initial_value)
803 }
804 FunctionInput::Constant(value) => Ok(value),
805 }
806}
807
808pub fn check_bit_size<F: AcirField>(
809 value: F,
810 num_bits: u32,
811) -> Result<(), OpcodeResolutionError<F>> {
812 if value.num_bits() <= num_bits {
813 Ok(())
814 } else {
815 let value_num_bits = value.num_bits();
816 let value = value.to_string();
817 Err(OpcodeResolutionError::InvalidInputBitSize {
818 opcode_location: ErrorLocation::Unresolved,
819 invalid_input_bit_size: InvalidInputBitSize {
820 value,
821 value_num_bits,
822 max_bits: num_bits,
823 },
824 })
825 }
826}
827
828pub fn get_value<F: AcirField>(
831 expr: &Expression<F>,
832 initial_witness: &WitnessMap<F>,
833) -> Result<F, OpcodeResolutionError<F>> {
834 let expr = ExpressionSolver::evaluate(expr, initial_witness);
835 match expr.to_const() {
836 Some(value) => Ok(*value),
837 None => Err(OpcodeResolutionError::OpcodeNotSolvable(
838 OpcodeNotSolvable::MissingAssignment(any_witness_from_expression(&expr).unwrap().0),
839 )),
840 }
841}
842
843pub fn insert_value<F: AcirField>(
848 witness: &Witness,
849 value_to_insert: F,
850 initial_witness: &mut WitnessMap<F>,
851) -> Result<(), OpcodeResolutionError<F>> {
852 use std::collections::btree_map::Entry;
853 match initial_witness.entry(*witness) {
854 Entry::Vacant(e) => {
855 e.insert(value_to_insert);
856 Ok(())
857 }
858 Entry::Occupied(e) => {
859 if *e.get() != value_to_insert {
860 Err(OpcodeResolutionError::UnsatisfiedConstrain {
861 opcode_location: ErrorLocation::Unresolved,
862 payload: None,
863 })
864 } else {
865 Ok(())
866 }
867 }
868 }
869}
870
871fn any_witness_from_expression<F>(expr: &Expression<F>) -> Option<Witness> {
875 if expr.linear_combinations.is_empty() {
876 if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) }
877 } else {
878 Some(expr.linear_combinations[0].1)
879 }
880}
881
882pub(crate) fn is_predicate_false<F: AcirField>(
886 witness: &WitnessMap<F>,
887 predicate: &Expression<F>,
888 opcode_location: &ErrorLocation,
889) -> Result<bool, OpcodeResolutionError<F>> {
890 let pred_value = get_value(predicate, witness)?;
891 let predicate_is_false = pred_value.is_zero();
892
893 if !predicate_is_false && !pred_value.is_one() {
895 let opcode_location = *opcode_location;
896 return Err(OpcodeResolutionError::PredicateLargerThanOne { opcode_location, pred_value });
897 }
898
899 Ok(predicate_is_false)
900}
901
902#[derive(Debug, Clone, PartialEq)]
907pub struct AcirCallWaitInfo<F> {
908 pub id: AcirFunctionId,
910 pub initial_witness: WitnessMap<F>,
912}
913
914#[cfg(test)]
915mod tests {
916 use std::collections::BTreeMap;
917
918 use acir::{
919 FieldElement,
920 native_types::{Witness, WitnessMap},
921 parse_opcodes,
922 };
923
924 use crate::pwg::{ACVM, ACVMStatus, OpcodeResolutionError};
925
926 #[test]
927 fn solve_simple_circuit() {
928 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
929 (Witness(1), FieldElement::from(1u128)),
930 (Witness(2), FieldElement::from(1u128)),
931 (Witness(3), FieldElement::from(2u128)),
932 ]));
933 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
934
935 let src = "
936 BLACKBOX::RANGE input: w1, bits: 32
937 BLACKBOX::RANGE input: w2, bits: 32
938 BLACKBOX::RANGE input: w3, bits: 32
939 ASSERT w4 = 2*w1 - w2
940 ASSERT w5 = -w2*w4 + 1
941 ";
942 let opcodes = parse_opcodes(src).unwrap();
943
944 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
945 assert_eq!(acvm.solve(), ACVMStatus::Solved);
946 assert_eq!(acvm.witness_map()[&Witness(5)], FieldElement::from(0u128));
947 }
948
949 #[test]
950 fn insert_value_does_not_overwrite_on_conflict() {
951 use crate::pwg::insert_value;
952
953 let old_value = FieldElement::from(1u128);
954 let new_value = FieldElement::from(2u128);
955 let witness = Witness(0);
956
957 let mut witness_map = WitnessMap::new();
958 insert_value(&witness, old_value, &mut witness_map).expect("first insert should succeed");
959
960 let result = insert_value(&witness, new_value, &mut witness_map);
961 assert!(
962 matches!(result, Err(OpcodeResolutionError::UnsatisfiedConstrain { .. })),
963 "expected UnsatisfiedConstrain error on conflicting insert"
964 );
965 assert_eq!(witness_map[&witness], old_value, "map should still hold the original value");
966 }
967
968 #[test]
969 fn errors_when_calling_function_zero() {
970 let initial_witness =
971 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
972 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
973
974 let src = "
975 CALL func: 0, predicate: 1, inputs: [w1], outputs: [w2]
976 ";
977 let opcodes = parse_opcodes(src).unwrap();
978
979 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
980 assert!(matches!(
981 acvm.solve(),
982 ACVMStatus::Failure(OpcodeResolutionError::AcirMainCallAttempted { .. })
983 ));
984 }
985}