diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index d7f0f5f6f1f..848d7bda84b 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -13,7 +13,9 @@ use serde::{Deserialize, Serialize}; mod black_box_function_call; mod memory_operation; -pub use black_box_function_call::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}; +pub use black_box_function_call::{ + BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, +}; pub use memory_operation::{BlockId, MemOp}; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -40,7 +42,7 @@ pub enum Opcode { /// values which define the opcode. /// /// A general expression of assert-zero opcode is the following: - /// ``` + /// ```text /// \sum_{i,j} {q_M}_{i,j}w_iw_j + \sum_i q_iw_i +q_c = 0 /// ``` /// diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 333bab419c8..f527522cceb 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -1,6 +1,8 @@ use crate::native_types::Witness; -use crate::BlackBoxFunc; +use crate::{AcirField, BlackBoxFunc}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use thiserror::Error; // Note: Some functions will not use all of the witness // So we need to supply how many bits of the witness is needed @@ -13,8 +15,8 @@ pub enum ConstantOrWitnessEnum { #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct FunctionInput { - pub input: ConstantOrWitnessEnum, - pub num_bits: u32, + input: ConstantOrWitnessEnum, + num_bits: u32, } impl FunctionInput { @@ -25,6 +27,14 @@ impl FunctionInput { } } + pub fn input(self) -> ConstantOrWitnessEnum { + self.input + } + + pub fn input_ref(&self) -> &ConstantOrWitnessEnum { + &self.input + } + pub fn num_bits(&self) -> u32 { self.num_bits } @@ -32,9 +42,25 @@ impl FunctionInput { pub fn witness(witness: Witness, num_bits: u32) -> FunctionInput { FunctionInput { input: ConstantOrWitnessEnum::Witness(witness), num_bits } } +} + +#[derive(Clone, PartialEq, Eq, Debug, Error)] +#[error("FunctionInput value has too many bits: value: {value}, {value_num_bits} >= {max_bits}")] +pub struct InvalidInputBitSize { + pub value: String, + pub value_num_bits: u32, + pub max_bits: u32, +} - pub fn constant(value: F, num_bits: u32) -> FunctionInput { - FunctionInput { input: ConstantOrWitnessEnum::Constant(value), num_bits } +impl FunctionInput { + pub fn constant(value: F, max_bits: u32) -> Result, InvalidInputBitSize> { + if value.num_bits() <= max_bits { + Ok(FunctionInput { input: ConstantOrWitnessEnum::Constant(value), num_bits: max_bits }) + } else { + let value_num_bits = value.num_bits(); + let value = format!("{}", value); + Err(InvalidInputBitSize { value, value_num_bits, max_bits }) + } } } diff --git a/acvm-repo/acir/src/lib.rs b/acvm-repo/acir/src/lib.rs index 845a1d6ad5a..36331427b9f 100644 --- a/acvm-repo/acir/src/lib.rs +++ b/acvm-repo/acir/src/lib.rs @@ -12,6 +12,7 @@ pub use acir_field; pub use acir_field::{AcirField, FieldElement}; pub use brillig; pub use circuit::black_box_functions::BlackBoxFunc; +pub use circuit::opcodes::InvalidInputBitSize; #[cfg(test)] mod reflection { diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 7bed57e22a0..172cfc71d95 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -62,13 +62,13 @@ fn multi_scalar_mul_circuit() { let multi_scalar_mul: Opcode = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::MultiScalarMul { points: vec![ - FunctionInput::witness(Witness(1), 128), - FunctionInput::witness(Witness(2), 128), + FunctionInput::witness(Witness(1), FieldElement::max_num_bits()), + FunctionInput::witness(Witness(2), FieldElement::max_num_bits()), FunctionInput::witness(Witness(3), 1), ], scalars: vec![ - FunctionInput::witness(Witness(4), 128), - FunctionInput::witness(Witness(5), 128), + FunctionInput::witness(Witness(4), FieldElement::max_num_bits()), + FunctionInput::witness(Witness(5), FieldElement::max_num_bits()), ], outputs: (Witness(6), Witness(7), Witness(8)), }); @@ -91,10 +91,10 @@ fn multi_scalar_mul_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 232, - 142, 158, 210, 130, 149, 240, 112, 234, 212, 156, 78, 12, 39, 67, 71, 158, 142, 80, 29, 44, - 228, 66, 90, 168, 119, 189, 74, 115, 131, 174, 78, 115, 58, 124, 70, 254, 130, 59, 74, 253, - 68, 255, 255, 221, 39, 54, 221, 93, 91, 132, 193, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 255, + 227, 70, 74, 11, 86, 194, 195, 169, 83, 115, 58, 49, 156, 12, 29, 121, 58, 66, 117, 176, + 144, 11, 105, 161, 222, 245, 42, 205, 13, 186, 58, 205, 233, 240, 25, 249, 11, 238, 40, + 245, 19, 253, 255, 119, 159, 216, 103, 157, 249, 169, 193, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index b03b6715abe..3570a36a7e7 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,6 +1,6 @@ use acir::{ circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, Circuit, Opcode, }, native_types::Witness, @@ -73,10 +73,13 @@ impl RangeOptimizer { } } - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: - FunctionInput { input: ConstantOrWitnessEnum::Witness(witness), num_bits }, - }) => Some((*witness, *num_bits)), + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + if let ConstantOrWitnessEnum::Witness(witness) = input.input() { + Some((witness, input.num_bits())) + } else { + None + } + } _ => None, }) else { @@ -106,17 +109,28 @@ impl RangeOptimizer { let mut new_order_list = Vec::with_capacity(order_list.len()); let mut optimized_opcodes = Vec::with_capacity(self.circuit.opcodes.len()); for (idx, opcode) in self.circuit.opcodes.into_iter().enumerate() { - let (witness, num_bits) = match opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: - FunctionInput { input: ConstantOrWitnessEnum::Witness(w), num_bits: bits }, - }) => (w, bits), - _ => { - // If its not the range opcode, add it to the opcode - // list and continue; + let (witness, num_bits) = { + // If its not the range opcode, add it to the opcode + // list and continue; + let mut push_non_range_opcode = || { optimized_opcodes.push(opcode.clone()); new_order_list.push(order_list[idx]); - continue; + }; + + match opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + match input.input() { + ConstantOrWitnessEnum::Witness(witness) => (witness, input.num_bits()), + _ => { + push_non_range_opcode(); + continue; + } + } + } + _ => { + push_non_range_opcode(); + continue; + } } }; // If we've already applied the range constraint for this witness then skip this opcode. diff --git a/acvm-repo/acvm/src/pwg/blackbox/bigint.rs b/acvm-repo/acvm/src/pwg/blackbox/bigint.rs index 1bce4aa6c5e..ccad3510682 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/bigint.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/bigint.rs @@ -27,7 +27,7 @@ impl AcvmBigIntSolver { ) -> Result<(), OpcodeResolutionError> { let bytes = inputs .iter() - .map(|input| input_to_value(initial_witness, *input).unwrap().to_u128() as u8) + .map(|input| input_to_value(initial_witness, *input, false).unwrap().to_u128() as u8) .collect::>(); self.bigint_solver.bigint_from_bytes(&bytes, modulus, output)?; Ok(()) diff --git a/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs index c290faeaa4a..9e511571275 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -15,11 +15,11 @@ pub(super) fn multi_scalar_mul( outputs: (Witness, Witness, Witness), ) -> Result<(), OpcodeResolutionError> { let points: Result, _> = - points.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + points.iter().map(|input| input_to_value(initial_witness, *input, false)).collect(); let points: Vec<_> = points?.into_iter().collect(); let scalars: Result, _> = - scalars.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + scalars.iter().map(|input| input_to_value(initial_witness, *input, false)).collect(); let mut scalars_lo = Vec::new(); let mut scalars_hi = Vec::new(); for (i, scalar) in scalars?.into_iter().enumerate() { @@ -47,12 +47,12 @@ pub(super) fn embedded_curve_add( input2: [FunctionInput; 3], outputs: (Witness, Witness, Witness), ) -> Result<(), OpcodeResolutionError> { - let input1_x = input_to_value(initial_witness, input1[0])?; - let input1_y = input_to_value(initial_witness, input1[1])?; - let input1_infinite = input_to_value(initial_witness, input1[2])?; - let input2_x = input_to_value(initial_witness, input2[0])?; - let input2_y = input_to_value(initial_witness, input2[1])?; - let input2_infinite = input_to_value(initial_witness, input2[2])?; + let input1_x = input_to_value(initial_witness, input1[0], false)?; + let input1_y = input_to_value(initial_witness, input1[1], false)?; + let input1_infinite = input_to_value(initial_witness, input1[2], false)?; + let input2_x = input_to_value(initial_witness, input2[0], false)?; + let input2_y = input_to_value(initial_witness, input2[1], false)?; + let input2_infinite = input_to_value(initial_witness, input2[2], false)?; let (res_x, res_y, res_infinite) = backend.ec_add( &input1_x, &input1_y, diff --git a/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/acvm-repo/acvm/src/pwg/blackbox/hash.rs index b51139f76b7..234ab6162ca 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -34,7 +34,7 @@ fn get_hash_input( for input in inputs.iter() { let num_bits = input.num_bits() as usize; - let witness_assignment = input_to_value(initial_witness, *input)?; + let witness_assignment = input_to_value(initial_witness, *input, false)?; let bytes = witness_assignment.fetch_nearest_bytes(num_bits); message_input.extend(bytes); } @@ -42,7 +42,8 @@ fn get_hash_input( // Truncate the message if there is a `message_size` parameter given match message_size { Some(input) => { - let num_bytes_to_take = input_to_value(initial_witness, *input)?.to_u128() as usize; + let num_bytes_to_take = + input_to_value(initial_witness, *input, false)?.to_u128() as usize; // If the number of bytes to take is more than the amount of bytes available // in the message, then we error. @@ -78,7 +79,7 @@ fn to_u32_array( ) -> Result<[u32; N], OpcodeResolutionError> { let mut result = [0; N]; for (it, input) in result.iter_mut().zip(inputs) { - let witness_value = input_to_value(initial_witness, *input)?; + let witness_value = input_to_value(initial_witness, *input, false)?; *it = witness_value.to_u128() as u32; } Ok(result) @@ -133,7 +134,7 @@ pub(crate) fn solve_poseidon2_permutation_opcode( // Read witness assignments let mut state = Vec::new(); for input in inputs.iter() { - let witness_assignment = input_to_value(initial_witness, *input)?; + let witness_assignment = input_to_value(initial_witness, *input, false)?; state.push(witness_assignment); } diff --git a/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 7ce0827d932..8468b0ca27a 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -51,8 +51,11 @@ fn solve_logic_opcode( result: Witness, logic_op: impl Fn(F, F) -> F, ) -> Result<(), OpcodeResolutionError> { - let w_l_value = input_to_value(initial_witness, *a)?; - let w_r_value = input_to_value(initial_witness, *b)?; + // TODO(https://github.com/noir-lang/noir/issues/5985): re-enable these once we figure out how to combine these with existing + // noirc_frontend/noirc_evaluator overflow error messages + let skip_bitsize_checks = true; + let w_l_value = input_to_value(initial_witness, *a, skip_bitsize_checks)?; + let w_r_value = input_to_value(initial_witness, *b, skip_bitsize_checks)?; let assignment = logic_op(w_l_value, w_r_value); insert_value(&result, assignment, initial_witness) diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index def4216fe15..8b8bfc5cfc5 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -42,11 +42,11 @@ fn first_missing_assignment( inputs: &[FunctionInput], ) -> Option { inputs.iter().find_map(|input| { - if let ConstantOrWitnessEnum::Witness(witness) = input.input { - if witness_assignments.contains_key(&witness) { + if let ConstantOrWitnessEnum::Witness(ref witness) = input.input_ref() { + if witness_assignments.contains_key(witness) { None } else { - Some(witness) + Some(*witness) } } else { None @@ -108,7 +108,7 @@ pub(crate) fn solve( for (it, input) in state.iter_mut().zip(inputs.as_ref()) { let num_bits = input.num_bits() as usize; assert_eq!(num_bits, 64); - let witness_assignment = input_to_value(initial_witness, *input)?; + let witness_assignment = input_to_value(initial_witness, *input, false)?; let lane = witness_assignment.try_to_u64(); *it = lane.unwrap(); } diff --git a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs index b1b95393b19..654814bf92d 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs @@ -17,7 +17,7 @@ pub(super) fn pedersen( outputs: (Witness, Witness), ) -> Result<(), OpcodeResolutionError> { let scalars: Result, _> = - inputs.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + inputs.iter().map(|input| input_to_value(initial_witness, *input, false)).collect(); let scalars: Vec<_> = scalars?.into_iter().collect(); let (res_x, res_y) = backend.pedersen_commitment(&scalars, domain_separator)?; @@ -36,7 +36,7 @@ pub(super) fn pedersen_hash( output: Witness, ) -> Result<(), OpcodeResolutionError> { let scalars: Result, _> = - inputs.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + inputs.iter().map(|input| input_to_value(initial_witness, *input, false)).collect(); let scalars: Vec<_> = scalars?.into_iter().collect(); let res = backend.pedersen_hash(&scalars, domain_separator)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/range.rs b/acvm-repo/acvm/src/pwg/blackbox/range.rs index 054730bb6c0..4f9be14360e 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -8,7 +8,10 @@ pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, input: &FunctionInput, ) -> Result<(), OpcodeResolutionError> { - let w_value = input_to_value(initial_witness, *input)?; + // TODO(https://github.com/noir-lang/noir/issues/5985): + // re-enable bitsize checks + let skip_bitsize_checks = true; + let w_value = input_to_value(initial_witness, *input, skip_bitsize_checks)?; if w_value.num_bits() > input.num_bits() { return Err(OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Unresolved, diff --git a/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs b/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs index 4f8e88373ba..a856303d065 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs @@ -21,8 +21,8 @@ pub(crate) fn schnorr_verify( message: &[FunctionInput], output: Witness, ) -> Result<(), OpcodeResolutionError> { - let public_key_x: &F = &input_to_value(initial_witness, public_key_x)?; - let public_key_y: &F = &input_to_value(initial_witness, public_key_y)?; + let public_key_x: &F = &input_to_value(initial_witness, public_key_x, false)?; + let public_key_y: &F = &input_to_value(initial_witness, public_key_y, false)?; let signature = to_u8_array(initial_witness, signature)?; let message = to_u8_vec(initial_witness, message)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/utils.rs b/acvm-repo/acvm/src/pwg/blackbox/utils.rs index 9b9157421e5..b966cb0cc5d 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/utils.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/utils.rs @@ -8,7 +8,7 @@ pub(crate) fn to_u8_array( ) -> Result<[u8; N], OpcodeResolutionError> { let mut result = [0; N]; for (it, input) in result.iter_mut().zip(inputs) { - let witness_value_bytes = input_to_value(initial_witness, *input)?.to_be_bytes(); + let witness_value_bytes = input_to_value(initial_witness, *input, false)?.to_be_bytes(); let byte = witness_value_bytes .last() .expect("Field element must be represented by non-zero amount of bytes"); @@ -23,7 +23,7 @@ pub(crate) fn to_u8_vec( ) -> Result, OpcodeResolutionError> { let mut result = Vec::with_capacity(inputs.len()); for input in inputs { - let witness_value_bytes = input_to_value(initial_witness, *input)?.to_be_bytes(); + let witness_value_bytes = input_to_value(initial_witness, *input, false)?.to_be_bytes(); let byte = witness_value_bytes .last() .expect("Field element must be represented by non-zero amount of bytes"); diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 647c11bd3c3..c73893ceea6 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -6,7 +6,9 @@ use acir::{ brillig::ForeignCallResult, circuit::{ brillig::{BrilligBytecode, BrilligFunctionId}, - opcodes::{AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{ + AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, + }, AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, STRING_ERROR_SELECTOR, }, @@ -128,6 +130,11 @@ pub enum OpcodeResolutionError { }, #[error("Index out of bounds, array has size {array_size:?}, but index was {index:?}")] IndexOutOfBounds { opcode_location: ErrorLocation, index: u32, array_size: u32 }, + #[error("Cannot solve opcode: {invalid_input_bit_size}")] + InvalidInputBitSize { + opcode_location: ErrorLocation, + invalid_input_bit_size: InvalidInputBitSize, + }, #[error("Failed to solve blackbox function: {0}, reason: {1}")] BlackBoxFunctionFailed(BlackBoxFunc, String), #[error("Failed to solve brillig function")] @@ -152,6 +159,15 @@ impl From for OpcodeResolutionError { } } +impl From for OpcodeResolutionError { + fn from(invalid_input_bit_size: InvalidInputBitSize) -> Self { + Self::InvalidInputBitSize { + opcode_location: ErrorLocation::Unresolved, + invalid_input_bit_size, + } + } +} + pub struct ACVM<'a, F, B: BlackBoxFunctionSolver> { status: ACVMStatus, @@ -387,6 +403,13 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { *opcode_index = ErrorLocation::Resolved(location); *assertion_payload = self.extract_assertion_payload(location); } + OpcodeResolutionError::InvalidInputBitSize { + opcode_location: opcode_index, + .. + } => { + let location = OpcodeLocation::Acir(self.instruction_pointer()); + *opcode_index = ErrorLocation::Resolved(location); + } // All other errors are thrown normally. _ => (), }; @@ -633,12 +656,31 @@ pub fn witness_to_value( } } +// TODO(https://github.com/noir-lang/noir/issues/5985): +// remove skip_bitsize_checks pub fn input_to_value( initial_witness: &WitnessMap, input: FunctionInput, + skip_bitsize_checks: bool, ) -> Result> { - match input.input { - ConstantOrWitnessEnum::Witness(witness) => Ok(*witness_to_value(initial_witness, witness)?), + match input.input() { + ConstantOrWitnessEnum::Witness(witness) => { + let initial_value = *witness_to_value(initial_witness, witness)?; + if skip_bitsize_checks || initial_value.num_bits() <= input.num_bits() { + Ok(initial_value) + } else { + let value_num_bits = initial_value.num_bits(); + let value = initial_value.to_string(); + Err(OpcodeResolutionError::InvalidInputBitSize { + opcode_location: ErrorLocation::Unresolved, + invalid_input_bit_size: InvalidInputBitSize { + value, + value_num_bits, + max_bits: input.num_bits(), + }, + }) + } + } ConstantOrWitnessEnum::Constant(value) => Ok(value), } } diff --git a/acvm-repo/acvm/tests/solver.rs b/acvm-repo/acvm/tests/solver.rs index 5a15586bf1b..a7b3808efc4 100644 --- a/acvm-repo/acvm/tests/solver.rs +++ b/acvm-repo/acvm/tests/solver.rs @@ -18,6 +18,7 @@ use acvm_blackbox_solver::StubbedBlackBoxSolver; use bn254_blackbox_solver::{field_from_hex, Bn254BlackBoxSolver, POSEIDON2_CONFIG}; use brillig_vm::brillig::HeapValueType; +use num_bigint::BigUint; use proptest::arbitrary::any; use proptest::prelude::*; use proptest::result::maybe_ok; @@ -776,15 +777,15 @@ fn constant_or_witness_to_function_inputs( xs: Vec, offset: usize, num_bits: Option, -) -> Vec> { +) -> Result>, OpcodeResolutionError> { let num_bits = num_bits.unwrap_or(FieldElement::max_num_bits()); xs.into_iter() .enumerate() .map(|(i, (x, use_constant))| { if use_constant { - FunctionInput::constant(x, num_bits) + FunctionInput::constant(x, num_bits).map_err(From::from) } else { - FunctionInput::witness(Witness((i + offset) as u32), num_bits) + Ok(FunctionInput::witness(Witness((i + offset) as u32), num_bits)) } }) .collect() @@ -810,9 +811,11 @@ fn solve_array_input_blackbox_call( num_outputs: usize, num_bits: Option, f: F, -) -> Vec +) -> Result, OpcodeResolutionError> where - F: FnOnce((Vec>, Vec)) -> BlackBoxFuncCall, + F: FnOnce( + (Vec>, Vec), + ) -> Result, OpcodeResolutionError>, { let initial_witness_vec: Vec<_> = inputs.iter().enumerate().map(|(i, (x, _))| (Witness(i as u32), *x)).collect(); @@ -821,8 +824,8 @@ where .collect(); let initial_witness = WitnessMap::from(BTreeMap::from_iter(initial_witness_vec)); - let inputs = constant_or_witness_to_function_inputs(inputs, 0, num_bits); - let op = Opcode::BlackBoxFuncCall(f((inputs.clone(), outputs.clone()))); + let inputs = constant_or_witness_to_function_inputs(inputs, 0, num_bits)?; + let op = Opcode::BlackBoxFuncCall(f((inputs.clone(), outputs.clone()))?); let opcodes = vec![op]; let unconstrained_functions = vec![]; let mut acvm = @@ -831,10 +834,10 @@ where assert_eq!(solver_status, ACVMStatus::Solved); let witness_map = acvm.finalize(); - outputs + Ok(outputs .iter() .map(|witness| *witness_map.get(witness).expect("all witnesses to be set")) - .collect() + .collect()) } prop_compose! { @@ -919,7 +922,7 @@ fn bigint_solve_binary_op_opt( modulus: Vec, lhs: Vec, rhs: Vec, -) -> Vec { +) -> Result, OpcodeResolutionError> { let initial_witness_vec: Vec<_> = lhs .iter() .chain(rhs.iter()) @@ -934,8 +937,8 @@ fn bigint_solve_binary_op_opt( .collect(); let initial_witness = WitnessMap::from(BTreeMap::from_iter(initial_witness_vec)); - let lhs = constant_or_witness_to_function_inputs(lhs, 0, None); - let rhs = constant_or_witness_to_function_inputs(rhs, lhs.len(), None); + let lhs = constant_or_witness_to_function_inputs(lhs, 0, None)?; + let rhs = constant_or_witness_to_function_inputs(rhs, lhs.len(), None)?; let to_op_input = if middle_op.is_some() { 2 } else { 0 }; @@ -966,10 +969,10 @@ fn bigint_solve_binary_op_opt( let solver_status = acvm.solve(); assert_eq!(solver_status, ACVMStatus::Solved); let witness_map = acvm.finalize(); - output_witnesses + Ok(output_witnesses .iter() .map(|witness| *witness_map.get(witness).expect("all witnesses to be set")) - .collect() + .collect()) } // Solve the given BlackBoxFuncCall with witnesses: 1, 2 as x, y, resp. @@ -978,10 +981,13 @@ fn solve_blackbox_func_call( blackbox_func_call: impl Fn( Option, Option, - ) -> BlackBoxFuncCall, + ) -> Result< + BlackBoxFuncCall, + OpcodeResolutionError, + >, lhs: (FieldElement, bool), // if false, use a Witness rhs: (FieldElement, bool), // if false, use a Witness -) -> FieldElement { +) -> Result> { let (lhs, lhs_constant) = lhs; let (rhs, rhs_constant) = rhs; @@ -998,7 +1004,7 @@ fn solve_blackbox_func_call( rhs_opt = Some(rhs); } - let op = Opcode::BlackBoxFuncCall(blackbox_func_call(lhs_opt, rhs_opt)); + let op = Opcode::BlackBoxFuncCall(blackbox_func_call(lhs_opt, rhs_opt)?); let opcodes = vec![op]; let unconstrained_functions = vec![]; let mut acvm = @@ -1007,60 +1013,60 @@ fn solve_blackbox_func_call( assert_eq!(solver_status, ACVMStatus::Solved); let witness_map = acvm.finalize(); - witness_map[&Witness(3)] + Ok(witness_map[&Witness(3)]) } // N inputs // 32 outputs fn sha256_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; - BlackBoxFuncCall::SHA256 { + Ok(BlackBoxFuncCall::SHA256 { inputs: function_inputs, outputs: outputs.try_into().expect("SHA256 returns 32 outputs"), - } + }) } // N inputs // 32 outputs fn blake2s_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; - BlackBoxFuncCall::Blake2s { + Ok(BlackBoxFuncCall::Blake2s { inputs: function_inputs, outputs: outputs.try_into().expect("Blake2s returns 32 outputs"), - } + }) } // N inputs // 32 outputs fn blake3_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; - BlackBoxFuncCall::Blake3 { + Ok(BlackBoxFuncCall::Blake3 { inputs: function_inputs, outputs: outputs.try_into().expect("Blake3 returns 32 outputs"), - } + }) } // variable inputs // 32 outputs fn keccak256_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; let function_inputs_len = function_inputs.len(); - BlackBoxFuncCall::Keccak256 { + Ok(BlackBoxFuncCall::Keccak256 { inputs: function_inputs, var_message_size: FunctionInput::constant( function_inputs_len.into(), FieldElement::max_num_bits(), - ), + )?, outputs: outputs.try_into().expect("Keccak256 returns 32 outputs"), - } + }) } // var_message_size is the number of bytes to take @@ -1072,65 +1078,65 @@ fn keccak256_op( // 32 outputs fn keccak256_invalid_message_size_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; let function_inputs_len = function_inputs.len(); - BlackBoxFuncCall::Keccak256 { + Ok(BlackBoxFuncCall::Keccak256 { inputs: function_inputs, var_message_size: FunctionInput::constant( (function_inputs_len - 1).into(), FieldElement::max_num_bits(), - ), + )?, outputs: outputs.try_into().expect("Keccak256 returns 32 outputs"), - } + }) } // 25 inputs // 25 outputs fn keccakf1600_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; - BlackBoxFuncCall::Keccakf1600 { + Ok(BlackBoxFuncCall::Keccakf1600 { inputs: function_inputs.try_into().expect("Keccakf1600 expects 25 inputs"), outputs: outputs.try_into().expect("Keccakf1600 returns 25 outputs"), - } + }) } // N inputs // N outputs fn poseidon2_permutation_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (inputs, outputs) = function_inputs_and_outputs; let len = inputs.len() as u32; - BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len } + Ok(BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len }) } // N inputs // N outputs fn poseidon2_permutation_invalid_len_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (inputs, outputs) = function_inputs_and_outputs; let len = (inputs.len() as u32) + 1; - BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len } + Ok(BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len }) } // 24 inputs (16 + 8) // 8 outputs fn sha256_compression_op( function_inputs_and_outputs: (Vec>, Vec), -) -> BlackBoxFuncCall { +) -> Result, OpcodeResolutionError> { let (function_inputs, outputs) = function_inputs_and_outputs; let mut function_inputs = function_inputs.into_iter(); let inputs = core::array::from_fn(|_| function_inputs.next().unwrap()); let hash_values = core::array::from_fn(|_| function_inputs.next().unwrap()); - BlackBoxFuncCall::Sha256Compression { + Ok(BlackBoxFuncCall::Sha256Compression { inputs: Box::new(inputs), hash_values: Box::new(hash_values), outputs: outputs.try_into().unwrap(), - } + }) } fn into_repr_vec(fields: T) -> Vec @@ -1150,13 +1156,13 @@ where fn run_both_poseidon2_permutations( inputs: Vec, -) -> (Vec, Vec) { +) -> Result<(Vec, Vec), OpcodeResolutionError> { let result = solve_array_input_blackbox_call( inputs.clone(), inputs.len(), None, poseidon2_permutation_op, - ); + )?; let poseidon2_t = POSEIDON2_CONFIG.t as usize; let poseidon2_d = 5; @@ -1179,7 +1185,7 @@ fn run_both_poseidon2_permutations( let expected_result = external_poseidon2.permutation(&into_repr_vec(drop_use_constant(&inputs))); - (into_repr_vec(result), expected_result) + Ok((into_repr_vec(result), expected_result)) } // Using the given BigInt modulus, solve the following circuit: @@ -1195,7 +1201,7 @@ fn bigint_solve_binary_op( lhs: Vec, rhs: Vec, ) -> Vec { - bigint_solve_binary_op_opt(Some(middle_op), modulus, lhs, rhs) + bigint_solve_binary_op_opt(Some(middle_op), modulus, lhs, rhs).unwrap() } // Using the given BigInt modulus, solve the following circuit: @@ -1206,69 +1212,89 @@ fn bigint_solve_from_to_le_bytes( modulus: Vec, inputs: Vec, ) -> Vec { - bigint_solve_binary_op_opt(None, modulus, inputs, vec![]) + bigint_solve_binary_op_opt(None, modulus, inputs, vec![]).unwrap() } fn function_input_from_option( witness: Witness, opt_constant: Option, -) -> FunctionInput { +) -> Result, OpcodeResolutionError> { opt_constant - .map(|constant| FunctionInput::constant(constant, FieldElement::max_num_bits())) - .unwrap_or(FunctionInput::witness(witness, FieldElement::max_num_bits())) + .map(|constant| { + FunctionInput::constant(constant, FieldElement::max_num_bits()).map_err(From::from) + }) + .unwrap_or(Ok(FunctionInput::witness(witness, FieldElement::max_num_bits()))) } -fn and_op(x: Option, y: Option) -> BlackBoxFuncCall { - let lhs = function_input_from_option(Witness(1), x); - let rhs = function_input_from_option(Witness(2), y); - BlackBoxFuncCall::AND { lhs, rhs, output: Witness(3) } +fn and_op( + x: Option, + y: Option, +) -> Result, OpcodeResolutionError> { + let lhs = function_input_from_option(Witness(1), x)?; + let rhs = function_input_from_option(Witness(2), y)?; + Ok(BlackBoxFuncCall::AND { lhs, rhs, output: Witness(3) }) } -fn xor_op(x: Option, y: Option) -> BlackBoxFuncCall { - let lhs = function_input_from_option(Witness(1), x); - let rhs = function_input_from_option(Witness(2), y); - BlackBoxFuncCall::XOR { lhs, rhs, output: Witness(3) } +fn xor_op( + x: Option, + y: Option, +) -> Result, OpcodeResolutionError> { + let lhs = function_input_from_option(Witness(1), x)?; + let rhs = function_input_from_option(Witness(2), y)?; + Ok(BlackBoxFuncCall::XOR { lhs, rhs, output: Witness(3) }) } fn prop_assert_commutative( - op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op: impl Fn( + Option, + Option, + ) -> Result, OpcodeResolutionError>, x: (FieldElement, bool), y: (FieldElement, bool), ) -> (FieldElement, FieldElement) { - (solve_blackbox_func_call(&op, x, y), solve_blackbox_func_call(&op, y, x)) + (solve_blackbox_func_call(&op, x, y).unwrap(), solve_blackbox_func_call(&op, y, x).unwrap()) } fn prop_assert_associative( - op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op: impl Fn( + Option, + Option, + ) -> Result, OpcodeResolutionError>, x: (FieldElement, bool), y: (FieldElement, bool), z: (FieldElement, bool), use_constant_xy: bool, use_constant_yz: bool, ) -> (FieldElement, FieldElement) { - let f_xy = (solve_blackbox_func_call(&op, x, y), use_constant_xy); - let f_f_xy_z = solve_blackbox_func_call(&op, f_xy, z); + let f_xy = (solve_blackbox_func_call(&op, x, y).unwrap(), use_constant_xy); + let f_f_xy_z = solve_blackbox_func_call(&op, f_xy, z).unwrap(); - let f_yz = (solve_blackbox_func_call(&op, y, z), use_constant_yz); - let f_x_f_yz = solve_blackbox_func_call(&op, x, f_yz); + let f_yz = (solve_blackbox_func_call(&op, y, z).unwrap(), use_constant_yz); + let f_x_f_yz = solve_blackbox_func_call(&op, x, f_yz).unwrap(); (f_f_xy_z, f_x_f_yz) } fn prop_assert_identity_l( - op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op: impl Fn( + Option, + Option, + ) -> Result, OpcodeResolutionError>, op_identity: (FieldElement, bool), x: (FieldElement, bool), ) -> (FieldElement, FieldElement) { - (solve_blackbox_func_call(op, op_identity, x), x.0) + (solve_blackbox_func_call(op, op_identity, x).unwrap(), x.0) } fn prop_assert_zero_l( - op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op: impl Fn( + Option, + Option, + ) -> Result, OpcodeResolutionError>, op_zero: (FieldElement, bool), x: (FieldElement, bool), ) -> (FieldElement, FieldElement) { - (solve_blackbox_func_call(op, op_zero, x), FieldElement::zero()) + (solve_blackbox_func_call(op, op_zero, x).unwrap(), FieldElement::zero()) } // Test that varying one of the inputs produces a different result @@ -1282,14 +1308,19 @@ fn prop_assert_injective( op: F, ) -> (bool, String) where - F: FnOnce((Vec>, Vec)) -> BlackBoxFuncCall + F: FnOnce( + (Vec>, Vec), + ) + -> Result, OpcodeResolutionError> + Clone, { let equal_inputs = drop_use_constant_eq(&inputs, &distinct_inputs); let message = format!("not injective:\n{:?}\n{:?}", &inputs, &distinct_inputs); let outputs_not_equal = solve_array_input_blackbox_call(inputs, num_outputs, num_bits, op.clone()) - != solve_array_input_blackbox_call(distinct_inputs, num_outputs, num_bits, op); + .expect("injectivity test operations to have valid input") + != solve_array_input_blackbox_call(distinct_inputs, num_outputs, num_bits, op) + .expect("injectivity test operations to have valid input"); (equal_inputs || outputs_not_equal, message) } @@ -1322,12 +1353,12 @@ prop_compose! { -> (Vec, Vec) { let (_size, patch_location, patch_value) = size_and_patch; let (inputs, distinct_inputs) = inputs_distinct_inputs; + let modulus = if let Some(max_input_bits) = max_input_bits { + 1u128 << max_input_bits + } else { + 1 + }; let to_input = |(x, use_constant)| { - let modulus = if let Some(max_input_bits) = max_input_bits { - 2u128 << max_input_bits - } else { - 1 - }; (FieldElement::from(x % modulus), use_constant) }; let inputs: Vec<_> = inputs.into_iter().map(to_input).collect(); @@ -1338,9 +1369,11 @@ prop_compose! { let distinct_inputs_len = distinct_inputs.len(); let positive_patch_value = std::cmp::max(patch_value, 1); if distinct_inputs_len != 0 { - distinct_inputs[patch_location % distinct_inputs_len].0 += FieldElement::from(positive_patch_value) + let previous_input = &mut distinct_inputs[patch_location % distinct_inputs_len].0; + let patched_input: BigUint = (*previous_input + FieldElement::from(positive_patch_value)).into_repr().into(); + *previous_input = FieldElement::from_be_bytes_reduce(&(patched_input % BigUint::from(modulus)).to_bytes_be()); } else { - distinct_inputs.push((FieldElement::zero(), true)) + distinct_inputs.push((FieldElement::zero(), true)); } } @@ -1352,17 +1385,17 @@ prop_compose! { fn poseidon2_permutation_zeroes() { let use_constants: [bool; 4] = [false; 4]; let inputs: Vec<_> = [FieldElement::zero(); 4].into_iter().zip(use_constants).collect(); - let (result, expected_result) = run_both_poseidon2_permutations(inputs); + let (results, expected_results) = run_both_poseidon2_permutations(inputs).unwrap(); - let internal_expected_result = vec![ + let internal_expected_results = vec![ field_from_hex("18DFB8DC9B82229CFF974EFEFC8DF78B1CE96D9D844236B496785C698BC6732E"), field_from_hex("095C230D1D37A246E8D2D5A63B165FE0FADE040D442F61E25F0590E5FB76F839"), field_from_hex("0BB9545846E1AFA4FA3C97414A60A20FC4949F537A68CCECA34C5CE71E28AA59"), field_from_hex("18A4F34C9C6F99335FF7638B82AEED9018026618358873C982BBDDE265B2ED6D"), ]; - assert_eq!(expected_result, into_repr_vec(internal_expected_result)); - assert_eq!(result, expected_result); + assert_eq!(expected_results, into_repr_vec(internal_expected_results)); + assert_eq!(results, expected_results); } #[test] @@ -1375,7 +1408,7 @@ fn sha256_zeros() { .into_iter() .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } #[test] @@ -1393,7 +1426,7 @@ fn sha256_compression_zeros() { .into_iter() .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } #[test] @@ -1406,7 +1439,7 @@ fn blake2s_zeros() { .into_iter() .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } #[test] @@ -1419,7 +1452,7 @@ fn blake3_zeros() { .into_iter() .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } #[test] @@ -1432,7 +1465,7 @@ fn keccak256_zeros() { .into_iter() .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } #[test] @@ -1474,7 +1507,7 @@ fn keccakf1600_zeros() { .map(|x: u128| FieldElement::from(x)) .collect(); - assert_eq!(results, expected_results); + assert_eq!(results, Ok(expected_results)); } // NOTE: an "average" bigint is large, so consider increasing the number of proptest shrinking @@ -1511,13 +1544,13 @@ proptest! { // test that AND(x, x) == x #[test] fn and_self_identity(x in field_element()) { - prop_assert_eq!(solve_blackbox_func_call(and_op, x, x), x.0); + prop_assert_eq!(solve_blackbox_func_call(and_op, x, x).unwrap(), x.0); } // test that XOR(x, x) == 0 #[test] fn xor_self_zero(x in field_element()) { - prop_assert_eq!(solve_blackbox_func_call(xor_op, x, x), FieldElement::zero()); + prop_assert_eq!(solve_blackbox_func_call(xor_op, x, x).unwrap(), FieldElement::zero()); } #[test] @@ -1547,7 +1580,7 @@ proptest! { #[test] fn poseidon2_permutation_matches_external_impl(inputs in proptest::collection::vec(field_element(), 4)) { - let (result, expected_result) = run_both_poseidon2_permutations(inputs); + let (result, expected_result) = run_both_poseidon2_permutations(inputs).unwrap(); prop_assert_eq!(result, expected_result) } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 98a0c4c3abe..c3627d0eff5 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -201,6 +201,10 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { opcode_location: ErrorLocation::Resolved(opcode_location), .. } => Some(vec![*opcode_location]), + OpcodeResolutionError::InvalidInputBitSize { + opcode_location: ErrorLocation::Resolved(opcode_location), + .. + } => Some(vec![*opcode_location]), OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { Some(call_stack.clone()) } diff --git a/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts b/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts index 80fbf14e8f1..ffb9952b136 100644 --- a/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts +++ b/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts @@ -1,8 +1,8 @@ // See `multi_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 232, 142, 158, 210, 130, 149, 240, - 112, 234, 212, 156, 78, 12, 39, 67, 71, 158, 142, 80, 29, 44, 228, 66, 90, 168, 119, 189, 74, 115, 131, 174, 78, 115, - 58, 124, 70, 254, 130, 59, 74, 253, 68, 255, 255, 221, 39, 54, 221, 93, 91, 132, 193, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 255, 227, 70, 74, 11, 86, 194, + 195, 169, 83, 115, 58, 49, 156, 12, 29, 121, 58, 66, 117, 176, 144, 11, 105, 161, 222, 245, 42, 205, 13, 186, 58, 205, + 233, 240, 25, 249, 11, 238, 40, 245, 19, 253, 255, 119, 159, 216, 103, 157, 249, 169, 193, 0, 0, 0, ]); export const initialWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index bcd6865b721..c4ba08f9acd 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -34,6 +34,13 @@ pub enum RuntimeError { UnInitialized { name: String, call_stack: CallStack }, #[error("Integer sized {num_bits:?} is over the max supported size of {max_num_bits:?}")] UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, call_stack: CallStack }, + #[error("Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs")] + InvalidBlackBoxInputBitSize { + value: String, + num_bits: u32, + max_num_bits: u32, + call_stack: CallStack, + }, #[error("Could not determine loop bound at compile-time")] UnknownLoopBound { call_stack: CallStack }, #[error("Argument is not constant")] @@ -156,6 +163,7 @@ impl RuntimeError { | RuntimeError::StaticAssertFailed { call_stack } | RuntimeError::IntegerOutOfBounds { call_stack, .. } | RuntimeError::UnsupportedIntegerSize { call_stack, .. } + | RuntimeError::InvalidBlackBoxInputBitSize { call_stack, .. } | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index d12d49784ec..9586d08e10c 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1420,7 +1420,7 @@ impl AcirContext { } BlackBoxFunc::AES128Encrypt => { let invalid_input = "aes128_encrypt - operation requires a plaintext to encrypt"; - let input_size = match inputs.first().expect(invalid_input) { + let input_size: usize = match inputs.first().expect(invalid_input) { AcirValue::Array(values) => Ok::(values.len()), AcirValue::DynamicArray(dyn_array) => Ok::(dyn_array.len), _ => { @@ -1510,7 +1510,18 @@ impl AcirContext { let num_bits = typ.bit_size::(); match self.vars[&input].as_constant() { Some(constant) if allow_constant_inputs => { - single_val_witnesses.push(FunctionInput::constant(*constant, num_bits)); + single_val_witnesses.push( + FunctionInput::constant(*constant, num_bits).map_err( + |invalid_input_bit_size| { + RuntimeError::InvalidBlackBoxInputBitSize { + value: invalid_input_bit_size.value, + num_bits: invalid_input_bit_size.value_num_bits, + max_num_bits: invalid_input_bit_size.max_bits, + call_stack: self.get_call_stack(), + } + }, + )?, + ); } _ => { let witness_var = self.get_or_create_witness_var(input)?; diff --git a/cspell.json b/cspell.json index 293523d7c15..4d83c535e7d 100644 --- a/cspell.json +++ b/cspell.json @@ -25,6 +25,7 @@ "bindgen", "bitand", "bitmask", + "bitsize", "blackbox", "boilerplate", "boilerplates", diff --git a/tooling/fuzzer/src/dictionary/mod.rs b/tooling/fuzzer/src/dictionary/mod.rs index 942462c4f37..e10da8cc54a 100644 --- a/tooling/fuzzer/src/dictionary/mod.rs +++ b/tooling/fuzzer/src/dictionary/mod.rs @@ -10,7 +10,7 @@ use acvm::{ circuit::{ brillig::{BrilligBytecode, BrilligInputs}, directives::Directive, - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, Circuit, Opcode, Program, }, native_types::Expression, @@ -83,21 +83,24 @@ fn build_dictionary_from_circuit(circuit: &Circuit) -> HashSet< } } - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { input: ConstantOrWitnessEnum::Constant(c), num_bits }, - }) => { - let field = 1u128.wrapping_shl(*num_bits); - constants.insert(F::from(field)); - constants.insert(F::from(field - 1)); - constants.insert(*c); - } - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { input: ConstantOrWitnessEnum::Witness(_), num_bits }, - }) => { - let field = 1u128.wrapping_shl(*num_bits); - constants.insert(F::from(field)); - constants.insert(F::from(field - 1)); + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) + if matches!(input.input(), ConstantOrWitnessEnum::Constant(..)) => + { + match input.input() { + ConstantOrWitnessEnum::Constant(c) => { + let field = 1u128.wrapping_shl(input.num_bits()); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + constants.insert(c); + } + _ => { + let field = 1u128.wrapping_shl(input.num_bits()); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + } + } } + _ => (), } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 0b8378702cb..4a72bb1ec78 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -115,6 +115,10 @@ fn extract_locations_from_error( OpcodeResolutionError::IndexOutOfBounds { opcode_location: error_location, .. }, acir_call_stack, ) + | ExecutionError::SolvingError( + OpcodeResolutionError::InvalidInputBitSize { opcode_location: error_location, .. }, + acir_call_stack, + ) | ExecutionError::SolvingError( OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: error_location, .. }, acir_call_stack, diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 59d554d7ca5..eb03bdf01c1 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -89,6 +89,10 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> | OpcodeResolutionError::IndexOutOfBounds { opcode_location: ErrorLocation::Resolved(opcode_location), .. + } + | OpcodeResolutionError::InvalidInputBitSize { + opcode_location: ErrorLocation::Resolved(opcode_location), + .. } => { let resolved_location = ResolvedOpcodeLocation { acir_function_index: self.current_function_index, diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index efe648e09b0..370a4235f61 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -191,8 +191,16 @@ fn check_expected_failure_message( None => return TestStatus::Pass, }; - let expected_failure_message_matches = - matches!(&failed_assertion, Some(message) if message.contains(expected_failure_message)); + // Match the failure message that the user will see, i.e. the failed_assertion + // if present or else the error_diagnostic's message, against the + // expected_failure_message + let expected_failure_message_matches = failed_assertion + .as_ref() + .or_else(|| { + error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.diagnostic.message) + }) + .map(|message| message.contains(expected_failure_message)) + .unwrap_or(false); if expected_failure_message_matches { return TestStatus::Pass; }