diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index e427eb3aa69..c6a143b5dfd 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,3 +1,5 @@ +//! Implementations for [binary field operations][acir::brillig::Opcode::BinaryFieldOp] and +//! [binary integer operations][acir::brillig::Opcode::BinaryIntOp]. use std::ops::{BitAnd, BitOr, BitXor, Shl, Shr}; use acir::AcirField; @@ -201,6 +203,15 @@ pub(crate) fn evaluate_binary_int_op( } } +/// Evaluates binary operations on 1-bit unsigned integers (booleans). +/// +/// # Returns +/// - Ok(result) if successful. +/// - Err([BrilligArithmeticError::DivisionByZero]) if division by zero occurs. +/// +/// # Panics +/// If an operation other than Add, Sub, Mul, Div, And, Or, Xor, Equals, LessThan, +/// or LessThanEquals is supplied as an argument. fn evaluate_binary_int_op_u1( op: &BinaryIntOp, lhs: bool, @@ -225,6 +236,11 @@ fn evaluate_binary_int_op_u1( Ok(result) } +/// Evaluates comparison operations (Equals, LessThan, LessThanEquals) +/// between two values of an ordered type (e.g., fields are unordered). +/// +/// # Panics +/// If an unsupported operator is provided (i.e., not Equals, LessThan, or LessThanEquals). fn evaluate_binary_int_op_cmp(op: &BinaryIntOp, lhs: T, rhs: T) -> bool { match op { BinaryIntOp::Equals => lhs == rhs, @@ -234,6 +250,11 @@ fn evaluate_binary_int_op_cmp(op: &BinaryIntOp, lhs: T, rhs: } } +/// Evaluates shift operations (Shl, Shr) for unsigned integers. +/// Ensures that shifting beyond the type width returns zero. +/// +/// # Panics +/// If an unsupported operator is provided (i.e., not Shl or Shr). fn evaluate_binary_int_op_shifts + Zero + Shl + Shr>( op: &BinaryIntOp, lhs: T, @@ -252,6 +273,15 @@ fn evaluate_binary_int_op_shifts + Zero + Shl + Shr( memory: &'a Memory, vector: &HeapVector, @@ -18,6 +20,7 @@ fn read_heap_vector<'a, F: AcirField>( memory.read_slice(memory.read_ref(vector.pointer), size.to_usize()) } +/// Reads a fixed-size [array][HeapArray] from memory. fn read_heap_array<'a, F: AcirField>( memory: &'a Memory, array: &HeapArray, @@ -34,12 +37,33 @@ fn to_u8_vec(inputs: &[MemoryValue]) -> Vec { result } +/// Converts a slice of u8 values into a Vec<[`MemoryValue`]>, +/// wrapping each byte as a [MemoryValue::U8]. fn to_value_vec(input: &[u8]) -> Vec> { input.iter().map(|&x| x.into()).collect() } pub(crate) type BrilligBigIntSolver = BigIntSolverWithId; +/// Evaluates a black box function inside the VM, performing the actual native computation. +/// +/// Delegates the execution to the corresponding cryptographic or arithmetic +/// function, depending on the [BlackBoxOp] variant. +/// Handles input conversion, writing the result to memory, and error propagation. +/// +/// # Arguments +/// - op: The black box operation to evaluate. +/// - solver: An implementation of [BlackBoxFunctionSolver] providing external function behavior. +/// - memory: The VM memory from which inputs are read and to which results are written. +/// - bigint_solver: A solver used for big integer operations. +/// +/// # Returns +/// - Ok(()) if evaluation succeeds. +/// - Err([BlackBoxResolutionError]) if an error occurs during execution or input is invalid. +/// +/// # Panics +/// If any required memory value cannot be converted to the expected type (e.g., [expect_u8][MemoryValue::expect_u8]) +/// or if the [radix decomposition][BlackBoxOp::ToRadix] constraints are violated internally, such as an invalid radix range (e.g., radix of 1). pub(crate) fn evaluate_black_box>( op: &BlackBoxOp, solver: &Solver, @@ -361,6 +385,11 @@ pub(crate) fn evaluate_black_box } } +/// Maps a [BlackBoxOp] variant to its corresponding [BlackBoxFunc]. +/// Used primarily for error reporting and resolution purposes. +/// +/// # Panics +/// If called with a [BlackBoxOp::ToRadix] operation, which is not part of the [BlackBoxFunc] enum. fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { match op { BlackBoxOp::AES128Encrypt { .. } => BlackBoxFunc::AES128Encrypt, diff --git a/acvm-repo/brillig_vm/src/cast.rs b/acvm-repo/brillig_vm/src/cast.rs index 378dcccadf5..66b2a0f00d8 100644 --- a/acvm-repo/brillig_vm/src/cast.rs +++ b/acvm-repo/brillig_vm/src/cast.rs @@ -1,3 +1,4 @@ +//! Implementation for the [cast operation][acir::brillig::Opcode::Cast]. use acir::{ AcirField, brillig::{BitSize, IntegerBitSize}, diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 3de772c4359..7aa5a880930 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -33,21 +33,44 @@ mod memory; /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; +/// Represents the reason why the Brillig VM failed during execution. #[derive(Debug, PartialEq, Eq, Clone)] pub enum FailureReason { - Trap { revert_data_offset: usize, revert_data_size: usize }, + /// A trap was encountered, which indicates an explicit failure from within the VM program. + /// + /// A trap is triggered explicitly by the [trap opcode][Opcode::Trap]. + /// The revert data is referenced by the offset and size in the VM memory. + Trap { + /// Offset in memory where the revert data begins. + revert_data_offset: usize, + /// Size of the revert data. + revert_data_size: usize, + }, + /// A runtime failure during execution. + /// This error is triggered by all opcodes aside the [trap opcode][Opcode::Trap]. + /// For example, a [binary operation][Opcode::BinaryIntOp] can trigger a division by zero error. RuntimeError { message: String }, } +/// Represents the current execution status of the Brillig VM. #[derive(Debug, PartialEq, Eq, Clone)] pub enum VMStatus { + /// The VM has completed execution successfully. + /// The output of the program is stored in the VM memory and can be accessed via the provided offset and size. Finished { + /// Offset in memory where the return data begins. return_data_offset: usize, + /// Size of the return data. return_data_size: usize, }, + /// The VM is still in progress and has not yet completed execution. + /// This is used when simulating execution. InProgress, + /// The VM encountered a failure and halted execution. Failure { + /// The reason for the failure. reason: FailureReason, + /// The call stack at the time the failure occurred, useful for debugging nested calls. call_stack: ErrorCallStack, }, /// The VM process is not solvable as a [foreign call][Opcode::ForeignCall] has been @@ -65,7 +88,7 @@ pub enum VMStatus { }, } -// A sample for each opcode that was executed. +/// All samples for each opcode that was executed pub type BrilligProfilingSamples = Vec; /// The position of an opcode that is currently being executed in the bytecode @@ -91,9 +114,10 @@ pub type UniqueFeatureIndex = usize; /// A map for translating encountered branching logic to features for fuzzing pub type BranchToFeatureMap = HashMap; +/// A sample for an executed opcode #[derive(Debug, PartialEq, Eq, Clone)] pub struct BrilligProfilingSample { - // The call stack when processing a given opcode. + /// The call stack when processing a given opcode. pub call_stack: Vec, } diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index bed457a22eb..dd0b390a864 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,10 +1,19 @@ +//! Implementation of the VM's memory use acir::{ AcirField, brillig::{BitSize, IntegerBitSize, MemoryAddress}, }; +/// The bit size used for addressing memory within the Brillig VM. +/// +/// All memory pointers are interpreted as `u32` values, meaning the VM can directly address up to 2^32 memory slots. pub const MEMORY_ADDRESSING_BIT_SIZE: IntegerBitSize = IntegerBitSize::U32; +/// A single typed value in the Brillig VM's memory. +/// +/// Memory in the VM is strongly typed and can represent either a native field element +/// or an integer of a specific bit width. This enum encapsulates all supported +/// in-memory types and allows conversion between representations. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MemoryValue { Field(F), @@ -16,12 +25,16 @@ pub enum MemoryValue { U128(u128), } +/// Represents errors that can occur when interpreting or converting typed memory values. #[derive(Debug, thiserror::Error)] pub enum MemoryTypeError { + /// The value's bit size does not match the expected bit size for the operation. #[error( "Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}" )] MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, + /// The memory value is not an integer and cannot be interpreted as one. + /// For example, this can be triggered when attempting to convert a field element to an integer such as in [MemoryValue::to_u128]. #[error("Value is not an integer")] NotAnInteger, } @@ -284,11 +297,12 @@ impl TryFrom> for u128 { memory_value.expect_u128() } } - +/// The VM's memory. +/// Memory is internally represented as a vector of values. +/// We grow the memory when values past the end are set, extending with 0s. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { - // Memory is a vector of values. - // We grow the memory when values past the end are set, extending with 0s. + // Internal memory representation inner: Vec>, }