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 { opcode_location: ErrorLocation, index: F, array_size: u32 },
216 #[error("Cannot solve opcode: {invalid_input_bit_size}")]
217 InvalidInputBitSize {
218 opcode_location: ErrorLocation,
219 invalid_input_bit_size: InvalidInputBitSize,
220 },
221 #[error("Failed to solve blackbox function: {0}, reason: {1}")]
222 BlackBoxFunctionFailed(BlackBoxFunc, String),
223 #[error("Failed to solve brillig function")]
224 BrilligFunctionFailed {
225 function_id: BrilligFunctionId,
226 call_stack: Vec<OpcodeLocation>,
227 payload: Option<ResolvedAssertionPayload<F>>,
228 },
229 #[error("Attempted to call `main` with a `Call` opcode")]
230 AcirMainCallAttempted { opcode_location: ErrorLocation },
231 #[error(
232 "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen"
233 )]
234 AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 },
235 #[error("Brillig function {function_id} returned data inconsistent with its call outputs")]
236 BrilligOutputsMismatch { function_id: BrilligFunctionId },
237 #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")]
238 PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F },
239 #[error("(--pedantic): Memory operations are expected to be 0 or 1, but found: {operation}")]
240 MemoryOperationLargerThanOne { opcode_location: ErrorLocation, operation: F },
241}
242
243impl<F> From<BlackBoxResolutionError> for OpcodeResolutionError<F> {
244 fn from(value: BlackBoxResolutionError) -> Self {
245 match value {
246 BlackBoxResolutionError::Failed(func, reason) => {
247 OpcodeResolutionError::BlackBoxFunctionFailed(func, reason)
248 }
249 BlackBoxResolutionError::AssertFailed(error) => {
250 OpcodeResolutionError::UnsatisfiedConstrain {
251 opcode_location: ErrorLocation::Unresolved,
252 payload: Some(ResolvedAssertionPayload::String(error)),
253 }
254 }
255 }
256 }
257}
258
259impl<F> From<InvalidInputBitSize> for OpcodeResolutionError<F> {
260 fn from(invalid_input_bit_size: InvalidInputBitSize) -> Self {
261 Self::InvalidInputBitSize {
262 opcode_location: ErrorLocation::Unresolved,
263 invalid_input_bit_size,
264 }
265 }
266}
267
268pub type ProfilingSamples = Vec<ProfilingSample>;
269
270#[derive(Default)]
271pub struct ProfilingSample {
272 pub call_stack: Vec<OpcodeLocation>,
273 pub brillig_function_id: Option<BrilligFunctionId>,
274}
275
276pub struct ACVM<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> {
277 status: ACVMStatus<F>,
278
279 backend: &'a B,
280
281 block_solvers: HashMap<BlockId, MemoryOpSolver<F>>,
283
284 opcodes: &'a [Opcode<F>],
286 instruction_pointer: usize,
288
289 witness_map: WitnessMap<F>,
292
293 brillig_solver: Option<BrilligSolver<'a, F, B>>,
294
295 acir_call_counter: usize,
298 acir_call_results: Vec<Vec<F>>,
301
302 unconstrained_functions: &'a [BrilligBytecode<F>],
304
305 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
306
307 profiling_active: bool,
308
309 profiling_samples: ProfilingSamples,
310
311 brillig_fuzzing_active: bool,
313
314 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
316
317 brillig_fuzzing_trace: Option<Vec<u32>>,
318}
319
320impl<'a, F: AcirField, B: BlackBoxFunctionSolver<F>> ACVM<'a, F, B> {
321 pub fn new(
322 backend: &'a B,
323 opcodes: &'a [Opcode<F>],
324 initial_witness: WitnessMap<F>,
325 unconstrained_functions: &'a [BrilligBytecode<F>],
326 assertion_payloads: &'a [(OpcodeLocation, AssertionPayload<F>)],
327 ) -> Self {
328 let status = if opcodes.is_empty() { ACVMStatus::Solved } else { ACVMStatus::InProgress };
329 ACVM {
330 status,
331 backend,
332 block_solvers: HashMap::default(),
333 opcodes,
334 instruction_pointer: 0,
335 witness_map: initial_witness,
336 brillig_solver: None,
337 acir_call_counter: 0,
338 acir_call_results: Vec::default(),
339 unconstrained_functions,
340 assertion_payloads,
341 profiling_active: false,
342 profiling_samples: Vec::new(),
343 brillig_fuzzing_active: false,
344 brillig_branch_to_feature_map: None,
345 brillig_fuzzing_trace: None,
346 }
347 }
348
349 pub fn with_profiler(&mut self, profiling_active: bool) {
351 self.profiling_active = profiling_active;
352 }
353
354 pub fn with_brillig_fuzzing(
356 &mut self,
357 brillig_branch_to_feature_map: Option<&'a BranchToFeatureMap>,
358 ) {
359 self.brillig_fuzzing_active = brillig_branch_to_feature_map.is_some();
360 self.brillig_branch_to_feature_map = brillig_branch_to_feature_map;
361 }
362
363 pub fn get_brillig_fuzzing_trace(&self) -> Option<Vec<u32>> {
364 self.brillig_fuzzing_trace.clone()
365 }
366
367 pub fn witness_map(&self) -> &WitnessMap<F> {
371 &self.witness_map
372 }
373
374 pub fn overwrite_witness(&mut self, witness: Witness, value: F) -> Option<F> {
375 self.witness_map.insert(witness, value)
376 }
377
378 pub fn opcodes(&self) -> &[Opcode<F>] {
380 self.opcodes
381 }
382
383 pub fn instruction_pointer(&self) -> usize {
385 self.instruction_pointer
386 }
387
388 pub fn take_profiling_samples(&mut self) -> ProfilingSamples {
389 std::mem::take(&mut self.profiling_samples)
390 }
391
392 pub fn finalize(self) -> WitnessMap<F> {
394 if self.status != ACVMStatus::Solved {
395 panic!("ACVM execution is not complete: ({})", self.status);
396 }
397 self.witness_map
398 }
399
400 fn status(&mut self, status: ACVMStatus<F>) -> ACVMStatus<F> {
403 self.status = status.clone();
404 status
405 }
406
407 pub fn get_status(&self) -> &ACVMStatus<F> {
408 &self.status
409 }
410
411 fn fail(&mut self, error: OpcodeResolutionError<F>) -> ACVMStatus<F> {
414 self.status(ACVMStatus::Failure(error))
415 }
416
417 fn wait_for_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo<F>) -> ACVMStatus<F> {
420 self.status(ACVMStatus::RequiresForeignCall(foreign_call))
421 }
422
423 pub fn get_pending_foreign_call(&self) -> Option<&ForeignCallWaitInfo<F>> {
425 if let ACVMStatus::RequiresForeignCall(foreign_call) = &self.status {
426 Some(foreign_call)
427 } else {
428 None
429 }
430 }
431
432 pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult<F>) {
436 if !matches!(self.status, ACVMStatus::RequiresForeignCall(_)) {
437 panic!("ACVM is not expecting a foreign call response as no call was made");
438 }
439
440 let brillig_solver = self.brillig_solver.as_mut().expect("No active Brillig solver");
441 brillig_solver.resolve_pending_foreign_call(foreign_call_result);
442
443 self.status(ACVMStatus::InProgress);
445 }
446
447 fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo<F>) -> ACVMStatus<F> {
450 self.status(ACVMStatus::RequiresAcirCall(acir_call))
451 }
452
453 pub fn resolve_pending_acir_call(&mut self, call_result: Vec<F>) {
457 if !matches!(self.status, ACVMStatus::RequiresAcirCall(_)) {
458 panic!("ACVM is not expecting an ACIR call response as no call was made");
459 }
460
461 if self.acir_call_counter < self.acir_call_results.len() {
462 panic!("No unresolved ACIR calls");
463 }
464 self.acir_call_results.push(call_result);
465
466 self.status(ACVMStatus::InProgress);
468 }
469
470 pub fn solve(&mut self) -> ACVMStatus<F> {
477 while self.status == ACVMStatus::InProgress {
478 self.solve_opcode();
479 }
480 self.status.clone()
481 }
482
483 fn current_opcode(&self) -> &'a Opcode<F> {
484 &self.opcodes[self.instruction_pointer]
485 }
486
487 pub fn solve_opcode(&mut self) -> ACVMStatus<F> {
494 let resolution = match self.current_opcode() {
495 Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr),
496 Opcode::BlackBoxFuncCall(bb_func) => {
497 blackbox::solve(self.backend, &mut self.witness_map, bb_func)
498 }
499 Opcode::MemoryInit { block_id, init, .. } => {
500 self.solve_memory_init_opcode(*block_id, init)
501 }
502 Opcode::MemoryOp { block_id, op } => match self.block_solvers.get_mut(block_id) {
503 Some(solver) => solver.solve_memory_op(op, &mut self.witness_map),
504 None => Err(OpcodeResolutionError::OpcodeNotSolvable(
505 OpcodeNotSolvable::MissingMemoryBlock(block_id.as_u32()),
506 )),
507 },
508 Opcode::BrilligCall { id, inputs, outputs, predicate } => {
509 match self.solve_brillig_call_opcode(id, inputs, outputs, predicate) {
510 Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call),
511 res => res.map(|_| ()),
512 }
513 }
514 Opcode::Call { id, inputs, outputs, predicate } => {
515 match self.solve_call_opcode(id, inputs, outputs, predicate) {
516 Ok(Some(input_values)) => return self.wait_for_acir_call(input_values),
517 res => res.map(|_| ()),
518 }
519 }
520 };
521 self.handle_opcode_resolution(resolution)
522 }
523
524 fn handle_opcode_resolution(
527 &mut self,
528 resolution: Result<(), OpcodeResolutionError<F>>,
529 ) -> ACVMStatus<F> {
530 match resolution {
531 Ok(()) => {
532 self.instruction_pointer += 1;
533 if self.instruction_pointer == self.opcodes.len() {
534 self.status(ACVMStatus::Solved)
535 } else {
536 self.status(ACVMStatus::InProgress)
537 }
538 }
539 Err(mut error) => {
540 match &mut error {
541 OpcodeResolutionError::IndexOutOfBounds {
545 opcode_location: opcode_index,
546 ..
547 } => {
548 *opcode_index = ErrorLocation::Resolved(OpcodeLocation::Acir(
549 self.instruction_pointer(),
550 ));
551 }
552 OpcodeResolutionError::UnsatisfiedConstrain {
553 opcode_location: opcode_index,
554 payload: assertion_payload,
555 } => {
556 let location = OpcodeLocation::Acir(self.instruction_pointer());
557 *opcode_index = ErrorLocation::Resolved(location);
558 *assertion_payload = self.extract_assertion_payload(location);
559 }
560 OpcodeResolutionError::InvalidInputBitSize {
561 opcode_location: opcode_index,
562 ..
563 } => {
564 let location = OpcodeLocation::Acir(self.instruction_pointer());
565 *opcode_index = ErrorLocation::Resolved(location);
566 }
567 _ => (),
569 }
570 self.fail(error)
571 }
572 }
573 }
574
575 fn extract_assertion_payload(
576 &self,
577 location: OpcodeLocation,
578 ) -> Option<ResolvedAssertionPayload<F>> {
579 let (_, assertion_descriptor) =
580 self.assertion_payloads.iter().find(|(loc, _)| location == *loc)?;
581 let mut fields = Vec::new();
582 for expr in &assertion_descriptor.payload {
583 match expr {
584 ExpressionOrMemory::Expression(expr) => {
585 let value = get_value(expr, &self.witness_map).ok()?;
586 fields.push(value);
587 }
588 ExpressionOrMemory::Memory(block_id) => {
589 let memory_block = self.block_solvers.get(block_id)?;
590 fields.extend(&memory_block.block_value);
591 }
592 }
593 }
594 let error_selector = ErrorSelector::new(assertion_descriptor.error_selector);
595
596 Some(ResolvedAssertionPayload::Raw(RawAssertionPayload {
597 selector: error_selector,
598 data: fields,
599 }))
600 }
601
602 fn solve_memory_init_opcode(
606 &mut self,
607 block_id: BlockId,
608 init: &[Witness],
609 ) -> Result<(), OpcodeResolutionError<F>> {
610 let solver = MemoryOpSolver::new(init, &self.witness_map)?;
611 if self.block_solvers.insert(block_id, solver).is_some() {
612 return Err(OpcodeResolutionError::UnsatisfiedConstrain {
613 opcode_location: ErrorLocation::Unresolved,
614 payload: None,
615 });
616 }
617 Ok(())
618 }
619
620 fn solve_brillig_call_opcode(
624 &mut self,
625 id: &BrilligFunctionId,
626 inputs: &'a [BrilligInputs<F>],
627 outputs: &[BrilligOutputs],
628 predicate: &Expression<F>,
629 ) -> Result<Option<ForeignCallWaitInfo<F>>, OpcodeResolutionError<F>> {
630 let opcode_location =
631 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
632 if id.as_usize() >= self.unconstrained_functions.len() {
633 return Err(OpcodeResolutionError::BrilligFunctionFailed {
634 function_id: *id,
635 call_stack: vec![OpcodeLocation::Acir(self.instruction_pointer())],
636 payload: None,
637 });
638 }
639 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
640 return BrilligSolver::<F, B>::zero_out_brillig_outputs(&mut self.witness_map, outputs)
641 .map(|_| None);
642 }
643
644 let mut solver: BrilligSolver<'_, F, B> = match self.brillig_solver.take() {
647 Some(solver) => solver,
648 None => BrilligSolver::new_call(
649 &self.witness_map,
650 &self.block_solvers,
651 inputs,
652 &self.unconstrained_functions[id.as_usize()].bytecode,
653 self.backend,
654 self.instruction_pointer,
655 *id,
656 self.profiling_active,
657 self.brillig_branch_to_feature_map,
658 )?,
659 };
660
661 let result = solver.solve().inspect_err(|_| {
663 if self.brillig_fuzzing_active {
664 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
665 }
666 })?;
667
668 match result {
669 BrilligSolverStatus::ForeignCallWait(foreign_call) => {
670 self.brillig_solver = Some(solver);
672 Ok(Some(foreign_call))
673 }
674 BrilligSolverStatus::InProgress => {
675 unreachable!("Brillig solver still in progress")
676 }
677 BrilligSolverStatus::Finished => {
678 if self.brillig_fuzzing_active {
679 self.brillig_fuzzing_trace = Some(solver.get_fuzzing_trace());
680 }
681 if self.profiling_active {
683 let profiling_info =
684 solver.finalize_with_profiling(&mut self.witness_map, outputs)?;
685 profiling_info.into_iter().for_each(|sample| {
686 let mapped =
687 sample.call_stack.into_iter().map(|loc| OpcodeLocation::Brillig {
688 acir_index: self.instruction_pointer,
689 brillig_index: loc,
690 });
691 self.profiling_samples.push(ProfilingSample {
692 call_stack: std::iter::once(OpcodeLocation::Acir(
693 self.instruction_pointer,
694 ))
695 .chain(mapped)
696 .collect(),
697 brillig_function_id: Some(*id),
698 });
699 });
700 } else {
701 solver.finalize(&mut self.witness_map, outputs)?;
702 }
703
704 Ok(None)
705 }
706 }
707 }
708
709 pub fn step_into_brillig(&mut self) -> StepResult<'a, F, B> {
711 let Opcode::BrilligCall { id, inputs, outputs, predicate } = self.current_opcode() else {
712 return StepResult::Status(self.solve_opcode());
713 };
714
715 let opcode_location =
716 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
717 if id.as_usize() >= self.unconstrained_functions.len() {
718 return StepResult::Status(self.handle_opcode_resolution(Err(
719 OpcodeResolutionError::BrilligFunctionFailed {
720 function_id: *id,
721 call_stack: vec![OpcodeLocation::Acir(self.instruction_pointer())],
722 payload: None,
723 },
724 )));
725 }
726 let witness = &mut self.witness_map;
727 let should_skip = match is_predicate_false(witness, predicate, &opcode_location) {
728 Ok(result) => result,
729 Err(err) => return StepResult::Status(self.handle_opcode_resolution(Err(err))),
730 };
731 if should_skip {
732 let resolution = BrilligSolver::<F, B>::zero_out_brillig_outputs(witness, outputs);
733 return StepResult::Status(self.handle_opcode_resolution(resolution));
734 }
735
736 let solver = BrilligSolver::new_call(
737 witness,
738 &self.block_solvers,
739 inputs,
740 &self.unconstrained_functions[id.as_usize()].bytecode,
741 self.backend,
742 self.instruction_pointer,
743 *id,
744 self.profiling_active,
745 self.brillig_branch_to_feature_map,
746 );
747 match solver {
748 Ok(solver) => StepResult::IntoBrillig(solver),
749 Err(..) => StepResult::Status(self.handle_opcode_resolution(solver.map(|_| ()))),
750 }
751 }
752
753 pub fn finish_brillig_with_solver(&mut self, solver: BrilligSolver<'a, F, B>) -> ACVMStatus<F> {
755 if !matches!(self.current_opcode(), Opcode::BrilligCall { .. }) {
756 unreachable!("Not executing a Brillig/BrilligCall opcode");
757 }
758 self.brillig_solver = Some(solver);
759 self.solve_opcode()
760 }
761
762 pub fn solve_call_opcode(
768 &mut self,
769 id: &AcirFunctionId,
770 inputs: &[Witness],
771 outputs: &[Witness],
772 predicate: &Expression<F>,
773 ) -> Result<Option<AcirCallWaitInfo<F>>, OpcodeResolutionError<F>> {
774 let opcode_location =
775 ErrorLocation::Resolved(OpcodeLocation::Acir(self.instruction_pointer()));
776 if *id == AcirFunctionId::new(0) {
777 return Err(OpcodeResolutionError::AcirMainCallAttempted { opcode_location });
778 }
779
780 if is_predicate_false(&self.witness_map, predicate, &opcode_location)? {
781 for output in outputs {
783 insert_value(output, F::zero(), &mut self.witness_map)?;
784 }
785 return Ok(None);
786 }
787
788 if self.acir_call_counter >= self.acir_call_results.len() {
789 let mut initial_witness = WitnessMap::default();
790 for (i, input_witness) in inputs.iter().enumerate() {
791 let input_value = *witness_to_value(&self.witness_map, *input_witness)?;
792 initial_witness.insert(Witness::new(i as u32), input_value);
793 }
794 return Ok(Some(AcirCallWaitInfo { id: *id, initial_witness }));
795 }
796
797 let result_values = &self.acir_call_results[self.acir_call_counter];
798 if outputs.len() != result_values.len() {
799 return Err(OpcodeResolutionError::AcirCallOutputsMismatch {
800 opcode_location,
801 results_size: result_values.len() as u32,
802 outputs_size: outputs.len() as u32,
803 });
804 }
805
806 for (output_witness, result_value) in outputs.iter().zip_eq(result_values) {
807 insert_value(output_witness, *result_value, &mut self.witness_map)?;
808 }
809
810 self.acir_call_counter += 1;
811 Ok(None)
812 }
813}
814
815pub fn witness_to_value<F>(
819 initial_witness: &WitnessMap<F>,
820 witness: Witness,
821) -> Result<&F, OpcodeResolutionError<F>> {
822 match initial_witness.get(&witness) {
823 Some(value) => Ok(value),
824 None => Err(OpcodeNotSolvable::MissingAssignment(witness.witness_index()).into()),
825 }
826}
827
828pub fn input_to_value<F: AcirField>(
829 initial_witness: &WitnessMap<F>,
830 input: FunctionInput<F>,
831) -> Result<F, OpcodeResolutionError<F>> {
832 match input {
833 FunctionInput::Witness(witness) => {
834 let initial_value = *witness_to_value(initial_witness, witness)?;
835 Ok(initial_value)
836 }
837 FunctionInput::Constant(value) => Ok(value),
838 }
839}
840
841pub fn check_bit_size<F: AcirField>(
842 value: F,
843 num_bits: u32,
844) -> Result<(), OpcodeResolutionError<F>> {
845 if value.num_bits() <= num_bits {
846 Ok(())
847 } else {
848 let value_num_bits = value.num_bits();
849 let value = value.to_string();
850 Err(OpcodeResolutionError::InvalidInputBitSize {
851 opcode_location: ErrorLocation::Unresolved,
852 invalid_input_bit_size: InvalidInputBitSize {
853 value,
854 value_num_bits,
855 max_bits: num_bits,
856 },
857 })
858 }
859}
860
861pub fn get_value<F: AcirField>(
864 expr: &Expression<F>,
865 initial_witness: &WitnessMap<F>,
866) -> Result<F, OpcodeResolutionError<F>> {
867 if let Some(&c) = expr.to_const() {
868 return Ok(c);
869 }
870 let expr = ExpressionSolver::evaluate(expr, initial_witness);
871 match expr.to_const() {
872 Some(value) => Ok(*value),
873 None => Err(OpcodeResolutionError::OpcodeNotSolvable(
874 OpcodeNotSolvable::MissingAssignment(any_witness_from_expression(&expr).unwrap().0),
875 )),
876 }
877}
878
879pub fn insert_value<F: AcirField>(
884 witness: &Witness,
885 value_to_insert: F,
886 initial_witness: &mut WitnessMap<F>,
887) -> Result<(), OpcodeResolutionError<F>> {
888 use std::collections::btree_map::Entry;
889 match initial_witness.entry(*witness) {
890 Entry::Vacant(e) => {
891 e.insert(value_to_insert);
892 Ok(())
893 }
894 Entry::Occupied(e) => {
895 if *e.get() != value_to_insert {
896 Err(OpcodeResolutionError::UnsatisfiedConstrain {
897 opcode_location: ErrorLocation::Unresolved,
898 payload: None,
899 })
900 } else {
901 Ok(())
902 }
903 }
904 }
905}
906
907fn any_witness_from_expression<F>(expr: &Expression<F>) -> Option<Witness> {
911 if expr.linear_combinations.is_empty() {
912 if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) }
913 } else {
914 Some(expr.linear_combinations[0].1)
915 }
916}
917
918pub(crate) fn is_predicate_false<F: AcirField>(
922 witness: &WitnessMap<F>,
923 predicate: &Expression<F>,
924 opcode_location: &ErrorLocation,
925) -> Result<bool, OpcodeResolutionError<F>> {
926 let pred_value = get_value(predicate, witness)?;
927 let predicate_is_false = pred_value.is_zero();
928
929 if !predicate_is_false && !pred_value.is_one() {
931 let opcode_location = *opcode_location;
932 return Err(OpcodeResolutionError::PredicateLargerThanOne { opcode_location, pred_value });
933 }
934
935 Ok(predicate_is_false)
936}
937
938#[derive(Debug, Clone, PartialEq)]
943pub struct AcirCallWaitInfo<F> {
944 pub id: AcirFunctionId,
946 pub initial_witness: WitnessMap<F>,
948}
949
950#[cfg(test)]
951mod tests {
952 use std::collections::BTreeMap;
953
954 use acir::{
955 FieldElement,
956 native_types::{Witness, WitnessMap},
957 parse_opcodes,
958 };
959
960 use crate::pwg::{ACVM, ACVMStatus, OpcodeNotSolvable, OpcodeResolutionError};
961
962 #[test]
963 fn solve_simple_circuit() {
964 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
965 (Witness(1), FieldElement::from(1u128)),
966 (Witness(2), FieldElement::from(1u128)),
967 (Witness(3), FieldElement::from(2u128)),
968 ]));
969 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
970
971 let src = "
972 BLACKBOX::RANGE input: w1, bits: 32
973 BLACKBOX::RANGE input: w2, bits: 32
974 BLACKBOX::RANGE input: w3, bits: 32
975 ASSERT w4 = 2*w1 - w2
976 ASSERT w5 = -w2*w4 + 1
977 ";
978 let opcodes = parse_opcodes(src).unwrap();
979
980 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
981 assert_eq!(acvm.solve(), ACVMStatus::Solved);
982 assert_eq!(acvm.witness_map()[&Witness(5)], FieldElement::from(0u128));
983 }
984
985 #[test]
986 fn insert_value_does_not_overwrite_on_conflict() {
987 use crate::pwg::insert_value;
988
989 let old_value = FieldElement::from(1u128);
990 let new_value = FieldElement::from(2u128);
991 let witness = Witness(0);
992
993 let mut witness_map = WitnessMap::new();
994 insert_value(&witness, old_value, &mut witness_map).expect("first insert should succeed");
995
996 let result = insert_value(&witness, new_value, &mut witness_map);
997 assert!(
998 matches!(result, Err(OpcodeResolutionError::UnsatisfiedConstrain { .. })),
999 "expected UnsatisfiedConstrain error on conflicting insert"
1000 );
1001 assert_eq!(witness_map[&witness], old_value, "map should still hold the original value");
1002 }
1003
1004 #[test]
1005 fn errors_on_memory_op_without_init() {
1006 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
1007 (Witness(0), FieldElement::from(0u128)),
1008 (Witness(1), FieldElement::from(0u128)),
1009 ]));
1010 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1011
1012 let src = "
1014 READ w1 = b0[w0]
1015 ";
1016 let opcodes = parse_opcodes(src).unwrap();
1017
1018 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1019 let status = acvm.solve();
1020 assert!(
1021 matches!(
1022 status,
1023 ACVMStatus::Failure(OpcodeResolutionError::OpcodeNotSolvable(
1024 OpcodeNotSolvable::MissingMemoryBlock(0)
1025 ))
1026 ),
1027 "expected MissingMemoryBlock(0) failure, got {status:?}",
1028 );
1029 }
1030
1031 #[test]
1032 fn errors_on_duplicate_memory_init() {
1033 let initial_witness = WitnessMap::from(BTreeMap::from_iter([
1034 (Witness(1), FieldElement::from(1u128)),
1035 (Witness(2), FieldElement::from(2u128)),
1036 ]));
1037 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1038
1039 let src = "
1040 INIT b0 = [w1, w2]
1041 INIT b0 = [w1, w2]
1042 ";
1043 let opcodes = parse_opcodes(src).unwrap();
1044
1045 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1046 let status = acvm.solve();
1047 assert!(
1048 matches!(
1049 status,
1050 ACVMStatus::Failure(OpcodeResolutionError::UnsatisfiedConstrain { .. })
1051 ),
1052 "expected UnsatisfiedConstrain failure, got {status:?}",
1053 );
1054 assert_eq!(acvm.get_status(), &status, "status field should reflect the returned failure");
1055 }
1056
1057 #[test]
1058 fn errors_when_calling_function_zero() {
1059 let initial_witness =
1060 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1061 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1062
1063 let src = "
1064 CALL func: 0, predicate: 1, inputs: [w1], outputs: [w2]
1065 ";
1066 let opcodes = parse_opcodes(src).unwrap();
1067
1068 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1069 assert!(matches!(
1070 acvm.solve(),
1071 ACVMStatus::Failure(OpcodeResolutionError::AcirMainCallAttempted { .. })
1072 ));
1073 }
1074
1075 mod brillig_oob {
1076 use std::collections::BTreeMap;
1077
1078 use acir::{
1079 FieldElement,
1080 circuit::{
1081 OpcodeLocation,
1082 brillig::{BrilligBytecode, BrilligFunctionId},
1083 },
1084 native_types::{Witness, WitnessMap},
1085 parse_opcodes,
1086 };
1087
1088 use crate::pwg::{ACVM, ACVMStatus, OpcodeResolutionError, StepResult};
1089 use test_case::test_case;
1090
1091 #[test_case(0, 1, 0 ; "empty function table")]
1092 #[test_case(3, 1, 1 ; "id past end of table")]
1093 #[test_case(5, 0, 0 ; "false predicate does not bypass check")]
1094 fn brillig_call_with_out_of_bounds_id_fails(
1095 func_id: u32,
1096 predicate: u32,
1097 table_size: usize,
1098 ) {
1099 let initial_witness =
1100 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1101 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1102
1103 let src = format!(
1104 "BRILLIG CALL func: {func_id}, predicate: {predicate}, inputs: [w1], outputs: [w2]"
1105 );
1106 let opcodes = parse_opcodes(&src).unwrap();
1107
1108 let unconstrained_functions =
1109 vec![
1110 BrilligBytecode { function_name: "unused".to_string(), bytecode: vec![] };
1111 table_size
1112 ];
1113
1114 let mut acvm =
1115 ACVM::new(&backend, &opcodes, initial_witness, &unconstrained_functions, &[]);
1116 assert_eq!(
1117 acvm.solve(),
1118 ACVMStatus::Failure(OpcodeResolutionError::BrilligFunctionFailed {
1119 function_id: BrilligFunctionId::new(func_id),
1120 call_stack: vec![OpcodeLocation::Acir(0)],
1121 payload: None,
1122 }),
1123 );
1124 }
1125
1126 #[test]
1127 fn step_into_brillig_fails_on_out_of_bounds_id() {
1128 let initial_witness =
1129 WitnessMap::from(BTreeMap::from_iter([(Witness(1), FieldElement::from(1u128))]));
1130 let backend = acvm_blackbox_solver::StubbedBlackBoxSolver;
1131
1132 let src = "
1133 BRILLIG CALL func: 2, predicate: 1, inputs: [w1], outputs: [w2]
1134 ";
1135 let opcodes = parse_opcodes(src).unwrap();
1136
1137 let mut acvm = ACVM::new(&backend, &opcodes, initial_witness, &[], &[]);
1138 let step = acvm.step_into_brillig();
1139 match step {
1140 StepResult::Status(ACVMStatus::Failure(
1141 OpcodeResolutionError::BrilligFunctionFailed {
1142 function_id,
1143 call_stack,
1144 payload,
1145 },
1146 )) => {
1147 assert_eq!(function_id, BrilligFunctionId::new(2));
1148 assert_eq!(call_stack, vec![OpcodeLocation::Acir(0)]);
1149 assert!(payload.is_none());
1150 }
1151 StepResult::Status(other) => {
1152 panic!("expected BrilligFunctionFailed, got status {other:?}")
1153 }
1154 StepResult::IntoBrillig(_) => {
1155 panic!("expected BrilligFunctionFailed, stepped into Brillig instead")
1156 }
1157 }
1158 }
1159 }
1160}