acvm/pwg/
mod.rs

1// Re-usable methods that backends can use to implement their PWG
2
3//! This module contains methods to implement the partial witness generation (PWG) of an ACIR program.
4//! The goal of ACIR execution is to compute the values of all the ACIR witnesses, or an error if it could not compute them all.
5//! A proving system will then be able to use the ACIR circuit and the values of the ACIR witnesses to generate a proof of this execution.
6//! The ACIR opcodes are not modified by the execution.
7//! Witness generation means getting valid values for the witnesses used by the ACIR opcodes of the program.
8//! They are called *partial* witness because a proving system may create additional witnesses on its own for
9//! generating the proof (and a corresponding low-level circuit). The PWG generates values for all the witnesses
10//! of the ACIR program, or returns an error if it cannot do it.
11//!
12//! Implementation details & examples:
13//! It starts by instantiating an ACVM (ACIR Virtual Machine), which executes the given ACIR opcodes in the `solve()` function.
14//!
15//! Parameters: When instantiating the ACVM, it needs to be provided with:
16//!  - a `backend` implementing the `BlackBoxFunctionSolver` trait. Different implementation can be used depending on the EC used by the underlying proving system.
17//!  - `opcodes`: the ACIR opcodes of the program to solve.
18//!  - `initial_witness`: a mapping of initial witness values representing the inputs of the program. The ACVM will update this map as it solves the opcodes.
19//!  - `unconstrained_functions`: the Brillig bytecode of the unconstrained functions used by the program.
20//!  - `assertion_payloads`: additional information used to provide feedback to the user when an assertion fails.
21//!
22//! Returns: [`ACVMStatus`]
23//!
24//! Each opcode is solved independently. In general we require its inputs to be already known, i.e previously solved,
25//! and the output is simply computed from the inputs, and then the output becomes 'known' for the subsequent opcodes.
26//!
27//! See [`acir::circuit::Opcode`] for more details.
28//!
29//! Example:
30// Compiled ACIR for main (non-transformed):
31// func 0
32// private parameters: [w0, w1, w2, w3, w4]
33// public parameters: []
34// return values: [w9]
35// BLACKBOX::RANGE input: w0, bits: 32
36// BLACKBOX::RANGE input: w1, bits: 32
37// BLACKBOX::RANGE input: w2, bits: 32
38// BLACKBOX::RANGE input: w3, bits: 32
39// BLACKBOX::RANGE input: w4, bits: 32
40// ASSERT w0 - w1 - w6 = 0
41// BRILLIG CALL func: 0, predicate: 1, inputs: [w6], outputs: [w7]
42// ASSERT w6*w7 + w8 - 1 = 0
43// ASSERT w6*w8 = 0
44// ASSERT w1*w8 = 0
45// ASSERT w0 - w2 - w9 = 0
46//!
47//! This ACIR program defines the 'main' function and indicates it is 'non-transformed'.
48//! Indeed, some ACIR pass can transform the ACIR program in order to apply optimizations,
49//! or to make it compatible with a specific proving system.
50//! However, ACIR execution is expected to work on any ACIR program (transformed or not).
51//! Then we see the parameters of the program as public and private inputs.
52//! The `initial_witness` needs to contain values for these parameters before execution, else
53//! the execution will fail.
54//! The first ACIR opcodes are RANGE opcodes which ensure the inputs have the expected range (as specified in the Noir source code).
55//! Solving this black-box simply means to validate that the values (from `initial_witness`) are indeed 32 bits for w0, w1, w2, w3, w4
56//! If `initial_witness` does not have values for w0, w1, w2, w3, w4, or if the values are over 32 bits, the execution will fail.
57//! The next opcode is an AssertZero opcode: ASSERT w0 - w1 - w6 = 0, which indicates that `w0 - w1 - w6` should be equal to 0.
58//! Since we know the values of `w0, w1` from `initial_witness`, we can compute `w6 = w0 + w1` so that the AssertZero is satisfied.
59//! Solving AssertZero means computing the unknown witness and adding the result to `initial_witness`, which now contains the value for `w6`.
60//! The next opcode is a Brillig Call where input is `w6` and output is `w7`. From the function id of the opcode, the solver will retrieve the
61//! corresponding Brillig bytecode and instantiate a Brillig VM with the value of the input. This value was just computed before.
62//! Executing the Brillig VM on this input will give us the output which is the value for `w7`, that we add to `initial_witness`.
63//! The next opcode is again an AssertZero: `w6 * w7 + w8 - 1 = 0`, which computes the value of `w8`.
64//! The two next opcodes are AssertZero without any unknown witnesses: `w6 * w8 = 0` and `w1 * w8 = 0`
65//! Solving such opcodes means that we compute `w6 * w8 ` and `w1 * w8` using the known values, and check that they evaluate to 0.
66//! If not, we would return an error.
67//! Finally, the last AssertZero computes `w9` which is the last witness. All of the witnesses have now been computed; execution is complete.
68
69use 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
89// arithmetic
90pub(crate) mod arithmetic;
91// Brillig bytecode
92pub(crate) mod brillig;
93// black box functions
94pub(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    /// All witnesses have been computed and all opcodes have been successfully resolved. Execution is complete.
104    Solved,
105
106    /// The ACVM is processing the circuit, i.e solving the opcodes. This status is used to resume execution after it has been paused.
107    InProgress,
108
109    /// The ACVM has encountered an irrecoverable error while executing the circuit and can not progress.
110    /// Most commonly this will be due to an unsatisfied constraint due to invalid inputs to the circuit.
111    Failure(OpcodeResolutionError<F>),
112
113    /// The ACVM has encountered a request for a Brillig [foreign call][brillig_vm::brillig::Opcode::ForeignCall]
114    /// to retrieve information from outside of the ACVM. The result of the foreign call must be passed back
115    /// to the ACVM using [`ACVM::resolve_pending_foreign_call`].
116    ///
117    /// Once this is done, the ACVM can be restarted to solve the remaining opcodes.
118    RequiresForeignCall(ForeignCallWaitInfo<F>),
119
120    /// The ACVM has encountered a request for an ACIR [call][acir::circuit::Opcode]
121    /// to execute a separate ACVM instance. The result of the ACIR call must be passed back to the ACVM.
122    ///
123    /// Once this is done, the ACVM can be restarted to solve the remaining opcodes.
124    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// This enum represents the different cases in which an
146// opcode can be unsolvable.
147// The most common being that one of its input has not been
148// assigned a value.
149//
150// TODO(https://github.com/noir-lang/noir/issues/10052): ExpressionHasTooManyUnknowns is specific for expression solver
151// TODO(https://github.com/noir-lang/noir/issues/10052): we could have a error enum for expression solver failure cases in that module
152// TODO(https://github.com/noir-lang/noir/issues/10052): that can be converted into an OpcodeNotSolvable or OpcodeResolutionError enum
153#[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/// Used by errors to point to a specific opcode as that error's cause
164///
165/// Some errors don't have a specific opcode associated with them, or are created without one and added later.
166#[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/// A dynamic assertion payload whose data has been resolved.
185/// This is instantiated upon hitting an assertion failure.
186#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
187pub struct RawAssertionPayload<F> {
188    /// Selector to the respective ABI type the data in this payload represents
189    pub selector: ErrorSelector,
190    /// Resolved data that represents some ABI type.
191    /// To be decoded in the final step of error resolution.
192    pub data: Vec<F>,
193}
194
195/// Enumeration of possible resolved assertion payloads.
196/// This is instantiated upon hitting an assertion failure,
197/// and can either be static strings or dynamic payloads.
198#[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    /// Stores the solver for memory operations acting on blocks of memory disambiguated by [block][`BlockId`].
279    block_solvers: HashMap<BlockId, MemoryOpSolver<F>>,
280
281    /// A list of opcodes which are to be executed by the ACVM.
282    opcodes: &'a [Opcode<F>],
283    /// Index of the next opcode to be executed.
284    instruction_pointer: usize,
285
286    /// A mapping of witnesses to their solved values
287    /// The map is updated as the ACVM executes.
288    witness_map: WitnessMap<F>,
289
290    brillig_solver: Option<BrilligSolver<'a, F, B>>,
291
292    /// A counter maintained throughout an ACVM process that determines
293    /// whether the caller has resolved the results of an ACIR [call][Opcode::Call].
294    acir_call_counter: usize,
295    /// Represents the outputs of all ACIR calls during an ACVM process
296    /// List is appended onto by the caller upon reaching a [ACVMStatus::RequiresAcirCall]
297    acir_call_results: Vec<Vec<F>>,
298
299    // Each unconstrained function referenced in the program
300    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    // Whether we need to trace brillig execution for fuzzing
309    brillig_fuzzing_active: bool,
310
311    // Brillig branch to feature map
312    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    /// Enable profiling
347    pub fn with_profiler(&mut self, profiling_active: bool) {
348        self.profiling_active = profiling_active;
349    }
350
351    /// Enable brillig fuzzing
352    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    /// Returns a reference to the current state of the ACVM's [`WitnessMap`].
365    ///
366    /// Once execution has completed, the witness map can be extracted using [`ACVM::finalize`]
367    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    /// Returns a slice containing the opcodes of the circuit being executed.
376    pub fn opcodes(&self) -> &[Opcode<F>] {
377        self.opcodes
378    }
379
380    /// Returns the index of the current opcode to be executed.
381    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    /// Finalize the ACVM execution, returning the resulting [`WitnessMap`].
390    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    /// Updates the current status of the VM.
398    /// Returns the given status.
399    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    /// Sets the VM status to [ACVMStatus::Failure] using the provided `error`.
409    /// Returns the new status.
410    fn fail(&mut self, error: OpcodeResolutionError<F>) -> ACVMStatus<F> {
411        self.status(ACVMStatus::Failure(error))
412    }
413
414    /// Sets the status of the VM to `RequiresForeignCall`.
415    /// Indicating that the VM is now waiting for a foreign call to be resolved.
416    fn wait_for_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo<F>) -> ACVMStatus<F> {
417        self.status(ACVMStatus::RequiresForeignCall(foreign_call))
418    }
419
420    /// Return a reference to the arguments for the next pending foreign call, if one exists.
421    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    /// Resolves a foreign call's [result][brillig_vm::brillig::ForeignCallResult] using a result calculated outside of the ACVM.
430    ///
431    /// The ACVM can then be restarted to solve the remaining Brillig VM process as well as the remaining ACIR opcodes.
432    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        // Now that the foreign call has been resolved then we can resume execution.
441        self.status(ACVMStatus::InProgress);
442    }
443
444    /// Sets the status of the VM to `RequiresAcirCall`
445    /// Indicating that the VM is now waiting for an ACIR call to be resolved
446    fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo<F>) -> ACVMStatus<F> {
447        self.status(ACVMStatus::RequiresAcirCall(acir_call))
448    }
449
450    /// Resolves an ACIR call's result (simply a list of fields) using a result calculated by a separate ACVM instance.
451    ///
452    /// The current ACVM instance can then be restarted to solve the remaining ACIR opcodes.
453    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        // Now that the ACIR call has been resolved then we can resume execution.
464        self.status(ACVMStatus::InProgress);
465    }
466
467    /// Executes the ACVM's circuit until execution halts.
468    ///
469    /// Execution can halt due to three reasons:
470    /// 1. All opcodes have been executed successfully.
471    /// 2. The circuit has been found to be unsatisfiable.
472    /// 2. A Brillig [foreign call][`ForeignCallWaitInfo`] has been encountered and must be resolved.
473    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    /// Executes a single opcode using the dedicated solver.
485    ///
486    /// Foreign or ACIR Calls are deferred to the caller, which will
487    /// either instantiate a new ACVM to execute the called ACIR function
488    /// or a custom implementation to execute the foreign call.
489    /// Then it will resume execution of the current ACVM with the results of the call.
490    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    /// Returns the status of the ACVM
526    /// If the status is an error, it converts the error into [OpcodeResolutionError]
527    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                    // If we have an index out of bounds, unsatisfied constraint, or an invalid input bit size,
543                    // the opcode label will be unresolved because the solvers do not have knowledge of this information.
544                    // We resolve, by setting this to the corresponding opcode that we just attempted to solve.
545                    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                    // All other errors are thrown normally.
569                    _ => (),
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    /// Solves a Brillig Call opcode, which represents a call to an unconstrained function.
604    /// It first handles the predicate and returns zero values if the predicate is false.
605    /// Then it executes (or resumes execution) the Brillig function using a Brillig VM.
606    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        // If we're resuming execution after resolving a foreign call then
621        // there will be a cached `BrilligSolver` to avoid recomputation.
622        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        // If we're fuzzing, we need to get the fuzzing trace on an error
638        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                // Cache the current state of the solver
647                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                // Write execution outputs
658                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    // This function is used by the debugger
686    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    // This function is used by the debugger
721    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    /// Defer execution of the ACIR call opcode to the caller, or finalize the execution.
730    /// 1. It first handles the predicate and return zero values if the predicate is false.
731    /// 2. If the results of the execution are not available, it issues a 'AcirCallWaitInfo'
732    ///    to notify the caller that it (the caller) needs to execute the ACIR function.
733    /// 3. If the results are available, it updates the witness map and indicates that the opcode is solved.
734    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            // Zero out the outputs if we have a false predicate
749            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
782// Returns the concrete value for a particular witness
783// If the witness has no assignment, then
784// an error is returned
785pub 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
828/// Returns the concrete value for a particular expression
829/// If the value cannot be computed, it returns an 'OpcodeNotSolvable' error.
830pub 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
843/// Inserts `value` into the initial witness map under the index `witness`.
844///
845/// Returns an error if there was already a value in the map
846/// which does not match the value that one is about to insert
847pub 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
871// Returns one witness belonging to an expression, in no relevant order
872// Returns None if the expression is const
873// The function is used during partial witness generation to report unsolved witness
874fn 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
882/// Returns `Ok(true)` if the predicate is zero
883/// A predicate is used to indicate whether we should skip a certain operation.
884/// If we have a zero predicate it means the operation should be skipped.
885pub(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    // We expect that the predicate should resolve to either 0 or 1.
894    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/// Encapsulates a request from the ACVM that encounters an [ACIR call opcode][brillig_vm::brillig::Opcode::Call]
903/// where the result of the circuit execution has not yet been provided.
904///
905/// The caller must resolve this opcode externally based upon the information in the request.
906#[derive(Debug, Clone, PartialEq)]
907pub struct AcirCallWaitInfo<F> {
908    /// Index in the list of ACIR function's that should be called
909    pub id: AcirFunctionId,
910    /// Initial witness for the given circuit to be called
911    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}