From 305bcdcbd01cb84dbaac900f14cb6cf867f83bda Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Fri, 12 Apr 2024 04:46:40 -0400 Subject: [PATCH] feat: Sync from aztec-packages (#4787) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE fix: avoid huge unrolling in hash_args (https://github.com/AztecProtocol/aztec-packages/pull/5703) feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5697) feat: Variable length returns (https://github.com/AztecProtocol/aztec-packages/pull/5633) feat(simulator): Fetch return values at circuit execution (https://github.com/AztecProtocol/aztec-packages/pull/5642) feat: Brillig heterogeneous memory cells (https://github.com/AztecProtocol/aztec-packages/pull/5608) chore!: remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant Co-authored-by: ludamad Co-authored-by: vezenovm Co-authored-by: Alvaro Rodriguez --- .aztec-sync-commit | 2 +- acvm-repo/acir/codegen/acir.cpp | 58 +--- .../opcodes/black_box_function_call.rs | 11 +- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 10 +- acvm-repo/acvm/src/pwg/brillig.rs | 6 +- acvm-repo/acvm_js/src/execute.rs | 56 +++- acvm-repo/acvm_js/src/js_witness_map.rs | 38 ++- acvm-repo/acvm_js/src/lib.rs | 3 +- acvm-repo/acvm_js/src/public_witness.rs | 9 +- acvm-repo/brillig_vm/src/arithmetic.rs | 68 +++-- acvm-repo/brillig_vm/src/black_box.rs | 14 +- acvm-repo/brillig_vm/src/lib.rs | 71 +++-- acvm-repo/brillig_vm/src/memory.rs | 234 ++++++++++++--- aztec_macros/src/transforms/functions.rs | 277 ++++++++---------- aztec_macros/src/utils/ast_utils.rs | 29 +- aztec_macros/src/utils/errors.rs | 6 + .../brillig/brillig_gen/brillig_directive.rs | 152 ++++++---- .../brillig/brillig_gen/brillig_slice_ops.rs | 8 +- .../src/brillig/brillig_ir/entry_point.rs | 4 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 4 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 5 +- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/debugger/src/context.rs | 6 +- tooling/debugger/src/repl.rs | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +- 27 files changed, 647 insertions(+), 446 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ec4c854ef2b..9ebd1dcccac 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -ff28080bcfb946177010960722925973ee19646b +10d9ad99200a5897417ff5669763ead4e38d87fa diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index e4203b579b0..7cd9fbefba0 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -159,6 +159,7 @@ namespace Program { struct Keccak256 { std::vector inputs; + Program::FunctionInput var_message_size; std::vector outputs; friend bool operator==(const Keccak256&, const Keccak256&); @@ -166,16 +167,6 @@ namespace Program { static Keccak256 bincodeDeserialize(std::vector); }; - struct Keccak256VariableLength { - std::vector inputs; - Program::FunctionInput var_message_size; - std::vector outputs; - - friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); - std::vector bincodeSerialize() const; - static Keccak256VariableLength bincodeDeserialize(std::vector); - }; - struct Keccakf1600 { std::vector inputs; std::vector outputs; @@ -275,7 +266,7 @@ namespace Program { static Sha256Compression bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -2582,6 +2573,7 @@ namespace Program { inline bool operator==(const BlackBoxFuncCall::Keccak256 &lhs, const BlackBoxFuncCall::Keccak256 &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.var_message_size == rhs.var_message_size)) { return false; } if (!(lhs.outputs == rhs.outputs)) { return false; } return true; } @@ -2607,6 +2599,7 @@ template <> template void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.var_message_size, serializer); serde::Serializable::serialize(obj.outputs, serializer); } @@ -2615,49 +2608,6 @@ template Program::BlackBoxFuncCall::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { Program::BlackBoxFuncCall::Keccak256 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - - inline bool operator==(const BlackBoxFuncCall::Keccak256VariableLength &lhs, const BlackBoxFuncCall::Keccak256VariableLength &rhs) { - if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.var_message_size == rhs.var_message_size)) { return false; } - if (!(lhs.outputs == rhs.outputs)) { return false; } - return true; - } - - inline std::vector BlackBoxFuncCall::Keccak256VariableLength::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BlackBoxFuncCall::Keccak256VariableLength BlackBoxFuncCall::Keccak256VariableLength::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256VariableLength &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.var_message_size, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Program::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxFuncCall::Keccak256VariableLength obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); obj.var_message_size = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; 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 c955e435b37..d9089578739 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 @@ -81,10 +81,6 @@ pub enum BlackBoxFuncCall { outputs: (Witness, Witness), }, Keccak256 { - inputs: Vec, - outputs: Vec, - }, - Keccak256VariableLength { inputs: Vec, /// This is the number of bytes to take /// from the input. Note: if `var_message_size` @@ -183,7 +179,6 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, - BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation, BlackBoxFuncCall::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, @@ -206,7 +201,6 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Blake3 { inputs, .. } - | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } @@ -280,7 +274,7 @@ impl BlackBoxFuncCall { inputs.extend(hashed_message.iter().copied()); inputs } - BlackBoxFuncCall::Keccak256VariableLength { inputs, var_message_size, .. } => { + BlackBoxFuncCall::Keccak256 { inputs, var_message_size, .. } => { let mut inputs = inputs.clone(); inputs.push(*var_message_size); inputs @@ -306,9 +300,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { outputs, .. } | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::Blake3 { outputs, .. } - | BlackBoxFuncCall::Keccak256 { outputs, .. } | BlackBoxFuncCall::Keccakf1600 { outputs, .. } - | BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } + | BlackBoxFuncCall::Keccak256 { outputs, .. } | BlackBoxFuncCall::Poseidon2Permutation { outputs, .. } | BlackBoxFuncCall::Sha256Compression { outputs, .. } => outputs.to_vec(), BlackBoxFuncCall::AND { output, .. } diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 6ee926043cd..e7ed402a8eb 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -95,15 +95,7 @@ pub(crate) fn solve( blake3, bb_func.get_black_box_func(), ), - BlackBoxFuncCall::Keccak256 { inputs, outputs } => solve_generic_256_hash_opcode( - initial_witness, - inputs, - None, - outputs, - keccak256, - bb_func.get_black_box_func(), - ), - BlackBoxFuncCall::Keccak256VariableLength { inputs, var_message_size, outputs } => { + BlackBoxFuncCall::Keccak256 { inputs, var_message_size, outputs } => { solve_generic_256_hash_opcode( initial_witness, inputs, diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index bcf736cd926..81e752d5656 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -206,13 +206,13 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, memory[current_ret_data_idx].value, witness_map)?; + insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { for witness in witness_arr.iter() { - let value = memory[current_ret_data_idx]; - insert_value(witness, value.value, witness_map)?; + let value = &memory[current_ret_data_idx]; + insert_value(witness, value.to_field(), witness_map)?; current_ret_data_idx += 1; } } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 0e58ccf039c..c97b8ea1a66 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -13,7 +13,8 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::{ foreign_call::{resolve_brillig, ForeignCallHandler}, - JsExecutionError, JsWitnessMap, JsWitnessStack, + public_witness::extract_indices, + JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack, }; #[wasm_bindgen] @@ -58,6 +59,44 @@ pub async fn execute_circuit( Ok(witness_map.into()) } +/// Executes an ACIR circuit to generate the solved witness from the initial witness. +/// This method also extracts the public return values from the solved witness into its own return witness. +/// +/// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver. +/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit +/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `circuit`.. +/// @param {ForeignCallHandler} foreign_call_handler - A callback to process any foreign calls from the circuit. +/// @returns {SolvedAndReturnWitness} The solved witness calculated by executing the circuit on the provided inputs, as well as the return witness indices as specified by the circuit. +#[wasm_bindgen(js_name = executeCircuitWithReturnWitness, skip_jsdoc)] +pub async fn execute_circuit_with_return_witness( + solver: &WasmBlackBoxFunctionSolver, + program: Vec, + initial_witness: JsWitnessMap, + foreign_call_handler: ForeignCallHandler, +) -> Result { + console_error_panic_hook::set_once(); + + let program: Program = Program::deserialize_program(&program) + .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + + let mut witness_stack = execute_program_with_native_program_and_return( + solver, + &program, + initial_witness, + &foreign_call_handler, + ) + .await?; + let solved_witness = + witness_stack.pop().expect("Should have at least one witness on the stack").witness; + + let main_circuit = &program.functions[0]; + let return_witness = + extract_indices(&solved_witness, main_circuit.return_values.0.iter().copied().collect()) + .map_err(|err| JsExecutionError::new(err, None))?; + + Ok((solved_witness, return_witness).into()) +} + /// Executes an ACIR circuit to generate the solved witness from the initial witness. /// /// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver. @@ -127,6 +166,21 @@ async fn execute_program_with_native_type_return( let program: Program = Program::deserialize_program(&program) .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + execute_program_with_native_program_and_return( + solver, + &program, + initial_witness, + foreign_call_executor, + ) + .await +} + +async fn execute_program_with_native_program_and_return( + solver: &WasmBlackBoxFunctionSolver, + program: &Program, + initial_witness: JsWitnessMap, + foreign_call_executor: &ForeignCallHandler, +) -> Result { let executor = ProgramExecutor::new(&program.functions, &solver.0, foreign_call_executor); let witness_stack = executor.execute(initial_witness.into()).await?; diff --git a/acvm-repo/acvm_js/src/js_witness_map.rs b/acvm-repo/acvm_js/src/js_witness_map.rs index 481b8caaa2d..c4482c4a234 100644 --- a/acvm-repo/acvm_js/src/js_witness_map.rs +++ b/acvm-repo/acvm_js/src/js_witness_map.rs @@ -2,13 +2,23 @@ use acvm::{ acir::native_types::{Witness, WitnessMap}, FieldElement, }; -use js_sys::{JsString, Map}; +use js_sys::{JsString, Map, Object}; use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; #[wasm_bindgen(typescript_custom_section)] const WITNESS_MAP: &'static str = r#" // Map from witness index to hex string value of witness. export type WitnessMap = Map; + +/** + * An execution result containing two witnesses. + * 1. The full solved witness of the execution. + * 2. The return witness which contains the given public return values within the full witness. + */ +export type SolvedAndReturnWitness = { + solvedWitness: WitnessMap; + returnWitness: WitnessMap; +} "#; // WitnessMap @@ -21,6 +31,12 @@ extern "C" { #[wasm_bindgen(constructor, js_class = "Map")] pub fn new() -> JsWitnessMap; + #[wasm_bindgen(extends = Object, js_name = "SolvedAndReturnWitness", typescript_type = "SolvedAndReturnWitness")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsSolvedAndReturnWitness; + + #[wasm_bindgen(constructor, js_class = "Object")] + pub fn new() -> JsSolvedAndReturnWitness; } impl Default for JsWitnessMap { @@ -29,6 +45,12 @@ impl Default for JsWitnessMap { } } +impl Default for JsSolvedAndReturnWitness { + fn default() -> Self { + Self::new() + } +} + impl From for JsWitnessMap { fn from(witness_map: WitnessMap) -> Self { let js_map = JsWitnessMap::new(); @@ -54,6 +76,20 @@ impl From for WitnessMap { } } +impl From<(WitnessMap, WitnessMap)> for JsSolvedAndReturnWitness { + fn from(witness_maps: (WitnessMap, WitnessMap)) -> Self { + let js_solved_witness = JsWitnessMap::from(witness_maps.0); + let js_return_witness = JsWitnessMap::from(witness_maps.1); + + let entry_map = Map::new(); + entry_map.set(&JsValue::from_str("solvedWitness"), &js_solved_witness); + entry_map.set(&JsValue::from_str("returnWitness"), &js_return_witness); + + let solved_and_return_witness = Object::from_entries(&entry_map).unwrap(); + JsSolvedAndReturnWitness { obj: solved_and_return_witness } + } +} + pub(crate) fn js_value_to_field_element(js_value: JsValue) -> Result { let hex_str = js_value.as_string().ok_or("failed to parse field element from non-string")?; diff --git a/acvm-repo/acvm_js/src/lib.rs b/acvm-repo/acvm_js/src/lib.rs index d7ecc0ae192..66a4388b132 100644 --- a/acvm-repo/acvm_js/src/lib.rs +++ b/acvm-repo/acvm_js/src/lib.rs @@ -22,9 +22,10 @@ pub use compression::{ }; pub use execute::{ create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver, - execute_program, execute_program_with_black_box_solver, + execute_circuit_with_return_witness, execute_program, execute_program_with_black_box_solver, }; pub use js_execution_error::JsExecutionError; +pub use js_witness_map::JsSolvedAndReturnWitness; pub use js_witness_map::JsWitnessMap; pub use js_witness_stack::JsWitnessStack; pub use logging::init_log_level; diff --git a/acvm-repo/acvm_js/src/public_witness.rs b/acvm-repo/acvm_js/src/public_witness.rs index a0d5b5f8be2..4ba054732d4 100644 --- a/acvm-repo/acvm_js/src/public_witness.rs +++ b/acvm-repo/acvm_js/src/public_witness.rs @@ -7,7 +7,10 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::JsWitnessMap; -fn extract_indices(witness_map: &WitnessMap, indices: Vec) -> Result { +pub(crate) fn extract_indices( + witness_map: &WitnessMap, + indices: Vec, +) -> Result { let mut extracted_witness_map = WitnessMap::new(); for witness in indices { let witness_value = witness_map.get(&witness).ok_or(format!( @@ -44,7 +47,7 @@ pub fn get_return_witness( let witness_map = WitnessMap::from(witness_map); let return_witness = - extract_indices(&witness_map, circuit.return_values.0.clone().into_iter().collect())?; + extract_indices(&witness_map, circuit.return_values.0.iter().copied().collect())?; Ok(JsWitnessMap::from(return_witness)) } @@ -71,7 +74,7 @@ pub fn get_public_parameters_witness( let witness_map = WitnessMap::from(solved_witness); let public_params_witness = - extract_indices(&witness_map, circuit.public_parameters.0.clone().into_iter().collect())?; + extract_indices(&witness_map, circuit.public_parameters.0.iter().copied().collect())?; Ok(JsWitnessMap::from(public_params_witness)) } diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 3d77982ffb1..2107d10c093 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,9 +1,10 @@ use acir::brillig::{BinaryFieldOp, BinaryIntOp}; use acir::FieldElement; use num_bigint::BigUint; -use num_traits::{One, ToPrimitive, Zero}; +use num_traits::ToPrimitive; +use num_traits::{One, Zero}; -use crate::memory::MemoryValue; +use crate::memory::{MemoryTypeError, MemoryValue}; #[derive(Debug, thiserror::Error)] pub(crate) enum BrilligArithmeticError { @@ -11,6 +12,8 @@ pub(crate) enum BrilligArithmeticError { MismatchedLhsBitSize { lhs_bit_size: u32, op_bit_size: u32 }, #[error("Bit size for rhs {rhs_bit_size} does not match op bit size {op_bit_size}")] MismatchedRhsBitSize { rhs_bit_size: u32, op_bit_size: u32 }, + #[error("Integer operation BinaryIntOp::{op:?} is not supported on FieldElement")] + IntegerOperationOnField { op: BinaryIntOp }, #[error("Shift with bit size {op_bit_size} is invalid")] InvalidShift { op_bit_size: u32 }, } @@ -21,21 +24,19 @@ pub(crate) fn evaluate_binary_field_op( lhs: MemoryValue, rhs: MemoryValue, ) -> Result { - if lhs.bit_size != FieldElement::max_num_bits() { + let MemoryValue::Field(a) = lhs else { return Err(BrilligArithmeticError::MismatchedLhsBitSize { - lhs_bit_size: lhs.bit_size, + lhs_bit_size: lhs.bit_size(), op_bit_size: FieldElement::max_num_bits(), }); - } - if rhs.bit_size != FieldElement::max_num_bits() { - return Err(BrilligArithmeticError::MismatchedRhsBitSize { - rhs_bit_size: rhs.bit_size, + }; + let MemoryValue::Field(b) = rhs else { + return Err(BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: rhs.bit_size(), op_bit_size: FieldElement::max_num_bits(), }); - } + }; - let a = lhs.value; - let b = rhs.value; Ok(match op { // Perform addition, subtraction, multiplication, and division based on the BinaryOp variant. BinaryFieldOp::Add => (a + b).into(), @@ -62,21 +63,26 @@ pub(crate) fn evaluate_binary_int_op( rhs: MemoryValue, bit_size: u32, ) -> Result { - if lhs.bit_size != bit_size { - return Err(BrilligArithmeticError::MismatchedLhsBitSize { - lhs_bit_size: lhs.bit_size, - op_bit_size: bit_size, - }); - } - if rhs.bit_size != bit_size { - return Err(BrilligArithmeticError::MismatchedRhsBitSize { - rhs_bit_size: rhs.bit_size, - op_bit_size: bit_size, - }); - } + let lhs = lhs.expect_integer_with_bit_size(bit_size).map_err(|err| match err { + MemoryTypeError::MismatchedBitSize { value_bit_size, expected_bit_size } => { + BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: value_bit_size, + op_bit_size: expected_bit_size, + } + } + })?; + let rhs = rhs.expect_integer_with_bit_size(bit_size).map_err(|err| match err { + MemoryTypeError::MismatchedBitSize { value_bit_size, expected_bit_size } => { + BrilligArithmeticError::MismatchedRhsBitSize { + rhs_bit_size: value_bit_size, + op_bit_size: expected_bit_size, + } + } + })?; - let lhs = BigUint::from_bytes_be(&lhs.value.to_be_bytes()); - let rhs = BigUint::from_bytes_be(&rhs.value.to_be_bytes()); + if bit_size == FieldElement::max_num_bits() { + return Err(BrilligArithmeticError::IntegerOperationOnField { op: *op }); + } let bit_modulo = &(BigUint::one() << bit_size); let result = match op { @@ -136,13 +142,11 @@ pub(crate) fn evaluate_binary_int_op( } }; - let result_as_field = FieldElement::from_be_bytes_reduce(&result.to_bytes_be()); - Ok(match op { BinaryIntOp::Equals | BinaryIntOp::LessThan | BinaryIntOp::LessThanEquals => { - MemoryValue::new(result_as_field, 1) + MemoryValue::new_integer(result, 1) } - _ => MemoryValue::new(result_as_field, bit_size), + _ => MemoryValue::new_integer(result, bit_size), }) } @@ -159,13 +163,13 @@ mod tests { fn evaluate_u128(op: &BinaryIntOp, a: u128, b: u128, bit_size: u32) -> u128 { let result_value = evaluate_binary_int_op( op, - MemoryValue::new(a.into(), bit_size), - MemoryValue::new(b.into(), bit_size), + MemoryValue::new_integer(a.into(), bit_size), + MemoryValue::new_integer(b.into(), bit_size), bit_size, ) .unwrap(); // Convert back to u128 - result_value.value.to_u128() + result_value.to_field().to_u128() } fn to_negative(a: u128, bit_size: u32) -> u128 { diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index bd33b5ee8fc..73981fb0625 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -20,7 +20,7 @@ fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [MemoryValu /// Extracts the last byte of every value fn to_u8_vec(inputs: &[MemoryValue]) -> Vec { let mut result = Vec::with_capacity(inputs.len()); - for &input in inputs { + for input in inputs { result.push(input.try_into().unwrap()); } result @@ -63,7 +63,7 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::Keccakf1600 { message, output } => { let state_vec: Vec = read_heap_vector(memory, message) .iter() - .map(|&memory_value| memory_value.try_into().unwrap()) + .map(|memory_value| memory_value.try_into().unwrap()) .collect(); let state: [u64; 25] = state_vec.try_into().unwrap(); @@ -151,7 +151,7 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect(); let domain_separator: u32 = memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( @@ -165,7 +165,7 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect(); let domain_separator: u32 = memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( @@ -185,7 +185,7 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::BigIntToLeBytes { .. } => todo!(), BlackBoxOp::Poseidon2Permutation { message, output, len } => { let input = read_heap_vector(memory, message); - let input: Vec = input.iter().map(|&x| x.try_into().unwrap()).collect(); + let input: Vec = input.iter().map(|x| x.try_into().unwrap()).collect(); let len = memory.read(*len).try_into().unwrap(); let result = solver.poseidon2_permutation(&input, len)?; let mut values = Vec::new(); @@ -204,7 +204,7 @@ pub(crate) fn evaluate_black_box( format!("Expected 16 inputs but encountered {}", &inputs.len()), )); } - for (i, &input) in inputs.iter().enumerate() { + for (i, input) in inputs.iter().enumerate() { message[i] = input.try_into().unwrap(); } let mut state = [0; 8]; @@ -215,7 +215,7 @@ pub(crate) fn evaluate_black_box( format!("Expected 8 values but encountered {}", &values.len()), )); } - for (i, &value) in values.iter().enumerate() { + for (i, value) in values.iter().enumerate() { state[i] = value.try_into().unwrap(); } diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 65654e24720..26d5da67576 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -289,8 +289,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // Convert our source_pointer to an address let source = self.memory.read_ref(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory.read(source); - self.memory.write(*destination_address, *value); + let value = self.memory.read(source); + self.memory.write(*destination_address, value); self.increment_program_counter() } Opcode::Store { destination_pointer, source: source_address } => { @@ -307,7 +307,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::Const { destination, value, bit_size } => { // Consts are not checked in runtime to fit in the bit size, since they can safely be checked statically. - self.memory.write(*destination, MemoryValue::new(*value, *bit_size)); + self.memory.write(*destination, MemoryValue::new_from_field(*value, *bit_size)); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { @@ -348,7 +348,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { ) -> ForeignCallParam { match (input, value_type) { (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(_)) => { - self.memory.read(value_index).value.into() + self.memory.read(value_index).to_field().into() } ( ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), @@ -357,7 +357,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let start = self.memory.read_ref(pointer_index); self.read_slice_of_values_from_memory(start, size, value_types) .into_iter() - .map(|mem_value| mem_value.value) + .map(|mem_value| mem_value.to_field()) .collect::>() .into() } @@ -369,7 +369,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let size = self.memory.read(size_index).to_usize(); self.read_slice_of_values_from_memory(start, size, value_types) .into_iter() - .map(|mem_value| mem_value.value) + .map(|mem_value| mem_value.to_field()) .collect::>() .into() } @@ -584,12 +584,9 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Casts a value to a different bit size. fn cast(&self, bit_size: u32, source_value: MemoryValue) -> MemoryValue { - let lhs_big = BigUint::from_bytes_be(&source_value.value.to_be_bytes()); + let lhs_big = source_value.to_integer(); let mask = BigUint::from(2_u32).pow(bit_size) - 1_u32; - MemoryValue { - value: FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()), - bit_size, - } + MemoryValue::new_from_integer(lhs_big & mask, bit_size) } } @@ -627,7 +624,7 @@ mod tests { let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value.value, FieldElement::from(27u128)); + assert_eq!(output_value.to_field(), FieldElement::from(27u128)); } #[test] @@ -666,7 +663,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(destination); - assert_eq!(output_cmp_value.value, true.into()); + assert_eq!(output_cmp_value.to_field(), true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -725,7 +722,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value.value, false.into()); + assert_eq!(output_cmp_value.to_field(), false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -742,7 +739,7 @@ mod tests { // The address at index `2` should have not changed as we jumped over the add opcode let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(2)); - assert_eq!(output_value.value, false.into()); + assert_eq!(output_value.to_field(), false.into()); } #[test] @@ -776,7 +773,7 @@ mod tests { let VM { memory, .. } = vm; let casted_value = memory.read(MemoryAddress::from(1)); - assert_eq!(casted_value.value, (2_u128.pow(8) - 1).into()); + assert_eq!(casted_value.to_field(), (2_u128.pow(8) - 1).into()); } #[test] @@ -804,10 +801,10 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(2)); - assert_eq!(destination_value.value, (1u128).into()); + assert_eq!(destination_value.to_field(), (1u128).into()); let source_value = memory.read(MemoryAddress::from(0)); - assert_eq!(source_value.value, (1u128).into()); + assert_eq!(source_value.to_field(), (1u128).into()); } #[test] @@ -869,10 +866,10 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(4)); - assert_eq!(destination_value.value, (3_u128).into()); + assert_eq!(destination_value.to_field(), (3_u128).into()); let source_value = memory.read(MemoryAddress::from(5)); - assert_eq!(source_value.value, (2_u128).into()); + assert_eq!(source_value.to_field(), (2_u128).into()); } #[test] @@ -1120,7 +1117,7 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.memory.read(r_sum).value + vm.memory.read(r_sum).to_field() } assert_eq!( @@ -1359,7 +1356,7 @@ mod tests { // Check result in memory let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!( - result_values.into_iter().map(|mem_value| mem_value.value).collect::>(), + result_values.into_iter().map(|mem_value| mem_value.to_field()).collect::>(), expected_result ); @@ -1459,7 +1456,7 @@ mod tests { .memory .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.clone().to_field()) .collect(); assert_eq!(result_values, output_string); @@ -1532,13 +1529,21 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(2), 4).iter().map(|mem_val| mem_val.value).collect(); + let initial_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(2), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(6), 4).iter().map(|mem_val| mem_val.value).collect(); + let result_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(6), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1622,8 +1627,12 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(0), 4).iter().map(|mem_val| mem_val.value).collect(); + let result_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(0), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1698,7 +1707,7 @@ mod tests { .chain(memory.iter().enumerate().map(|(index, mem_value)| Opcode::Cast { destination: MemoryAddress(index), source: MemoryAddress(index), - bit_size: mem_value.bit_size, + bit_size: mem_value.bit_size(), })) .chain(vec![ // input = 0 @@ -1721,7 +1730,7 @@ mod tests { .collect(); let mut vm = brillig_execute_and_get_vm( - memory.into_iter().map(|mem_value| mem_value.value).collect(), + memory.into_iter().map(|mem_value| mem_value.to_field()).collect(), &program, ); diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index d563e13be2e..feeb3706bde 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,11 +1,13 @@ use acir::{brillig::MemoryAddress, FieldElement}; +use num_bigint::BigUint; +use num_traits::{One, Zero}; pub const MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct MemoryValue { - pub value: FieldElement, - pub bit_size: u32, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum MemoryValue { + Field(FieldElement), + Integer(BigUint, u32), } #[derive(Debug, thiserror::Error)] @@ -15,53 +17,147 @@ pub enum MemoryTypeError { } impl MemoryValue { - pub fn new(value: FieldElement, bit_size: u32) -> Self { - MemoryValue { value, bit_size } + /// Builds a memory value from a field element. + pub fn new_from_field(value: FieldElement, bit_size: u32) -> Self { + if bit_size == FieldElement::max_num_bits() { + MemoryValue::new_field(value) + } else { + MemoryValue::new_integer(BigUint::from_bytes_be(&value.to_be_bytes()), bit_size) + } + } + + /// Builds a memory value from an integer + pub fn new_from_integer(value: BigUint, bit_size: u32) -> Self { + if bit_size == FieldElement::max_num_bits() { + MemoryValue::new_field(FieldElement::from_be_bytes_reduce(&value.to_bytes_be())) + } else { + MemoryValue::new_integer(value, bit_size) + } } + /// Builds a memory value from a field element, checking that the value is within the bit size. pub fn new_checked(value: FieldElement, bit_size: u32) -> Option { - if value.num_bits() > bit_size { + if bit_size < FieldElement::max_num_bits() && value.num_bits() > bit_size { return None; } - Some(MemoryValue::new(value, bit_size)) + Some(MemoryValue::new_from_field(value, bit_size)) } + /// Builds a field-typed memory value. pub fn new_field(value: FieldElement) -> Self { - MemoryValue { value, bit_size: FieldElement::max_num_bits() } + MemoryValue::Field(value) + } + + /// Builds an integer-typed memory value. + pub fn new_integer(value: BigUint, bit_size: u32) -> Self { + assert!( + bit_size != FieldElement::max_num_bits(), + "Tried to build a field memory value via new_integer" + ); + MemoryValue::Integer(value, bit_size) + } + + /// Extracts the field element from the memory value, if it is typed as field element. + pub fn extract_field(&self) -> Option<&FieldElement> { + match self { + MemoryValue::Field(value) => Some(value), + _ => None, + } + } + + /// Extracts the integer from the memory value, if it is typed as integer. + pub fn extract_integer(&self) -> Option<(&BigUint, u32)> { + match self { + MemoryValue::Integer(value, bit_size) => Some((value, *bit_size)), + _ => None, + } + } + + /// Converts the memory value to a field element, independent of its type. + pub fn to_field(&self) -> FieldElement { + match self { + MemoryValue::Field(value) => *value, + MemoryValue::Integer(value, _) => { + FieldElement::from_be_bytes_reduce(&value.to_bytes_be()) + } + } + } + + /// Converts the memory value to an integer, independent of its type. + pub fn to_integer(self) -> BigUint { + match self { + MemoryValue::Field(value) => BigUint::from_bytes_be(&value.to_be_bytes()), + MemoryValue::Integer(value, _) => value, + } + } + + pub fn bit_size(&self) -> u32 { + match self { + MemoryValue::Field(_) => FieldElement::max_num_bits(), + MemoryValue::Integer(_, bit_size) => *bit_size, + } } pub fn to_usize(&self) -> usize { - assert!(self.bit_size == MEMORY_ADDRESSING_BIT_SIZE, "value is not typed as brillig usize"); - self.value.to_u128() as usize + assert!( + self.bit_size() == MEMORY_ADDRESSING_BIT_SIZE, + "value is not typed as brillig usize" + ); + self.extract_integer().unwrap().0.try_into().unwrap() } - pub fn expect_bit_size(&self, expected_bit_size: u32) -> Result<(), MemoryTypeError> { - if self.bit_size != expected_bit_size { - return Err(MemoryTypeError::MismatchedBitSize { - value_bit_size: self.bit_size, + pub fn expect_field(&self) -> Result<&FieldElement, MemoryTypeError> { + match self { + MemoryValue::Integer(_, bit_size) => Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: *bit_size, + expected_bit_size: FieldElement::max_num_bits(), + }), + MemoryValue::Field(field) => Ok(field), + } + } + + pub fn expect_integer_with_bit_size( + &self, + expected_bit_size: u32, + ) -> Result<&BigUint, MemoryTypeError> { + match self { + MemoryValue::Integer(value, bit_size) => { + if *bit_size != expected_bit_size { + return Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: *bit_size, + expected_bit_size, + }); + } + Ok(value) + } + MemoryValue::Field(_) => Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: FieldElement::max_num_bits(), expected_bit_size, - }); + }), } - Ok(()) } } impl std::fmt::Display for MemoryValue { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - let typ = match self.bit_size { - 0 => "null".to_string(), - 1 => "bool".to_string(), - _ if self.bit_size == FieldElement::max_num_bits() => "field".to_string(), - _ => format!("u{}", self.bit_size), - }; - f.write_str(format!("{}: {}", self.value, typ).as_str()) + match self { + MemoryValue::Field(value) => write!(f, "{}: field", value), + MemoryValue::Integer(value, bit_size) => { + let typ = match bit_size { + 0 => "null".to_string(), + 1 => "bool".to_string(), + _ => format!("u{}", bit_size), + }; + write!(f, "{}: {}", value, typ) + } + } } } impl Default for MemoryValue { fn default() -> Self { - MemoryValue::new(FieldElement::zero(), 0) + MemoryValue::new_integer(BigUint::zero(), 0) } } @@ -73,31 +169,32 @@ impl From for MemoryValue { impl From for MemoryValue { fn from(value: usize) -> Self { - MemoryValue::new(value.into(), MEMORY_ADDRESSING_BIT_SIZE) + MemoryValue::new_integer(value.into(), MEMORY_ADDRESSING_BIT_SIZE) } } impl From for MemoryValue { fn from(value: u64) -> Self { - MemoryValue::new((value as u128).into(), 64) + MemoryValue::new_integer(value.into(), 64) } } impl From for MemoryValue { fn from(value: u32) -> Self { - MemoryValue::new((value as u128).into(), 32) + MemoryValue::new_integer(value.into(), 32) } } impl From for MemoryValue { fn from(value: u8) -> Self { - MemoryValue::new((value as u128).into(), 8) + MemoryValue::new_integer(value.into(), 8) } } impl From for MemoryValue { fn from(value: bool) -> Self { - MemoryValue::new(value.into(), 1) + let value = if value { BigUint::one() } else { BigUint::zero() }; + MemoryValue::new_integer(value, 1) } } @@ -105,8 +202,7 @@ impl TryFrom for FieldElement { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(FieldElement::max_num_bits())?; - Ok(memory_value.value) + memory_value.expect_field().copied() } } @@ -114,8 +210,7 @@ impl TryFrom for u64 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(64)?; - Ok(memory_value.value.to_u128() as u64) + memory_value.expect_integer_with_bit_size(64).map(|value| value.try_into().unwrap()) } } @@ -123,8 +218,7 @@ impl TryFrom for u32 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(32)?; - Ok(memory_value.value.to_u128() as u32) + memory_value.expect_integer_with_bit_size(32).map(|value| value.try_into().unwrap()) } } @@ -132,9 +226,7 @@ impl TryFrom for u8 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(8)?; - - Ok(memory_value.value.to_u128() as u8) + memory_value.expect_integer_with_bit_size(8).map(|value| value.try_into().unwrap()) } } @@ -142,11 +234,65 @@ impl TryFrom for bool { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(1)?; + let as_integer = memory_value.expect_integer_with_bit_size(1)?; + + if as_integer.is_zero() { + Ok(false) + } else if as_integer.is_one() { + Ok(true) + } else { + unreachable!("value typed as bool is greater than one") + } + } +} + +impl TryFrom<&MemoryValue> for FieldElement { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_field().copied() + } +} + +impl TryFrom<&MemoryValue> for u64 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(64).map(|value| { + value.try_into().expect("memory_value has been asserted to contain a 64 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for u32 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(32).map(|value| { + value.try_into().expect("memory_value has been asserted to contain a 32 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for u8 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(8).map(|value| { + value.try_into().expect("memory_value has been asserted to contain an 8 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for bool { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + let as_integer = memory_value.expect_integer_with_bit_size(1)?; - if memory_value.value == FieldElement::zero() { + if as_integer.is_zero() { Ok(false) - } else if memory_value.value == FieldElement::one() { + } else if as_integer.is_one() { Ok(true) } else { unreachable!("value typed as bool is greater than one") @@ -164,7 +310,7 @@ pub struct Memory { impl Memory { /// Gets the value at pointer pub fn read(&self, ptr: MemoryAddress) -> MemoryValue { - self.inner.get(ptr.to_usize()).copied().unwrap_or_default() + self.inner.get(ptr.to_usize()).cloned().unwrap_or_default() } pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { @@ -191,7 +337,7 @@ impl Memory { /// Sets the values after pointer `ptr` to `values` pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[MemoryValue]) { self.resize_to_fit(ptr.to_usize() + values.len()); - self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); + self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].clone_from_slice(values); } /// Returns the values of the memory diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index a3064ecdd01..534d24289b7 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -11,10 +11,9 @@ use crate::{ chained_dep, chained_path, utils::{ ast_utils::{ - assignment, call, cast, expression, ident, ident_path, index_array, - index_array_variable, make_eq, make_statement, make_type, member_access, method_call, - mutable_assignment, mutable_reference, path, return_type, variable, variable_ident, - variable_path, + assignment, assignment_with_type, call, cast, expression, ident, ident_path, + index_array, make_eq, make_statement, make_type, method_call, mutable_assignment, + mutable_reference, path, return_type, variable, variable_ident, variable_path, }, errors::AztecMacroError, }, @@ -74,12 +73,12 @@ pub fn transform_function( // Abstract return types such that they get added to the kernel's return_values if !is_avm { - if let Some(return_values) = abstract_return_values(func) { + if let Some(return_values_statements) = abstract_return_values(func)? { // In case we are pushing return values to the context, we remove the statement that originated it // This avoids running duplicate code, since blocks like if/else can be value returning statements func.def.body.statements.pop(); // Add the new return statement - func.def.body.statements.push(return_values); + func.def.body.statements.extend(return_values_statements); } } @@ -302,6 +301,51 @@ fn create_assert_initializer(ty: &str) -> Statement { ))) } +fn serialize_to_hasher( + identifier: &Ident, + typ: &UnresolvedTypeData, + hasher_name: &str, +) -> Option> { + let mut statements = Vec::new(); + + // Match the type to determine the padding to do + match typ { + // `{hasher_name}.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => { + statements.push(add_struct_to_hasher(identifier, hasher_name)); + } + UnresolvedTypeData::Array(_, arr_type) => { + statements.push(add_array_to_hasher(identifier, arr_type, hasher_name)); + } + // `{hasher_name}.push({ident})` + UnresolvedTypeData::FieldElement => { + statements.push(add_field_to_hasher(identifier, hasher_name)); + } + // Add the integer to the bounded vec, casted to a field + // `{hasher_name}.push({ident} as Field)` + UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { + statements.push(add_cast_to_hasher(identifier, hasher_name)); + } + UnresolvedTypeData::String(..) => { + let (var_bytes, id) = str_to_bytes(identifier); + statements.push(var_bytes); + statements.push(add_array_to_hasher( + &id, + &UnresolvedType { + typ: UnresolvedTypeData::Integer( + Signedness::Unsigned, + noirc_frontend::IntegerBitSize::ThirtyTwo, + ), + span: None, + }, + hasher_name, + )) + } + _ => return None, + }; + Some(statements) +} + /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be /// appended into the args hash object. /// @@ -328,7 +372,7 @@ fn create_assert_initializer(ty: &str) -> Statement { /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { - let mut injected_expressions: Vec = vec![]; + let mut injected_statements: Vec = vec![]; let hasher_name = "args_hasher"; @@ -342,7 +386,7 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac ); // Completes: `let mut args_hasher = Hasher::new();` - injected_expressions.push(let_hasher); + injected_statements.push(let_hasher); // Iterate over each of the function parameters, adding to them to the hasher for Param { pattern, typ, span, .. } in params { @@ -350,44 +394,14 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac Pattern::Identifier(identifier) => { // Match the type to determine the padding to do let unresolved_type = &typ.typ; - let expression = match unresolved_type { - // `hasher.add_multiple({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier, hasher_name), - UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type, hasher_name) - } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => { - add_field_to_hasher(identifier, hasher_name) - } - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` - UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier, hasher_name) - } - UnresolvedTypeData::String(..) => { - let (var_bytes, id) = str_to_bytes(identifier); - injected_expressions.push(var_bytes); - add_array_to_hasher( - &id, - &UnresolvedType { - typ: UnresolvedTypeData::Integer( - Signedness::Unsigned, - noirc_frontend::IntegerBitSize::ThirtyTwo, - ), - span: None, - }, - hasher_name, - ) - } - _ => { - return Err(AztecMacroError::UnsupportedFunctionArgumentType { + injected_statements.extend( + serialize_to_hasher(identifier, unresolved_type, hasher_name).ok_or_else( + || AztecMacroError::UnsupportedFunctionArgumentType { typ: unresolved_type.clone(), span: *span, - }) - } - }; - injected_expressions.push(expression); + }, + )?, + ); } _ => todo!(), // Maybe unreachable? } @@ -412,10 +426,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac vec![inputs_expression, hash_call], // args ), ); - injected_expressions.push(let_context); + injected_statements.push(let_context); // Return all expressions that will be injected by the hasher - Ok(injected_expressions) + Ok(injected_statements) } /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be @@ -452,53 +466,86 @@ fn create_context_avm() -> Result, AztecMacroError> { /// Abstract Return Type /// -/// This function intercepts the function's current return type and replaces it with pushes -/// To the kernel +/// This function intercepts the function's current return type and replaces it with pushes to a hasher +/// that will be used to generate the returns hash for the kernel. /// /// The replaced code: /// ```noir /// /// Before /// #[aztec(private)] -/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { -/// // ... -/// let my_return_value: Field = 10; -/// context.return_values.push(my_return_value); -/// } -/// -/// /// After -/// #[aztec(private)] /// fn foo() -> Field { /// // ... /// let my_return_value: Field = 10; /// my_return_value /// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { +/// // ... +/// let my_return_value: Field = 10; +/// let macro__returned__values = my_return_value; +/// let mut returns_hasher = ArgsHasher::new(); +/// returns_hasher.add(macro__returned__values); +/// context.set_return_hash(returns_hasher); +/// } /// ``` -/// Similarly; Structs will be pushed to the context, after serialize() is called on them. -/// Arrays will be iterated over and each element will be pushed to the context. -/// Any primitive type that can be cast will be casted to a field and pushed to the context. -fn abstract_return_values(func: &NoirFunction) -> Option { +/// Similarly; Structs will be pushed to the hasher, after serialize() is called on them. +/// Arrays will be iterated over and each element will be pushed to the hasher. +/// Any primitive type that can be cast will be casted to a field and pushed to the hasher. +fn abstract_return_values(func: &NoirFunction) -> Result>, AztecMacroError> { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.statements.last()?; - // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size - // Doesn't need done until we have settled on a kernel size + // Short circuit if the function doesn't return anything + match current_return_type { + UnresolvedTypeData::Unit | UnresolvedTypeData::Unspecified => return Ok(None), + _ => (), + } + + let Some(last_statement) = func.def.body.statements.last() else { + return Ok(None); + }; + // TODO: support tuples here and in inputs -> convert into an issue // Check if the return type is an expression, if it is, we can handle it match last_statement { Statement { kind: StatementKind::Expression(expression), .. } => { - match current_return_type { - // Call serialize on structs, push the whole array, calling push_array - UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), - UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), - // Cast these types to a field before pushing - UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { - Some(make_castable_return_type(expression.clone())) - } - UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), - _ => None, - } + let return_value_name = "macro__returned__values"; + let hasher_name = "returns_hasher"; + + let mut replacement_statements = vec![ + assignment_with_type( + return_value_name, // Assigned to + current_return_type.clone(), + expression.clone(), + ), + mutable_assignment( + hasher_name, // Assigned to + call( + variable_path(chained_dep!("aztec", "hash", "ArgsHasher", "new")), // Path + vec![], // args + ), + ), + ]; + + let serialization_statements = + serialize_to_hasher(&ident(return_value_name), ¤t_return_type, hasher_name) + .ok_or_else(|| AztecMacroError::UnsupportedFunctionReturnType { + typ: current_return_type.clone(), + span: func.return_type().span.unwrap_or_default(), + })?; + + replacement_statements.extend(serialization_statements); + + replacement_statements.push(make_statement(StatementKind::Semi(method_call( + variable("context"), + "set_return_hash", + vec![variable(hasher_name)], + )))); + + Ok(Some(replacement_statements)) } - _ => None, + _ => Ok(None), } } @@ -547,86 +594,6 @@ fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { ) } -/// Context Return Values -/// -/// Creates an instance to the context return values -/// ```noir -/// `context.return_values` -/// ``` -fn context_return_values() -> Expression { - member_access("context", "return_values") -} - -/// Make return Push -/// -/// Translates to: -/// `context.return_values.push({push_value})` -fn make_return_push(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "push", - vec![push_value], - ))) -} - -/// Make Return push array -/// -/// Translates to: -/// `context.return_values.extend_from_array({push_value})` -fn make_return_extend_from_array(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "extend_from_array", - vec![push_value], - ))) -} - -/// Make struct return type -/// -/// Translates to: -/// ```noir -/// `context.return_values.extend_from_array({push_value}.serialize())` -fn make_struct_return_type(expression: Expression) -> Statement { - let serialized_call = method_call( - expression, // variable - "serialize", // method name - vec![], // args - ); - make_return_extend_from_array(serialized_call) -} - -/// Make array return type -/// -/// Translates to: -/// ```noir -/// for i in 0..{ident}.len() { -/// context.return_values.push({ident}[i] as Field) -/// } -/// ``` -fn make_array_return_type(expression: Expression) -> Statement { - let inner_cast_expression = - cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); - let assignment = make_statement(StatementKind::Semi(method_call( - context_return_values(), // variable - "push", // method name - vec![inner_cast_expression], - ))); - - create_loop_over(expression, vec![assignment]) -} - -/// Castable return type -/// -/// Translates to: -/// ```noir -/// context.return_values.push({ident} as Field) -/// ``` -fn make_castable_return_type(expression: Expression) -> Statement { - // Cast these types to a field before pushing - let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); - make_return_push(cast_expression) -} - /// Create Return Type /// /// Public functions return protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs while diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index eb8f5a4156d..1731dfab49c 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -2,9 +2,8 @@ use noirc_errors::{Span, Spanned}; use noirc_frontend::{ token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, - LetStatement, MemberAccessExpression, MethodCallExpression, NoirTraitImpl, Path, Pattern, - PrefixExpression, Statement, StatementKind, TraitImplItem, UnaryOp, UnresolvedType, - UnresolvedTypeData, + LetStatement, MethodCallExpression, NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, + StatementKind, TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData, }; // @@ -79,21 +78,22 @@ pub fn mutable_reference(variable_name: &str) -> Expression { } pub fn assignment(name: &str, assigned_to: Expression) -> Statement { + assignment_with_type(name, UnresolvedTypeData::Unspecified, assigned_to) +} + +pub fn assignment_with_type( + name: &str, + typ: UnresolvedTypeData, + assigned_to: Expression, +) -> Statement { make_statement(StatementKind::Let(LetStatement { pattern: pattern(name), - r#type: make_type(UnresolvedTypeData::Unspecified), + r#type: make_type(typ), expression: assigned_to, attributes: vec![], })) } -pub fn member_access(lhs: &str, rhs: &str) -> Expression { - expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: variable(lhs), - rhs: ident(rhs), - }))) -} - pub fn return_type(path: Path) -> FunctionReturnType { let ty = make_type(UnresolvedTypeData::Named(path, vec![], true)); FunctionReturnType::Ty(ty) @@ -169,13 +169,6 @@ pub fn index_array(array: Ident, index: &str) -> Expression { }))) } -pub fn index_array_variable(array: Expression, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: array, - index: variable(index), - }))) -} - pub fn check_trait_method_implemented(trait_impl: &NoirTraitImpl, method_name: &str) -> bool { trait_impl.items.iter().any(|item| match item { TraitImplItem::Function(func) => func.def.name.0.contents == method_name, diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 9aead1756f9..5d3a61a51dc 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -8,6 +8,7 @@ pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + UnsupportedFunctionReturnType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, @@ -36,6 +37,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::UnsupportedFunctionReturnType { span, typ } => MacroError { + primary_message: format!("Provided return type `{typ:?}` is not supported in Aztec contract interface"), + secondary_message: None, + span: Some(span), + }, AztecMacroError::UnsupportedStorageType { span, typ } => MacroError { primary_message: format!("Provided storage type `{typ:?}` is not directly supported in Aztec. Please provide a custom storage implementation"), secondary_message: None, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 15a2a531e78..4b97a61491d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -67,61 +67,105 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { /// (a/b, a-a/b*b) /// } /// ``` -pub(crate) fn directive_quotient(mut bit_size: u32) -> GeneratedBrillig { +pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `a` is (0) (i.e register index 0) // `b` is (1) - if bit_size > FieldElement::max_num_bits() { - bit_size = FieldElement::max_num_bits(); - } - GeneratedBrillig { - byte_code: vec![ - BrilligOpcode::CalldataCopy { - destination_address: MemoryAddress::from(0), - size: 2, - offset: 0, - }, - BrilligOpcode::Cast { - destination: MemoryAddress(0), - source: MemoryAddress(0), - bit_size, - }, - BrilligOpcode::Cast { - destination: MemoryAddress(1), - source: MemoryAddress(1), - bit_size, - }, - //q = a/b is set into register (2) - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Div, - lhs: MemoryAddress::from(0), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(2), - bit_size, - }, - //(1)= q*b - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Mul, - lhs: MemoryAddress::from(2), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(1), - bit_size, - }, - //(1) = a-q*b - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Sub, - lhs: MemoryAddress::from(0), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(1), - bit_size, - }, - //(0) = q - BrilligOpcode::Mov { - destination: MemoryAddress::from(0), - source: MemoryAddress::from(2), - }, - BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, - ], - assert_messages: Default::default(), - locations: Default::default(), + + // TODO: The only difference between these implementations is the integer version will truncate the input to the `bit_size` via cast. + // Once we deduplicate brillig functions then we can modify this so that fields and integers share the same quotient function. + if bit_size >= FieldElement::max_num_bits() { + // Field version + GeneratedBrillig { + byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, + // No cast, since calldata is typed as field by default + //q = a/b is set into register (2) + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::IntegerDiv, // We want integer division, not field division! + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), + }, + //(1)= q*b + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Mul, + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + }, + //(1) = a-q*b + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Sub, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + }, + //(0) = q + BrilligOpcode::Mov { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, + ], + assert_messages: Default::default(), + locations: Default::default(), + } + } else { + // Integer version + GeneratedBrillig { + byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, + BrilligOpcode::Cast { + destination: MemoryAddress(0), + source: MemoryAddress(0), + bit_size, + }, + BrilligOpcode::Cast { + destination: MemoryAddress(1), + source: MemoryAddress(1), + bit_size, + }, + //q = a/b is set into register (2) + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Div, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), + bit_size, + }, + //(1)= q*b + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Mul, + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + bit_size, + }, + //(1) = a-q*b + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Sub, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + bit_size, + }, + //(0) = q + BrilligOpcode::Mov { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, + ], + assert_messages: Default::default(), + locations: Default::default(), + } } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index b93693d9c79..3e1515b1eed 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -465,7 +465,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -590,7 +590,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -686,7 +686,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -838,7 +838,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index db872487fcc..fe3c5e0bb9c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -527,7 +527,7 @@ mod tests { let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata.clone(), &bytecode); assert_eq!(return_data_size, 1, "Return data size is incorrect"); - assert_eq!(vm.get_memory()[return_data_offset].value, FieldElement::from(1_usize)); + assert_eq!(vm.get_memory()[return_data_offset].to_field(), FieldElement::from(1_usize)); } #[test] @@ -569,7 +569,7 @@ mod tests { assert_eq!( memory[return_data_pointer..(return_data_pointer + flattened_array.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), flattened_array ); 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 53d9e2530cc..775571f4a41 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 @@ -1623,7 +1623,7 @@ impl AcirContext { let outputs_var = vecmap(outputs_types.iter(), |output| match output { AcirType::NumericType(_) => { let var = self.add_data(AcirVarData::Const( - memory.next().expect("Missing return data").value, + memory.next().expect("Missing return data").to_field(), )); AcirValue::Var(var, output.clone()) } @@ -1657,7 +1657,7 @@ impl AcirContext { AcirType::NumericType(_) => { let memory_value = memory_iter.next().expect("ICE: Unexpected end of memory"); - let var = self.add_data(AcirVarData::Const(memory_value.value)); + let var = self.add_data(AcirVarData::Const(memory_value.to_field())); array_values.push_back(AcirValue::Var(var, element_type.clone())); } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index b43110b2f5b..ba4e03bff95 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -240,11 +240,7 @@ impl GeneratedAcir { } }; - BlackBoxFuncCall::Keccak256VariableLength { - inputs: inputs[0].clone(), - var_message_size, - outputs, - } + BlackBoxFuncCall::Keccak256 { inputs: inputs[0].clone(), var_message_size, outputs } } BlackBoxFunc::Keccakf1600 => { BlackBoxFuncCall::Keccakf1600 { inputs: inputs[0].clone(), outputs } diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index c6bf7923fa8..6cf155f85ab 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -151,7 +151,10 @@ impl Loops { if next_loop.blocks.iter().any(|block| self.modified_blocks.contains(block)) { let mut new_context = find_all_loops(function); new_context.failed_to_unroll = self.failed_to_unroll; - return new_context.unroll_each_loop(function); + return unroll_errors + .into_iter() + .chain(new_context.unroll_each_loop(function)) + .collect(); } // Don't try to unroll the loop again if it is known to fail diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index e055d7a3a5f..0f9770c805d 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.33.0"; +const VERSION: &str = "0.34.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 1acd581b2be..b211832518d 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -513,7 +513,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement, bit_size: u32) { if let Some(solver) = self.brillig_solver.as_mut() { - solver.write_memory_at(ptr, MemoryValue::new(value, bit_size)); + solver.write_memory_at( + ptr, + MemoryValue::new_checked(value, bit_size) + .expect("Invalid value for the given bit size"), + ); } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 1c077c6ee9b..e30d519b62e 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -319,7 +319,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { return; }; - for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size > 0) { + for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size() > 0) { println!("{index} = {}", value); } } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index e592ed440ee..98bfdf1c3a8 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.33.0", + "@aztec/bb.js": "0.34.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 0fdad4ad2ad..38e13814929 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.33.0": - version: 0.33.0 - resolution: "@aztec/bb.js@npm:0.33.0" +"@aztec/bb.js@npm:0.34.0": + version: 0.34.0 + resolution: "@aztec/bb.js@npm:0.34.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 16244a52ef1cb5efca582e863a3521d04f0fb66b02cd584b904e6e65f684e392eec56679439d1a831127e126d117bf0e116166fc4b24efdd6e1ebe9097efed06 + checksum: 9d07834d81ed19e4d6fd5c1f3b07c565648df1165c30115f020ece9660b2b8599a5ed894a2090410f14020e73dd290484b30b76c9c71e863b8390fa2b7c1b729 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.33.0 + "@aztec/bb.js": 0.34.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3