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;
83use itertools::Itertools;
84
85use self::{arithmetic::ExpressionSolver, memory_op::MemoryOpSolver};
86use crate::BlackBoxFunctionSolver;
87
88use thiserror::Error;
89
90// arithmetic
91pub(crate) mod arithmetic;
92// Brillig bytecode
93pub(crate) mod brillig;
94// black box functions
95pub(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    /// All witnesses have been computed and all opcodes have been successfully resolved. Execution is complete.
105    Solved,
106
107    /// The ACVM is processing the circuit, i.e solving the opcodes. This status is used to resume execution after it has been paused.
108    InProgress,
109
110    /// The ACVM has encountered an irrecoverable error while executing the circuit and can not progress.
111    /// Most commonly this will be due to an unsatisfied constraint due to invalid inputs to the circuit.
112    Failure(OpcodeResolutionError<F>),
113
114    /// The ACVM has encountered a request for a Brillig [foreign call][brillig_vm::brillig::Opcode::ForeignCall]
115    /// to retrieve information from outside of the ACVM. The result of the foreign call must be passed back
116    /// to the ACVM using [`ACVM::resolve_pending_foreign_call`].
117    ///
118    /// Once this is done, the ACVM can be restarted to solve the remaining opcodes.
119    RequiresForeignCall(ForeignCallWaitInfo<F>),
120
121    /// The ACVM has encountered a request for an ACIR [call][acir::circuit::Opcode]
122    /// to execute a separate ACVM instance. The result of the ACIR call must be passed back to the ACVM.
123    ///
124    /// Once this is done, the ACVM can be restarted to solve the remaining opcodes.
125    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// This enum represents the different cases in which an
147// opcode can be unsolvable.
148// The most common being that one of its input has not been
149// assigned a value.
150//
151// TODO(https://github.com/noir-lang/noir/issues/10052): ExpressionHasTooManyUnknowns is specific for expression solver
152// TODO(https://github.com/noir-lang/noir/issues/10052): we could have a error enum for expression solver failure cases in that module
153// TODO(https://github.com/noir-lang/noir/issues/10052): that can be converted into an OpcodeNotSolvable or OpcodeResolutionError enum
154#[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/// Used by errors to point to a specific opcode as that error's cause
165///
166/// Some errors don't have a specific opcode associated with them, or are created without one and added later.
167#[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/// A dynamic assertion payload whose data has been resolved.
186/// This is instantiated upon hitting an assertion failure.
187#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
188pub struct RawAssertionPayload<F> {
189    /// Selector to the respective ABI type the data in this payload represents
190    pub selector: ErrorSelector,
191    /// Resolved data that represents some ABI type.
192    /// To be decoded in the final step of error resolution.
193    pub data: Vec<F>,
194}
195
196/// Enumeration of possible resolved assertion payloads.
197/// This is instantiated upon hitting an assertion failure,
198/// and can either be static strings or dynamic payloads.
199#[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    /// Stores the solver for memory operations acting on blocks of memory disambiguated by [block][`BlockId`].
282    block_solvers: HashMap<BlockId, MemoryOpSolver<F>>,
283
284    /// A list of opcodes which are to be executed by the ACVM.
285    opcodes: &'a [Opcode<F>],
286    /// Index of the next opcode to be executed.
287    instruction_pointer: usize,
288
289    /// A mapping of witnesses to their solved values
290    /// The map is updated as the ACVM executes.
291    witness_map: WitnessMap<F>,
292
293    brillig_solver: Option<BrilligSolver<'a, F, B>>,
294
295    /// A counter maintained throughout an ACVM process that determines
296    /// whether the caller has resolved the results of an ACIR [call][Opcode::Call].
297    acir_call_counter: usize,
298    /// Represents the outputs of all ACIR calls during an ACVM process
299    /// List is appended onto by the caller upon reaching a [`ACVMStatus::RequiresAcirCall`]
300    acir_call_results: Vec<Vec<F>>,
301
302    // Each unconstrained function referenced in the program
303    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    // Whether we need to trace brillig execution for fuzzing
312    brillig_fuzzing_active: bool,
313
314    // Brillig branch to feature map
315    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    /// Enable profiling
350    pub fn with_profiler(&mut self, profiling_active: bool) {
351        self.profiling_active = profiling_active;
352    }
353
354    /// Enable brillig fuzzing
355    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    /// Returns a reference to the current state of the ACVM's [`WitnessMap`].
368    ///
369    /// Once execution has completed, the witness map can be extracted using [`ACVM::finalize`]
370    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    /// Returns a slice containing the opcodes of the circuit being executed.
379    pub fn opcodes(&self) -> &[Opcode<F>] {
380        self.opcodes
381    }
382
383    /// Returns the index of the current opcode to be executed.
384    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    /// Finalize the ACVM execution, returning the resulting [`WitnessMap`].
393    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    /// Updates the current status of the VM.
401    /// Returns the given status.
402    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    /// Sets the VM status to [`ACVMStatus::Failure`] using the provided `error`.
412    /// Returns the new status.
413    fn fail(&mut self, error: OpcodeResolutionError<F>) -> ACVMStatus<F> {
414        self.status(ACVMStatus::Failure(error))
415    }
416
417    /// Sets the status of the VM to `RequiresForeignCall`.
418    /// Indicating that the VM is now waiting for a foreign call to be resolved.
419    fn wait_for_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo<F>) -> ACVMStatus<F> {
420        self.status(ACVMStatus::RequiresForeignCall(foreign_call))
421    }
422
423    /// Return a reference to the arguments for the next pending foreign call, if one exists.
424    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    /// Resolves a foreign call's [result][brillig_vm::brillig::ForeignCallResult] using a result calculated outside of the ACVM.
433    ///
434    /// The ACVM can then be restarted to solve the remaining Brillig VM process as well as the remaining ACIR opcodes.
435    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        // Now that the foreign call has been resolved then we can resume execution.
444        self.status(ACVMStatus::InProgress);
445    }
446
447    /// Sets the status of the VM to `RequiresAcirCall`
448    /// Indicating that the VM is now waiting for an ACIR call to be resolved
449    fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo<F>) -> ACVMStatus<F> {
450        self.status(ACVMStatus::RequiresAcirCall(acir_call))
451    }
452
453    /// Resolves an ACIR call's result (simply a list of fields) using a result calculated by a separate ACVM instance.
454    ///
455    /// The current ACVM instance can then be restarted to solve the remaining ACIR opcodes.
456    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        // Now that the ACIR call has been resolved then we can resume execution.
467        self.status(ACVMStatus::InProgress);
468    }
469
470    /// Executes the ACVM's circuit until execution halts.
471    ///
472    /// Execution can halt due to three reasons:
473    /// 1. All opcodes have been executed successfully.
474    /// 2. The circuit has been found to be unsatisfiable.
475    /// 2. A Brillig [foreign call][`ForeignCallWaitInfo`] has been encountered and must be resolved.
476    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    /// Executes a single opcode using the dedicated solver.
488    ///
489    /// Foreign or ACIR Calls are deferred to the caller, which will
490    /// either instantiate a new ACVM to execute the called ACIR function
491    /// or a custom implementation to execute the foreign call.
492    /// Then it will resume execution of the current ACVM with the results of the call.
493    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    /// Returns the status of the ACVM
525    /// If the status is an error, it converts the error into [`OpcodeResolutionError`]
526    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                    // If we have an index out of bounds, unsatisfied constraint, or an invalid input bit size,
542                    // the opcode label will be unresolved because the solvers do not have knowledge of this information.
543                    // We resolve, by setting this to the corresponding opcode that we just attempted to solve.
544                    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                    // All other errors are thrown normally.
568                    _ => (),
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    /// Initializes a memory block with values loaded from the witness map.
603    ///
604    /// Fails if the block has already been initialized.
605    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    /// Solves a Brillig Call opcode, which represents a call to an unconstrained function.
621    /// It first handles the predicate and returns zero values if the predicate is false.
622    /// Then it executes (or resumes execution) the Brillig function using a Brillig VM.
623    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        // If we're resuming execution after resolving a foreign call then
645        // there will be a cached `BrilligSolver` to avoid recomputation.
646        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        // If we're fuzzing, we need to get the fuzzing trace on an error
662        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                // Cache the current state of the solver
671                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                // Write execution outputs
682                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    // This function is used by the debugger
710    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    // This function is used by the debugger
754    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    /// Defer execution of the ACIR call opcode to the caller, or finalize the execution.
763    /// 1. It first handles the predicate and return zero values if the predicate is false.
764    /// 2. If the results of the execution are not available, it issues a '`AcirCallWaitInfo`'
765    ///    to notify the caller that it (the caller) needs to execute the ACIR function.
766    /// 3. If the results are available, it updates the witness map and indicates that the opcode is solved.
767    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            // Zero out the outputs if we have a false predicate
782            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
815// Returns the concrete value for a particular witness
816// If the witness has no assignment, then
817// an error is returned
818pub 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
861/// Returns the concrete value for a particular expression
862/// If the value cannot be computed, it returns an '`OpcodeNotSolvable`' error.
863pub 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
879/// Inserts `value` into the initial witness map under the index `witness`.
880///
881/// Returns an error if there was already a value in the map
882/// which does not match the value that one is about to insert
883pub 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
907// Returns one witness belonging to an expression, in no relevant order
908// Returns None if the expression is const
909// The function is used during partial witness generation to report unsolved witness
910fn 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
918/// Returns `Ok(true)` if the predicate is zero
919/// A predicate is used to indicate whether we should skip a certain operation.
920/// If we have a zero predicate it means the operation should be skipped.
921pub(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    // We expect that the predicate should resolve to either 0 or 1.
930    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/// Encapsulates a request from the ACVM that encounters an [ACIR call opcode][brillig_vm::brillig::Opcode::Call]
939/// where the result of the circuit execution has not yet been provided.
940///
941/// The caller must resolve this opcode externally based upon the information in the request.
942#[derive(Debug, Clone, PartialEq)]
943pub struct AcirCallWaitInfo<F> {
944    /// Index in the list of ACIR function's that should be called
945    pub id: AcirFunctionId,
946    /// Initial witness for the given circuit to be called
947    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        // READ against b0 without a prior `INIT b0 = ...` should error rather than panic.
1013        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}