From 05c2eaa5766f6916acd6f6c3cfb130b6dcaf0845 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 25 Mar 2024 16:20:02 +0000 Subject: [PATCH 1/3] feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5416) Automated pull of development from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE chore(github): Improve PR template "document later" checkbox description (https://github.com/noir-lang/noir/pull/4625) chore: Update integers.md to note support for Fields using `from_integer` (https://github.com/noir-lang/noir/pull/4536) chore: update docs with function names to match version 0.25.0 specifications (https://github.com/noir-lang/noir/pull/4466) feat: add specific error for attempting `string[x] = ".."` (https://github.com/noir-lang/noir/pull/4611) fix(ssa): Use accurate type during SSA AsSlice simplficiation (https://github.com/noir-lang/noir/pull/4610) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant --- .aztec-sync-commit | 2 +- .github/pull_request_template.md | 2 +- aztec_macros/src/transforms/functions.rs | 117 +++++++++--------- .../src/ssa/ir/instruction/call.rs | 9 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 7 ++ .../src/hir/type_check/errors.rs | 5 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 5 + docs/docs/how_to/how-to-oracles.md | 2 +- .../docs/noir/concepts/data_types/integers.md | 2 +- docs/docs/tutorials/noirjs_app.md | 4 +- .../version-v0.24.0/how_to/how-to-oracles.md | 2 +- .../version-v0.24.0/tutorials/noirjs_app.md | 4 +- .../version-v0.25.0/how_to/how-to-oracles.md | 2 +- .../version-v0.25.0/tutorials/noirjs_app.md | 4 +- noir_stdlib/src/collections/bounded_vec.nr | 9 ++ noir_stdlib/src/field.nr | 2 +- .../array_to_slice/src/main.nr | 23 ++++ 17 files changed, 124 insertions(+), 77 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ead8f3147c0..56c61953dac 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -208abbb63af4c9a3f25d723fe1c49e82aa461061 +13a12d5255e788be94d575c726da141e652f14e3 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e81ede7199d..dfb141e29f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Resolves Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. -- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. +- [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index c719651e10e..51a90c2ee28 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -232,62 +232,62 @@ fn create_assert_initializer() -> Statement { /// ```noir /// #[aztec(private)] /// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); +/// // Create the bounded vec object +/// let mut serialized_args = BoundedVec::new(); /// /// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); +/// serialized_args.extend_from_array(structInput.serialize()); /// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// // Array inputs are iterated over and each element is added to the bounded vec (as a field) /// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); +/// serialized_args.push(arrayInput[i] as Field); /// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); +/// // Field inputs are added to the bounded vec +/// serialized_args.push({ident}); /// /// // Create the context /// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args)); /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { let mut injected_expressions: Vec = vec![]; - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to + // `let mut serialized_args = BoundedVec::new();` + let let_serialized_args = mutable_assignment( + "serialized_args", // Assigned to call( - variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path + vec![], // args ), ); - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); + // Completes: `let mut serialized_args = BoundedVec::new();` + injected_expressions.push(let_serialized_args); - // Iterate over each of the function parameters, adding to them to the hasher + // Iterate over each of the function parameters, adding to them to the bounded vec for Param { pattern, typ, span, .. } in params { match pattern { 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), + // `serialized_args.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier), UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type) + add_array_to_serialized_args(identifier, arr_type) } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident})` + UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier), + // Add the integer to the serialized args, casted to a field + // `serialized_args.push({ident} as Field)` UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) + add_cast_to_serialized_args(identifier) } UnresolvedTypeData::String(..) => { let (var_bytes, id) = str_to_bytes(identifier); injected_expressions.push(var_bytes); - add_array_to_hasher( + add_array_to_serialized_args( &id, &UnresolvedType { typ: UnresolvedTypeData::Integer( @@ -313,11 +313,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac // Create the inputs to the context let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args + // `hash_args(serialized_args)` + let hash_call = call( + variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable + vec![variable("serialized_args")], // args ); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context @@ -598,11 +597,11 @@ fn create_context_finish() -> Statement { } // -// Methods to create hasher inputs +// Methods to create hash_args inputs // -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher +fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the serialized args let serialized_call = method_call( variable_path(path(identifier.clone())), // variable "serialize", // method name @@ -610,9 +609,9 @@ fn add_struct_to_hasher(identifier: &Ident) -> Statement { ); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args + variable("serialized_args"), // variable + "extend_from_array", // method name + vec![serialized_call], // args ))) } @@ -632,7 +631,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { } fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // If this is an array of primitive types (integers / fields) we can add them each to the serialized args // casted to a field let span = var.span; @@ -644,7 +643,7 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { ); // What will be looped over - // - `hasher.add({ident}[i] as Field)` + // - `serialized_args.push({ident}[i] as Field)` let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); // `for i in 0..{ident}.len()` @@ -662,66 +661,66 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { })) } -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher +fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the serialized_args // casted to a field // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` + // `serialized_args.push({ident}[i] as Field)` let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { + let (add_expression, vec_method_name) = match arr_type.typ { UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); + let vec_method_name = "extend_from_array".to_owned(); let call = method_call( // All serialize on each element arr_index, // variable "serialize", // method name vec![], // args ); - (call, hasher_method_name) + (call, vec_method_name) } _ => { - let hasher_method_name = "add".to_owned(); + let vec_method_name = "push".to_owned(); let call = cast( arr_index, // lhs - `ident[i]` UnresolvedTypeData::FieldElement, // cast to - `as Field` ); - (call, hasher_method_name) + (call, vec_method_name) } }; let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name + variable("serialized_args"), // variable + &vec_method_name, // method name vec![add_expression], ))); create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) } -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` +fn add_field_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident})` let ident = variable_path(path(identifier.clone())); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args + variable("serialized_args"), // variable + "push", // method name + vec![ident], // args ))) } -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` +fn add_cast_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident} as Field)` // `{ident} as Field` let cast_operation = cast( variable_path(path(identifier.clone())), // lhs UnresolvedTypeData::FieldElement, // rhs ); - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident} as Field)` make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args + variable("serialized_args"), // variable + "push", // method name + vec![cast_operation], // args ))) } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8b800e0db54..5b268de239d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -85,10 +85,11 @@ pub(super) fn simplify_call( } } Intrinsic::AsSlice => { - let slice = dfg.get_array_constant(arguments[0]); - if let Some((slice, element_type)) = slice { - let slice_length = dfg.make_constant(slice.len().into(), Type::length_type()); - let new_slice = dfg.make_array(slice, element_type); + let array = dfg.get_array_constant(arguments[0]); + if let Some((array, array_type)) = array { + let slice_length = dfg.make_constant(array.len().into(), Type::length_type()); + let inner_element_types = array_type.element_types(); + let new_slice = dfg.make_array(array, Type::Slice(inner_element_types)); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) } else { SimplifyResult::None diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index ea3f5393245..48036580d29 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -159,6 +159,13 @@ impl Type { Type::Reference(element) => element.contains_an_array(), } } + + pub(crate) fn element_types(self) -> Rc> { + match self { + Type::Array(element_types, _) | Type::Slice(element_types) => element_types, + other => panic!("element_types: Expected array or slice, found {other}"), + } + } } /// Composite Types are essentially flattened struct or tuple types. diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 642cebc83b0..6beb6929ce1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -140,6 +140,8 @@ pub enum TypeCheckError { method_name: String, span: Span, }, + #[error("Strings do not support indexed assignment")] + StringIndexAssign { span: Span }, } impl TypeCheckError { @@ -237,7 +239,8 @@ impl From for Diagnostic { | TypeCheckError::ConstrainedReferenceToUnconstrained { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantSliceLength { span } => { + | TypeCheckError::NonConstantSliceLength { span } + | TypeCheckError::StringIndexAssign { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 49ba3244dc9..69363d5f00a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -258,6 +258,11 @@ impl<'interner> TypeChecker<'interner> { Type::Array(_, elem_type) => *elem_type, Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, + Type::String(_) => { + let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); + self.errors.push(TypeCheckError::StringIndexAssign { span: lvalue_span }); + Type::Error + } other => { // TODO: Need a better span here self.errors.push(TypeCheckError::TypeMismatch { diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index 4d58d96fed5..1c6b375db49 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -51,7 +51,7 @@ The built-in structure `U128` allows you to use 128-bit unsigned integers almost - You cannot cast between a native integer and `U128` - There is a higher performance cost when using `U128`, compared to a native type. -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. ```rust fn main() { diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 752b96d6591..6d5fbd44247 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -48,6 +48,15 @@ impl BoundedVec { self.len = new_len; } + pub fn extend_from_slice(&mut self, slice: [T]) { + let new_len = self.len + slice.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_slice out of bounds"); + for i in 0..slice.len() { + self.storage[self.len + i] = slice[i]; + } + self.len = new_len; + } + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { let append_len = vec.len(); let new_len = self.len + append_len; diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 0f4c2caffdf..b876bcc967b 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -97,7 +97,7 @@ pub fn modulus_be_bytes() -> [u8] {} #[builtin(modulus_le_bytes)] pub fn modulus_le_bytes() -> [u8] {} -// Convert a 32 byte array to a field element +// Convert a 32 byte array to a field element by modding pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field { // Convert it to a field element let mut v = 1; diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index b97f68fc280..0d0f9562d7b 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -7,6 +7,7 @@ fn as_slice_push(xs: [T; N]) -> [T] { slice } +// Expected that x == 0 and y == 1 fn main(x: Field, y: pub Field) { let xs: [Field; 0] = []; let ys: [Field; 1] = [1]; @@ -30,4 +31,26 @@ fn main(x: Field, y: pub Field) { assert(dynamic.as_slice()[2] == dynamic_expected[2]); assert(dynamic.as_slice()[3] == dynamic_expected[3]); assert(dynamic.as_slice().len() == 4); + + regression_4609_append_slices(x, y); + regression_4609_append_dynamic_slices(x, y); +} + +fn regression_4609_append_slices(x: Field, y: Field) { + let sl = [x, 1, 2, 3].as_slice(); + let sl2 = [y, 5, 6].as_slice(); + let sl3 = sl.append(sl2); + assert(sl3[0] == x); + assert(sl3[4] == y); +} + +fn regression_4609_append_dynamic_slices(x: Field, y: Field) { + let mut sl = [x, 1, 2, 3].as_slice(); + sl[x] = x + 10; + let mut sl2 = [y, 5, 6].as_slice(); + sl2[y] = y + 5; + let sl3 = sl.append(sl2); + assert(sl3[0] == 10); + assert(sl3[4] == y); + assert(sl3[5] == 6); } From 27fce0983814b67f28a69d4102866e89c1c32011 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 25 Mar 2024 17:46:54 +0000 Subject: [PATCH 2/3] feat(avm): add AvmContextInputs (https://github.com/AztecProtocol/aztec-packages/pull/5396) NOTE: I don't know why this triggered a change in the snapshot. --- This structure lets us easily pass things from the (TS) context to the constructor of the `AvmContext` in Noir, using `calldata` as a vehicle (effectively adding them as public inputs). The choice to add the structure to the function arguments as LAST and not first is because adding them first would break any non-noir-generated bytecode (since they would have to shift their expected calldata by a magic number `N = sizeof(AvmContextInputs)`). Putting it last, makes it transparent for them. A calldatacopy would still work. However, having this makes any external call always have `AvmContextInputs` in the calldata, bloating it (on chain) for non-noir-generated bytecode. This is not an issue now. For the moment, this is temporary, but might be useful long term. (Sean mentioned passing maybe env getters like this). --- Implemented `AvmContext.selector()` and `AvmContext.get_args_hash()` using the above. --- .aztec-sync-commit | 2 +- .github/pull_request_template.md | 2 +- aztec_macros/src/transforms/functions.rs | 126 +++++++++--------- .../src/ssa/ir/instruction/call.rs | 9 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 7 + .../src/hir/type_check/errors.rs | 5 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 5 + docs/docs/how_to/how-to-oracles.md | 2 +- .../docs/noir/concepts/data_types/integers.md | 2 +- docs/docs/tutorials/noirjs_app.md | 4 +- .../version-v0.24.0/how_to/how-to-oracles.md | 2 +- .../version-v0.24.0/tutorials/noirjs_app.md | 4 +- .../version-v0.25.0/how_to/how-to-oracles.md | 2 +- .../version-v0.25.0/tutorials/noirjs_app.md | 4 +- noir_stdlib/src/collections/bounded_vec.nr | 9 ++ noir_stdlib/src/field.nr | 2 +- .../array_to_slice/src/main.nr | 23 ++++ 17 files changed, 132 insertions(+), 78 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ead8f3147c0..425b994546c 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -208abbb63af4c9a3f25d723fe1c49e82aa461061 +12e2844f9af433beb1a586640b08ce284ad91095 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e81ede7199d..dfb141e29f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Resolves Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. -- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. +- [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index c719651e10e..5e42dccdd78 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -116,6 +116,10 @@ pub fn transform_vm_function( let create_context = create_avm_context()?; func.def.body.0.insert(0, create_context); + // Add the inputs to the params (first!) + let input = create_inputs("AvmContextInputs"); + func.def.parameters.insert(0, input); + // We want the function to be seen as a public function func.def.is_unconstrained = true; @@ -232,62 +236,62 @@ fn create_assert_initializer() -> Statement { /// ```noir /// #[aztec(private)] /// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); +/// // Create the bounded vec object +/// let mut serialized_args = BoundedVec::new(); /// /// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); +/// serialized_args.extend_from_array(structInput.serialize()); /// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// // Array inputs are iterated over and each element is added to the bounded vec (as a field) /// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); +/// serialized_args.push(arrayInput[i] as Field); /// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); +/// // Field inputs are added to the bounded vec +/// serialized_args.push({ident}); /// /// // Create the context /// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args)); /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { let mut injected_expressions: Vec = vec![]; - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to + // `let mut serialized_args = BoundedVec::new();` + let let_serialized_args = mutable_assignment( + "serialized_args", // Assigned to call( - variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path + vec![], // args ), ); - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); + // Completes: `let mut serialized_args = BoundedVec::new();` + injected_expressions.push(let_serialized_args); - // Iterate over each of the function parameters, adding to them to the hasher + // Iterate over each of the function parameters, adding to them to the bounded vec for Param { pattern, typ, span, .. } in params { match pattern { 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), + // `serialized_args.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier), UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type) + add_array_to_serialized_args(identifier, arr_type) } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident})` + UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier), + // Add the integer to the serialized args, casted to a field + // `serialized_args.push({ident} as Field)` UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) + add_cast_to_serialized_args(identifier) } UnresolvedTypeData::String(..) => { let (var_bytes, id) = str_to_bytes(identifier); injected_expressions.push(var_bytes); - add_array_to_hasher( + add_array_to_serialized_args( &id, &UnresolvedType { typ: UnresolvedTypeData::Integer( @@ -313,11 +317,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac // Create the inputs to the context let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args + // `hash_args(serialized_args)` + let hash_call = call( + variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable + vec![variable("serialized_args")], // args ); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context @@ -354,11 +357,14 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac /// // ... /// } fn create_avm_context() -> Result { + // Create the inputs to the context + let inputs_expression = variable("inputs"); + let let_context = mutable_assignment( "context", // Assigned to call( variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args + vec![inputs_expression], // args ), ); @@ -598,11 +604,11 @@ fn create_context_finish() -> Statement { } // -// Methods to create hasher inputs +// Methods to create hash_args inputs // -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher +fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the serialized args let serialized_call = method_call( variable_path(path(identifier.clone())), // variable "serialize", // method name @@ -610,9 +616,9 @@ fn add_struct_to_hasher(identifier: &Ident) -> Statement { ); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args + variable("serialized_args"), // variable + "extend_from_array", // method name + vec![serialized_call], // args ))) } @@ -632,7 +638,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { } fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // If this is an array of primitive types (integers / fields) we can add them each to the serialized args // casted to a field let span = var.span; @@ -644,7 +650,7 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { ); // What will be looped over - // - `hasher.add({ident}[i] as Field)` + // - `serialized_args.push({ident}[i] as Field)` let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); // `for i in 0..{ident}.len()` @@ -662,66 +668,66 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { })) } -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher +fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the serialized_args // casted to a field // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` + // `serialized_args.push({ident}[i] as Field)` let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { + let (add_expression, vec_method_name) = match arr_type.typ { UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); + let vec_method_name = "extend_from_array".to_owned(); let call = method_call( // All serialize on each element arr_index, // variable "serialize", // method name vec![], // args ); - (call, hasher_method_name) + (call, vec_method_name) } _ => { - let hasher_method_name = "add".to_owned(); + let vec_method_name = "push".to_owned(); let call = cast( arr_index, // lhs - `ident[i]` UnresolvedTypeData::FieldElement, // cast to - `as Field` ); - (call, hasher_method_name) + (call, vec_method_name) } }; let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name + variable("serialized_args"), // variable + &vec_method_name, // method name vec![add_expression], ))); create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) } -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` +fn add_field_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident})` let ident = variable_path(path(identifier.clone())); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args + variable("serialized_args"), // variable + "push", // method name + vec![ident], // args ))) } -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` +fn add_cast_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident} as Field)` // `{ident} as Field` let cast_operation = cast( variable_path(path(identifier.clone())), // lhs UnresolvedTypeData::FieldElement, // rhs ); - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident} as Field)` make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args + variable("serialized_args"), // variable + "push", // method name + vec![cast_operation], // args ))) } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8b800e0db54..5b268de239d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -85,10 +85,11 @@ pub(super) fn simplify_call( } } Intrinsic::AsSlice => { - let slice = dfg.get_array_constant(arguments[0]); - if let Some((slice, element_type)) = slice { - let slice_length = dfg.make_constant(slice.len().into(), Type::length_type()); - let new_slice = dfg.make_array(slice, element_type); + let array = dfg.get_array_constant(arguments[0]); + if let Some((array, array_type)) = array { + let slice_length = dfg.make_constant(array.len().into(), Type::length_type()); + let inner_element_types = array_type.element_types(); + let new_slice = dfg.make_array(array, Type::Slice(inner_element_types)); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) } else { SimplifyResult::None diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index ea3f5393245..48036580d29 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -159,6 +159,13 @@ impl Type { Type::Reference(element) => element.contains_an_array(), } } + + pub(crate) fn element_types(self) -> Rc> { + match self { + Type::Array(element_types, _) | Type::Slice(element_types) => element_types, + other => panic!("element_types: Expected array or slice, found {other}"), + } + } } /// Composite Types are essentially flattened struct or tuple types. diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 642cebc83b0..6beb6929ce1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -140,6 +140,8 @@ pub enum TypeCheckError { method_name: String, span: Span, }, + #[error("Strings do not support indexed assignment")] + StringIndexAssign { span: Span }, } impl TypeCheckError { @@ -237,7 +239,8 @@ impl From for Diagnostic { | TypeCheckError::ConstrainedReferenceToUnconstrained { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantSliceLength { span } => { + | TypeCheckError::NonConstantSliceLength { span } + | TypeCheckError::StringIndexAssign { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 49ba3244dc9..69363d5f00a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -258,6 +258,11 @@ impl<'interner> TypeChecker<'interner> { Type::Array(_, elem_type) => *elem_type, Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, + Type::String(_) => { + let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); + self.errors.push(TypeCheckError::StringIndexAssign { span: lvalue_span }); + Type::Error + } other => { // TODO: Need a better span here self.errors.push(TypeCheckError::TypeMismatch { diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index 4d58d96fed5..1c6b375db49 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -51,7 +51,7 @@ The built-in structure `U128` allows you to use 128-bit unsigned integers almost - You cannot cast between a native integer and `U128` - There is a higher performance cost when using `U128`, compared to a native type. -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. ```rust fn main() { diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 752b96d6591..6d5fbd44247 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -48,6 +48,15 @@ impl BoundedVec { self.len = new_len; } + pub fn extend_from_slice(&mut self, slice: [T]) { + let new_len = self.len + slice.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_slice out of bounds"); + for i in 0..slice.len() { + self.storage[self.len + i] = slice[i]; + } + self.len = new_len; + } + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { let append_len = vec.len(); let new_len = self.len + append_len; diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 0f4c2caffdf..b876bcc967b 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -97,7 +97,7 @@ pub fn modulus_be_bytes() -> [u8] {} #[builtin(modulus_le_bytes)] pub fn modulus_le_bytes() -> [u8] {} -// Convert a 32 byte array to a field element +// Convert a 32 byte array to a field element by modding pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field { // Convert it to a field element let mut v = 1; diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index b97f68fc280..0d0f9562d7b 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -7,6 +7,7 @@ fn as_slice_push(xs: [T; N]) -> [T] { slice } +// Expected that x == 0 and y == 1 fn main(x: Field, y: pub Field) { let xs: [Field; 0] = []; let ys: [Field; 1] = [1]; @@ -30,4 +31,26 @@ fn main(x: Field, y: pub Field) { assert(dynamic.as_slice()[2] == dynamic_expected[2]); assert(dynamic.as_slice()[3] == dynamic_expected[3]); assert(dynamic.as_slice().len() == 4); + + regression_4609_append_slices(x, y); + regression_4609_append_dynamic_slices(x, y); +} + +fn regression_4609_append_slices(x: Field, y: Field) { + let sl = [x, 1, 2, 3].as_slice(); + let sl2 = [y, 5, 6].as_slice(); + let sl3 = sl.append(sl2); + assert(sl3[0] == x); + assert(sl3[4] == y); +} + +fn regression_4609_append_dynamic_slices(x: Field, y: Field) { + let mut sl = [x, 1, 2, 3].as_slice(); + sl[x] = x + 10; + let mut sl2 = [y, 5, 6].as_slice(); + sl2[y] = y + 5; + let sl3 = sl.append(sl2); + assert(sl3[0] == 10); + assert(sl3[4] == y); + assert(sl3[5] == 6); } From 322de9325a602010d61fd49d2b8444d17a84815a Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 26 Mar 2024 14:27:45 +0000 Subject: [PATCH 3/3] feat!: Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) Brillig had implicitely typed memory, where the memory typing was supossedly respected by the compiler (brillig_gen) but was never checked in runtime. Instead, in runtime the values were truncated to the appropiate bit size when used. This hid some bugs in typing that the compiler was outputting and that would have made the AVM crash. Memory typing bugs found and fixed: - to_radix vector length type - to_radix limb type - keccak256 length type (u32 vs u64) - directive quotient not casting arguments - directive invert jump condition on non boolean This PR aligns brillig more with the AVM by having typed memory actually checked in runtime, and removing the truncations that arithmetic.rs was doing. --- .aztec-sync-commit | 2 +- .github/pull_request_template.md | 2 +- Cargo.lock | 1 + acvm-repo/acir/codegen/acir.cpp | 1245 ++++++++--------- .../acir/tests/test_program_serialization.rs | 46 +- acvm-repo/acvm/src/pwg/brillig.rs | 20 +- acvm-repo/acvm/tests/solver.rs | 41 +- acvm-repo/acvm_js/src/foreign_call/inputs.rs | 4 +- acvm-repo/acvm_js/src/foreign_call/outputs.rs | 8 +- .../test/shared/complex_foreign_call.ts | 14 +- acvm-repo/acvm_js/test/shared/foreign_call.ts | 8 +- acvm-repo/brillig/src/foreign_call.rs | 26 +- acvm-repo/brillig/src/lib.rs | 3 - acvm-repo/brillig/src/opcodes.rs | 15 +- acvm-repo/brillig/src/value.rs | 103 -- acvm-repo/brillig_vm/Cargo.toml | 1 + acvm-repo/brillig_vm/src/arithmetic.rs | 136 +- acvm-repo/brillig_vm/src/black_box.rs | 67 +- acvm-repo/brillig_vm/src/lib.rs | 754 +++++----- acvm-repo/brillig_vm/src/memory.rs | 176 ++- aztec_macros/src/transforms/functions.rs | 126 +- .../brillig/brillig_gen/brillig_black_box.rs | 12 +- .../src/brillig/brillig_gen/brillig_block.rs | 10 +- .../brillig_gen/brillig_block_variables.rs | 7 +- .../brillig/brillig_gen/brillig_directive.rs | 37 +- .../src/brillig/brillig_gen/brillig_fn.rs | 16 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 238 ++-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 17 +- .../brillig/brillig_ir/brillig_variable.rs | 15 +- .../src/brillig/brillig_ir/codegen_binary.rs | 4 +- .../brillig/brillig_ir/codegen_intrinsic.rs | 14 +- .../src/brillig/brillig_ir/debug_show.rs | 11 +- .../src/brillig/brillig_ir/entry_point.rs | 34 +- .../src/brillig/brillig_ir/instructions.rs | 42 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 16 +- .../src/ssa/ir/instruction/call.rs | 9 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 7 + .../src/hir/type_check/errors.rs | 5 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 5 + compiler/noirc_printable_type/src/lib.rs | 16 +- docs/docs/how_to/how-to-oracles.md | 2 +- .../docs/noir/concepts/data_types/integers.md | 2 +- docs/docs/tutorials/noirjs_app.md | 4 +- .../version-v0.24.0/how_to/how-to-oracles.md | 2 +- .../version-v0.24.0/tutorials/noirjs_app.md | 4 +- .../version-v0.25.0/how_to/how-to-oracles.md | 2 +- .../version-v0.25.0/tutorials/noirjs_app.md | 4 +- noir_stdlib/src/collections/bounded_vec.nr | 9 + noir_stdlib/src/field.nr | 2 +- .../array_to_slice/src/main.nr | 23 + tooling/debugger/src/context.rs | 12 +- tooling/debugger/src/foreign_calls.rs | 17 +- tooling/debugger/src/repl.rs | 12 +- tooling/nargo/src/artifacts/debug_vars.rs | 12 +- tooling/nargo/src/ops/foreign_calls.rs | 43 +- .../noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 13 +- 57 files changed, 1900 insertions(+), 1578 deletions(-) delete mode 100644 acvm-repo/brillig/src/value.rs diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ead8f3147c0..f0d7985cbc9 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -208abbb63af4c9a3f25d723fe1c49e82aa461061 +16b0bdd7fbca6ce296906dc9d3affa308571cbfe diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e81ede7199d..dfb141e29f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Resolves Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. -- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. +- [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* diff --git a/Cargo.lock b/Cargo.lock index 99c19417267..3cb7c48dfb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,6 +637,7 @@ dependencies = [ "acvm_blackbox_solver", "num-bigint", "num-traits", + "thiserror", ] [[package]] diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index ca281d89637..d7ef849ab75 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -5,957 +5,951 @@ namespace Program { - struct BinaryFieldOp { + struct Witness { + uint32_t value; - struct Add { - friend bool operator==(const Add&, const Add&); + friend bool operator==(const Witness&, const Witness&); + std::vector bincodeSerialize() const; + static Witness bincodeDeserialize(std::vector); + }; + + struct FunctionInput { + Program::Witness witness; + uint32_t num_bits; + + friend bool operator==(const FunctionInput&, const FunctionInput&); + std::vector bincodeSerialize() const; + static FunctionInput bincodeDeserialize(std::vector); + }; + + struct BlackBoxFuncCall { + + struct AND { + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; + + friend bool operator==(const AND&, const AND&); std::vector bincodeSerialize() const; - static Add bincodeDeserialize(std::vector); + static AND bincodeDeserialize(std::vector); }; - struct Sub { - friend bool operator==(const Sub&, const Sub&); + struct XOR { + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; + + friend bool operator==(const XOR&, const XOR&); std::vector bincodeSerialize() const; - static Sub bincodeDeserialize(std::vector); + static XOR bincodeDeserialize(std::vector); }; - struct Mul { - friend bool operator==(const Mul&, const Mul&); + struct RANGE { + Program::FunctionInput input; + + friend bool operator==(const RANGE&, const RANGE&); std::vector bincodeSerialize() const; - static Mul bincodeDeserialize(std::vector); + static RANGE bincodeDeserialize(std::vector); }; - struct Div { - friend bool operator==(const Div&, const Div&); + struct SHA256 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const SHA256&, const SHA256&); std::vector bincodeSerialize() const; - static Div bincodeDeserialize(std::vector); + static SHA256 bincodeDeserialize(std::vector); }; - struct IntegerDiv { - friend bool operator==(const IntegerDiv&, const IntegerDiv&); + struct Blake2s { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; - static IntegerDiv bincodeDeserialize(std::vector); + static Blake2s bincodeDeserialize(std::vector); }; - struct Equals { - friend bool operator==(const Equals&, const Equals&); + struct Blake3 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; - static Equals bincodeDeserialize(std::vector); + static Blake3 bincodeDeserialize(std::vector); }; - struct LessThan { - friend bool operator==(const LessThan&, const LessThan&); + struct SchnorrVerify { + Program::FunctionInput public_key_x; + Program::FunctionInput public_key_y; + std::vector signature; + std::vector message; + Program::Witness output; + + friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; - static LessThan bincodeDeserialize(std::vector); + static SchnorrVerify bincodeDeserialize(std::vector); }; - struct LessThanEquals { - friend bool operator==(const LessThanEquals&, const LessThanEquals&); + struct PedersenCommitment { + std::vector inputs; + uint32_t domain_separator; + std::array outputs; + + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; - static LessThanEquals bincodeDeserialize(std::vector); + static PedersenCommitment bincodeDeserialize(std::vector); }; - std::variant value; + struct PedersenHash { + std::vector inputs; + uint32_t domain_separator; + Program::Witness output; - friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); - std::vector bincodeSerialize() const; - static BinaryFieldOp bincodeDeserialize(std::vector); - }; + friend bool operator==(const PedersenHash&, const PedersenHash&); + std::vector bincodeSerialize() const; + static PedersenHash bincodeDeserialize(std::vector); + }; - struct BinaryIntOp { + struct EcdsaSecp256k1 { + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; - struct Add { - friend bool operator==(const Add&, const Add&); + friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; - static Add bincodeDeserialize(std::vector); + static EcdsaSecp256k1 bincodeDeserialize(std::vector); }; - struct Sub { - friend bool operator==(const Sub&, const Sub&); + struct EcdsaSecp256r1 { + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; + + friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; - static Sub bincodeDeserialize(std::vector); + static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct Mul { - friend bool operator==(const Mul&, const Mul&); + struct FixedBaseScalarMul { + Program::FunctionInput low; + Program::FunctionInput high; + std::array outputs; + + friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; - static Mul bincodeDeserialize(std::vector); + static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - struct Div { - friend bool operator==(const Div&, const Div&); + struct EmbeddedCurveAdd { + Program::FunctionInput input1_x; + Program::FunctionInput input1_y; + Program::FunctionInput input2_x; + Program::FunctionInput input2_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; - static Div bincodeDeserialize(std::vector); + static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct Equals { - friend bool operator==(const Equals&, const Equals&); + struct Keccak256 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; - static Equals bincodeDeserialize(std::vector); + static Keccak256 bincodeDeserialize(std::vector); }; - struct LessThan { - friend bool operator==(const LessThan&, const LessThan&); + 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 LessThan bincodeDeserialize(std::vector); + static Keccak256VariableLength bincodeDeserialize(std::vector); }; - struct LessThanEquals { - friend bool operator==(const LessThanEquals&, const LessThanEquals&); + struct Keccakf1600 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; - static LessThanEquals bincodeDeserialize(std::vector); + static Keccakf1600 bincodeDeserialize(std::vector); }; - struct And { - friend bool operator==(const And&, const And&); + struct RecursiveAggregation { + std::vector verification_key; + std::vector proof; + std::vector public_inputs; + Program::FunctionInput key_hash; + + friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&); std::vector bincodeSerialize() const; - static And bincodeDeserialize(std::vector); + static RecursiveAggregation bincodeDeserialize(std::vector); }; - struct Or { - friend bool operator==(const Or&, const Or&); + struct BigIntAdd { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; - static Or bincodeDeserialize(std::vector); + static BigIntAdd bincodeDeserialize(std::vector); }; - struct Xor { - friend bool operator==(const Xor&, const Xor&); + struct BigIntSub { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntSub&, const BigIntSub&); std::vector bincodeSerialize() const; - static Xor bincodeDeserialize(std::vector); + static BigIntSub bincodeDeserialize(std::vector); }; - struct Shl { - friend bool operator==(const Shl&, const Shl&); + struct BigIntMul { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; - static Shl bincodeDeserialize(std::vector); + static BigIntMul bincodeDeserialize(std::vector); }; - struct Shr { - friend bool operator==(const Shr&, const Shr&); + struct BigIntDiv { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; - static Shr bincodeDeserialize(std::vector); + static BigIntDiv bincodeDeserialize(std::vector); }; - std::variant value; + struct BigIntFromLeBytes { + std::vector inputs; + std::vector modulus; + uint32_t output; - friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); - std::vector bincodeSerialize() const; - static BinaryIntOp bincodeDeserialize(std::vector); - }; + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; - struct MemoryAddress { - uint64_t value; + struct BigIntToLeBytes { + uint32_t input; + std::vector outputs; - friend bool operator==(const MemoryAddress&, const MemoryAddress&); - std::vector bincodeSerialize() const; - static MemoryAddress bincodeDeserialize(std::vector); - }; + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; - struct HeapArray { - Program::MemoryAddress pointer; - uint64_t size; + struct Poseidon2Permutation { + std::vector inputs; + std::vector outputs; + uint32_t len; - friend bool operator==(const HeapArray&, const HeapArray&); + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + std::vector inputs; + std::vector hash_values; + std::vector outputs; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; + + std::variant value; + + friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; - static HeapArray bincodeDeserialize(std::vector); + static BlackBoxFuncCall bincodeDeserialize(std::vector); }; - struct HeapVector { - Program::MemoryAddress pointer; - Program::MemoryAddress size; + struct BlockId { + uint32_t value; - friend bool operator==(const HeapVector&, const HeapVector&); + friend bool operator==(const BlockId&, const BlockId&); std::vector bincodeSerialize() const; - static HeapVector bincodeDeserialize(std::vector); + static BlockId bincodeDeserialize(std::vector); }; - struct BlackBoxOp { + struct Expression { + std::vector> mul_terms; + std::vector> linear_combinations; + std::string q_c; - struct Sha256 { - Program::HeapVector message; - Program::HeapArray output; + friend bool operator==(const Expression&, const Expression&); + std::vector bincodeSerialize() const; + static Expression bincodeDeserialize(std::vector); + }; - friend bool operator==(const Sha256&, const Sha256&); - std::vector bincodeSerialize() const; - static Sha256 bincodeDeserialize(std::vector); - }; + struct BrilligInputs { - struct Blake2s { - Program::HeapVector message; - Program::HeapArray output; + struct Single { + Program::Expression value; - friend bool operator==(const Blake2s&, const Blake2s&); + friend bool operator==(const Single&, const Single&); std::vector bincodeSerialize() const; - static Blake2s bincodeDeserialize(std::vector); + static Single bincodeDeserialize(std::vector); }; - struct Blake3 { - Program::HeapVector message; - Program::HeapArray output; + struct Array { + std::vector value; - friend bool operator==(const Blake3&, const Blake3&); + friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; - static Blake3 bincodeDeserialize(std::vector); + static Array bincodeDeserialize(std::vector); }; - struct Keccak256 { - Program::HeapVector message; - Program::HeapArray output; + struct MemoryArray { + Program::BlockId value; - friend bool operator==(const Keccak256&, const Keccak256&); + friend bool operator==(const MemoryArray&, const MemoryArray&); std::vector bincodeSerialize() const; - static Keccak256 bincodeDeserialize(std::vector); + static MemoryArray bincodeDeserialize(std::vector); }; - struct Keccakf1600 { - Program::HeapVector message; - Program::HeapArray output; + std::variant value; - friend bool operator==(const Keccakf1600&, const Keccakf1600&); - std::vector bincodeSerialize() const; - static Keccakf1600 bincodeDeserialize(std::vector); - }; + friend bool operator==(const BrilligInputs&, const BrilligInputs&); + std::vector bincodeSerialize() const; + static BrilligInputs bincodeDeserialize(std::vector); + }; - struct EcdsaSecp256k1 { - Program::HeapVector hashed_msg; - Program::HeapArray public_key_x; - Program::HeapArray public_key_y; - Program::HeapArray signature; - Program::MemoryAddress result; + struct BinaryFieldOp { - friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); + struct Add { + friend bool operator==(const Add&, const Add&); std::vector bincodeSerialize() const; - static EcdsaSecp256k1 bincodeDeserialize(std::vector); + static Add bincodeDeserialize(std::vector); }; - struct EcdsaSecp256r1 { - Program::HeapVector hashed_msg; - Program::HeapArray public_key_x; - Program::HeapArray public_key_y; - Program::HeapArray signature; - Program::MemoryAddress result; - - friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); + struct Sub { + friend bool operator==(const Sub&, const Sub&); std::vector bincodeSerialize() const; - static EcdsaSecp256r1 bincodeDeserialize(std::vector); + static Sub bincodeDeserialize(std::vector); }; - struct SchnorrVerify { - Program::MemoryAddress public_key_x; - Program::MemoryAddress public_key_y; - Program::HeapVector message; - Program::HeapVector signature; - Program::MemoryAddress result; - - friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); + struct Mul { + friend bool operator==(const Mul&, const Mul&); std::vector bincodeSerialize() const; - static SchnorrVerify bincodeDeserialize(std::vector); + static Mul bincodeDeserialize(std::vector); }; - struct PedersenCommitment { - Program::HeapVector inputs; - Program::MemoryAddress domain_separator; - Program::HeapArray output; - - friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); + struct Div { + friend bool operator==(const Div&, const Div&); std::vector bincodeSerialize() const; - static PedersenCommitment bincodeDeserialize(std::vector); + static Div bincodeDeserialize(std::vector); }; - struct PedersenHash { - Program::HeapVector inputs; - Program::MemoryAddress domain_separator; - Program::MemoryAddress output; - - friend bool operator==(const PedersenHash&, const PedersenHash&); + struct IntegerDiv { + friend bool operator==(const IntegerDiv&, const IntegerDiv&); std::vector bincodeSerialize() const; - static PedersenHash bincodeDeserialize(std::vector); + static IntegerDiv bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::MemoryAddress low; - Program::MemoryAddress high; - Program::HeapArray result; - - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + struct Equals { + friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); + static Equals bincodeDeserialize(std::vector); }; - struct EmbeddedCurveAdd { - Program::MemoryAddress input1_x; - Program::MemoryAddress input1_y; - Program::MemoryAddress input2_x; - Program::MemoryAddress input2_y; - Program::HeapArray result; - - friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); std::vector bincodeSerialize() const; - static EmbeddedCurveAdd bincodeDeserialize(std::vector); + static LessThan bincodeDeserialize(std::vector); }; - struct BigIntAdd { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; - - friend bool operator==(const BigIntAdd&, const BigIntAdd&); + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); std::vector bincodeSerialize() const; - static BigIntAdd bincodeDeserialize(std::vector); + static LessThanEquals bincodeDeserialize(std::vector); }; - struct BigIntSub { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; + std::variant value; - friend bool operator==(const BigIntSub&, const BigIntSub&); - std::vector bincodeSerialize() const; - static BigIntSub bincodeDeserialize(std::vector); - }; + friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); + std::vector bincodeSerialize() const; + static BinaryFieldOp bincodeDeserialize(std::vector); + }; - struct BigIntMul { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; + struct BinaryIntOp { - friend bool operator==(const BigIntMul&, const BigIntMul&); + struct Add { + friend bool operator==(const Add&, const Add&); std::vector bincodeSerialize() const; - static BigIntMul bincodeDeserialize(std::vector); + static Add bincodeDeserialize(std::vector); }; - struct BigIntDiv { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; - - friend bool operator==(const BigIntDiv&, const BigIntDiv&); + struct Sub { + friend bool operator==(const Sub&, const Sub&); std::vector bincodeSerialize() const; - static BigIntDiv bincodeDeserialize(std::vector); + static Sub bincodeDeserialize(std::vector); }; - struct BigIntFromLeBytes { - Program::HeapVector inputs; - Program::HeapVector modulus; - Program::MemoryAddress output; - - friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + struct Mul { + friend bool operator==(const Mul&, const Mul&); std::vector bincodeSerialize() const; - static BigIntFromLeBytes bincodeDeserialize(std::vector); + static Mul bincodeDeserialize(std::vector); }; - struct BigIntToLeBytes { - Program::MemoryAddress input; - Program::HeapVector output; - - friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + struct Div { + friend bool operator==(const Div&, const Div&); std::vector bincodeSerialize() const; - static BigIntToLeBytes bincodeDeserialize(std::vector); + static Div bincodeDeserialize(std::vector); }; - struct Poseidon2Permutation { - Program::HeapVector message; - Program::HeapArray output; - Program::MemoryAddress len; - - friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + struct Equals { + friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; - static Poseidon2Permutation bincodeDeserialize(std::vector); + static Equals bincodeDeserialize(std::vector); }; - struct Sha256Compression { - Program::HeapVector input; - Program::HeapVector hash_values; - Program::HeapArray output; - - friend bool operator==(const Sha256Compression&, const Sha256Compression&); + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); std::vector bincodeSerialize() const; - static Sha256Compression bincodeDeserialize(std::vector); + static LessThan bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); - std::vector bincodeSerialize() const; - static BlackBoxOp bincodeDeserialize(std::vector); - }; - - struct HeapValueType; - - struct HeapValueType { + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); + std::vector bincodeSerialize() const; + static LessThanEquals bincodeDeserialize(std::vector); + }; - struct Simple { - friend bool operator==(const Simple&, const Simple&); + struct And { + friend bool operator==(const And&, const And&); std::vector bincodeSerialize() const; - static Simple bincodeDeserialize(std::vector); + static And bincodeDeserialize(std::vector); }; - struct Array { - std::vector value_types; - uint64_t size; + struct Or { + friend bool operator==(const Or&, const Or&); + std::vector bincodeSerialize() const; + static Or bincodeDeserialize(std::vector); + }; - friend bool operator==(const Array&, const Array&); + struct Xor { + friend bool operator==(const Xor&, const Xor&); std::vector bincodeSerialize() const; - static Array bincodeDeserialize(std::vector); + static Xor bincodeDeserialize(std::vector); }; - struct Vector { - std::vector value_types; + struct Shl { + friend bool operator==(const Shl&, const Shl&); + std::vector bincodeSerialize() const; + static Shl bincodeDeserialize(std::vector); + }; - friend bool operator==(const Vector&, const Vector&); + struct Shr { + friend bool operator==(const Shr&, const Shr&); std::vector bincodeSerialize() const; - static Vector bincodeDeserialize(std::vector); + static Shr bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; - friend bool operator==(const HeapValueType&, const HeapValueType&); + friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; - static HeapValueType bincodeDeserialize(std::vector); + static BinaryIntOp bincodeDeserialize(std::vector); }; - struct Value { - std::string inner; + struct MemoryAddress { + uint64_t value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; - struct ValueOrArray { + struct HeapArray { + Program::MemoryAddress pointer; + uint64_t size; - struct MemoryAddress { - Program::MemoryAddress value; + friend bool operator==(const HeapArray&, const HeapArray&); + std::vector bincodeSerialize() const; + static HeapArray bincodeDeserialize(std::vector); + }; - friend bool operator==(const MemoryAddress&, const MemoryAddress&); - std::vector bincodeSerialize() const; - static MemoryAddress bincodeDeserialize(std::vector); - }; + struct HeapVector { + Program::MemoryAddress pointer; + Program::MemoryAddress size; - struct HeapArray { - Program::HeapArray value; + friend bool operator==(const HeapVector&, const HeapVector&); + std::vector bincodeSerialize() const; + static HeapVector bincodeDeserialize(std::vector); + }; - friend bool operator==(const HeapArray&, const HeapArray&); - std::vector bincodeSerialize() const; - static HeapArray bincodeDeserialize(std::vector); - }; + struct BlackBoxOp { - struct HeapVector { - Program::HeapVector value; + struct Sha256 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const HeapVector&, const HeapVector&); + friend bool operator==(const Sha256&, const Sha256&); std::vector bincodeSerialize() const; - static HeapVector bincodeDeserialize(std::vector); + static Sha256 bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const ValueOrArray&, const ValueOrArray&); - std::vector bincodeSerialize() const; - static ValueOrArray bincodeDeserialize(std::vector); - }; - - struct BrilligOpcode { - - struct BinaryFieldOp { - Program::MemoryAddress destination; - Program::BinaryFieldOp op; - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; + struct Blake2s { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); + friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; - static BinaryFieldOp bincodeDeserialize(std::vector); + static Blake2s bincodeDeserialize(std::vector); }; - struct BinaryIntOp { - Program::MemoryAddress destination; - Program::BinaryIntOp op; - uint32_t bit_size; - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; + struct Blake3 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); + friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; - static BinaryIntOp bincodeDeserialize(std::vector); + static Blake3 bincodeDeserialize(std::vector); }; - struct Cast { - Program::MemoryAddress destination; - Program::MemoryAddress source; - uint32_t bit_size; + struct Keccak256 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const Cast&, const Cast&); + friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; - static Cast bincodeDeserialize(std::vector); + static Keccak256 bincodeDeserialize(std::vector); }; - struct JumpIfNot { - Program::MemoryAddress condition; - uint64_t location; + struct Keccakf1600 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const JumpIfNot&, const JumpIfNot&); + friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; - static JumpIfNot bincodeDeserialize(std::vector); + static Keccakf1600 bincodeDeserialize(std::vector); }; - struct JumpIf { - Program::MemoryAddress condition; - uint64_t location; + struct EcdsaSecp256k1 { + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; - friend bool operator==(const JumpIf&, const JumpIf&); + friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; - static JumpIf bincodeDeserialize(std::vector); + static EcdsaSecp256k1 bincodeDeserialize(std::vector); }; - struct Jump { - uint64_t location; + struct EcdsaSecp256r1 { + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; - friend bool operator==(const Jump&, const Jump&); + friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; - static Jump bincodeDeserialize(std::vector); + static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct CalldataCopy { - Program::MemoryAddress destination_address; - uint64_t size; - uint64_t offset; + struct SchnorrVerify { + Program::MemoryAddress public_key_x; + Program::MemoryAddress public_key_y; + Program::HeapVector message; + Program::HeapVector signature; + Program::MemoryAddress result; - friend bool operator==(const CalldataCopy&, const CalldataCopy&); + friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; - static CalldataCopy bincodeDeserialize(std::vector); + static SchnorrVerify bincodeDeserialize(std::vector); }; - struct Call { - uint64_t location; + struct PedersenCommitment { + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::HeapArray output; - friend bool operator==(const Call&, const Call&); + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; - static Call bincodeDeserialize(std::vector); + static PedersenCommitment bincodeDeserialize(std::vector); }; - struct Const { - Program::MemoryAddress destination; - uint32_t bit_size; - Program::Value value; + struct PedersenHash { + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::MemoryAddress output; - friend bool operator==(const Const&, const Const&); + friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; - static Const bincodeDeserialize(std::vector); + static PedersenHash bincodeDeserialize(std::vector); }; - struct Return { - friend bool operator==(const Return&, const Return&); + struct FixedBaseScalarMul { + Program::MemoryAddress low; + Program::MemoryAddress high; + Program::HeapArray result; + + friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; - static Return bincodeDeserialize(std::vector); + static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - struct ForeignCall { - std::string function; - std::vector destinations; - std::vector destination_value_types; - std::vector inputs; - std::vector input_value_types; + struct EmbeddedCurveAdd { + Program::MemoryAddress input1_x; + Program::MemoryAddress input1_y; + Program::MemoryAddress input2_x; + Program::MemoryAddress input2_y; + Program::HeapArray result; - friend bool operator==(const ForeignCall&, const ForeignCall&); + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; - static ForeignCall bincodeDeserialize(std::vector); + static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct Mov { - Program::MemoryAddress destination; - Program::MemoryAddress source; + struct BigIntAdd { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Mov&, const Mov&); + friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; - static Mov bincodeDeserialize(std::vector); + static BigIntAdd bincodeDeserialize(std::vector); }; - struct ConditionalMov { - Program::MemoryAddress destination; - Program::MemoryAddress source_a; - Program::MemoryAddress source_b; - Program::MemoryAddress condition; + struct BigIntSub { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const ConditionalMov&, const ConditionalMov&); + friend bool operator==(const BigIntSub&, const BigIntSub&); std::vector bincodeSerialize() const; - static ConditionalMov bincodeDeserialize(std::vector); + static BigIntSub bincodeDeserialize(std::vector); }; - struct Load { - Program::MemoryAddress destination; - Program::MemoryAddress source_pointer; + struct BigIntMul { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Load&, const Load&); + friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; - static Load bincodeDeserialize(std::vector); + static BigIntMul bincodeDeserialize(std::vector); }; - struct Store { - Program::MemoryAddress destination_pointer; - Program::MemoryAddress source; + struct BigIntDiv { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Store&, const Store&); + friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; - static Store bincodeDeserialize(std::vector); + static BigIntDiv bincodeDeserialize(std::vector); }; - struct BlackBox { - Program::BlackBoxOp value; + struct BigIntFromLeBytes { + Program::HeapVector inputs; + Program::HeapVector modulus; + Program::MemoryAddress output; - friend bool operator==(const BlackBox&, const BlackBox&); + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; - static BlackBox bincodeDeserialize(std::vector); + static BigIntFromLeBytes bincodeDeserialize(std::vector); }; - struct Trap { - friend bool operator==(const Trap&, const Trap&); + struct BigIntToLeBytes { + Program::MemoryAddress input; + Program::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); std::vector bincodeSerialize() const; - static Trap bincodeDeserialize(std::vector); + static BigIntToLeBytes bincodeDeserialize(std::vector); }; - struct Stop { - uint64_t return_data_offset; - uint64_t return_data_size; + struct Poseidon2Permutation { + Program::HeapVector message; + Program::HeapArray output; + Program::MemoryAddress len; - friend bool operator==(const Stop&, const Stop&); + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; - static Stop bincodeDeserialize(std::vector); + static Poseidon2Permutation bincodeDeserialize(std::vector); }; - std::variant value; + struct Sha256Compression { + Program::HeapVector input; + Program::HeapVector hash_values; + Program::HeapArray output; - friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); - std::vector bincodeSerialize() const; - static BrilligOpcode bincodeDeserialize(std::vector); - }; + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; - struct Witness { - uint32_t value; + std::variant value; - friend bool operator==(const Witness&, const Witness&); + friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; - static Witness bincodeDeserialize(std::vector); + static BlackBoxOp bincodeDeserialize(std::vector); }; - struct FunctionInput { - Program::Witness witness; - uint32_t num_bits; - - friend bool operator==(const FunctionInput&, const FunctionInput&); - std::vector bincodeSerialize() const; - static FunctionInput bincodeDeserialize(std::vector); - }; + struct HeapValueType; - struct BlackBoxFuncCall { + struct HeapValueType { - struct AND { - Program::FunctionInput lhs; - Program::FunctionInput rhs; - Program::Witness output; + struct Simple { + uint32_t value; - friend bool operator==(const AND&, const AND&); + friend bool operator==(const Simple&, const Simple&); std::vector bincodeSerialize() const; - static AND bincodeDeserialize(std::vector); + static Simple bincodeDeserialize(std::vector); }; - struct XOR { - Program::FunctionInput lhs; - Program::FunctionInput rhs; - Program::Witness output; + struct Array { + std::vector value_types; + uint64_t size; - friend bool operator==(const XOR&, const XOR&); + friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; - static XOR bincodeDeserialize(std::vector); + static Array bincodeDeserialize(std::vector); }; - struct RANGE { - Program::FunctionInput input; + struct Vector { + std::vector value_types; - friend bool operator==(const RANGE&, const RANGE&); + friend bool operator==(const Vector&, const Vector&); std::vector bincodeSerialize() const; - static RANGE bincodeDeserialize(std::vector); + static Vector bincodeDeserialize(std::vector); }; - struct SHA256 { - std::vector inputs; - std::vector outputs; - - friend bool operator==(const SHA256&, const SHA256&); - std::vector bincodeSerialize() const; - static SHA256 bincodeDeserialize(std::vector); - }; + std::variant value; - struct Blake2s { - std::vector inputs; - std::vector outputs; + friend bool operator==(const HeapValueType&, const HeapValueType&); + std::vector bincodeSerialize() const; + static HeapValueType bincodeDeserialize(std::vector); + }; - friend bool operator==(const Blake2s&, const Blake2s&); - std::vector bincodeSerialize() const; - static Blake2s bincodeDeserialize(std::vector); - }; + struct ValueOrArray { - struct Blake3 { - std::vector inputs; - std::vector outputs; + struct MemoryAddress { + Program::MemoryAddress value; - friend bool operator==(const Blake3&, const Blake3&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static Blake3 bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; - struct SchnorrVerify { - Program::FunctionInput public_key_x; - Program::FunctionInput public_key_y; - std::vector signature; - std::vector message; - Program::Witness output; + struct HeapArray { + Program::HeapArray value; - friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); + friend bool operator==(const HeapArray&, const HeapArray&); std::vector bincodeSerialize() const; - static SchnorrVerify bincodeDeserialize(std::vector); + static HeapArray bincodeDeserialize(std::vector); }; - struct PedersenCommitment { - std::vector inputs; - uint32_t domain_separator; - std::array outputs; + struct HeapVector { + Program::HeapVector value; - friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); + friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; - static PedersenCommitment bincodeDeserialize(std::vector); + static HeapVector bincodeDeserialize(std::vector); }; - struct PedersenHash { - std::vector inputs; - uint32_t domain_separator; - Program::Witness output; - - friend bool operator==(const PedersenHash&, const PedersenHash&); - std::vector bincodeSerialize() const; - static PedersenHash bincodeDeserialize(std::vector); - }; + std::variant value; - struct EcdsaSecp256k1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Program::Witness output; + friend bool operator==(const ValueOrArray&, const ValueOrArray&); + std::vector bincodeSerialize() const; + static ValueOrArray bincodeDeserialize(std::vector); + }; - friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); - std::vector bincodeSerialize() const; - static EcdsaSecp256k1 bincodeDeserialize(std::vector); - }; + struct BrilligOpcode { - struct EcdsaSecp256r1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Program::Witness output; + struct BinaryFieldOp { + Program::MemoryAddress destination; + Program::BinaryFieldOp op; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; - friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); + friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; - static EcdsaSecp256r1 bincodeDeserialize(std::vector); + static BinaryFieldOp bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::FunctionInput low; - Program::FunctionInput high; - std::array outputs; + struct BinaryIntOp { + Program::MemoryAddress destination; + Program::BinaryIntOp op; + uint32_t bit_size; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); + static BinaryIntOp bincodeDeserialize(std::vector); }; - struct EmbeddedCurveAdd { - Program::FunctionInput input1_x; - Program::FunctionInput input1_y; - Program::FunctionInput input2_x; - Program::FunctionInput input2_y; - std::array outputs; + struct Cast { + Program::MemoryAddress destination; + Program::MemoryAddress source; + uint32_t bit_size; - friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + friend bool operator==(const Cast&, const Cast&); std::vector bincodeSerialize() const; - static EmbeddedCurveAdd bincodeDeserialize(std::vector); + static Cast bincodeDeserialize(std::vector); }; - struct Keccak256 { - std::vector inputs; - std::vector outputs; + struct JumpIfNot { + Program::MemoryAddress condition; + uint64_t location; - friend bool operator==(const Keccak256&, const Keccak256&); + friend bool operator==(const JumpIfNot&, const JumpIfNot&); std::vector bincodeSerialize() const; - static Keccak256 bincodeDeserialize(std::vector); + static JumpIfNot bincodeDeserialize(std::vector); }; - struct Keccak256VariableLength { - std::vector inputs; - Program::FunctionInput var_message_size; - std::vector outputs; + struct JumpIf { + Program::MemoryAddress condition; + uint64_t location; - friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); + friend bool operator==(const JumpIf&, const JumpIf&); std::vector bincodeSerialize() const; - static Keccak256VariableLength bincodeDeserialize(std::vector); + static JumpIf bincodeDeserialize(std::vector); }; - struct Keccakf1600 { - std::vector inputs; - std::vector outputs; + struct Jump { + uint64_t location; - friend bool operator==(const Keccakf1600&, const Keccakf1600&); + friend bool operator==(const Jump&, const Jump&); std::vector bincodeSerialize() const; - static Keccakf1600 bincodeDeserialize(std::vector); + static Jump bincodeDeserialize(std::vector); }; - struct RecursiveAggregation { - std::vector verification_key; - std::vector proof; - std::vector public_inputs; - Program::FunctionInput key_hash; + struct CalldataCopy { + Program::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; - friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&); + friend bool operator==(const CalldataCopy&, const CalldataCopy&); std::vector bincodeSerialize() const; - static RecursiveAggregation bincodeDeserialize(std::vector); + static CalldataCopy bincodeDeserialize(std::vector); }; - struct BigIntAdd { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct Call { + uint64_t location; - friend bool operator==(const BigIntAdd&, const BigIntAdd&); + friend bool operator==(const Call&, const Call&); std::vector bincodeSerialize() const; - static BigIntAdd bincodeDeserialize(std::vector); + static Call bincodeDeserialize(std::vector); }; - struct BigIntSub { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct Const { + Program::MemoryAddress destination; + uint32_t bit_size; + std::string value; - friend bool operator==(const BigIntSub&, const BigIntSub&); + friend bool operator==(const Const&, const Const&); std::vector bincodeSerialize() const; - static BigIntSub bincodeDeserialize(std::vector); + static Const bincodeDeserialize(std::vector); }; - struct BigIntMul { - uint32_t lhs; - uint32_t rhs; - uint32_t output; - - friend bool operator==(const BigIntMul&, const BigIntMul&); + struct Return { + friend bool operator==(const Return&, const Return&); std::vector bincodeSerialize() const; - static BigIntMul bincodeDeserialize(std::vector); + static Return bincodeDeserialize(std::vector); }; - struct BigIntDiv { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct ForeignCall { + std::string function; + std::vector destinations; + std::vector destination_value_types; + std::vector inputs; + std::vector input_value_types; - friend bool operator==(const BigIntDiv&, const BigIntDiv&); + friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; - static BigIntDiv bincodeDeserialize(std::vector); + static ForeignCall bincodeDeserialize(std::vector); }; - struct BigIntFromLeBytes { - std::vector inputs; - std::vector modulus; - uint32_t output; + struct Mov { + Program::MemoryAddress destination; + Program::MemoryAddress source; - friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; - static BigIntFromLeBytes bincodeDeserialize(std::vector); + static Mov bincodeDeserialize(std::vector); }; - struct BigIntToLeBytes { - uint32_t input; - std::vector outputs; + struct ConditionalMov { + Program::MemoryAddress destination; + Program::MemoryAddress source_a; + Program::MemoryAddress source_b; + Program::MemoryAddress condition; - friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + friend bool operator==(const ConditionalMov&, const ConditionalMov&); std::vector bincodeSerialize() const; - static BigIntToLeBytes bincodeDeserialize(std::vector); + static ConditionalMov bincodeDeserialize(std::vector); }; - struct Poseidon2Permutation { - std::vector inputs; - std::vector outputs; - uint32_t len; + struct Load { + Program::MemoryAddress destination; + Program::MemoryAddress source_pointer; - friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; - static Poseidon2Permutation bincodeDeserialize(std::vector); + static Load bincodeDeserialize(std::vector); }; - struct Sha256Compression { - std::vector inputs; - std::vector hash_values; - std::vector outputs; + struct Store { + Program::MemoryAddress destination_pointer; + Program::MemoryAddress source; - friend bool operator==(const Sha256Compression&, const Sha256Compression&); + friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; - static Sha256Compression bincodeDeserialize(std::vector); + static Store bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); - std::vector bincodeSerialize() const; - static BlackBoxFuncCall bincodeDeserialize(std::vector); - }; - - struct BlockId { - uint32_t value; - - friend bool operator==(const BlockId&, const BlockId&); - std::vector bincodeSerialize() const; - static BlockId bincodeDeserialize(std::vector); - }; - - struct Expression { - std::vector> mul_terms; - std::vector> linear_combinations; - std::string q_c; - - friend bool operator==(const Expression&, const Expression&); - std::vector bincodeSerialize() const; - static Expression bincodeDeserialize(std::vector); - }; - - struct BrilligInputs { - - struct Single { - Program::Expression value; + struct BlackBox { + Program::BlackBoxOp value; - friend bool operator==(const Single&, const Single&); + friend bool operator==(const BlackBox&, const BlackBox&); std::vector bincodeSerialize() const; - static Single bincodeDeserialize(std::vector); + static BlackBox bincodeDeserialize(std::vector); }; - struct Array { - std::vector value; - - friend bool operator==(const Array&, const Array&); + struct Trap { + friend bool operator==(const Trap&, const Trap&); std::vector bincodeSerialize() const; - static Array bincodeDeserialize(std::vector); + static Trap bincodeDeserialize(std::vector); }; - struct MemoryArray { - Program::BlockId value; + struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; - friend bool operator==(const MemoryArray&, const MemoryArray&); + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; - static MemoryArray bincodeDeserialize(std::vector); + static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; - friend bool operator==(const BrilligInputs&, const BrilligInputs&); + friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; - static BrilligInputs bincodeDeserialize(std::vector); + static BrilligOpcode bincodeDeserialize(std::vector); }; struct BrilligOutputs { @@ -5645,6 +5639,7 @@ Program::HeapValueType serde::Deserializable::deserializ namespace Program { inline bool operator==(const HeapValueType::Simple &lhs, const HeapValueType::Simple &rhs) { + if (!(lhs.value == rhs.value)) { return false; } return true; } @@ -5668,12 +5663,14 @@ namespace Program { template <> template void serde::Serializable::serialize(const Program::HeapValueType::Simple &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.value, serializer); } template <> template Program::HeapValueType::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { Program::HeapValueType::Simple obj; + obj.value = serde::Deserializable::deserialize(deserializer); return obj; } @@ -6419,48 +6416,6 @@ Program::PublicInputs serde::Deserializable::deserialize( return obj; } -namespace Program { - - inline bool operator==(const Value &lhs, const Value &rhs) { - if (!(lhs.inner == rhs.inner)) { return false; } - return true; - } - - inline std::vector Value::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline Value Value::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::Value &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Program::Value serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Program::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - namespace Program { inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 64385a37582..8b04292dfaa 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -190,9 +190,9 @@ fn simple_brillig_foreign_call() { brillig::Opcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], @@ -211,11 +211,11 @@ fn simple_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, - 159, 216, 31, 244, 51, 61, 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, - 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, 251, 164, 235, 53, 94, 218, 247, 75, - 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, 251, 208, - 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 61, 10, 192, 48, 8, 133, 53, 133, 82, 186, + 245, 38, 233, 13, 122, 153, 14, 93, 58, 132, 144, 227, 135, 252, 41, 56, 36, 46, 201, 7, + 162, 168, 200, 123, 34, 52, 142, 28, 72, 245, 38, 106, 9, 247, 30, 202, 118, 142, 27, 215, + 221, 178, 82, 175, 33, 15, 133, 189, 163, 159, 57, 197, 252, 251, 195, 235, 188, 230, 186, + 16, 65, 255, 12, 239, 92, 131, 89, 149, 198, 77, 3, 10, 9, 119, 8, 198, 242, 152, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -264,8 +264,8 @@ fn complex_brillig_foreign_call() { }, brillig::Opcode::Const { destination: MemoryAddress(0), - value: brillig::Value::from(32_usize), - bit_size: 32, + value: FieldElement::from(32_usize), + bit_size: 64, }, brillig::Opcode::CalldataCopy { destination_address: MemoryAddress(1), @@ -280,8 +280,8 @@ fn complex_brillig_foreign_call() { ValueOrArray::MemoryAddress(MemoryAddress::from(1)), ], input_value_types: vec![ - HeapValueType::Array { size: 3, value_types: vec![HeapValueType::Simple] }, - HeapValueType::Simple, + HeapValueType::Array { size: 3, value_types: vec![HeapValueType::field()] }, + HeapValueType::field(), ], destinations: vec![ ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), @@ -289,9 +289,9 @@ fn complex_brillig_foreign_call() { ValueOrArray::MemoryAddress(MemoryAddress::from(36)), ], destination_value_types: vec![ - HeapValueType::Array { size: 3, value_types: vec![HeapValueType::Simple] }, - HeapValueType::Simple, - HeapValueType::Simple, + HeapValueType::Array { size: 3, value_types: vec![HeapValueType::field()] }, + HeapValueType::field(), + HeapValueType::field(), ], }, brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 }, @@ -311,15 +311,15 @@ fn complex_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, - 246, 7, 38, 187, 15, 96, 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, - 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, 205, 200, 157, 49, 124, 227, 44, 129, 207, - 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, 160, 209, 156, - 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, - 254, 196, 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, - 47, 178, 186, 251, 37, 116, 86, 93, 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, - 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, 164, 50, 165, 0, 137, 17, - 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 93, 10, 131, 48, 12, 78, 218, 233, 100, 111, + 187, 193, 96, 59, 64, 231, 9, 188, 139, 248, 166, 232, 163, 167, 23, 11, 126, 197, 24, 250, + 34, 86, 208, 64, 72, 218, 252, 125, 36, 105, 153, 22, 42, 60, 51, 116, 235, 217, 64, 103, + 156, 37, 5, 191, 10, 210, 29, 163, 63, 167, 203, 229, 206, 194, 104, 110, 128, 209, 158, + 128, 49, 236, 195, 69, 231, 157, 114, 46, 73, 251, 103, 35, 239, 231, 225, 57, 243, 156, + 227, 252, 132, 44, 112, 79, 176, 125, 84, 223, 73, 248, 145, 152, 69, 149, 4, 107, 233, + 114, 90, 119, 145, 85, 237, 151, 192, 89, 247, 221, 208, 54, 163, 85, 174, 26, 234, 87, + 232, 63, 101, 103, 21, 55, 169, 216, 73, 72, 249, 5, 197, 234, 132, 123, 179, 35, 247, 155, + 214, 246, 102, 20, 73, 204, 72, 168, 123, 191, 161, 25, 66, 136, 159, 187, 53, 5, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 51c7f4c6203..bcf736cd926 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, Value}, + brillig::{ForeignCallParam, ForeignCallResult}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::BlockId, @@ -11,7 +11,7 @@ use acir::{ FieldElement, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{VMStatus, VM}; +use brillig_vm::{MemoryValue, VMStatus, VM}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -73,7 +73,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { acir_index: usize, ) -> Result { // Set input values - let mut calldata: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents an expression or array of expressions to evaluate. // Iterate over each input and evaluate the expression(s) associated with it. // Push the results into memory. @@ -81,7 +81,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { for input in &brillig.inputs { match input { BrilligInputs::Single(expr) => match get_value(expr, initial_witness) { - Ok(value) => calldata.push(value.into()), + Ok(value) => calldata.push(value), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -92,7 +92,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Attempt to fetch all array input values for expr in expr_arr.iter() { match get_value(expr, initial_witness) { - Ok(value) => calldata.push(value.into()), + Ok(value) => calldata.push(value), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -110,7 +110,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { .block_value .get(&memory_index) .expect("All memory is initialized on creation"); - calldata.push((*memory_value).into()); + calldata.push(*memory_value); } } } @@ -122,11 +122,11 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { Ok(Self { vm, acir_index }) } - pub fn get_memory(&self) -> &[Value] { + pub fn get_memory(&self) -> &[MemoryValue] { self.vm.get_memory() } - pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + pub fn write_memory_at(&mut self, ptr: usize, value: MemoryValue) { self.vm.write_memory_at(ptr, value); } @@ -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].to_field(), witness_map)?; + insert_value(witness, memory[current_ret_data_idx].value, 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.to_field(), witness_map)?; + insert_value(witness, value.value, witness_map)?; current_ret_data_idx += 1; } } diff --git a/acvm-repo/acvm/tests/solver.rs b/acvm-repo/acvm/tests/solver.rs index b267c3005a8..a708db5b030 100644 --- a/acvm-repo/acvm/tests/solver.rs +++ b/acvm-repo/acvm/tests/solver.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, Value, ValueOrArray}, + brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlockId, MemOp}, @@ -70,9 +70,9 @@ fn inversion_brillig_oracle_equivalence() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 3 }, ], @@ -120,8 +120,7 @@ fn inversion_brillig_oracle_equivalence() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // As caller of VM, need to resolve foreign calls - let foreign_call_result = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let foreign_call_result = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); // Alter Brillig oracle opcode with foreign call resolution acvm.resolve_pending_foreign_call(foreign_call_result.into()); @@ -199,16 +198,16 @@ fn double_inversion_brillig_oracle() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 5 }, ], @@ -257,8 +256,7 @@ fn double_inversion_brillig_oracle() { acvm.get_pending_foreign_call().expect("should have a brillig foreign call request"); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let x_plus_y_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let x_plus_y_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); // Resolve Brillig foreign call acvm.resolve_pending_foreign_call(x_plus_y_inverse.into()); @@ -275,8 +273,7 @@ fn double_inversion_brillig_oracle() { acvm.get_pending_foreign_call().expect("should have a brillig foreign call request"); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let i_plus_j_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let i_plus_j_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); assert_ne!(x_plus_y_inverse, i_plus_j_inverse); // Alter Brillig oracle opcode @@ -334,16 +331,16 @@ fn oracle_dependent_execution() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 4 }, ], @@ -389,8 +386,7 @@ fn oracle_dependent_execution() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Resolve Brillig foreign call - let x_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let x_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); acvm.resolve_pending_foreign_call(x_inverse.into()); // After filling data request, continue solving @@ -406,8 +402,7 @@ fn oracle_dependent_execution() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Resolve Brillig foreign call - let y_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let y_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); acvm.resolve_pending_foreign_call(y_inverse.into()); // We've resolved all the brillig foreign calls so we should be able to complete execution now. @@ -464,9 +459,9 @@ fn brillig_oracle_predicate() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, ], predicate: Some(Expression::default()), diff --git a/acvm-repo/acvm_js/src/foreign_call/inputs.rs b/acvm-repo/acvm_js/src/foreign_call/inputs.rs index 728fc2d3d2f..ebd29fb7d58 100644 --- a/acvm-repo/acvm_js/src/foreign_call/inputs.rs +++ b/acvm-repo/acvm_js/src/foreign_call/inputs.rs @@ -8,8 +8,8 @@ pub(super) fn encode_foreign_call_inputs( let inputs = js_sys::Array::default(); for input in foreign_call_inputs { let input_array = js_sys::Array::default(); - for value in input.values() { - let hex_js_string = field_element_to_js_string(&value.to_field()); + for value in input.fields() { + let hex_js_string = field_element_to_js_string(&value); input_array.push(&hex_js_string); } inputs.push(&input_array); diff --git a/acvm-repo/acvm_js/src/foreign_call/outputs.rs b/acvm-repo/acvm_js/src/foreign_call/outputs.rs index 630b1afb6fd..78fa520aa15 100644 --- a/acvm-repo/acvm_js/src/foreign_call/outputs.rs +++ b/acvm-repo/acvm_js/src/foreign_call/outputs.rs @@ -1,18 +1,18 @@ -use acvm::brillig_vm::brillig::{ForeignCallParam, ForeignCallResult, Value}; +use acvm::brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}; use wasm_bindgen::JsValue; use crate::js_witness_map::js_value_to_field_element; fn decode_foreign_call_output(output: JsValue) -> Result { if output.is_string() { - let value = Value::from(js_value_to_field_element(output)?); + let value = js_value_to_field_element(output)?; Ok(ForeignCallParam::Single(value)) } else if output.is_array() { let output = js_sys::Array::from(&output); - let mut values: Vec = Vec::with_capacity(output.length() as usize); + let mut values: Vec<_> = Vec::with_capacity(output.length() as usize); for elem in output.iter() { - values.push(Value::from(js_value_to_field_element(elem)?)); + values.push(js_value_to_field_element(elem)?); } Ok(ForeignCallParam::Array(values)) } else { diff --git a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index fba8470585f..e074cf1ad38 100644 --- a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,13 +2,13 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, 246, 7, 38, 187, 15, 96, - 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, - 205, 200, 157, 49, 124, 227, 44, 129, 207, 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, - 160, 209, 156, 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, 254, 196, - 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, 47, 178, 186, 251, 37, 116, 86, 93, - 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, - 164, 50, 165, 0, 137, 17, 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 93, 10, 131, 48, 12, 78, 218, 233, 100, 111, 187, 193, 96, 59, 64, 231, 9, + 188, 139, 248, 166, 232, 163, 167, 23, 11, 126, 197, 24, 250, 34, 86, 208, 64, 72, 218, 252, 125, 36, 105, 153, 22, + 42, 60, 51, 116, 235, 217, 64, 103, 156, 37, 5, 191, 10, 210, 29, 163, 63, 167, 203, 229, 206, 194, 104, 110, 128, + 209, 158, 128, 49, 236, 195, 69, 231, 157, 114, 46, 73, 251, 103, 35, 239, 231, 225, 57, 243, 156, 227, 252, 132, 44, + 112, 79, 176, 125, 84, 223, 73, 248, 145, 152, 69, 149, 4, 107, 233, 114, 90, 119, 145, 85, 237, 151, 192, 89, 247, + 221, 208, 54, 163, 85, 174, 26, 234, 87, 232, 63, 101, 103, 21, 55, 169, 216, 73, 72, 249, 5, 197, 234, 132, 123, 179, + 35, 247, 155, 214, 246, 102, 20, 73, 204, 72, 168, 123, 191, 161, 25, 66, 136, 159, 187, 53, 5, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/foreign_call.ts b/acvm-repo/acvm_js/test/shared/foreign_call.ts index dd010f0c5e5..eb14cb2e9f1 100644 --- a/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, 159, 216, 31, 244, 51, 61, - 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, - 251, 164, 235, 53, 94, 218, 247, 75, 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, - 251, 208, 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 61, 10, 192, 48, 8, 133, 53, 133, 82, 186, 245, 38, 233, 13, 122, 153, + 14, 93, 58, 132, 144, 227, 135, 252, 41, 56, 36, 46, 201, 7, 162, 168, 200, 123, 34, 52, 142, 28, 72, 245, 38, 106, 9, + 247, 30, 202, 118, 142, 27, 215, 221, 178, 82, 175, 33, 15, 133, 189, 163, 159, 57, 197, 252, 251, 195, 235, 188, 230, + 186, 16, 65, 255, 12, 239, 92, 131, 89, 149, 198, 77, 3, 10, 9, 119, 8, 198, 242, 152, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/acvm-repo/brillig/src/foreign_call.rs b/acvm-repo/brillig/src/foreign_call.rs index 3f124a9a0a7..e547b99f0eb 100644 --- a/acvm-repo/brillig/src/foreign_call.rs +++ b/acvm-repo/brillig/src/foreign_call.rs @@ -1,34 +1,34 @@ -use crate::value::Value; +use acir_field::FieldElement; use serde::{Deserialize, Serialize}; /// Single output of a [foreign call][crate::Opcode::ForeignCall]. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub enum ForeignCallParam { - Single(Value), - Array(Vec), + Single(FieldElement), + Array(Vec), } -impl From for ForeignCallParam { - fn from(value: Value) -> Self { +impl From for ForeignCallParam { + fn from(value: FieldElement) -> Self { ForeignCallParam::Single(value) } } -impl From> for ForeignCallParam { - fn from(values: Vec) -> Self { +impl From> for ForeignCallParam { + fn from(values: Vec) -> Self { ForeignCallParam::Array(values) } } impl ForeignCallParam { - pub fn values(&self) -> Vec { + pub fn fields(&self) -> Vec { match self { ForeignCallParam::Single(value) => vec![*value], ForeignCallParam::Array(values) => values.clone(), } } - pub fn unwrap_value(&self) -> Value { + pub fn unwrap_field(&self) -> FieldElement { match self { ForeignCallParam::Single(value) => *value, ForeignCallParam::Array(_) => panic!("Expected single value, found array"), @@ -43,14 +43,14 @@ pub struct ForeignCallResult { pub values: Vec, } -impl From for ForeignCallResult { - fn from(value: Value) -> Self { +impl From for ForeignCallResult { + fn from(value: FieldElement) -> Self { ForeignCallResult { values: vec![value.into()] } } } -impl From> for ForeignCallResult { - fn from(values: Vec) -> Self { +impl From> for ForeignCallResult { + fn from(values: Vec) -> Self { ForeignCallResult { values: vec![values.into()] } } } diff --git a/acvm-repo/brillig/src/lib.rs b/acvm-repo/brillig/src/lib.rs index 0661e794360..40f2e15acfe 100644 --- a/acvm-repo/brillig/src/lib.rs +++ b/acvm-repo/brillig/src/lib.rs @@ -13,7 +13,6 @@ mod black_box; mod foreign_call; mod opcodes; -mod value; pub use black_box::BlackBoxOp; pub use foreign_call::{ForeignCallParam, ForeignCallResult}; @@ -21,5 +20,3 @@ pub use opcodes::{ BinaryFieldOp, BinaryIntOp, HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray, }; pub use opcodes::{BrilligOpcode as Opcode, Label}; -pub use value::Typ; -pub use value::Value; diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 22a0ebe1170..d1345351986 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -1,4 +1,5 @@ -use crate::{black_box::BlackBoxOp, Value}; +use crate::black_box::BlackBoxOp; +use acir_field::FieldElement; use serde::{Deserialize, Serialize}; pub type Label = usize; @@ -22,8 +23,8 @@ impl From for MemoryAddress { /// Describes the memory layout for an array/vector element #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum HeapValueType { - // A single field element is enough to represent the value - Simple, + // A single field element is enough to represent the value with a given bit size + Simple(u32), // The value read should be interpreted as a pointer to a heap array, which // consists of a pointer to a slice of memory of size elements, and a // reference count @@ -36,7 +37,11 @@ pub enum HeapValueType { impl HeapValueType { pub fn all_simple(types: &[HeapValueType]) -> bool { - types.iter().all(|typ| matches!(typ, HeapValueType::Simple)) + types.iter().all(|typ| matches!(typ, HeapValueType::Simple(_))) + } + + pub fn field() -> HeapValueType { + HeapValueType::Simple(FieldElement::max_num_bits()) } } @@ -131,7 +136,7 @@ pub enum BrilligOpcode { Const { destination: MemoryAddress, bit_size: u32, - value: Value, + value: FieldElement, }, Return, /// Used to get data from an outside source. diff --git a/acvm-repo/brillig/src/value.rs b/acvm-repo/brillig/src/value.rs deleted file mode 100644 index 5a532cbc1a7..00000000000 --- a/acvm-repo/brillig/src/value.rs +++ /dev/null @@ -1,103 +0,0 @@ -use acir_field::FieldElement; -use serde::{Deserialize, Serialize}; -use std::ops::{Add, Div, Mul, Neg, Sub}; - -/// Types of values allowed in the VM -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] -pub enum Typ { - Field, - Unsigned { bit_size: u32 }, - Signed { bit_size: u32 }, -} - -/// `Value` represents the base descriptor for a value in the VM. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Value { - inner: FieldElement, -} - -impl Value { - /// Returns `true` if the `Value` represents `zero` - pub fn is_zero(&self) -> bool { - self.inner.is_zero() - } - - /// Converts `Value` into a `FieldElement`. - pub fn to_field(&self) -> FieldElement { - self.inner - } - - /// Converts `Value` into a `u128`. - // TODO: Check what happens if `Value` cannot fit into a u128 - pub fn to_u128(&self) -> u128 { - self.to_field().to_u128() - } - - /// Converts `Value` into a u64 and then casts it into a usize. - /// Panics: If `Value` cannot fit into a u64 or `Value` does - //// not fit into a usize. - pub fn to_usize(&self) -> usize { - usize::try_from(self.inner.try_to_u64().expect("value does not fit into u64")) - .expect("value does not fit into usize") - } -} - -impl From for Value { - fn from(value: usize) -> Self { - Value { inner: FieldElement::from(value as u128) } - } -} - -impl From for Value { - fn from(value: u128) -> Self { - Value { inner: FieldElement::from(value) } - } -} - -impl From for Value { - fn from(value: FieldElement) -> Self { - Value { inner: value } - } -} - -impl From for Value { - fn from(value: bool) -> Self { - Value { inner: FieldElement::from(value) } - } -} - -impl Add for Value { - type Output = Value; - - fn add(self, rhs: Self) -> Self::Output { - Value { inner: self.inner + rhs.inner } - } -} -impl Sub for Value { - type Output = Value; - - fn sub(self, rhs: Self) -> Self::Output { - Value { inner: self.inner - rhs.inner } - } -} -impl Mul for Value { - type Output = Value; - - fn mul(self, rhs: Self) -> Self::Output { - Value { inner: self.inner * rhs.inner } - } -} -impl Div for Value { - type Output = Value; - - fn div(self, rhs: Self) -> Self::Output { - Value { inner: self.inner / rhs.inner } - } -} -impl Neg for Value { - type Output = Value; - - fn neg(self) -> Self::Output { - Value { inner: -self.inner } - } -} diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 1c7add5cb40..c73a923f0f8 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -17,6 +17,7 @@ acir.workspace = true acvm_blackbox_solver.workspace = true num-bigint.workspace = true num-traits.workspace = true +thiserror.workspace = true [features] default = ["bn254"] diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 81103be582d..3d77982ffb1 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -3,56 +3,98 @@ use acir::FieldElement; use num_bigint::BigUint; use num_traits::{One, ToPrimitive, Zero}; -/// Evaluate a binary operation on two FieldElements and return the result as a FieldElement. +use crate::memory::MemoryValue; + +#[derive(Debug, thiserror::Error)] +pub(crate) enum BrilligArithmeticError { + #[error("Bit size for lhs {lhs_bit_size} does not match op bit size {op_bit_size}")] + 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("Shift with bit size {op_bit_size} is invalid")] + InvalidShift { op_bit_size: u32 }, +} + +/// Evaluate a binary operation on two FieldElement memory values. pub(crate) fn evaluate_binary_field_op( op: &BinaryFieldOp, - a: FieldElement, - b: FieldElement, -) -> FieldElement { - match op { + lhs: MemoryValue, + rhs: MemoryValue, +) -> Result { + if lhs.bit_size != FieldElement::max_num_bits() { + return Err(BrilligArithmeticError::MismatchedLhsBitSize { + 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, + 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, - BinaryFieldOp::Sub => a - b, - BinaryFieldOp::Mul => a * b, - BinaryFieldOp::Div => a / b, + BinaryFieldOp::Add => (a + b).into(), + BinaryFieldOp::Sub => (a - b).into(), + BinaryFieldOp::Mul => (a * b).into(), + BinaryFieldOp::Div => (a / b).into(), BinaryFieldOp::IntegerDiv => { let a_big = BigUint::from_bytes_be(&a.to_be_bytes()); let b_big = BigUint::from_bytes_be(&b.to_be_bytes()); let result = a_big / b_big; - FieldElement::from_be_bytes_reduce(&result.to_bytes_be()) + FieldElement::from_be_bytes_reduce(&result.to_bytes_be()).into() } BinaryFieldOp::Equals => (a == b).into(), BinaryFieldOp::LessThan => (a < b).into(), BinaryFieldOp::LessThanEquals => (a <= b).into(), - } + }) } -/// Evaluate a binary operation on two unsigned big integers with a given bit size and return the result as a big integer. -pub(crate) fn evaluate_binary_bigint_op( +/// Evaluate a binary operation on two unsigned big integers with a given bit size. +pub(crate) fn evaluate_binary_int_op( op: &BinaryIntOp, - a: BigUint, - b: BigUint, + lhs: MemoryValue, + rhs: MemoryValue, bit_size: u32, -) -> Result { +) -> 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 = BigUint::from_bytes_be(&lhs.value.to_be_bytes()); + let rhs = BigUint::from_bytes_be(&rhs.value.to_be_bytes()); + let bit_modulo = &(BigUint::one() << bit_size); let result = match op { // Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size. - BinaryIntOp::Add => (a + b) % bit_modulo, - BinaryIntOp::Sub => (bit_modulo + a - b) % bit_modulo, - BinaryIntOp::Mul => (a * b) % bit_modulo, + BinaryIntOp::Add => (lhs + rhs) % bit_modulo, + BinaryIntOp::Sub => (bit_modulo + lhs - rhs) % bit_modulo, + BinaryIntOp::Mul => (lhs * rhs) % bit_modulo, // Perform unsigned division using the modulo operation on a and b. BinaryIntOp::Div => { - let b_mod = b % bit_modulo; - if b_mod.is_zero() { + if rhs.is_zero() { BigUint::zero() } else { - (a % bit_modulo) / b_mod + lhs / rhs } } // Perform a == operation, returning 0 or 1 BinaryIntOp::Equals => { - if (a % bit_modulo) == (b % bit_modulo) { + if lhs == rhs { BigUint::one() } else { BigUint::zero() @@ -60,7 +102,7 @@ pub(crate) fn evaluate_binary_bigint_op( } // Perform a < operation, returning 0 or 1 BinaryIntOp::LessThan => { - if (a % bit_modulo) < (b % bit_modulo) { + if lhs < rhs { BigUint::one() } else { BigUint::zero() @@ -68,29 +110,40 @@ pub(crate) fn evaluate_binary_bigint_op( } // Perform a <= operation, returning 0 or 1 BinaryIntOp::LessThanEquals => { - if (a % bit_modulo) <= (b % bit_modulo) { + if lhs <= rhs { BigUint::one() } else { BigUint::zero() } } // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. - BinaryIntOp::And => (a & b) % bit_modulo, - BinaryIntOp::Or => (a | b) % bit_modulo, - BinaryIntOp::Xor => (a ^ b) % bit_modulo, + BinaryIntOp::And => lhs & rhs, + BinaryIntOp::Or => lhs | rhs, + BinaryIntOp::Xor => lhs ^ rhs, BinaryIntOp::Shl => { - assert!(bit_size <= 128, "unsupported bit size for right shift"); - let b = b.to_u128().unwrap(); - (a << b) % bit_modulo + if bit_size > 128 { + return Err(BrilligArithmeticError::InvalidShift { op_bit_size: bit_size }); + } + let rhs = rhs.to_u128().unwrap(); + (lhs << rhs) % bit_modulo } BinaryIntOp::Shr => { - assert!(bit_size <= 128, "unsupported bit size for right shift"); - let b = b.to_u128().unwrap(); - (a >> b) % bit_modulo + if bit_size > 128 { + return Err(BrilligArithmeticError::InvalidShift { op_bit_size: bit_size }); + } + let rhs = rhs.to_u128().unwrap(); + lhs >> rhs } }; - Ok(result) + 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(result_as_field, bit_size), + }) } #[cfg(test)] @@ -104,12 +157,15 @@ mod tests { } fn evaluate_u128(op: &BinaryIntOp, a: u128, b: u128, bit_size: u32) -> u128 { - // Convert to big integers - let lhs_big = BigUint::from(a); - let rhs_big = BigUint::from(b); - let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size).unwrap(); + let result_value = evaluate_binary_int_op( + op, + MemoryValue::new(a.into(), bit_size), + MemoryValue::new(b.into(), bit_size), + bit_size, + ) + .unwrap(); // Convert back to u128 - result_value.to_u128().unwrap() + result_value.value.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 73b57b907f3..ab4358739e9 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -1,33 +1,33 @@ -use acir::brillig::{BlackBoxOp, HeapArray, HeapVector, Value}; +use acir::brillig::{BlackBoxOp, HeapArray, HeapVector}; use acir::{BlackBoxFunc, FieldElement}; use acvm_blackbox_solver::{ blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, keccakf1600, sha256, sha256compression, BlackBoxFunctionSolver, BlackBoxResolutionError, }; +use crate::memory::MemoryValue; use crate::Memory; -fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [Value] { - memory.read_slice(memory.read_ref(vector.pointer), memory.read(vector.size).to_usize()) +fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [MemoryValue] { + let size = memory.read(vector.size); + memory.read_slice(memory.read_ref(vector.pointer), size.to_usize()) } -fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [Value] { +fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [MemoryValue] { memory.read_slice(memory.read_ref(array.pointer), array.size) } /// Extracts the last byte of every value -fn to_u8_vec(inputs: &[Value]) -> Vec { +fn to_u8_vec(inputs: &[MemoryValue]) -> Vec { let mut result = Vec::with_capacity(inputs.len()); - for input in inputs { - let field_bytes = input.to_field().to_be_bytes(); - let byte = field_bytes.last().unwrap(); - result.push(*byte); + for &input in inputs { + result.push(input.try_into().unwrap()); } result } -fn to_value_vec(input: &[u8]) -> Vec { - input.iter().map(|x| Value::from(*x as usize)).collect() +fn to_value_vec(input: &[u8]) -> Vec { + input.iter().map(|&x| x.into()).collect() } pub(crate) fn evaluate_black_box( @@ -63,14 +63,13 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::Keccakf1600 { message, output } => { let state_vec: Vec = read_heap_vector(memory, message) .iter() - .map(|value| value.to_field().try_to_u64().unwrap()) + .map(|&memory_value| memory_value.try_into().unwrap()) .collect(); let state: [u64; 25] = state_vec.try_into().unwrap(); let new_state = keccakf1600(state)?; - let new_state: Vec = - new_state.into_iter().map(|x| Value::from(x as usize)).collect(); + let new_state: Vec = new_state.into_iter().map(|x| x.into()).collect(); memory.write_slice(memory.read_ref(output.pointer), &new_state); Ok(()) } @@ -125,8 +124,8 @@ pub(crate) fn evaluate_black_box( Ok(()) } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => { - let public_key_x = memory.read(*public_key_x).to_field(); - let public_key_y = memory.read(*public_key_y).to_field(); + let public_key_x = memory.read(*public_key_x).try_into().unwrap(); + let public_key_y = memory.read(*public_key_y).try_into().unwrap(); let message: Vec = to_u8_vec(read_heap_vector(memory, message)); let signature: Vec = to_u8_vec(read_heap_vector(memory, signature)); let verified = @@ -135,26 +134,26 @@ pub(crate) fn evaluate_black_box( Ok(()) } BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = memory.read(*low).to_field(); - let high = memory.read(*high).to_field(); + let low = memory.read(*low).try_into().unwrap(); + let high = memory.read(*high).try_into().unwrap(); let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { - let input1_x = memory.read(*input1_x).to_field(); - let input1_y = memory.read(*input1_y).to_field(); - let input2_x = memory.read(*input2_x).to_field(); - let input2_y = memory.read(*input2_y).to_field(); + let input1_x = memory.read(*input1_x).try_into().unwrap(); + let input1_y = memory.read(*input1_y).try_into().unwrap(); + let input2_x = memory.read(*input2_x).try_into().unwrap(); + let input2_y = memory.read(*input2_y).try_into().unwrap(); let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); let domain_separator: u32 = - memory.read(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), @@ -166,9 +165,9 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); let domain_separator: u32 = - memory.read(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), @@ -186,12 +185,12 @@ 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.to_field()).collect(); - let len = memory.read(*len).to_u128() as u32; + 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(); for i in result { - values.push(Value::from(i)); + values.push(i.into()); } memory.write_slice(memory.read_ref(output.pointer), &values); Ok(()) @@ -205,8 +204,8 @@ pub(crate) fn evaluate_black_box( format!("Expected 16 inputs but encountered {}", &inputs.len()), )); } - for (i, input) in inputs.iter().enumerate() { - message[i] = input.to_u128() as u32; + for (i, &input) in inputs.iter().enumerate() { + message[i] = input.try_into().unwrap(); } let mut state = [0; 8]; let values = read_heap_vector(memory, hash_values); @@ -216,12 +215,12 @@ pub(crate) fn evaluate_black_box( format!("Expected 8 values but encountered {}", &values.len()), )); } - for (i, value) in values.iter().enumerate() { - state[i] = value.to_u128() as u32; + for (i, &value) in values.iter().enumerate() { + state[i] = value.try_into().unwrap(); } sha256compression(&mut state, &message); - let state = state.map(|x| Value::from(x as u128)); + let state = state.map(|x| x.into()); memory.write_slice(memory.read_ref(output.pointer), &state); Ok(()) diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index e2a037618a4..0f430b0d5b2 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -13,23 +13,22 @@ use acir::brillig::{ BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapValueType, - HeapVector, MemoryAddress, Opcode, Value, ValueOrArray, + HeapVector, MemoryAddress, Opcode, ValueOrArray, }; use acir::FieldElement; +use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; +use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; +use black_box::evaluate_black_box; +use num_bigint::BigUint; + // Re-export `brillig`. pub use acir::brillig; +pub use memory::{Memory, MemoryValue, MEMORY_ADDRESSING_BIT_SIZE}; mod arithmetic; mod black_box; mod memory; -use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; -use arithmetic::{evaluate_binary_bigint_op, evaluate_binary_field_op}; -use black_box::evaluate_black_box; - -pub use memory::Memory; -use num_bigint::BigUint; - /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; @@ -63,7 +62,7 @@ pub enum VMStatus { /// VM encapsulates the state of the Brillig VM during execution. pub struct VM<'a, B: BlackBoxFunctionSolver> { /// Calldata to the brillig function - calldata: Vec, + calldata: Vec, /// Instruction pointer program_counter: usize, /// A counter maintained throughout a Brillig process that determines @@ -79,7 +78,7 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { /// Memory of the VM memory: Memory, /// Call stack - call_stack: Vec, + call_stack: Vec, /// The solver for blackbox functions black_box_solver: &'a B, } @@ -87,7 +86,7 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Constructs a new VM instance pub fn new( - calldata: Vec, + calldata: Vec, bytecode: &'a [Opcode], foreign_call_results: Vec, black_box_solver: &'a B, @@ -143,8 +142,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Indicating that the VM encountered a `Trap` Opcode /// or an invalid state. fn fail(&mut self, message: String) -> VMStatus { - let mut error_stack: Vec<_> = - self.call_stack.iter().map(|value| value.to_usize()).collect(); + let mut error_stack: Vec<_> = self.call_stack.clone(); error_stack.push(self.program_counter); self.status(VMStatus::Failure { call_stack: error_stack, message }); self.status.clone() @@ -159,22 +157,18 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.status.clone() } - pub fn get_memory(&self) -> &[Value] { + pub fn get_memory(&self) -> &[MemoryValue] { self.memory.values() } - pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + pub fn write_memory_at(&mut self, ptr: usize, value: MemoryValue) { self.memory.write(MemoryAddress(ptr), value); } /// Returns the VM's current call stack, including the actual program /// counter in the last position of the returned vector. pub fn get_call_stack(&self) -> Vec { - self.call_stack - .iter() - .map(|program_counter| program_counter.to_usize()) - .chain(std::iter::once(self.program_counter)) - .collect() + self.call_stack.iter().copied().chain(std::iter::once(self.program_counter)).collect() } /// Process a single opcode and modify the program counter. @@ -182,13 +176,16 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let opcode = &self.bytecode[self.program_counter]; match opcode { Opcode::BinaryFieldOp { op, lhs, rhs, destination: result } => { - self.process_binary_field_op(*op, *lhs, *rhs, *result); - self.increment_program_counter() + if let Err(error) = self.process_binary_field_op(*op, *lhs, *rhs, *result) { + self.fail(error.to_string()) + } else { + self.increment_program_counter() + } } Opcode::BinaryIntOp { op, bit_size, lhs, rhs, destination: result } => { if let Err(error) = self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result) { - self.fail(error) + self.fail(error.to_string()) } else { self.increment_program_counter() } @@ -204,26 +201,29 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // Check if condition is true // We use 0 to mean false and any other value to mean true let condition_value = self.memory.read(*condition); - if !condition_value.is_zero() { + if condition_value.try_into().expect("condition value is not a boolean") { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { let condition_value = self.memory.read(*condition); - if condition_value.is_zero() { - return self.set_program_counter(*destination); + if condition_value.try_into().expect("condition value is not a boolean") { + return self.increment_program_counter(); } - self.increment_program_counter() + self.set_program_counter(*destination) } Opcode::CalldataCopy { destination_address, size, offset } => { - let values = &self.calldata[*offset..(*offset + size)]; - self.memory.write_slice(*destination_address, values); + let values: Vec<_> = self.calldata[*offset..(*offset + size)] + .iter() + .map(|value| MemoryValue::new_field(*value)) + .collect(); + self.memory.write_slice(*destination_address, &values); self.increment_program_counter() } Opcode::Return => { if let Some(return_location) = self.call_stack.pop() { - self.set_program_counter(return_location.to_usize() + 1) + self.set_program_counter(return_location + 1) } else { self.fail("return opcode hit, but callstack already empty".to_string()) } @@ -254,81 +254,14 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { return self.wait_for_foreign_call(function.clone(), resolved_inputs); } - let values = &self.foreign_call_results[self.foreign_call_counter].values; + let write_result = self.write_foreign_call_result( + destinations, + destination_value_types, + self.foreign_call_counter, + ); - let mut invalid_foreign_call_result = false; - for ((destination, value_type), output) in - destinations.iter().zip(destination_value_types).zip(values) - { - match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple) => { - match output { - ForeignCallParam::Single(value) => { - self.memory.write(*value_index, *value); - } - _ => unreachable!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" - ), - } - } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - invalid_foreign_call_result = true; - break; - } - // Convert the destination pointer to a usize - let destination = self.memory.read_ref(*pointer_index); - // Write to our destination memory - self.memory.write_slice(destination, values); - } - _ => { - unreachable!("Function result size does not match brillig bytecode size") - } - } - } else { - unimplemented!("deflattening heap arrays from foreign calls"); - } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, Value::from(values.len())); - // Convert the destination pointer to a usize - let destination = self.memory.read_ref(*pointer_index); - // Write to our destination memory - self.memory.write_slice(destination, values); - } - _ => { - unreachable!("Function result size does not match brillig bytecode size") - } - } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); - } - } - _ => { - unreachable!("Unexpected value type {value_type:?} for destination {destination:?}"); - } - } - } - - // These checks must come after resolving the foreign call outputs as `fail` uses a mutable reference - if destinations.len() != values.len() { - self.fail(format!("{} output values were provided as a foreign call result for {} destination slots", values.len(), destinations.len())); - } - if invalid_foreign_call_result { - self.fail("Function result size does not match brillig bytecode".to_owned()); + if let Err(e) = write_result { + return self.fail(e); } self.foreign_call_counter += 1; @@ -341,10 +274,10 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::ConditionalMov { destination, source_a, source_b, condition } => { let condition_value = self.memory.read(*condition); - if condition_value.is_zero() { - self.memory.write(*destination, self.memory.read(*source_b)); - } else { + if condition_value.try_into().expect("condition value is not a boolean") { self.memory.write(*destination, self.memory.read(*source_a)); + } else { + self.memory.write(*destination, self.memory.read(*source_b)); } self.increment_program_counter() } @@ -369,11 +302,12 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::Call { location } => { // Push a return location - self.call_stack.push(Value::from(self.program_counter)); + self.call_stack.push(self.program_counter); self.set_program_counter(*location) } - Opcode::Const { destination, value, bit_size: _ } => { - self.memory.write(*destination, *value); + 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.increment_program_counter() } Opcode::BlackBox(black_box_op) => { @@ -413,15 +347,19 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { value_type: &HeapValueType, ) -> ForeignCallParam { match (input, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple) => { - self.memory.read(value_index).into() + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(_)) => { + self.memory.read(value_index).value.into() } ( ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), HeapValueType::Array { value_types, size: type_size }, ) if *type_size == size => { let start = self.memory.read_ref(pointer_index); - self.read_slice_of_values_from_memory(start, size, value_types).into() + self.read_slice_of_values_from_memory(start, size, value_types) + .into_iter() + .map(|mem_value| mem_value.value) + .collect::>() + .into() } ( ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }), @@ -429,7 +367,11 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { ) => { let start = self.memory.read_ref(pointer_index); let size = self.memory.read(size_index).to_usize(); - self.read_slice_of_values_from_memory(start, size, value_types).into() + self.read_slice_of_values_from_memory(start, size, value_types) + .into_iter() + .map(|mem_value| mem_value.value) + .collect::>() + .into() } _ => { unreachable!("Unexpected value type {value_type:?} for input {input:?}"); @@ -444,7 +386,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { start: MemoryAddress, size: usize, value_types: &[HeapValueType], - ) -> Vec { + ) -> Vec { if HeapValueType::all_simple(value_types) { self.memory.read_slice(start, size).to_vec() } else { @@ -459,9 +401,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { .flat_map(|(i, value_type)| { let value_address: MemoryAddress = (start.to_usize() + i).into(); match value_type { - HeapValueType::Simple => { - let value = self.memory.read(value_address); - vec![value] + HeapValueType::Simple(_) => { + vec![self.memory.read(value_address)] } HeapValueType::Array { value_types, size } => { let array_address = self.memory.read_ref(value_address); @@ -486,6 +427,124 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } + fn write_foreign_call_result( + &mut self, + destinations: &[ValueOrArray], + destination_value_types: &[HeapValueType], + foreign_call_index: usize, + ) -> Result<(), String> { + let values = &self.foreign_call_results[foreign_call_index].values; + + if destinations.len() != values.len() { + return Err(format!( + "{} output values were provided as a foreign call result for {} destination slots", + values.len(), + destinations.len() + )); + } + + for ((destination, value_type), output) in + destinations.iter().zip(destination_value_types).zip(values) + { + match (destination, value_type) { + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { + match output { + ForeignCallParam::Single(value) => { + let memory_value = MemoryValue::new_checked(*value, *bit_size); + if let Some(memory_value) = memory_value { + self.memory.write(*value_index, memory_value); + } else { + return Err(format!( + "Foreign call result value {} does not fit in bit size {}", + value, + bit_size + )); + } + } + _ => return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") + ), + } + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + let bit_sizes_iterator = value_types.iter().map(|typ| match typ { + HeapValueType::Simple(bit_size) => *bit_size, + _ => unreachable!("Expected simple value type"), + }).cycle(); + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + return Err("Foreign call result array doesn't match expected size".to_string()); + } + // Convert the destination pointer to a usize + let destination = self.memory.read_ref(*pointer_index); + // Write to our destination memory + let memory_values: Option> = values.iter().zip(bit_sizes_iterator).map( + |(value, bit_size)| MemoryValue::new_checked(*value, bit_size)).collect(); + if let Some(memory_values) = memory_values { + self.memory.write_slice(destination, &memory_values); + } else { + return Err(format!( + "Foreign call result values {:?} do not match expected bit sizes", + values, + )); + } + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); + } + } + } else { + unimplemented!("deflattening heap arrays from foreign calls"); + } + } + ( + ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + let bit_sizes_iterator = value_types.iter().map(|typ| match typ { + HeapValueType::Simple(bit_size) => *bit_size, + _ => unreachable!("Expected simple value type"), + }).cycle(); + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + // Convert the destination pointer to a usize + let destination = self.memory.read_ref(*pointer_index); + // Write to our destination memory + let memory_values: Option> = values.iter().zip(bit_sizes_iterator).map(|(value, bit_size)| MemoryValue::new_checked(*value, bit_size)).collect(); + if let Some(memory_values) = memory_values { + self.memory.write_slice(destination, &memory_values); + }else{ + return Err(format!( + "Foreign call result values {:?} do not match expected bit sizes", + values, + )); + } + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); + } + } + } else { + unimplemented!("deflattening heap vectors from foreign calls"); + } + } + _ => { + return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); + } + } + } + + Ok(()) + } + /// Process a binary operation. /// This method will not modify the program counter. fn process_binary_field_op( @@ -494,14 +553,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { lhs: MemoryAddress, rhs: MemoryAddress, result: MemoryAddress, - ) { + ) -> Result<(), BrilligArithmeticError> { let lhs_value = self.memory.read(lhs); let rhs_value = self.memory.read(rhs); - let result_value = - evaluate_binary_field_op(&op, lhs_value.to_field(), rhs_value.to_field()); + let result_value = evaluate_binary_field_op(&op, lhs_value, rhs_value)?; + + self.memory.write(result, result_value); - self.memory.write(result, result_value.into()); + Ok(()) } /// Process a binary operation. @@ -513,25 +573,23 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { lhs: MemoryAddress, rhs: MemoryAddress, result: MemoryAddress, - ) -> Result<(), String> { + ) -> Result<(), BrilligArithmeticError> { let lhs_value = self.memory.read(lhs); let rhs_value = self.memory.read(rhs); - // Convert to big integers - let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); - let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); - let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; - // Convert back to field element - self.memory - .write(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + let result_value = evaluate_binary_int_op(&op, lhs_value, rhs_value, bit_size)?; + self.memory.write(result, result_value); Ok(()) } /// Casts a value to a different bit size. - fn cast(&self, bit_size: u32, value: Value) -> Value { - let lhs_big = BigUint::from_bytes_be(&value.to_field().to_be_bytes()); + fn cast(&self, bit_size: u32, source_value: MemoryValue) -> MemoryValue { + let lhs_big = BigUint::from_bytes_be(&source_value.value.to_be_bytes()); let mask = BigUint::from(2_u32).pow(bit_size) - 1_u32; - FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()).into() + MemoryValue { + value: FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()), + bit_size, + } } } @@ -592,7 +650,7 @@ mod tests { #[test] fn add_single_step_smoke() { - let calldata = vec![Value::from(27u128)]; + let calldata = vec![FieldElement::from(27u128)]; // Add opcode to add the value in address `0` and `1` // and place the output in address `2` @@ -618,7 +676,7 @@ mod tests { let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value, Value::from(27u128)); + assert_eq!(output_value.value, FieldElement::from(27u128)); } #[test] @@ -627,12 +685,12 @@ mod tests { let mut opcodes = vec![]; let lhs = { - calldata.push(Value::from(2u128)); + calldata.push(2u128.into()); MemoryAddress::from(calldata.len() - 1) }; let rhs = { - calldata.push(Value::from(2u128)); + calldata.push(2u128.into()); MemoryAddress::from(calldata.len() - 1) }; @@ -643,11 +701,10 @@ mod tests { size: 2, offset: 0, }); - let equal_cmp_opcode = - Opcode::BinaryIntOp { op: BinaryIntOp::Equals, bit_size: 1, lhs, rhs, destination }; - opcodes.push(equal_cmp_opcode); + + opcodes.push(Opcode::BinaryFieldOp { destination, op: BinaryFieldOp::Equals, lhs, rhs }); opcodes.push(Opcode::Jump { location: 3 }); - opcodes.push(Opcode::JumpIf { condition: MemoryAddress::from(2), location: 4 }); + opcodes.push(Opcode::JumpIf { condition: destination, location: 4 }); let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); @@ -657,8 +714,8 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value, Value::from(true)); + let output_cmp_value = vm.memory.read(destination); + assert_eq!(output_cmp_value.value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -669,7 +726,7 @@ mod tests { #[test] fn jmpifnot_opcode() { - let calldata = vec![Value::from(1u128), Value::from(2u128)]; + let calldata = vec![1u128.into(), 2u128.into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -717,7 +774,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value, Value::from(false)); + assert_eq!(output_cmp_value.value, false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -734,12 +791,12 @@ 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::from(false)); + assert_eq!(output_value.value, false.into()); } #[test] fn cast_opcode() { - let calldata = vec![Value::from((2_u128.pow(32)) - 1)]; + let calldata = vec![((2_u128.pow(32)) - 1).into()]; let opcodes = &[ Opcode::CalldataCopy { @@ -768,12 +825,12 @@ mod tests { let VM { memory, .. } = vm; let casted_value = memory.read(MemoryAddress::from(1)); - assert_eq!(casted_value, Value::from(2_u128.pow(8) - 1)); + assert_eq!(casted_value.value, (2_u128.pow(8) - 1).into()); } #[test] fn mov_opcode() { - let calldata = vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + let calldata = vec![(1u128).into(), (2u128).into(), (3u128).into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -796,16 +853,15 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(2)); - assert_eq!(destination_value, Value::from(1u128)); + assert_eq!(destination_value.value, (1u128).into()); let source_value = memory.read(MemoryAddress::from(0)); - assert_eq!(source_value, Value::from(1u128)); + assert_eq!(source_value.value, (1u128).into()); } #[test] fn cmov_opcode() { - let calldata = - vec![Value::from(0u128), Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + let calldata = vec![(0u128).into(), (1u128).into(), (2u128).into(), (3u128).into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -813,8 +869,22 @@ mod tests { offset: 0, }; + let cast_zero = Opcode::Cast { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(0), + bit_size: 1, + }; + + let cast_one = Opcode::Cast { + destination: MemoryAddress::from(1), + source: MemoryAddress::from(1), + bit_size: 1, + }; + let opcodes = &[ calldata_copy, + cast_zero, + cast_one, Opcode::ConditionalMov { destination: MemoryAddress(4), // Sets 3_u128 to memory address 4 source_a: MemoryAddress(2), @@ -836,28 +906,30 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(4)); - assert_eq!(destination_value, Value::from(3_u128)); + assert_eq!(destination_value.value, (3_u128).into()); let source_value = memory.read(MemoryAddress::from(5)); - assert_eq!(source_value, Value::from(2_u128)); + assert_eq!(source_value.value, (2_u128).into()); } #[test] fn cmp_binary_ops() { let bit_size = 32; - let calldata = vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(6u128), - ]; + let calldata = + vec![(2u128).into(), (2u128).into(), (0u128).into(), (5u128).into(), (6u128).into()]; + let calldata_size = calldata.len(); let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -865,6 +937,14 @@ mod tests { offset: 0, }; + let cast_opcodes: Vec<_> = (0..calldata_size) + .map(|index| Opcode::Cast { + destination: MemoryAddress::from(index), + source: MemoryAddress::from(index), + bit_size, + }) + .collect(); + let equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, @@ -897,42 +977,47 @@ mod tests { destination: MemoryAddress::from(2), }; - let opcodes = [ - calldata_copy, - equal_opcode, - not_equal_opcode, - less_than_opcode, - less_than_equal_opcode, - ]; + let opcodes: Vec<_> = std::iter::once(calldata_copy) + .chain(cast_opcodes) + .chain([equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]) + .collect(); let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + // Calldata copy let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); + for _ in 0..calldata_size { + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + } + + // Equals let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let output_eq_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_eq_value, Value::from(true)); + assert_eq!(output_eq_value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let output_neq_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_neq_value, Value::from(false)); + assert_eq!(output_neq_value, false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let lt_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(lt_value, Value::from(true)); + assert_eq!(lt_value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); let lte_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(lte_value, Value::from(true)); + assert_eq!(lte_value, true.into()); } + #[test] fn store_opcode() { /// Brillig code for the following: @@ -942,8 +1027,8 @@ mod tests { /// memory[i] = i as Value; /// i += 1; /// } - fn brillig_write_memory(item_count: usize) -> Vec { - let bit_size = 32; + fn brillig_write_memory(item_count: usize) -> Vec { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_tmp = MemoryAddress::from(2); @@ -951,21 +1036,17 @@ mod tests { let start = [ // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, // len = memory.len() (approximation) - Opcode::Const { - destination: r_len, - value: Value::from(item_count as u128), - bit_size: 32, - }, + Opcode::Const { destination: r_len, value: item_count.into(), bit_size }, // pointer = free_memory_ptr - Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size }, ]; let loop_body = [ // *i = i Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1000,17 +1081,12 @@ mod tests { } let memory = brillig_write_memory(5); - let expected = vec![ - Value::from(0u128), - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - ]; + let expected = + vec![(0u64).into(), (1u64).into(), (2u64).into(), (3u64).into(), (4u64).into()]; assert_eq!(memory, expected); let memory = brillig_write_memory(1024); - let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + let expected: Vec<_> = (0..1024).map(|i: u64| i.into()).collect(); assert_eq!(memory, expected); } @@ -1024,8 +1100,8 @@ mod tests { /// sum += memory[i]; /// i += 1; /// } - fn brillig_sum_memory(memory: Vec) -> Value { - let bit_size = 32; + fn brillig_sum_memory(memory: Vec) -> FieldElement { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_sum = MemoryAddress::from(2); @@ -1034,17 +1110,17 @@ mod tests { let start = [ // sum = 0 - Opcode::Const { destination: r_sum, value: 0u128.into(), bit_size: 32 }, - // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, - // len = array.len() (approximation) Opcode::Const { - destination: r_len, - value: Value::from(memory.len() as u128), - bit_size: 32, + destination: r_sum, + value: 0u128.into(), + bit_size: FieldElement::max_num_bits(), }, + // i = 0 + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, + // len = array.len() (approximation) + Opcode::Const { destination: r_len, value: memory.len().into(), bit_size }, // pointer = array_ptr - Opcode::Const { destination: r_pointer, value: 5u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 5u128.into(), bit_size }, Opcode::CalldataCopy { destination_address: MemoryAddress(5), size: memory.len(), @@ -1055,15 +1131,14 @@ mod tests { // tmp = *i Opcode::Load { destination: r_tmp, source_pointer: r_pointer }, // sum = sum + tmp - Opcode::BinaryIntOp { + Opcode::BinaryFieldOp { destination: r_sum, lhs: r_sum, - op: BinaryIntOp::Add, + op: BinaryFieldOp::Add, rhs: r_tmp, - bit_size, }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1094,20 +1169,20 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.memory.read(r_sum) + vm.memory.read(r_sum).value } assert_eq!( brillig_sum_memory(vec![ - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - Value::from(5u128), + (1u128).into(), + (2u128).into(), + (3u128).into(), + (4u128).into(), + (5u128).into(), ]), - Value::from(15u128) + (15u128).into() ); - assert_eq!(brillig_sum_memory(vec![Value::from(1u128); 1024]), Value::from(1024u128)); + assert_eq!(brillig_sum_memory(vec![(1u128).into(); 1024]), (1024u128).into()); } #[test] @@ -1121,8 +1196,8 @@ mod tests { /// recursive_write(memory, i + 1, len); /// } /// Note we represent a 100% in-stack optimized form in brillig - fn brillig_recursive_write_memory(size: usize) -> Vec { - let bit_size = 32; + fn brillig_recursive_write_memory(size: usize) -> Vec { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_tmp = MemoryAddress::from(2); @@ -1130,15 +1205,15 @@ mod tests { let start = [ // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, // len = size - Opcode::Const { destination: r_len, value: size.into(), bit_size: 32 }, + Opcode::Const { destination: r_len, value: size.into(), bit_size }, // pointer = free_memory_ptr - Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size }, // call recursive_fn Opcode::Call { - location: 5, // Call after 'start' - }, + location: 5, // Call after 'start' + }, // end program by jumping to end Opcode::Jump { location: 100 }, ]; @@ -1160,7 +1235,7 @@ mod tests { // *i = i Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1188,23 +1263,18 @@ mod tests { } let memory = brillig_recursive_write_memory(5); - let expected = vec![ - Value::from(0u128), - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - ]; + let expected = + vec![(0u64).into(), (1u64).into(), (2u64).into(), (3u64).into(), (4u64).into()]; assert_eq!(memory, expected); let memory = brillig_recursive_write_memory(1024); - let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + let expected: Vec<_> = (0..1024).map(|i: u64| i.into()).collect(); assert_eq!(memory, expected); } /// Helper to execute brillig code fn brillig_execute_and_get_vm( - calldata: Vec, + calldata: Vec, opcodes: &[Opcode], ) -> VM<'_, DummyBlackBoxSolver> { let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); @@ -1230,14 +1300,14 @@ mod tests { let double_program = vec![ // Load input address with value 5 - Opcode::Const { destination: r_input, value: Value::from(5u128), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (5u128).into(), bit_size: 32 }, // Call foreign function "double" with the input address Opcode::ForeignCall { function: "double".into(), destinations: vec![ValueOrArray::MemoryAddress(r_result)], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::Simple(32)], inputs: vec![ValueOrArray::MemoryAddress(r_input)], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::Simple(32)], }, ]; @@ -1248,13 +1318,13 @@ mod tests { vm.status, VMStatus::ForeignCallWait { function: "double".into(), - inputs: vec![Value::from(5u128).into()] + inputs: vec![FieldElement::from(5usize).into()] } ); // Push result we're waiting for vm.resolve_foreign_call( - Value::from(10u128).into(), // Result of doubling 5u128 + FieldElement::from(10u128).into(), // Result of doubling 5u128 ); // Resume VM @@ -1265,7 +1335,7 @@ mod tests { // Check result address let result_value = vm.memory.read(r_result); - assert_eq!(result_value, Value::from(10u128)); + assert_eq!(result_value, (10u32).into()); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); @@ -1277,12 +1347,11 @@ mod tests { let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory - let initial_matrix = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let initial_matrix = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = - vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; + let expected_result: Vec = + vec![(1u128).into(), (3u128).into(), (2u128).into(), (4u128).into()]; let invert_program = vec![ Opcode::CalldataCopy { @@ -1291,9 +1360,9 @@ mod tests { offset: 0, }, // input = 0 - Opcode::Const { destination: r_input, value: 2_usize.into(), bit_size: 32 }, + Opcode::Const { destination: r_input, value: 2_usize.into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: 2_usize.into(), bit_size: 32 }, + Opcode::Const { destination: r_output, value: 2_usize.into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1303,14 +1372,14 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], input_value_types: vec![HeapValueType::Array { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], size: initial_matrix.len(), }], }, @@ -1338,7 +1407,10 @@ mod tests { // Check result in memory let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); - assert_eq!(result_values, expected_result); + assert_eq!( + result_values.into_iter().map(|mem_value| mem_value.value).collect::>(), + expected_result + ); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); @@ -1354,10 +1426,10 @@ mod tests { let r_output_size = MemoryAddress::from(3); // Our first string to use the identity function with - let input_string = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let input_string: Vec = + vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Double the string (concatenate it with itself) - let mut output_string: Vec = + let mut output_string: Vec<_> = input_string.iter().cloned().chain(input_string.clone()).collect(); // Reverse the concatenated string output_string.reverse(); @@ -1370,24 +1442,24 @@ mod tests { offset: 0, }, // input_pointer = 4 - Opcode::Const { destination: r_input_pointer, value: Value::from(4u128), bit_size: 32 }, + Opcode::Const { destination: r_input_pointer, value: (4u128).into(), bit_size: 64 }, // input_size = input_string.len() (constant here) Opcode::Const { destination: r_input_size, - value: Value::from(input_string.len()), - bit_size: 32, + value: input_string.len().into(), + bit_size: 64, }, // output_pointer = 4 + input_size Opcode::Const { destination: r_output_pointer, - value: Value::from(4 + input_string.len()), - bit_size: 32, + value: (4 + input_string.len()).into(), + bit_size: 64, }, // output_size = input_size * 2 Opcode::Const { destination: r_output_size, - value: Value::from(input_string.len() * 2), - bit_size: 32, + value: (input_string.len() * 2).into(), + bit_size: 64, }, // output_pointer[0..output_size] = string_double(input_pointer[0...input_size]) Opcode::ForeignCall { @@ -1397,14 +1469,14 @@ mod tests { size: r_output_size, })], destination_value_types: vec![HeapValueType::Vector { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_input_pointer, size: r_input_size, })], input_value_types: vec![HeapValueType::Vector { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], }, ]; @@ -1432,10 +1504,12 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm + let result_values: Vec<_> = vm .memory .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) - .to_vec(); + .iter() + .map(|mem_val| mem_val.value) + .collect(); assert_eq!(result_values, output_string); // Ensure the foreign call counter has been incremented @@ -1448,12 +1522,11 @@ mod tests { let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory - let initial_matrix = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let initial_matrix = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = - vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; + let expected_result: Vec = + vec![(1u128).into(), (3u128).into(), (2u128).into(), (4u128).into()]; let invert_program = vec![ Opcode::CalldataCopy { @@ -1462,9 +1535,9 @@ mod tests { offset: 0, }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(2u128), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (2u128).into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(6u128), bit_size: 32 }, + Opcode::Const { destination: r_output, value: (6u128).into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1474,7 +1547,7 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, @@ -1482,7 +1555,7 @@ mod tests { })], input_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], }, ]; @@ -1508,11 +1581,13 @@ 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 = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); + let initial_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(2), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values = vm.memory.read_slice(MemoryAddress(6), 4).to_vec(); + let result_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(6), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1526,23 +1601,13 @@ mod tests { let r_output = MemoryAddress::from(2); // Define a simple 2x2 matrix in memory - let matrix_a = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; - - let matrix_b = vec![ - Value::from(10u128), - Value::from(11u128), - Value::from(12u128), - Value::from(13u128), - ]; + let matrix_a = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; + + let matrix_b = vec![(10u128).into(), (11u128).into(), (12u128).into(), (13u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = vec![ - Value::from(34u128), - Value::from(37u128), - Value::from(78u128), - Value::from(85u128), - ]; + let expected_result: Vec = + vec![(34u128).into(), (37u128).into(), (78u128).into(), (85u128).into()]; let matrix_mul_program = vec![ Opcode::CalldataCopy { @@ -1551,11 +1616,11 @@ mod tests { offset: 0, }, // input = 3 - Opcode::Const { destination: r_input_a, value: Value::from(3u128), bit_size: 32 }, + Opcode::Const { destination: r_input_a, value: (3u128).into(), bit_size: 64 }, // input = 7 - Opcode::Const { destination: r_input_b, value: Value::from(7u128), bit_size: 32 }, + Opcode::Const { destination: r_input_b, value: (7u128).into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(0u128), bit_size: 32 }, + Opcode::Const { destination: r_output, value: (0u128).into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1565,7 +1630,7 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: matrix_a.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ ValueOrArray::HeapArray(HeapArray { pointer: r_input_a, size: matrix_a.len() }), @@ -1574,11 +1639,11 @@ mod tests { input_value_types: vec![ HeapValueType::Array { size: matrix_a.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }, HeapValueType::Array { size: matrix_b.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }, ], }, @@ -1606,7 +1671,8 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(MemoryAddress(0), 4).to_vec(); + let result_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(0), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1617,47 +1683,54 @@ mod tests { fn foreign_call_opcode_nested_arrays_and_slices_input() { // [(1, <2,3>, [4]), (5, <6,7,8>, [9])] - let v2 = vec![Value::from(2u128), Value::from(3u128)]; - let a4 = vec![Value::from(4u128)]; - let v6 = vec![Value::from(6u128), Value::from(7u128), Value::from(8u128)]; - let a9 = vec![Value::from(9u128)]; + let v2: Vec = vec![ + MemoryValue::from(FieldElement::from(2u128)), + MemoryValue::from(FieldElement::from(3u128)), + ]; + let a4: Vec = vec![FieldElement::from(4u128).into()]; + let v6: Vec = vec![ + MemoryValue::from(FieldElement::from(6u128)), + MemoryValue::from(FieldElement::from(7u128)), + MemoryValue::from(FieldElement::from(8u128)), + ]; + let a9: Vec = vec![FieldElement::from(9u128).into()]; // construct memory by declaring all inner arrays/vectors first - let v2_ptr = 0u128; + let v2_ptr: usize = 0usize; let mut memory = v2.clone(); let v2_start = memory.len(); - memory.extend(vec![Value::from(v2_ptr), Value::from(v2.len()), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(v2_ptr), v2.len().into(), MemoryValue::from(1_usize)]); let a4_ptr = memory.len(); memory.extend(a4.clone()); let a4_start = memory.len(); - memory.extend(vec![Value::from(a4_ptr), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(a4_ptr), MemoryValue::from(1_usize)]); let v6_ptr = memory.len(); memory.extend(v6.clone()); let v6_start = memory.len(); - memory.extend(vec![Value::from(v6_ptr), Value::from(v6.len()), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(v6_ptr), v6.len().into(), MemoryValue::from(1_usize)]); let a9_ptr = memory.len(); memory.extend(a9.clone()); let a9_start = memory.len(); - memory.extend(vec![Value::from(a9_ptr), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(a9_ptr), MemoryValue::from(1_usize)]); // finally we add the contents of the outer array let outer_ptr = memory.len(); let outer_array = vec![ - Value::from(1u128), - Value::from(v2.len()), - Value::from(v2_start), - Value::from(a4_start), - Value::from(5u128), - Value::from(v6.len()), - Value::from(v6_start), - Value::from(a9_start), + MemoryValue::from(FieldElement::from(1u128)), + MemoryValue::from(v2.len()), + MemoryValue::from(v2_start), + MemoryValue::from(a4_start), + MemoryValue::from(FieldElement::from(5u128)), + MemoryValue::from(v6.len()), + MemoryValue::from(v6_start), + MemoryValue::from(a9_start), ]; memory.extend(outer_array.clone()); - let input_array_value_types = vec![ - HeapValueType::Simple, - HeapValueType::Simple, // size of following vector - HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }, - HeapValueType::Array { value_types: vec![HeapValueType::Simple], size: 1 }, + let input_array_value_types: Vec = vec![ + HeapValueType::field(), + HeapValueType::Simple(64), // size of following vector + HeapValueType::Vector { value_types: vec![HeapValueType::field()] }, + HeapValueType::Array { value_types: vec![HeapValueType::field()], size: 1 }, ]; // memory address of the end of the above data structures @@ -1666,19 +1739,24 @@ mod tests { let r_input = MemoryAddress::from(r_ptr); let r_output = MemoryAddress::from(r_ptr + 1); - let program = vec![ - Opcode::CalldataCopy { - destination_address: MemoryAddress::from(0), - size: memory.len(), - offset: 0, - }, + let program: Vec<_> = std::iter::once(Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: memory.len(), + offset: 0, + }) + .chain(memory.iter().enumerate().map(|(index, mem_value)| Opcode::Cast { + destination: MemoryAddress(index), + source: MemoryAddress(index), + bit_size: mem_value.bit_size, + })) + .chain(vec![ // input = 0 - Opcode::Const { destination: r_input, value: Value::from(outer_ptr), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (outer_ptr).into(), bit_size: 64 }, // some_function(input) Opcode::ForeignCall { function: "flat_sum".into(), destinations: vec![ValueOrArray::MemoryAddress(r_output)], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: outer_array.len(), @@ -1688,9 +1766,13 @@ mod tests { size: outer_array.len(), }], }, - ]; + ]) + .collect(); - let mut vm = brillig_execute_and_get_vm(memory, &program); + let mut vm = brillig_execute_and_get_vm( + memory.into_iter().map(|mem_value| mem_value.value).collect(), + &program, + ); // Check that VM is waiting assert_eq!( @@ -1698,23 +1780,23 @@ mod tests { VMStatus::ForeignCallWait { function: "flat_sum".into(), inputs: vec![ForeignCallParam::Array(vec![ - Value::from(1u128), - Value::from(2u128), // size of following vector - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - Value::from(5u128), - Value::from(3u128), // size of following vector - Value::from(6u128), - Value::from(7u128), - Value::from(8u128), - Value::from(9u128), + (1u128).into(), + (2u128).into(), // size of following vector + (2u128).into(), + (3u128).into(), + (4u128).into(), + (5u128).into(), + (3u128).into(), // size of following vector + (6u128).into(), + (7u128).into(), + (8u128).into(), + (9u128).into(), ])], } ); // Push result we're waiting for - vm.resolve_foreign_call(Value::from(45u128).into()); + vm.resolve_foreign_call(FieldElement::from(45u128).into()); // Resume VM brillig_execute(&mut vm); @@ -1724,7 +1806,7 @@ mod tests { // Check result let result_value = vm.memory.read(r_output); - assert_eq!(result_value, Value::from(45u128)); + assert_eq!(result_value, MemoryValue::from(FieldElement::from(45u128))); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index d1c81447170..ae442ac2628 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,30 +1,188 @@ use acir::{brillig::MemoryAddress, FieldElement}; -use crate::Value; +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, thiserror::Error)] +pub enum MemoryTypeError { + #[error("Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}")] + MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, +} + +impl MemoryValue { + pub fn new(value: FieldElement, bit_size: u32) -> Self { + MemoryValue { value, bit_size } + } + + pub fn new_checked(value: FieldElement, bit_size: u32) -> Option { + if value.num_bits() > bit_size { + return None; + } + + Some(MemoryValue::new(value, bit_size)) + } + + pub fn new_field(value: FieldElement) -> Self { + MemoryValue { value, bit_size: FieldElement::max_num_bits() } + } + + pub fn to_usize(&self) -> usize { + assert!(self.bit_size == MEMORY_ADDRESSING_BIT_SIZE, "value is not typed as brillig usize"); + usize::try_from(self.value.try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize") + } + + 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, + 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()) + } +} + +impl Default for MemoryValue { + fn default() -> Self { + MemoryValue::new(FieldElement::zero(), 0) + } +} + +impl From for MemoryValue { + fn from(field: FieldElement) -> Self { + MemoryValue::new_field(field) + } +} + +impl From for MemoryValue { + fn from(value: usize) -> Self { + MemoryValue::new(value.into(), MEMORY_ADDRESSING_BIT_SIZE) + } +} + +impl From for MemoryValue { + fn from(value: u64) -> Self { + MemoryValue::new((value as u128).into(), 64) + } +} + +impl From for MemoryValue { + fn from(value: u32) -> Self { + MemoryValue::new((value as u128).into(), 32) + } +} + +impl From for MemoryValue { + fn from(value: u8) -> Self { + MemoryValue::new((value as u128).into(), 8) + } +} + +impl From for MemoryValue { + fn from(value: bool) -> Self { + MemoryValue::new(value.into(), 1) + } +} + +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) + } +} + +impl TryFrom for u64 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(64)?; + Ok(memory_value.value.try_to_u64().expect("value typed as u64 does not fit into u64")) + } +} + +impl TryFrom for u32 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(32)?; + let as_u64 = + memory_value.value.try_to_u64().expect("value typed as u32 does not fit into u64"); + Ok(u32::try_from(as_u64).expect("value typed as u32 does not fit into u32")) + } +} + +impl TryFrom for u8 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(8)?; + + Ok(u8::try_from( + memory_value.value.try_to_u64().expect("value typed as u8 does not fit into u64"), + ) + .expect("value typed as u8 does not fit into u8")) + } +} + +impl TryFrom for bool { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(1)?; + + if memory_value.value == FieldElement::zero() { + Ok(false) + } else if memory_value.value == FieldElement::one() { + Ok(true) + } else { + unreachable!("value typed as bool is greater than one") + } + } +} #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { // Memory is a vector of values. // We grow the memory when values past the end are set, extending with 0s. - inner: Vec, + inner: Vec, } impl Memory { /// Gets the value at pointer - pub fn read(&self, ptr: MemoryAddress) -> Value { - self.inner.get(ptr.to_usize()).copied().unwrap_or(0_u128.into()) + pub fn read(&self, ptr: MemoryAddress) -> MemoryValue { + self.inner.get(ptr.to_usize()).copied().unwrap_or_default() } pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { MemoryAddress(self.read(ptr).to_usize()) } - pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[Value] { + pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[MemoryValue] { &self.inner[addr.to_usize()..(addr.to_usize() + len)] } /// Sets the value at pointer `ptr` to `value` - pub fn write(&mut self, ptr: MemoryAddress, value: Value) { + pub fn write(&mut self, ptr: MemoryAddress, value: MemoryValue) { self.resize_to_fit(ptr.to_usize() + 1); self.inner[ptr.to_usize()] = value; } @@ -33,17 +191,17 @@ impl Memory { // Calculate new memory size let new_size = std::cmp::max(self.inner.len(), size); // Expand memory to new size with default values if needed - self.inner.resize(new_size, Value::from(FieldElement::zero())); + self.inner.resize(new_size, MemoryValue::default()); } /// Sets the values after pointer `ptr` to `values` - pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[Value]) { + 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); } /// Returns the values of the memory - pub fn values(&self) -> &[Value] { + pub fn values(&self) -> &[MemoryValue] { &self.inner } } diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index c719651e10e..5e42dccdd78 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -116,6 +116,10 @@ pub fn transform_vm_function( let create_context = create_avm_context()?; func.def.body.0.insert(0, create_context); + // Add the inputs to the params (first!) + let input = create_inputs("AvmContextInputs"); + func.def.parameters.insert(0, input); + // We want the function to be seen as a public function func.def.is_unconstrained = true; @@ -232,62 +236,62 @@ fn create_assert_initializer() -> Statement { /// ```noir /// #[aztec(private)] /// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); +/// // Create the bounded vec object +/// let mut serialized_args = BoundedVec::new(); /// /// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); +/// serialized_args.extend_from_array(structInput.serialize()); /// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// // Array inputs are iterated over and each element is added to the bounded vec (as a field) /// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); +/// serialized_args.push(arrayInput[i] as Field); /// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); +/// // Field inputs are added to the bounded vec +/// serialized_args.push({ident}); /// /// // Create the context /// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args)); /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { let mut injected_expressions: Vec = vec![]; - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to + // `let mut serialized_args = BoundedVec::new();` + let let_serialized_args = mutable_assignment( + "serialized_args", // Assigned to call( - variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path + vec![], // args ), ); - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); + // Completes: `let mut serialized_args = BoundedVec::new();` + injected_expressions.push(let_serialized_args); - // Iterate over each of the function parameters, adding to them to the hasher + // Iterate over each of the function parameters, adding to them to the bounded vec for Param { pattern, typ, span, .. } in params { match pattern { 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), + // `serialized_args.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier), UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type) + add_array_to_serialized_args(identifier, arr_type) } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident})` + UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier), + // Add the integer to the serialized args, casted to a field + // `serialized_args.push({ident} as Field)` UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) + add_cast_to_serialized_args(identifier) } UnresolvedTypeData::String(..) => { let (var_bytes, id) = str_to_bytes(identifier); injected_expressions.push(var_bytes); - add_array_to_hasher( + add_array_to_serialized_args( &id, &UnresolvedType { typ: UnresolvedTypeData::Integer( @@ -313,11 +317,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac // Create the inputs to the context let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args + // `hash_args(serialized_args)` + let hash_call = call( + variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable + vec![variable("serialized_args")], // args ); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context @@ -354,11 +357,14 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac /// // ... /// } fn create_avm_context() -> Result { + // Create the inputs to the context + let inputs_expression = variable("inputs"); + let let_context = mutable_assignment( "context", // Assigned to call( variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args + vec![inputs_expression], // args ), ); @@ -598,11 +604,11 @@ fn create_context_finish() -> Statement { } // -// Methods to create hasher inputs +// Methods to create hash_args inputs // -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher +fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the serialized args let serialized_call = method_call( variable_path(path(identifier.clone())), // variable "serialize", // method name @@ -610,9 +616,9 @@ fn add_struct_to_hasher(identifier: &Ident) -> Statement { ); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args + variable("serialized_args"), // variable + "extend_from_array", // method name + vec![serialized_call], // args ))) } @@ -632,7 +638,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { } fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // If this is an array of primitive types (integers / fields) we can add them each to the serialized args // casted to a field let span = var.span; @@ -644,7 +650,7 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { ); // What will be looped over - // - `hasher.add({ident}[i] as Field)` + // - `serialized_args.push({ident}[i] as Field)` let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); // `for i in 0..{ident}.len()` @@ -662,66 +668,66 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { })) } -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher +fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the serialized_args // casted to a field // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` + // `serialized_args.push({ident}[i] as Field)` let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { + let (add_expression, vec_method_name) = match arr_type.typ { UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); + let vec_method_name = "extend_from_array".to_owned(); let call = method_call( // All serialize on each element arr_index, // variable "serialize", // method name vec![], // args ); - (call, hasher_method_name) + (call, vec_method_name) } _ => { - let hasher_method_name = "add".to_owned(); + let vec_method_name = "push".to_owned(); let call = cast( arr_index, // lhs - `ident[i]` UnresolvedTypeData::FieldElement, // cast to - `as Field` ); - (call, hasher_method_name) + (call, vec_method_name) } }; let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name + variable("serialized_args"), // variable + &vec_method_name, // method name vec![add_expression], ))); create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) } -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` +fn add_field_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident})` let ident = variable_path(path(identifier.clone())); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args + variable("serialized_args"), // variable + "push", // method name + vec![ident], // args ))) } -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` +fn add_cast_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident} as Field)` // `{ident} as Field` let cast_operation = cast( variable_path(path(identifier.clone())), // lhs UnresolvedTypeData::FieldElement, // rhs ); - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident} as Field)` make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args + variable("serialized_args"), // variable + "push", // method name + vec![cast_operation], // args ))) } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 36e5c99a2ca..874be06b86c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,7 +1,7 @@ use acvm::acir::{brillig::BlackBoxOp, BlackBoxFunc}; use crate::brillig::brillig_ir::{ - brillig_variable::{BrilligVariable, BrilligVector}, + brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, BrilligContext, }; @@ -56,17 +56,23 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::Keccak256 => { if let ( - [message, BrilligVariable::SingleAddr(array_size)], + [message, BrilligVariable::SingleAddr(message_size)], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { let mut message_vector = convert_array_or_vector(brillig_context, message, bb_func); - message_vector.size = array_size.address; + let message_size_as_usize = + SingleAddrVariable::new_usize(brillig_context.allocate_register()); + // Message_size is not usize + brillig_context.cast_instruction(message_size_as_usize, *message_size); + + message_vector.size = message_size_as_usize.address; brillig_context.black_box_op_instruction(BlackBoxOp::Keccak256 { message: message_vector.to_heap_vector(), output: result_array.to_heap_array(), }); + brillig_context.deallocate_single_addr(message_size_as_usize); } else { unreachable!("ICE: Keccak256 expects message, message size and result array") } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 94e81d02053..cf2501ab1c0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -514,7 +514,7 @@ impl<'block> BrilligBlock<'block> { .extract_vector(); // Update the user-facing slice length - self.brillig_context.mov_instruction(target_len.address, limb_count.address); + self.brillig_context.cast_instruction(target_len, limb_count); self.brillig_context.codegen_to_radix( source, @@ -522,6 +522,7 @@ impl<'block> BrilligBlock<'block> { radix, limb_count, matches!(endianness, Endian::Big), + 8, ); } Value::Intrinsic(Intrinsic::ToBits(endianness)) => { @@ -554,7 +555,7 @@ impl<'block> BrilligBlock<'block> { let radix = self.brillig_context.make_constant_instruction(2_usize.into(), 32); // Update the user-facing slice length - self.brillig_context.mov_instruction(target_len.address, limb_count.address); + self.brillig_context.cast_instruction(target_len, limb_count); self.brillig_context.codegen_to_radix( source, @@ -562,6 +563,7 @@ impl<'block> BrilligBlock<'block> { radix, limb_count, matches!(endianness, Endian::Big), + 1, ); self.brillig_context.deallocate_single_addr(radix); @@ -654,7 +656,7 @@ impl<'block> BrilligBlock<'block> { // Create a field constant with the max let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); let right = self.brillig_context.make_constant_instruction( - FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), + FieldElement::from_be_bytes_reduce(&max.to_bytes_be()), FieldElement::max_num_bits(), ); @@ -1584,7 +1586,7 @@ impl<'block> BrilligBlock<'block> { self.variables.allocate_constant(self.brillig_context, value_id, dfg); self.brillig_context - .const_instruction(new_variable.extract_single_addr(), (*constant).into()); + .const_instruction(new_variable.extract_single_addr(), *constant); new_variable } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index dc9900daee3..892e82d771a 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -2,7 +2,10 @@ use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::brillig_ir::{ - brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, + brillig_variable::{ + get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, + SingleAddrVariable, + }, BrilligContext, }, ssa::ir::{ @@ -13,7 +16,7 @@ use crate::{ }, }; -use super::brillig_fn::{get_bit_size_from_ssa_type, FunctionContext}; +use super::brillig_fn::FunctionContext; #[derive(Debug, Default)] pub(crate) struct BlockVariables { 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 26b21e918ff..15a2a531e78 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode, Value}, + acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode}, FieldElement, }; @@ -16,18 +16,32 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // We store the result in this register too. let input = MemoryAddress::from(0); let one_const = MemoryAddress::from(1); + let zero_const = MemoryAddress::from(2); + let input_is_zero = MemoryAddress::from(3); // Location of the stop opcode - let stop_location = 3; + let stop_location = 6; GeneratedBrillig { byte_code: vec![ BrilligOpcode::CalldataCopy { destination_address: input, size: 1, offset: 0 }, + // Put value zero in register (2) + BrilligOpcode::Const { + destination: zero_const, + value: FieldElement::from(0_usize), + bit_size: FieldElement::max_num_bits(), + }, + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Equals, + lhs: input, + rhs: zero_const, + destination: input_is_zero, + }, // If the input is zero, then we jump to the stop opcode - BrilligOpcode::JumpIfNot { condition: input, location: stop_location }, + BrilligOpcode::JumpIf { condition: input_is_zero, location: stop_location }, // Put value one in register (1) BrilligOpcode::Const { destination: one_const, - value: Value::from(1_usize), + value: FieldElement::from(1_usize), bit_size: FieldElement::max_num_bits(), }, // Divide 1 by the input, and set the result of the division into register (0) @@ -53,9 +67,12 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { /// (a/b, a-a/b*b) /// } /// ``` -pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { +pub(crate) fn directive_quotient(mut 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 { @@ -63,6 +80,16 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { 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, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index 42765d10ce2..92027026ce8 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -3,8 +3,8 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::{BrilligParameter, Label}, - brillig_variable::BrilligVariable, - BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, + BrilligContext, }, ssa::ir::{ basic_block::BasicBlockId, @@ -112,15 +112,3 @@ impl FunctionContext { .collect() } } - -pub(crate) fn get_bit_size_from_ssa_type(typ: &Type) -> u32 { - match typ { - Type::Numeric(num_type) => num_type.bit_size(), - Type::Reference(_) => BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - // NB. function references are converted to a constant when - // translating from SSA to Brillig (to allow for debugger - // instrumentation to work properly) - Type::Function => 32, - _ => unreachable!("ICE bit size not on a non numeric type"), - } -} 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 98dd17ce080..e42b2787f73 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 @@ -354,7 +354,7 @@ impl<'block> BrilligBlock<'block> { mod tests { use std::vec; - use acvm::acir::brillig::Value; + use acvm::FieldElement; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; @@ -400,9 +400,9 @@ mod tests { fn test_slice_push_operation() { fn test_case_push( push_back: bool, - array: Vec, - item_to_push: Value, - expected_return: Vec, + array: Vec, + item_to_push: FieldElement, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array( @@ -462,44 +462,65 @@ mod tests { create_and_run_vm(array.into_iter().chain(vec![item_to_push]).collect(), &bytecode); assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_push( true, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], + FieldElement::from(27_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(27_usize), + ], + ); + test_case_push( + true, + vec![], + FieldElement::from(27_usize), + vec![FieldElement::from(27_usize)], ); - test_case_push(true, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); test_case_push( false, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), vec![ - Value::from(27_usize), - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + vec![ + FieldElement::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); - test_case_push(false, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); + test_case_push( + false, + vec![], + FieldElement::from(27_usize), + vec![FieldElement::from(27_usize)], + ); } #[test] fn test_slice_pop_back_operation() { fn test_case_pop( pop_back: bool, - array: Vec, - expected_return_array: Vec, - expected_return_item: Value, + array: Vec, + expected_return_array: Vec, + expected_return_item: FieldElement, ) { let arguments = vec![BrilligParameter::Array( vec![BrilligParameter::SingleAddr(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE)], @@ -566,33 +587,44 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_pop( true, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![Value::from(1_usize), Value::from(2_usize)], - Value::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + vec![FieldElement::from(1_usize), FieldElement::from(2_usize)], + FieldElement::from(3_usize), ); - test_case_pop(true, vec![Value::from(1_usize)], vec![], Value::from(1_usize)); + test_case_pop(true, vec![FieldElement::from(1_usize)], vec![], FieldElement::from(1_usize)); test_case_pop( false, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + vec![FieldElement::from(2_usize), FieldElement::from(3_usize)], + FieldElement::from(1_usize), ); } #[test] fn test_slice_insert_operation() { fn test_case_insert( - array: Vec, - item: Value, - index: Value, - expected_return: Vec, + array: Vec, + item: FieldElement, + index: FieldElement, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array( @@ -651,71 +683,90 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(1_usize), vec![ - Value::from(1_usize), - Value::from(27_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(27_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(0_usize), vec![ - Value::from(27_usize), - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(0_usize), + vec![ + FieldElement::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(2_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(27_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(2_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(27_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(3_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(27_usize), ], ); test_case_insert( vec![], - Value::from(27_usize), - Value::from(0_usize), - vec![Value::from(27_usize)], + FieldElement::from(27_usize), + FieldElement::from(0_usize), + vec![FieldElement::from(27_usize)], ); } #[test] fn test_slice_remove_operation() { fn test_case_remove( - array: Vec, - index: Value, - expected_array: Vec, - expected_removed_item: Value, + array: Vec, + index: FieldElement, + expected_array: Vec, + expected_removed_item: FieldElement, ) { let arguments = vec![ BrilligParameter::Array( @@ -784,36 +835,51 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(0_usize), - vec![Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(0_usize), + vec![FieldElement::from(2_usize), FieldElement::from(3_usize)], + FieldElement::from(1_usize), ); test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), - vec![Value::from(1_usize), Value::from(3_usize)], - Value::from(2_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(1_usize), + vec![FieldElement::from(1_usize), FieldElement::from(3_usize)], + FieldElement::from(2_usize), ); test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(2_usize), - vec![Value::from(1_usize), Value::from(2_usize)], - Value::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(2_usize), + vec![FieldElement::from(1_usize), FieldElement::from(2_usize)], + FieldElement::from(3_usize), ); test_case_remove( - vec![Value::from(1_usize)], - Value::from(0_usize), + vec![FieldElement::from(1_usize)], + FieldElement::from(0_usize), vec![], - Value::from(1_usize), + FieldElement::from(1_usize), ); } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 9138f57083a..e5c731be679 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -122,7 +122,7 @@ pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, ValueOrArray, + ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; use acvm::brillig_vm::{VMStatus, VM}; @@ -205,7 +205,7 @@ pub(crate) mod tests { } pub(crate) fn create_and_run_vm( - calldata: Vec, + calldata: Vec, bytecode: &[BrilligOpcode], ) -> (VM<'_, DummyBlackBoxSolver>, usize, usize) { let mut vm = VM::new(calldata, bytecode, vec![], &DummyBlackBoxSolver); @@ -234,20 +234,20 @@ pub(crate) mod tests { let mut context = BrilligContext::new(true); let r_stack = ReservedRegisters::free_memory_pointer(); // Start stack pointer at 0 - context.usize_const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); + context.usize_const_instruction(r_stack, FieldElement::from(ReservedRegisters::len() + 3)); let r_input_size = MemoryAddress::from(ReservedRegisters::len()); let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); - context.usize_const_instruction(r_input_size, Value::from(12_usize)); + context.usize_const_instruction(r_input_size, FieldElement::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( "make_number_sequence".into(), &[ValueOrArray::MemoryAddress(r_input_size)], - &[HeapValueType::Simple], + &[HeapValueType::Simple(32)], &[ValueOrArray::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], - &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }], + &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple(32)] }], ); // push stack frame by r_returned_size context.memory_op_instruction(r_stack, r_output_size, r_stack, BrilligBinaryOp::Add); @@ -266,8 +266,9 @@ pub(crate) mod tests { context.stop_instruction(); - let bytecode = context.artifact().finish().byte_code; - let number_sequence: Vec = (0_usize..12_usize).map(Value::from).collect(); + let bytecode: Vec = context.artifact().finish().byte_code; + let number_sequence: Vec = + (0_usize..12_usize).map(FieldElement::from).collect(); let mut vm = VM::new( vec![], &bytecode, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index b415421dd92..bbfdbb69f7c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -125,7 +125,9 @@ impl BrilligVariable { pub(crate) fn type_to_heap_value_type(typ: &Type) -> HeapValueType { match typ { - Type::Numeric(_) | Type::Reference(_) | Type::Function => HeapValueType::Simple, + Type::Numeric(_) | Type::Reference(_) | Type::Function => { + HeapValueType::Simple(get_bit_size_from_ssa_type(typ)) + } Type::Array(elem_type, size) => HeapValueType::Array { value_types: elem_type.as_ref().iter().map(type_to_heap_value_type).collect(), size: typ.element_size() * size, @@ -135,3 +137,14 @@ pub(crate) fn type_to_heap_value_type(typ: &Type) -> HeapValueType { }, } } + +pub(crate) fn get_bit_size_from_ssa_type(typ: &Type) -> u32 { + match typ { + Type::Reference(_) => BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + // NB. function references are converted to a constant when + // translating from SSA to Brillig (to allow for debugger + // instrumentation to work properly) + Type::Function => 32, + typ => typ.bit_size(), + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 248a304d820..4ef279bd532 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,4 +1,4 @@ -use acvm::acir::brillig::{MemoryAddress, Value}; +use acvm::{acir::brillig::MemoryAddress, FieldElement}; use super::{instructions::BrilligBinaryOp, BrilligContext}; @@ -21,7 +21,7 @@ impl BrilligContext { op: BrilligBinaryOp, constant: usize, ) { - let const_register = self.make_usize_constant_instruction(Value::from(constant)); + let const_register = self.make_usize_constant_instruction(FieldElement::from(constant)); self.memory_op_instruction(operand, const_register.address, destination, op); // Mark as no longer used for this purpose, frees for reuse self.deallocate_single_addr(const_register); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index be262d9dee7..ab756217bcd 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -39,6 +39,7 @@ impl BrilligContext { radix: SingleAddrVariable, limb_count: SingleAddrVariable, big_endian: bool, + limb_bit_size: u32, ) { assert!(source_field.bit_size == FieldElement::max_num_bits()); assert!(radix.bit_size == 32); @@ -55,19 +56,23 @@ impl BrilligContext { SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); self.mov_instruction(shifted_field.address, source_field.address); - let modulus_field = + let limb_field = SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + let limb_casted = SingleAddrVariable::new(self.allocate_register(), limb_bit_size); + self.codegen_loop(target_vector.size, |ctx, iterator_register| { // Compute the modulus ctx.binary_instruction( shifted_field, radix_as_field, - modulus_field, + limb_field, BrilligBinaryOp::Modulo, ); + // Cast it + ctx.cast_instruction(limb_casted, limb_field); // Write it - ctx.codegen_array_set(target_vector.pointer, iterator_register, modulus_field.address); + ctx.codegen_array_set(target_vector.pointer, iterator_register, limb_casted.address); // Integer div the field ctx.binary_instruction( shifted_field, @@ -79,7 +84,8 @@ impl BrilligContext { // Deallocate our temporary registers self.deallocate_single_addr(shifted_field); - self.deallocate_single_addr(modulus_field); + self.deallocate_single_addr(limb_field); + self.deallocate_single_addr(limb_casted); self.deallocate_single_addr(radix_as_field); if big_endian { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 5611905697c..4ca1144b6a4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -2,7 +2,10 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; -use acvm::acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, ValueOrArray}; +use acvm::{ + acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, + FieldElement, +}; /// Trait for converting values into debug-friendly strings. trait DebugToString { @@ -66,9 +69,9 @@ impl DebugToString for BrilligBinaryOp { } } -impl DebugToString for Value { +impl DebugToString for FieldElement { fn debug_to_string(&self) -> String { - self.to_usize().to_string() + self.to_string() } } @@ -167,7 +170,7 @@ impl DebugShow { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: Value) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: FieldElement) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } 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 14c4ada8606..1d823ded718 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -453,7 +453,8 @@ impl BrilligContext { #[cfg(test)] mod tests { - use acvm::brillig_vm::brillig::Value; + + use acvm::FieldElement; use crate::brillig::brillig_ir::{ artifact::BrilligParameter, @@ -464,12 +465,12 @@ mod tests { #[test] fn entry_point_with_nested_array_parameter() { let calldata = vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(4_usize), + FieldElement::from(5_usize), + FieldElement::from(6_usize), ]; let arguments = vec![BrilligParameter::Array( vec![ @@ -496,18 +497,18 @@ 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::from(1_usize)); + assert_eq!(vm.get_memory()[return_data_offset].value, FieldElement::from(1_usize)); } #[test] fn entry_point_with_nested_array_return() { let flattened_array = vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(4_usize), + FieldElement::from(5_usize), + FieldElement::from(6_usize), ]; let array_param = BrilligParameter::Array( vec![ @@ -536,7 +537,10 @@ mod tests { let memory = vm.get_memory(); assert_eq!( - memory[return_data_pointer..(return_data_pointer + flattened_array.len())], + memory[return_data_pointer..(return_data_pointer + flattened_array.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), flattened_array ); assert_eq!(return_data_size, flattened_array.len()); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 99fb4c89f64..f305eb81b01 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -1,7 +1,7 @@ use acvm::{ acir::brillig::{ BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapValueType, MemoryAddress, - Opcode as BrilligOpcode, Value, ValueOrArray, + Opcode as BrilligOpcode, ValueOrArray, }, FieldElement, }; @@ -43,7 +43,7 @@ impl BrilligContext { // Compile !x as ((-1) - x) let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - FieldElement::one(); - let max = self.make_constant(Value::from(u_max), input.bit_size); + let max = self.make_constant(u_max, input.bit_size); self.binary(max, input, result, BrilligBinaryOp::Sub); self.deallocate_single_addr(max); @@ -397,21 +397,23 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: FieldElement) { self.debug_show.const_instruction(result.address, constant); self.constant(result, constant); } - fn constant(&mut self, result: SingleAddrVariable, constant: Value) { - if result.bit_size > 128 && !constant.to_field().fits_in_u128() { - let high = Value::from(FieldElement::from_be_bytes_reduce( - constant - .to_field() - .to_be_bytes() - .get(0..16) - .expect("FieldElement::to_be_bytes() too short!"), - )); - let low = Value::from(constant.to_u128()); + fn constant(&mut self, result: SingleAddrVariable, constant: FieldElement) { + assert!( + result.bit_size >= constant.num_bits(), + "Constant {} does not fit in bit size {}", + constant, + result.bit_size + ); + if result.bit_size > 128 && !constant.fits_in_u128() { + let high = FieldElement::from_be_bytes_reduce( + constant.to_be_bytes().get(0..16).expect("FieldElement::to_be_bytes() too short!"), + ); + let low = FieldElement::from(constant.to_u128()); let high_register = SingleAddrVariable::new(self.allocate_register(), 254); let low_register = SingleAddrVariable::new(self.allocate_register(), 254); let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); @@ -419,7 +421,7 @@ impl BrilligContext { self.constant(low_register, low); // I want to multiply high by 2^128, but I can't get that big constant in. // So I'll multiply by 2^64 twice. - self.constant(intermediate_register, Value::from(1_u128 << 64)); + self.constant(intermediate_register, FieldElement::from(1_u128 << 64)); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); // Now we can add. @@ -437,14 +439,18 @@ impl BrilligContext { } } - pub(crate) fn usize_const_instruction(&mut self, result: MemoryAddress, constant: Value) { + pub(crate) fn usize_const_instruction( + &mut self, + result: MemoryAddress, + constant: FieldElement, + ) { self.const_instruction(SingleAddrVariable::new_usize(result), constant); } /// Returns a register which holds the value of a constant pub(crate) fn make_constant_instruction( &mut self, - constant: Value, + constant: FieldElement, bit_size: u32, ) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); @@ -452,7 +458,7 @@ impl BrilligContext { var } - fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { + fn make_constant(&mut self, constant: FieldElement, bit_size: u32) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); self.constant(var, constant); var @@ -461,7 +467,7 @@ impl BrilligContext { /// Returns a register which holds the value of an usize constant pub(crate) fn make_usize_constant_instruction( &mut self, - constant: Value, + constant: FieldElement, ) -> SingleAddrVariable { let register = self.allocate_register(); self.usize_const_instruction(register, constant); 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 fb11bae556c..7e951cf4e00 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 @@ -11,7 +11,7 @@ use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; -use acvm::brillig_vm::{brillig::Value, VMStatus, VM}; +use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; use acvm::{ acir::{ brillig::Opcode as BrilligOpcode, @@ -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").to_field(), + memory.next().expect("Missing return data").value, )); AcirValue::Var(var, output.clone()) } @@ -1640,7 +1640,7 @@ impl AcirContext { &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator, + memory_iter: &mut impl Iterator, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -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.to_field())); + let var = self.add_data(AcirVarData::Const(memory_value.value)); array_values.push_back(AcirValue::Var(var, element_type.clone())); } } @@ -1839,21 +1839,21 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { +fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { // Set input values - let mut calldata: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. for input in inputs { match input { BrilligInputs::Single(expr) => { - calldata.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?); } BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values for expr in expr_arr.iter() { - calldata.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?); } } BrilligInputs::MemoryArray(_) => { diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8b800e0db54..5b268de239d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -85,10 +85,11 @@ pub(super) fn simplify_call( } } Intrinsic::AsSlice => { - let slice = dfg.get_array_constant(arguments[0]); - if let Some((slice, element_type)) = slice { - let slice_length = dfg.make_constant(slice.len().into(), Type::length_type()); - let new_slice = dfg.make_array(slice, element_type); + let array = dfg.get_array_constant(arguments[0]); + if let Some((array, array_type)) = array { + let slice_length = dfg.make_constant(array.len().into(), Type::length_type()); + let inner_element_types = array_type.element_types(); + let new_slice = dfg.make_array(array, Type::Slice(inner_element_types)); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) } else { SimplifyResult::None diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index ea3f5393245..48036580d29 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -159,6 +159,13 @@ impl Type { Type::Reference(element) => element.contains_an_array(), } } + + pub(crate) fn element_types(self) -> Rc> { + match self { + Type::Array(element_types, _) | Type::Slice(element_types) => element_types, + other => panic!("element_types: Expected array or slice, found {other}"), + } + } } /// Composite Types are essentially flattened struct or tuple types. diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 642cebc83b0..6beb6929ce1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -140,6 +140,8 @@ pub enum TypeCheckError { method_name: String, span: Span, }, + #[error("Strings do not support indexed assignment")] + StringIndexAssign { span: Span }, } impl TypeCheckError { @@ -237,7 +239,8 @@ impl From for Diagnostic { | TypeCheckError::ConstrainedReferenceToUnconstrained { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantSliceLength { span } => { + | TypeCheckError::NonConstantSliceLength { span } + | TypeCheckError::StringIndexAssign { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 49ba3244dc9..69363d5f00a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -258,6 +258,11 @@ impl<'interner> TypeChecker<'interner> { Type::Array(_, elem_type) => *elem_type, Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, + Type::String(_) => { + let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); + self.errors.push(TypeCheckError::StringIndexAssign { span: lvalue_span }); + Type::Error + } other => { // TODO: Need a better span here self.errors.push(TypeCheckError::TypeMismatch { diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index d3d20654adc..b9240203a5e 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -88,7 +88,7 @@ impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { let (is_fmt_str, foreign_call_inputs) = foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?; - if is_fmt_str.unwrap_value().to_field().is_one() { + if is_fmt_str.unwrap_field().is_one() { convert_fmt_string_inputs(foreign_call_inputs) } else { convert_string_inputs(foreign_call_inputs) @@ -106,8 +106,7 @@ fn convert_string_inputs( let printable_type = fetch_printable_type(printable_type_as_values)?; // We must use a flat map here as each value in a struct will be in a separate input value - let mut input_values_as_fields = - input_values.iter().flat_map(|param| vecmap(param.values(), |value| value.to_field())); + let mut input_values_as_fields = input_values.iter().flat_map(|param| param.fields()); let value = decode_value(&mut input_values_as_fields, &printable_type); @@ -120,7 +119,7 @@ fn convert_fmt_string_inputs( let (message, input_and_printable_types) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - let message_as_fields = vecmap(message.values(), |value| value.to_field()); + let message_as_fields = message.fields(); let message_as_string = decode_string_value(&message_as_fields); let (num_values, input_and_printable_types) = input_and_printable_types @@ -128,12 +127,11 @@ fn convert_fmt_string_inputs( .ok_or(ForeignCallError::MissingForeignCallInputs)?; let mut output = Vec::new(); - let num_values = num_values.unwrap_value().to_field().to_u128() as usize; + let num_values = num_values.unwrap_field().to_u128() as usize; let types_start_at = input_and_printable_types.len() - num_values; - let mut input_iter = input_and_printable_types[0..types_start_at] - .iter() - .flat_map(|param| vecmap(param.values(), |value| value.to_field())); + let mut input_iter = + input_and_printable_types[0..types_start_at].iter().flat_map(|param| param.fields()); for printable_type in input_and_printable_types.iter().skip(types_start_at) { let printable_type = fetch_printable_type(printable_type)?; let value = decode_value(&mut input_iter, &printable_type); @@ -147,7 +145,7 @@ fn convert_fmt_string_inputs( fn fetch_printable_type( printable_type: &ForeignCallParam, ) -> Result { - let printable_type_as_fields = vecmap(printable_type.values(), |value| value.to_field()); + let printable_type_as_fields = printable_type.fields(); let printable_type_as_string = decode_string_value(&printable_type_as_fields); let printable_type: PrintableType = serde_json::from_str(&printable_type_as_string)?; diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index 4d58d96fed5..1c6b375db49 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -51,7 +51,7 @@ The built-in structure `U128` allows you to use 128-bit unsigned integers almost - You cannot cast between a native integer and `U128` - There is a higher performance cost when using `U128`, compared to a native type. -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. ```rust fn main() { diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 752b96d6591..6d5fbd44247 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -48,6 +48,15 @@ impl BoundedVec { self.len = new_len; } + pub fn extend_from_slice(&mut self, slice: [T]) { + let new_len = self.len + slice.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_slice out of bounds"); + for i in 0..slice.len() { + self.storage[self.len + i] = slice[i]; + } + self.len = new_len; + } + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { let append_len = vec.len(); let new_len = self.len + append_len; diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 0f4c2caffdf..b876bcc967b 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -97,7 +97,7 @@ pub fn modulus_be_bytes() -> [u8] {} #[builtin(modulus_le_bytes)] pub fn modulus_le_bytes() -> [u8] {} -// Convert a 32 byte array to a field element +// Convert a 32 byte array to a field element by modding pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field { // Convert it to a field element let mut v = 1; diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index b97f68fc280..0d0f9562d7b 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -7,6 +7,7 @@ fn as_slice_push(xs: [T; N]) -> [T] { slice } +// Expected that x == 0 and y == 1 fn main(x: Field, y: pub Field) { let xs: [Field; 0] = []; let ys: [Field; 1] = [1]; @@ -30,4 +31,26 @@ fn main(x: Field, y: pub Field) { assert(dynamic.as_slice()[2] == dynamic_expected[2]); assert(dynamic.as_slice()[3] == dynamic_expected[3]); assert(dynamic.as_slice().len() == 4); + + regression_4609_append_slices(x, y); + regression_4609_append_dynamic_slices(x, y); +} + +fn regression_4609_append_slices(x: Field, y: Field) { + let sl = [x, 1, 2, 3].as_slice(); + let sl2 = [y, 5, 6].as_slice(); + let sl3 = sl.append(sl2); + assert(sl3[0] == x); + assert(sl3[4] == y); +} + +fn regression_4609_append_dynamic_slices(x: Field, y: Field) { + let mut sl = [x, 1, 2, 3].as_slice(); + sl[x] = x + 10; + let mut sl2 = [y, 5, 6].as_slice(); + sl2[y] = y + 5; + let sl3 = sl.append(sl2); + assert(sl3[0] == 10); + assert(sl3[4] == y); + assert(sl3[5] == 6); } diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index a3ee89263a4..f0de8d5d1c8 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -2,7 +2,7 @@ use crate::foreign_calls::DebugForeignCallExecutor; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; use acvm::brillig_vm::brillig::ForeignCallResult; -use acvm::brillig_vm::brillig::Value; +use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; @@ -506,13 +506,13 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) } - pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { + pub(super) fn get_brillig_memory(&self) -> Option<&[MemoryValue]> { self.brillig_solver.as_ref().map(|solver| solver.get_memory()) } - pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement) { + 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, value.into()); + solver.write_memory_at(ptr, MemoryValue::new(value, bit_size)); } } @@ -667,7 +667,7 @@ mod tests { }, BrilligOpcode::Const { destination: MemoryAddress::from(1), - value: Value::from(fe_0), + value: fe_0, bit_size: 32, }, BrilligOpcode::ForeignCall { @@ -675,7 +675,7 @@ mod tests { destinations: vec![], destination_value_types: vec![], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, ], diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 25f126ff490..aae2212fd54 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -1,6 +1,7 @@ use acvm::{ - acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, + acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, + FieldElement, }; use nargo::{ artifacts::debug::{DebugArtifact, DebugVars, StackFrame}, @@ -81,11 +82,11 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { } } -fn debug_var_id(value: &Value) -> DebugVarId { +fn debug_var_id(value: &FieldElement) -> DebugVarId { DebugVarId(value.to_u128() as u32) } -fn debug_fn_id(value: &Value) -> DebugFnId { +fn debug_fn_id(value: &FieldElement) -> DebugFnId { DebugFnId(value.to_u128() as u32) } @@ -100,8 +101,8 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { let fcp_var_id = &foreign_call.inputs[0]; if let ForeignCallParam::Single(var_id_value) = fcp_var_id { let var_id = debug_var_id(var_id_value); - let values: Vec = - foreign_call.inputs[1..].iter().flat_map(|x| x.values()).collect(); + let values: Vec = + foreign_call.inputs[1..].iter().flat_map(|x| x.fields()).collect(); self.debug_vars.assign_var(var_id, &values); } Ok(ForeignCallResult::default().into()) @@ -129,12 +130,12 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { } }) .collect(); - let values: Vec = (0..n - 1 - arity) + let values: Vec = (0..n - 1 - arity) .flat_map(|i| { foreign_call .inputs .get(1 + i) - .map(|fci| fci.values()) + .map(|fci| fci.fields()) .unwrap_or_default() }) .collect(); @@ -147,7 +148,7 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { let fcp_value = &foreign_call.inputs[1]; if let ForeignCallParam::Single(var_id_value) = fcp_var_id { let var_id = debug_var_id(var_id_value); - self.debug_vars.assign_deref(var_id, &fcp_value.values()); + self.debug_vars.assign_deref(var_id, &fcp_value.fields()); } Ok(ForeignCallResult::default().into()) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 41dbf604f99..1c077c6ee9b 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -319,12 +319,12 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { return; }; - for (index, value) in memory.iter().enumerate() { - println!("{index} = {}", value.to_field()); + for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size > 0) { + println!("{index} = {}", value); } } - pub fn write_brillig_memory(&mut self, index: usize, value: String) { + pub fn write_brillig_memory(&mut self, index: usize, value: String, bit_size: u32) { let Some(field_value) = FieldElement::try_from_str(&value) else { println!("Invalid value: {value}"); return; @@ -333,7 +333,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { println!("Not executing a Brillig block"); return; } - self.context.write_brillig_memory(index, field_value); + self.context.write_brillig_memory(index, field_value, bit_size); } pub fn show_vars(&self) { @@ -513,8 +513,8 @@ pub fn run( "memset", command! { "update a Brillig memory cell with the given value", - (index: usize, value: String) => |index, value| { - ref_context.borrow_mut().write_brillig_memory(index, value); + (index: usize, value: String, bit_size: u32) => |index, value, bit_size| { + ref_context.borrow_mut().write_brillig_memory(index, value, bit_size); Ok(CommandStatus::Done) } }, diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 0e9e177e023..8e5c2bc46a4 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::Value; +use acvm::FieldElement; use noirc_errors::debug_info::{ DebugFnId, DebugFunction, DebugInfo, DebugTypeId, DebugVarId, DebugVariable, }; @@ -66,7 +66,7 @@ impl DebugVars { } } - pub fn assign_var(&mut self, var_id: DebugVarId, values: &[Value]) { + pub fn assign_var(&mut self, var_id: DebugVarId, values: &[FieldElement]) { let type_id = &self.variables.get(&var_id).unwrap().debug_type_id; let ptype = self.types.get(type_id).unwrap(); @@ -74,10 +74,10 @@ impl DebugVars { .last_mut() .expect("unexpected empty stack frames") .1 - .insert(var_id, decode_value(&mut values.iter().map(|v| v.to_field()), ptype)); + .insert(var_id, decode_value(&mut values.iter().copied(), ptype)); } - pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[Value]) { + pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[FieldElement]) { let current_frame = &mut self.frames.last_mut().expect("unexpected empty stack frames").1; let mut cursor: &mut PrintableValue = current_frame .get_mut(&var_id) @@ -147,10 +147,10 @@ impl DebugVars { } }; } - *cursor = decode_value(&mut values.iter().map(|v| v.to_field()), cursor_type); + *cursor = decode_value(&mut values.iter().copied(), cursor_type); } - pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[Value]) { + pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[FieldElement]) { unimplemented![] } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index f7f36c65c90..ea67f17af2a 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,6 +1,7 @@ use acvm::{ - acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, + acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, + FieldElement, }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; @@ -46,15 +47,15 @@ impl From for NargoForeignCallResult { } } -impl From for NargoForeignCallResult { - fn from(value: Value) -> Self { +impl From for NargoForeignCallResult { + fn from(value: FieldElement) -> Self { let foreign_call_result: ForeignCallResult = value.into(); foreign_call_result.into() } } -impl From> for NargoForeignCallResult { - fn from(values: Vec) -> Self { +impl From> for NargoForeignCallResult { + fn from(values: Vec) -> Self { let foreign_call_result: ForeignCallResult = values.into(); foreign_call_result.into() } @@ -178,7 +179,10 @@ impl DefaultForeignCallExecutor { ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { let (id, params) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - Ok((id.unwrap_value().to_usize(), params)) + let id = + usize::try_from(id.unwrap_field().try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize"); + Ok((id, params)) } fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { @@ -186,12 +190,12 @@ impl DefaultForeignCallExecutor { } fn parse_string(param: &ForeignCallParam) -> String { - let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); + let fields: Vec<_> = param.fields().to_vec(); decode_string_value(&fields) } fn execute_print(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { - let skip_newline = foreign_call_inputs[0].unwrap_value().is_zero(); + let skip_newline = foreign_call_inputs[0].unwrap_field().is_zero(); let foreign_call_inputs = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?.1; @@ -242,7 +246,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(Value::from(id).into()) + Ok(FieldElement::from(id).into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -262,11 +266,8 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } Some(ForeignCall::SetMockTimes) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; - let times = params[0] - .unwrap_value() - .to_field() - .try_to_u64() - .expect("Invalid bit size of times"); + let times = + params[0].unwrap_field().try_to_u64().expect("Invalid bit size of times"); self.find_mock_by_id(id) .unwrap_or_else(|| panic!("Unknown mock id {}", id)) @@ -325,10 +326,8 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { #[cfg(test)] mod tests { use acvm::{ - acir::brillig::ForeignCallParam, - brillig_vm::brillig::{ForeignCallResult, Value}, - pwg::ForeignCallWaitInfo, - FieldElement, + acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, + pwg::ForeignCallWaitInfo, FieldElement, }; use jsonrpc_core::Result as RpcResult; use jsonrpc_derive::rpc; @@ -356,11 +355,11 @@ mod tests { fn sum(&self, array: ForeignCallParam) -> RpcResult { let mut res: FieldElement = 0_usize.into(); - for value in array.values() { - res += value.to_field(); + for value in array.fields() { + res += value; } - Ok(Value::from(res).into()) + Ok(res.into()) } } @@ -406,7 +405,7 @@ mod tests { }; let result = executor.execute(&foreign_call); - assert_eq!(result.unwrap(), Value::from(3_usize).into()); + assert_eq!(result.unwrap(), FieldElement::from(3_usize).into()); server.close(); } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 92b8460d1db..019c5ab8c5f 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.30.1", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 3932935167d..b45678f5d8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.30.1": - version: 0.30.1 - resolution: "@aztec/bb.js@npm:0.30.1" +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: 52d0acaaf0966aa969b863adeb688df5c1abe7c8a0595bc2dca8603a8649f11eb39071aacbdc05b19f320f022b98691a41cb18a601ad84e6d82955ae6f885106 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4396,7 +4395,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.30.1 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3