From 606f59aefad504a789e7b649ec11a77de56db1da Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:27:57 +0000 Subject: [PATCH 1/6] Fix 1 , 3, 4, 5, 8 --- .../barretenberg/dsl/acir_format/README.md | 52 +++++++++---------- .../acir_format/acir_to_constraint_buf.hpp | 10 ++-- .../acir_format/arithmetic_constraints.hpp | 6 +-- .../arithmetic_constraints.test.cpp | 2 +- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md index fea6cc440d86..2c6fb13c37de 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md @@ -16,19 +16,19 @@ From now on, we focus only on the case in which the backend is barretenberg. ## `Opcode` -The following is the list of opcodes (see [`Opcode`](serde/acir.hpp#L3905)): +The following is the list of opcodes (see [`Opcode`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3224)): -- [`AssertZero`](serde/acir.hpp#L3907) -- [`MemoryInit`](serde/acir.hpp#L3971) -- [`MemoryOp`](serde/acir.hpp#L3947) -- [`BrilligCall`](serde/acir.hpp#L3998) -- [`BlackBoxFuncCall`](serde/acir.hpp#L3927) +- [`AssertZero`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3226) +- [`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) +- [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) +- [`BrilligCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3307) +- [`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) -**Note:** There is another opcode: [`Call`](serde/acir.hpp#L4028), which was meant to be used to expose folding to Noir. We are not supporting this functionality, so barretenberg fails the reconstruction of a serialized Noir program if it encounters a `Call` opcode. +**Note:** There is another opcode: [`Call`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3336), which was meant to be used to expose folding to Noir. We are not supporting this functionality, so barretenberg fails the reconstruction of a serialized Noir program if it encounters a `Call` opcode. -### `AssertZero` +### [`AssertZero`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3226) -`AssertZero` opcodes represent expressions of the following form: +[`AssertZero`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3226) opcodes represent expressions of the following form: $$ \sum_{i, j} c_{i,j} \cdot w_i w_j + \sum_i c_i \cdot w_i + c = 0 @@ -36,40 +36,40 @@ $$ where $w_i, w_j$ are witnesses and $c_{i,j}, c_j, c$ are constants. -### `MemoryInit` +### [`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) -`MemoryInit` opcodes represent the initialization of a memory table. This can be either a `ROM` table, a `RAM` table, a `Calldata` databus column, or a `Returndata` databus column. +[`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) opcodes represent the initialization of a memory table. This can be either a `ROM` table, a `RAM` table, a `Calldata` databus column, or a `Returndata` databus column. -A `MemoryInit` opcode contains a list of witness indices representing the indices of the data with which to initialize the table. +A [`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) opcode contains a list of witness indices representing the indices of the data with which to initialize the table. -### `MemoryOp` +### [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) -`MemoryOp` opcodes represent operations on a memory table. `ROM` and `Calldata` tables only support read operations, `RAM` supports both read and write operations, `Returndata` doesn't support any type of operation. +[`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) opcodes represent operations on a memory table. `ROM` and `Calldata` tables only support read operations, `RAM` supports both read and write operations, `Returndata` doesn't support any type of operation. -A `MemoryOp` opcode contains the type of the operation, the index of the element of the table on which to perform the operation, and the value to be read or written. +A [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) opcode contains the type of the operation, the index of the element of the table on which to perform the operation, and the value to be read or written. -**Note:** `MemoryOp` use [`Acir::Expression`](serde/acir.hpp#L3565)s to represent the operation type, the index, and the value. Barretenberg enforces that the expressions encode the data type they are supposed to represent. For example, the type of the operation is supposed to be represented by and expression with no multiplication terms, no linear terms, and with a constant term equal to either one or zero. When converting the expression to a memory operation type, barretenberg asserts that these assumptions are satisfied. +**Note:** [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) use [`Acir::Expression`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L2959)s to represent the operation type, the index, and the value. Barretenberg enforces that the expressions encode the data type they are supposed to represent. For example, the type of the operation is supposed to be represented by and expression with no multiplication terms, no linear terms, and with a constant term equal to either one or zero. When converting the expression to a memory operation type, barretenberg asserts that these assumptions are satisfied. -### `BrilligCall` +### [`BrilligCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3307) -`BrilligCall` opcodes are no-ops in barretenberg. They are unconstrained functions in Noir that add witnesses without adding constraints for them. +[`BrilligCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3307) opcodes are no-ops in barretenberg. They are unconstrained functions in Noir that add witnesses without adding constraints for them. -### `BlackBoxFunctionCall` +### [`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) -`BlackBoxFunctionCall` opcodes represent calls from Noir to functions that are implemented in barretenberg. An example is recursive verification: to perform recursive verification from Noir, we call `std::verify_with_type`, which adds a `BlackBoxFunctionCall` opcode for recursive verification. Then, when barretenberg parses this opcode, it adds the constraints for recursive verification using the witness indices passed by Noir. +[`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) opcodes represent calls from Noir to functions that are implemented in barretenberg. An example is recursive verification: to perform recursive verification from Noir, we call `std::verify_with_type`, which adds a [`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) opcode for recursive verification. Then, when barretenberg parses this opcode, it adds the constraints for recursive verification using the witness indices passed by Noir. ## Bytes to `Builder` -The conversion from a buffer of bytes to a `Builder` object happens in two steps. The buffer of bytes is first converted into an instance of the [`AcirFormat`](acir_format.hpp#L69) struct. Then, this struct is used to construct a `Builder`. +The conversion from a buffer of bytes to a `Builder` object happens in two steps. The buffer of bytes is first converted into an instance of the [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) struct. Then, this struct is used to construct a `Builder`. ### Bytes to `AcirFormat` -Instances of the [`AcirFormat`](acir_format.hpp#L69) struct contain a record of all the constraints written in Noir. Barretenberg's role is to take this record and construct a builder out of it. The `Builder` object can then be used to generate a Honk proof, a verification key, or accumulated during the generation of a Chonk proof. +Instances of the [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) struct contain a record of all the constraints written in Noir. Barretenberg's role is to take this record and construct a builder out of it. The `Builder` object can then be used to generate a Honk proof, a verification key, or accumulated during the generation of a Chonk proof. -The single entrypoint for the conversion from a buffer of bytes into an instance of `AcirFormat` is the function [`circuit_buf_to_acir_format`](acir_to_constraint_buf.cpp#L166). This function deserializes the buffer according to the msgpack serialization format. The result of the deserialization is an instance of the [`Acir::Circuit`](serde/acir.hpp#L4502) struct, which the opcodes representing the Noir program. +The single entrypoint for the conversion from a buffer of bytes into an instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) is the function [`circuit_buf_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L109). This function deserializes the buffer according to the msgpack serialization format. The result of the deserialization is an instance of the [`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705) struct, which the opcodes representing the Noir program. -The `Acir::Circuit` is passed to [`circuit_serde_to_acir_format`](acir_to_constraint_buf.cpp#L115), which processes all of the opcodes and adds them to an instance of `AcirFormat`. This step is simply converting one representation (`Acir::Circuit`) into another (`AcirFormat`). +The [`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705) is passed to [`circuit_serde_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L96), which processes all of the opcodes and adds them to an instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82). This step is simply converting one representation ([`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705)) into another ([`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82)). -### `AcirFormat` to `Builder` +### [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) to `Builder` -The instance of [`AcirFormat`](acir_format.hpp#L69) is then passed to the function [`create_circuit`](acir_format.cpp#L640), which constructs a `Builder` object with all the required constraints. This step is where barretenberg's handling of opcodes comes into play: the gates added to the builder depend on the internals of barretenberg and they would be different if we used a different backend. +The instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) is then passed to the function [`create_circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L156), which constructs a `Builder` object with all the required constraints. This step is where barretenberg's handling of opcodes comes into play: the gates added to the builder depend on the internals of barretenberg and they would be different if we used a different backend. diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index 7fad1bb7dc1f..fb871c8ce234 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -98,7 +98,7 @@ AcirFormat circuit_serde_to_acir_format(Acir::Circuit const& circuit); /** * @brief Convert from the ACIR-native `WitnessMap` format to Barretenberg's internal `WitnessVector` format. * - * @note This transformation results in all unassigned witnesses within the `WitnessMap` being assigned the value 0. + * @note This transformation results in all unassigned witnesses within the `WitnessMap` being assigned a random value. * Converting the `WitnessVector` back to a `WitnessMap` is unlikely to return the exact same `WitnessMap`. */ WitnessVector witness_map_to_witness_vector(Witnesses::WitnessMap const& witness_map); @@ -156,9 +156,9 @@ bool is_single_arithmetic_gate(Acir::Expression const& arg, const std::map void create_quad_constraint(Builder& builder, QuadCo * arithmetic gate as it contains 2 multiplications terms (and also because it contains 7 distinct witnesses). We turn this expression into * the following series of gates (where w4_shift is toggled on in all gates but the last one): * - * | a_idx | b_idx | c_idx | d_idx | mul_scaling | a_scaling | b_scaling | c_scaling | d_scaling | const_idx | + * | a_idx | b_idx | c_idx | d_idx | mul_scaling | a_scaling | b_scaling | c_scaling | d_scaling | const | * |-------|-------|-------|------------------------------|-------------|-----------|-----------|-----------|-----------|-------------| - * | w1 | w2 | w5 | w6 | 1 | 1 | 1 | 1 | 1 | const | - * | w3 | w4 | w7 | -(w1 * w2 + w5 + w6 + const) | 1 | 1 | 1 | 1 | -1 | IS_CONSTANT | + * | w1 | w2 | w5 | w6 | 1 | 0 | 0 | 1 | 1 | const | + * | w3 | w4 | w7 | -(w1 * w2 + w5 + w6 + const) | 1 | 1 | 1 | 1 | -1 | 0 | * * If we didn't have the option of using w4_shift, we would have needed a third gate to accomodate the expression. Note that we * don't know the witness index of the witness -(w1 * w2 + w5 + w6 + const) when we split the expression into multiple gates. diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/arithmetic_constraints.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/arithmetic_constraints.test.cpp index c8374a0894e0..a35451b373ea 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/arithmetic_constraints.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/arithmetic_constraints.test.cpp @@ -341,7 +341,7 @@ using QuadConstraintConfigs = testing::Types< ArithmeticConstraintParams, // Maximal case in one gate ArithmeticConstraintParams, // Maximal case in one gate ArithmeticConstraintParams, - ArithmeticConstraintParams, // Maximal case in one gate + ArithmeticConstraintParams, // Maximal case in one gate ArithmeticConstraintParams, ArithmeticConstraintParams, ArithmeticConstraintParams, From d97a4602cbe6d7fcec4ea78ee15a3cf931e37742 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:39:27 +0000 Subject: [PATCH 2/6] Fix 6 --- .../acir_format/acir_to_constraint_buf.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 562e22016322..3a472ac73af9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -363,20 +363,20 @@ AcirFormat circuit_serde_to_acir_format(Acir::Circuit const& circuit) AcirFormat circuit_buf_to_acir_format(std::vector&& buf) { // We need to deserialize into Acir::Program first because the buffer returned by Noir has this structure - auto program = deserialize_msgpack_compact(std::move(buf), [](auto o) -> Acir::Program { - Acir::Program program; - try { - // Deserialize into a partial structure that ignores the Brillig parts, - // so that new opcodes can be added without breaking Barretenberg. + auto program = deserialize_msgpack_compact( + std::move(buf), [](auto o) -> Acir::ProgramWithoutBrillig { Acir::ProgramWithoutBrillig program_wob; - o.convert(program_wob); - program.functions = program_wob.functions; - } catch (const msgpack::type_error&) { - std::cerr << o << std::endl; - bb::assert_failure("acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program"); - } - return program; - }); + try { + // Deserialize into a partial structure that ignores the Brillig parts, + // so that new opcodes can be added without breaking Barretenberg. + o.convert(program_wob); + } catch (const msgpack::type_error&) { + std::cerr << o << std::endl; + bb::assert_failure( + "acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program"); + } + return program_wob; + }); BB_ASSERT_EQ(program.functions.size(), 1U, "circuit_buf_to_acir_format: expected single function in ACIR program"); return circuit_serde_to_acir_format(program.functions[0]); From 897bcf7f0e37f5b25c1413f4b6a4b71d104099bb Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:01:04 +0000 Subject: [PATCH 3/6] Fix 2 --- .../acir_format/acir_to_constraint_buf.cpp | 711 +++++++++--------- 1 file changed, 370 insertions(+), 341 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 3a472ac73af9..34d9aa902146 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -29,6 +29,10 @@ using namespace bb; /// ========= HELPERS ========= /// +template struct overloaded : Ts... { + using Ts::operator()...; +}; + bb::fr from_buffer_with_bound_checks(const std::vector& buffer) { BB_ASSERT_EQ(buffer.size(), 32U, "acir_format::from_buffer_with_bound_checks: buffer size must be 32 bytes."); @@ -37,27 +41,21 @@ bb::fr from_buffer_with_bound_checks(const std::vector& buffer) WitnessOrConstant parse_input(const Acir::FunctionInput& input) { - WitnessOrConstant result = std::visit( - [&](auto&& e) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return WitnessOrConstant{ - .index = e.value.value, - .value = bb::fr::zero(), - .is_constant = false, - }; - } else if constexpr (std::is_same_v) { - return WitnessOrConstant{ - .index = bb::stdlib::IS_CONSTANT, - .value = from_buffer_with_bound_checks(e.value), - .is_constant = true, - }; - } else { - bb::assert_failure("acir_format::parse_input: unrecognized Acir::FunctionInput variant. An error here " - "means there was a serialization error."); - } - }, - input.value); + WitnessOrConstant result = std::visit(overloaded{ [&](const Acir::FunctionInput::Witness& e) { + return WitnessOrConstant{ + .index = e.value.value, + .value = bb::fr::zero(), + .is_constant = false, + }; + }, + [&](const Acir::FunctionInput::Constant& e) { + return WitnessOrConstant{ + .index = bb::stdlib::IS_CONSTANT, + .value = from_buffer_with_bound_checks(e.value), + .is_constant = true, + }; + } }, + input.value); return result; } @@ -104,166 +102,195 @@ void update_max_witness_index_from_opcode(Acir::Opcode const& opcode, AcirFormat }; std::visit( - [&](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - update_max_witness_index_from_expression(arg.value, af); - } else if constexpr (std::is_same_v) { - std::visit( - [&](auto&& bb_arg) { - using BBT = std::decay_t; - if constexpr (std::is_same_v || - std::is_same_v) { - update_max_witness_index_from_function_input(bb_arg.lhs); - update_max_witness_index_from_function_input(bb_arg.rhs); - update_max_witness_index_from_witness(bb_arg.output); - } else if constexpr (std::is_same_v) { - update_max_witness_index_from_function_input(bb_arg.input); - } else if constexpr (std::is_same_v) { - for (const auto& input : bb_arg.inputs) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.iv) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.key) { - update_max_witness_index_from_function_input(input); - } - for (const auto& output : bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v) { - for (const auto& input : *bb_arg.inputs) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.hash_values) { - update_max_witness_index_from_function_input(input); - } - for (const auto& output : *bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto& input : bb_arg.inputs) { - update_max_witness_index_from_function_input(input); - } - for (const auto& output : *bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto& input : *bb_arg.public_key_x) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.public_key_y) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.signature) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.hashed_message) { - update_max_witness_index_from_function_input(input); - } - update_max_witness_index_from_function_input(bb_arg.predicate); - update_max_witness_index_from_witness(bb_arg.output); - } else if constexpr (std::is_same_v) { - for (const auto& input : bb_arg.points) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : bb_arg.scalars) { - update_max_witness_index_from_function_input(input); - } - update_max_witness_index_from_function_input(bb_arg.predicate); - for (const auto& output : *bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v) { - for (const auto& input : *bb_arg.input1) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : *bb_arg.input2) { - update_max_witness_index_from_function_input(input); - } - update_max_witness_index_from_function_input(bb_arg.predicate); - for (const auto& output : *bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v) { - for (const auto& input : *bb_arg.inputs) { - update_max_witness_index_from_function_input(input); - } - for (const auto& output : *bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } else if constexpr (std::is_same_v) { - for (const auto& input : bb_arg.verification_key) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : bb_arg.proof) { - update_max_witness_index_from_function_input(input); - } - for (const auto& input : bb_arg.public_inputs) { - update_max_witness_index_from_function_input(input); - } - update_max_witness_index_from_function_input(bb_arg.key_hash); - update_max_witness_index_from_function_input(bb_arg.predicate); - } else if constexpr (std::is_same_v) { - for (const auto& input : bb_arg.inputs) { - update_max_witness_index_from_function_input(input); - } - for (const auto& output : bb_arg.outputs) { - update_max_witness_index_from_witness(output); - } - } - }, - arg.value.value); - } else if constexpr (std::is_same_v) { + overloaded{ + [&](const Acir::Opcode::AssertZero& arg) { update_max_witness_index_from_expression(arg.value, af); }, + [&](const Acir::Opcode::BlackBoxFuncCall& arg) { + std::visit(overloaded{ [&](const Acir::BlackBoxFuncCall::AND& bb_arg) { + update_max_witness_index_from_function_input(bb_arg.lhs); + update_max_witness_index_from_function_input(bb_arg.rhs); + update_max_witness_index_from_witness(bb_arg.output); + }, + [&](const Acir::BlackBoxFuncCall::XOR& bb_arg) { + update_max_witness_index_from_function_input(bb_arg.lhs); + update_max_witness_index_from_function_input(bb_arg.rhs); + update_max_witness_index_from_witness(bb_arg.output); + }, + [&](const Acir::BlackBoxFuncCall::RANGE& bb_arg) { + update_max_witness_index_from_function_input(bb_arg.input); + }, + [&](const Acir::BlackBoxFuncCall::AES128Encrypt& bb_arg) { + for (const auto& input : bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.iv) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.key) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::Sha256Compression& bb_arg) { + for (const auto& input : *bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.hash_values) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::Blake2s& bb_arg) { + for (const auto& input : bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::Blake3& bb_arg) { + for (const auto& input : bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::EcdsaSecp256k1& bb_arg) { + for (const auto& input : *bb_arg.public_key_x) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.public_key_y) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.signature) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.hashed_message) { + update_max_witness_index_from_function_input(input); + } + update_max_witness_index_from_function_input(bb_arg.predicate); + update_max_witness_index_from_witness(bb_arg.output); + }, + [&](const Acir::BlackBoxFuncCall::EcdsaSecp256r1& bb_arg) { + for (const auto& input : *bb_arg.public_key_x) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.public_key_y) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.signature) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.hashed_message) { + update_max_witness_index_from_function_input(input); + } + update_max_witness_index_from_function_input(bb_arg.predicate); + update_max_witness_index_from_witness(bb_arg.output); + }, + [&](const Acir::BlackBoxFuncCall::MultiScalarMul& bb_arg) { + for (const auto& input : bb_arg.points) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : bb_arg.scalars) { + update_max_witness_index_from_function_input(input); + } + update_max_witness_index_from_function_input(bb_arg.predicate); + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::EmbeddedCurveAdd& bb_arg) { + for (const auto& input : *bb_arg.input1) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : *bb_arg.input2) { + update_max_witness_index_from_function_input(input); + } + update_max_witness_index_from_function_input(bb_arg.predicate); + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::Keccakf1600& bb_arg) { + for (const auto& input : *bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : *bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + }, + [&](const Acir::BlackBoxFuncCall::RecursiveAggregation& bb_arg) { + for (const auto& input : bb_arg.verification_key) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : bb_arg.proof) { + update_max_witness_index_from_function_input(input); + } + for (const auto& input : bb_arg.public_inputs) { + update_max_witness_index_from_function_input(input); + } + update_max_witness_index_from_function_input(bb_arg.key_hash); + update_max_witness_index_from_function_input(bb_arg.predicate); + }, + [&](const Acir::BlackBoxFuncCall::Poseidon2Permutation& bb_arg) { + for (const auto& input : bb_arg.inputs) { + update_max_witness_index_from_function_input(input); + } + for (const auto& output : bb_arg.outputs) { + update_max_witness_index_from_witness(output); + } + } }, + arg.value.value); + }, + [&](const Acir::Opcode::MemoryInit& arg) { for (const auto& init : arg.init) { update_max_witness_index_from_witness(init); } - } else if constexpr (std::is_same_v) { + }, + [&](const Acir::Opcode::MemoryOp& arg) { update_max_witness_index_from_expression(arg.op.index, af); update_max_witness_index_from_expression(arg.op.value, af); update_max_witness_index_from_expression(arg.op.operation, af); - } else if constexpr (std::is_same_v) { - // Process inputs + }, + [&](const Acir::Opcode::BrilligCall& arg) { for (const auto& input : arg.inputs) { - std::visit( - [&](auto&& e) { - using IT = std::decay_t; - if constexpr (std::is_same_v) { - update_max_witness_index_from_expression(e.value, af); - } else if constexpr (std::is_same_v) { - for (const auto& expr : e.value) { - update_max_witness_index_from_expression(expr, af); - } - } - // MemoryArray contains a BlockId, no direct witnesses to track - }, - input.value); + std::visit(overloaded{ + [&](const Acir::BrilligInputs::Single& e) { + update_max_witness_index_from_expression(e.value, af); + }, + [&](const Acir::BrilligInputs::Array& e) { + for (const auto& expr : e.value) { + update_max_witness_index_from_expression(expr, af); + } + }, + [&](const Acir::BrilligInputs::MemoryArray&) { + // MemoryArray does not contain witnesses directly, so nothing to do here. + }, + }, + input.value); } - // Process outputs for (const auto& output : arg.outputs) { - std::visit( - [&](auto&& e) { - using OT = std::decay_t; - if constexpr (std::is_same_v) { - update_max_witness_index_from_witness(e.value); - } else if constexpr (std::is_same_v) { - for (const auto& witness : e.value) { - update_max_witness_index_from_witness(witness); - } - } - }, - output.value); + std::visit(overloaded{ + [&](const Acir::BrilligOutputs::Simple& e) { + update_max_witness_index_from_witness(e.value); + }, + [&](const Acir::BrilligOutputs::Array& e) { + for (const auto& witness : e.value) { + update_max_witness_index_from_witness(witness); + } + }, + }, + output.value); } - // Process optional predicate if (arg.predicate.has_value()) { update_max_witness_index_from_expression(arg.predicate.value(), af); } - } else { - bb::assert_failure("acir_format::update_max_witness_index_from_opcode: Unrecognized opcode."); - } + }, + [&](const Acir::Opcode::Call&) { bb::assert_failure("Call opcode is not supported."); }, }, opcode.value); } @@ -325,29 +352,24 @@ AcirFormat circuit_serde_to_acir_format(Acir::Circuit const& circuit) const auto& gate = circuit.opcodes[i]; update_max_witness_index_from_opcode(gate, af); std::visit( - [&](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - assert_zero_to_quad_constraints(arg, af, i); - } else if constexpr (std::is_same_v) { - add_blackbox_func_call_to_acir_format(arg, af, i); - } else if constexpr (std::is_same_v) { + overloaded{ + [&](const Acir::Opcode::AssertZero& arg) { assert_zero_to_quad_constraints(arg, af, i); }, + [&](const Acir::Opcode::BlackBoxFuncCall& arg) { add_blackbox_func_call_to_acir_format(arg, af, i); }, + [&](const Acir::Opcode::MemoryInit& arg) { auto block = memory_init_to_block_constraint(arg); uint32_t block_id = arg.block_id.value; block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } }; - } else if constexpr (std::is_same_v) { + }, + [&](const Acir::Opcode::MemoryOp& arg) { auto block = block_id_to_block_constraint.find(arg.block_id.value); if (block == block_id_to_block_constraint.end()) { bb::assert_failure("acir_format::circuit_serder_to_acir_format: unitialized MemoryOp."); } add_memory_op_to_block_constraint(arg, block->second.first); block->second.second.push_back(i); - } else if constexpr (std::is_same_v) { - // This is a no-op in barretenberg - } else { - bb::assert_failure("acir_format::circuit_serde_to_acir_format: Unrecognized Acir Opcode. An error " - "here means there was a serialization error."); - } + }, + [&](const Acir::Opcode::BrilligCall&) {}, + [&](const Acir::Opcode::Call&) { bb::assert_failure("Call opcode is not supported."); }, }, gate.value); } @@ -579,165 +601,172 @@ void add_blackbox_func_call_to_acir_format(Acir::Opcode::BlackBoxFuncCall const& auto to_witness_from_input = [&](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); }; std::visit( - [&](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - af.logic_constraints.push_back(LogicConstraint{ - .a = parse_input(arg.lhs), - .b = parse_input(arg.rhs), - .result = to_witness(arg.output), - .num_bits = arg.num_bits, - .is_xor_gate = false, - }); - af.original_opcode_indices.logic_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.logic_constraints.push_back(LogicConstraint{ - .a = parse_input(arg.lhs), - .b = parse_input(arg.rhs), - .result = to_witness(arg.output), - .num_bits = arg.num_bits, - .is_xor_gate = true, - }); - af.original_opcode_indices.logic_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.range_constraints.push_back(RangeConstraint{ - .witness = get_witness_from_function_input(arg.input), - .num_bits = arg.num_bits, - }); - af.original_opcode_indices.range_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.aes128_constraints.push_back(AES128Constraint{ - .inputs = transform::map(arg.inputs, to_witness_or_constant), - .iv = transform::map(*arg.iv, to_witness_or_constant), - .key = transform::map(*arg.key, to_witness_or_constant), - .outputs = transform::map(arg.outputs, to_witness), - }); - af.original_opcode_indices.aes128_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.sha256_compression.push_back(Sha256Compression{ - .inputs = transform::map(*arg.inputs, to_witness_or_constant), - .hash_values = transform::map(*arg.hash_values, to_witness_or_constant), - .result = transform::map(*arg.outputs, to_witness), - }); - af.original_opcode_indices.sha256_compression.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.blake2s_constraints.push_back(Blake2sConstraint{ - .inputs = transform::map(arg.inputs, to_witness_or_constant), - .result = transform::map(*arg.outputs, to_witness), - }); - af.original_opcode_indices.blake2s_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.blake3_constraints.push_back(Blake3Constraint{ - .inputs = transform::map(arg.inputs, to_witness_or_constant), - .result = transform::map(*arg.outputs, to_witness), - }); - af.original_opcode_indices.blake3_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.ecdsa_k1_constraints.push_back(EcdsaConstraint{ - .type = bb::CurveType::SECP256K1, - .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input), - .signature = transform::map(*arg.signature, to_witness_from_input), - .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input), - .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input), - .predicate = parse_input(arg.predicate), - .result = to_witness(arg.output), - }); - af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.ecdsa_r1_constraints.push_back(EcdsaConstraint{ - .type = bb::CurveType::SECP256R1, - .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input), - .signature = transform::map(*arg.signature, to_witness_from_input), - .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input), - .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input), - .predicate = parse_input(arg.predicate), - .result = to_witness(arg.output), - }); - af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.multi_scalar_mul_constraints.push_back(MultiScalarMul{ - .points = transform::map(arg.points, to_witness_or_constant), - .scalars = transform::map(arg.scalars, to_witness_or_constant), - .predicate = parse_input(arg.predicate), - .out_point_x = to_witness((*arg.outputs)[0]), - .out_point_y = to_witness((*arg.outputs)[1]), - .out_point_is_infinite = to_witness((*arg.outputs)[2]), - }); - af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.ec_add_constraints.push_back(EcAdd{ - .input1_x = parse_input((*arg.input1)[0]), - .input1_y = parse_input((*arg.input1)[1]), - .input1_infinite = parse_input((*arg.input1)[2]), - .input2_x = parse_input((*arg.input2)[0]), - .input2_y = parse_input((*arg.input2)[1]), - .input2_infinite = parse_input((*arg.input2)[2]), - .predicate = parse_input(arg.predicate), - .result_x = to_witness((*arg.outputs)[0]), - .result_y = to_witness((*arg.outputs)[1]), - .result_infinite = to_witness((*arg.outputs)[2]), - }); - af.original_opcode_indices.ec_add_constraints.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - af.keccak_permutations.push_back(Keccakf1600{ - .state = transform::map(*arg.inputs, to_witness_or_constant), - .result = transform::map(*arg.outputs, to_witness), - }); - af.original_opcode_indices.keccak_permutations.push_back(opcode_index); - } else if constexpr (std::is_same_v) { - auto predicate = parse_input(arg.predicate); - if (predicate.is_constant && predicate.value.is_zero()) { - // No constraint if the recursion is disabled - return; - } - auto c = RecursionConstraint{ - .key = transform::map(arg.verification_key, to_witness_from_input), - .proof = transform::map(arg.proof, to_witness_from_input), - .public_inputs = transform::map(arg.public_inputs, to_witness_from_input), - .key_hash = get_witness_from_function_input(arg.key_hash), - .proof_type = arg.proof_type, - .predicate = predicate, - }; - - // Add the recursion constraint to the appropriate container based on proof type - switch (c.proof_type) { - case HONK_ZK: - case HONK: - case ROLLUP_HONK: - case ROOT_ROLLUP_HONK: - af.honk_recursion_constraints.push_back(c); - af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index); - break; - case OINK: - case HN: - case HN_TAIL: - case HN_FINAL: - af.hn_recursion_constraints.push_back(c); - af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index); - break; - case AVM: - af.avm_recursion_constraints.push_back(c); - af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index); - break; - case CHONK: - af.chonk_recursion_constraints.push_back(c); - af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index); - break; - default: - bb::assert_failure( - "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint."); - } - } else if constexpr (std::is_same_v) { - af.poseidon2_constraints.push_back(Poseidon2Constraint{ - .state = transform::map(arg.inputs, to_witness_or_constant), - .result = transform::map(arg.outputs, to_witness), - }); - af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index); - } else { - bb::assert_failure("acir_format::handle_blackbox_func_call: Unrecognized BlackBoxFuncCall variant. An " - "error here means there was a serialization error."); - } - }, + overloaded{ [&](const Acir::BlackBoxFuncCall::AND& arg) { + af.logic_constraints.push_back(LogicConstraint{ + .a = parse_input(arg.lhs), + .b = parse_input(arg.rhs), + .result = to_witness(arg.output), + .num_bits = arg.num_bits, + .is_xor_gate = false, + }); + af.original_opcode_indices.logic_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::XOR& arg) { + af.logic_constraints.push_back(LogicConstraint{ + .a = parse_input(arg.lhs), + .b = parse_input(arg.rhs), + .result = to_witness(arg.output), + .num_bits = arg.num_bits, + .is_xor_gate = true, + }); + af.original_opcode_indices.logic_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::RANGE& arg) { + af.range_constraints.push_back(RangeConstraint{ + .witness = get_witness_from_function_input(arg.input), + .num_bits = arg.num_bits, + }); + af.original_opcode_indices.range_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::AES128Encrypt& arg) { + af.aes128_constraints.push_back(AES128Constraint{ + .inputs = transform::map(arg.inputs, to_witness_or_constant), + .iv = transform::map(*arg.iv, to_witness_or_constant), + .key = transform::map(*arg.key, to_witness_or_constant), + .outputs = transform::map(arg.outputs, to_witness), + }); + af.original_opcode_indices.aes128_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::Sha256Compression& arg) { + af.sha256_compression.push_back(Sha256Compression{ + .inputs = transform::map(*arg.inputs, to_witness_or_constant), + .hash_values = transform::map(*arg.hash_values, to_witness_or_constant), + .result = transform::map(*arg.outputs, to_witness), + }); + af.original_opcode_indices.sha256_compression.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::Blake2s& arg) { + af.blake2s_constraints.push_back(Blake2sConstraint{ + .inputs = transform::map(arg.inputs, to_witness_or_constant), + .result = transform::map(*arg.outputs, to_witness), + }); + af.original_opcode_indices.blake2s_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::Blake3& arg) { + af.blake3_constraints.push_back(Blake3Constraint{ + .inputs = transform::map(arg.inputs, to_witness_or_constant), + .result = transform::map(*arg.outputs, to_witness), + }); + af.original_opcode_indices.blake3_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::EcdsaSecp256k1& arg) { + af.ecdsa_k1_constraints.push_back(EcdsaConstraint{ + .type = bb::CurveType::SECP256K1, + .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input), + .signature = transform::map(*arg.signature, to_witness_from_input), + .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input), + .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input), + .predicate = parse_input(arg.predicate), + .result = to_witness(arg.output), + }); + af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::EcdsaSecp256r1& arg) { + af.ecdsa_r1_constraints.push_back(EcdsaConstraint{ + .type = bb::CurveType::SECP256R1, + .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input), + .signature = transform::map(*arg.signature, to_witness_from_input), + .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input), + .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input), + .predicate = parse_input(arg.predicate), + .result = to_witness(arg.output), + }); + af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::MultiScalarMul& arg) { + af.multi_scalar_mul_constraints.push_back(MultiScalarMul{ + .points = transform::map(arg.points, to_witness_or_constant), + .scalars = transform::map(arg.scalars, to_witness_or_constant), + .predicate = parse_input(arg.predicate), + .out_point_x = to_witness((*arg.outputs)[0]), + .out_point_y = to_witness((*arg.outputs)[1]), + .out_point_is_infinite = to_witness((*arg.outputs)[2]), + }); + af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::EmbeddedCurveAdd& arg) { + af.ec_add_constraints.push_back(EcAdd{ + .input1_x = parse_input((*arg.input1)[0]), + .input1_y = parse_input((*arg.input1)[1]), + .input1_infinite = parse_input((*arg.input1)[2]), + .input2_x = parse_input((*arg.input2)[0]), + .input2_y = parse_input((*arg.input2)[1]), + .input2_infinite = parse_input((*arg.input2)[2]), + .predicate = parse_input(arg.predicate), + .result_x = to_witness((*arg.outputs)[0]), + .result_y = to_witness((*arg.outputs)[1]), + .result_infinite = to_witness((*arg.outputs)[2]), + }); + af.original_opcode_indices.ec_add_constraints.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::Keccakf1600& arg) { + af.keccak_permutations.push_back(Keccakf1600{ + .state = transform::map(*arg.inputs, to_witness_or_constant), + .result = transform::map(*arg.outputs, to_witness), + }); + af.original_opcode_indices.keccak_permutations.push_back(opcode_index); + }, + [&](const Acir::BlackBoxFuncCall::RecursiveAggregation& arg) { + auto predicate = parse_input(arg.predicate); + if (predicate.is_constant && predicate.value.is_zero()) { + // No constraint if the recursion is disabled + return; + } + auto c = RecursionConstraint{ + .key = transform::map(arg.verification_key, to_witness_from_input), + .proof = transform::map(arg.proof, to_witness_from_input), + .public_inputs = transform::map(arg.public_inputs, to_witness_from_input), + .key_hash = get_witness_from_function_input(arg.key_hash), + .proof_type = arg.proof_type, + .predicate = predicate, + }; + + // Add the recursion constraint to the appropriate container based on proof type + switch (c.proof_type) { + case HONK_ZK: + case HONK: + case ROLLUP_HONK: + case ROOT_ROLLUP_HONK: + af.honk_recursion_constraints.push_back(c); + af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index); + break; + case OINK: + case HN: + case HN_TAIL: + case HN_FINAL: + af.hn_recursion_constraints.push_back(c); + af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index); + break; + case AVM: + af.avm_recursion_constraints.push_back(c); + af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index); + break; + case CHONK: + af.chonk_recursion_constraints.push_back(c); + af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index); + break; + default: + bb::assert_failure( + "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint."); + } + }, + [&](const Acir::BlackBoxFuncCall::Poseidon2Permutation& arg) { + af.poseidon2_constraints.push_back(Poseidon2Constraint{ + .state = transform::map(arg.inputs, to_witness_or_constant), + .result = transform::map(arg.outputs, to_witness), + }); + af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index); + } }, arg.value.value); } From cbd39ac64efa81a7f3566df15b7e578e9f73a44c Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:30:18 +0000 Subject: [PATCH 4/6] Typos --- .../dsl/acir_format/acir_to_constraint_buf.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 34d9aa902146..47d19f3690f0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -290,7 +290,9 @@ void update_max_witness_index_from_opcode(Acir::Opcode const& opcode, AcirFormat update_max_witness_index_from_expression(arg.predicate.value(), af); } }, - [&](const Acir::Opcode::Call&) { bb::assert_failure("Call opcode is not supported."); }, + [&](const Acir::Opcode::Call&) { + bb::assert_failure("acir_format::update_max_witness_index_from_opcode: Call opcode is not supported."); + }, }, opcode.value); } @@ -363,13 +365,15 @@ AcirFormat circuit_serde_to_acir_format(Acir::Circuit const& circuit) [&](const Acir::Opcode::MemoryOp& arg) { auto block = block_id_to_block_constraint.find(arg.block_id.value); if (block == block_id_to_block_constraint.end()) { - bb::assert_failure("acir_format::circuit_serder_to_acir_format: unitialized MemoryOp."); + bb::assert_failure("acir_format::circuit_serde_to_acir_format: unitialized MemoryOp."); } add_memory_op_to_block_constraint(arg, block->second.first); block->second.second.push_back(i); }, [&](const Acir::Opcode::BrilligCall&) {}, - [&](const Acir::Opcode::Call&) { bb::assert_failure("Call opcode is not supported."); }, + [&](const Acir::Opcode::Call&) { + bb::assert_failure("acir_format::circuit_serde_to_acir_format: Call opcode is not supported."); + }, }, gate.value); } From 775f2174d341e81d3ff435d76a7f1c61d22cf125 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:52:09 +0000 Subject: [PATCH 5/6] Keep only first link for everything --- .../barretenberg/dsl/acir_format/README.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md index 2c6fb13c37de..ba89e51fccd8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md @@ -26,9 +26,9 @@ The following is the list of opcodes (see [`Opcode`](https://github.com/AztecPro **Note:** There is another opcode: [`Call`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3336), which was meant to be used to expose folding to Noir. We are not supporting this functionality, so barretenberg fails the reconstruction of a serialized Noir program if it encounters a `Call` opcode. -### [`AssertZero`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3226) +### `AssertZero` -[`AssertZero`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3226) opcodes represent expressions of the following form: +`AssertZero` opcodes represent expressions of the following form: $$ \sum_{i, j} c_{i,j} \cdot w_i w_j + \sum_i c_i \cdot w_i + c = 0 @@ -36,27 +36,27 @@ $$ where $w_i, w_j$ are witnesses and $c_{i,j}, c_j, c$ are constants. -### [`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) +### `MemoryInit` -[`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) opcodes represent the initialization of a memory table. This can be either a `ROM` table, a `RAM` table, a `Calldata` databus column, or a `Returndata` databus column. +`MemoryInit` opcodes represent the initialization of a memory table. This can be either a `ROM` table, a `RAM` table, a `Calldata` databus column, or a `Returndata` databus column. -A [`MemoryInit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3281) opcode contains a list of witness indices representing the indices of the data with which to initialize the table. +A `MemoryInit` opcode contains a list of witness indices representing the indices of the data with which to initialize the table. -### [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) +### `MemoryOp` -[`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) opcodes represent operations on a memory table. `ROM` and `Calldata` tables only support read operations, `RAM` supports both read and write operations, `Returndata` doesn't support any type of operation. +`MemoryOp` opcodes represent operations on a memory table. `ROM` and `Calldata` tables only support read operations, `RAM` supports both read and write operations, `Returndata` doesn't support any type of operation. -A [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) opcode contains the type of the operation, the index of the element of the table on which to perform the operation, and the value to be read or written. +A `MemoryOp` opcode contains the type of the operation, the index of the element of the table on which to perform the operation, and the value to be read or written. -**Note:** [`MemoryOp`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3258) use [`Acir::Expression`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L2959)s to represent the operation type, the index, and the value. Barretenberg enforces that the expressions encode the data type they are supposed to represent. For example, the type of the operation is supposed to be represented by and expression with no multiplication terms, no linear terms, and with a constant term equal to either one or zero. When converting the expression to a memory operation type, barretenberg asserts that these assumptions are satisfied. +**Note:** `MemoryOp` uses [`Acir::Expression`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L2959)s to represent the operation type, the index, and the value. Barretenberg enforces that the expressions encode the data type they are supposed to represent. For example, the type of the operation is supposed to be represented by and expression with no multiplication terms, no linear terms, and with a constant term equal to either one or zero. When converting the expression to a memory operation type, barretenberg asserts that these assumptions are satisfied. -### [`BrilligCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3307) +### `BrilligCall` -[`BrilligCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3307) opcodes are no-ops in barretenberg. They are unconstrained functions in Noir that add witnesses without adding constraints for them. +`BrilligCall` opcodes are no-ops in barretenberg. They are unconstrained functions in Noir that add witnesses without adding constraints for them. -### [`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) +### `BlackBoxFuncCall` -[`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) opcodes represent calls from Noir to functions that are implemented in barretenberg. An example is recursive verification: to perform recursive verification from Noir, we call `std::verify_with_type`, which adds a [`BlackBoxFuncCall`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3242) opcode for recursive verification. Then, when barretenberg parses this opcode, it adds the constraints for recursive verification using the witness indices passed by Noir. +`BlackBoxFuncCall` opcodes represent calls from Noir to functions that are implemented in barretenberg. An example is recursive verification: to perform recursive verification from Noir, we call `std::verify_with_type`, which adds a `BlackBoxFuncCall` opcode for recursive verification. Then, when barretenberg parses this opcode, it adds the constraints for recursive verification using the witness indices passed by Noir. ## Bytes to `Builder` @@ -64,12 +64,12 @@ The conversion from a buffer of bytes to a `Builder` object happens in two steps ### Bytes to `AcirFormat` -Instances of the [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) struct contain a record of all the constraints written in Noir. Barretenberg's role is to take this record and construct a builder out of it. The `Builder` object can then be used to generate a Honk proof, a verification key, or accumulated during the generation of a Chonk proof. +Instances of the `AcirFormat` struct contain a record of all the constraints written in Noir. Barretenberg's role is to take this record and construct a builder out of it. The `Builder` object can then be used to generate a Honk proof, a verification key, or accumulated during the generation of a Chonk proof. -The single entrypoint for the conversion from a buffer of bytes into an instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) is the function [`circuit_buf_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L109). This function deserializes the buffer according to the msgpack serialization format. The result of the deserialization is an instance of the [`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705) struct, which the opcodes representing the Noir program. +The single entrypoint for the conversion from a buffer of bytes into an instance of `AcirFormat` is the function [`circuit_buf_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L109). This function deserializes the buffer according to the msgpack serialization format. The result of the deserialization is an instance of the [`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705) struct, which the opcodes representing the Noir program. -The [`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705) is passed to [`circuit_serde_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L96), which processes all of the opcodes and adds them to an instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82). This step is simply converting one representation ([`Acir::Circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp#L3705)) into another ([`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82)). +The `Acir::Circuit` is passed to [`circuit_serde_to_acir_format`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp#L96), which processes all of the opcodes and adds them to an instance of `AcirFormat`. This step is simply converting one representation (`Acir::Circuit`) into another (`AcirFormat`). -### [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) to `Builder` +### `AcirFormat` to `Builder` -The instance of [`AcirFormat`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L82) is then passed to the function [`create_circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L156), which constructs a `Builder` object with all the required constraints. This step is where barretenberg's handling of opcodes comes into play: the gates added to the builder depend on the internals of barretenberg and they would be different if we used a different backend. +The instance of `AcirFormat` is then passed to the function [`create_circuit`](https://github.com/AztecProtocol/aztec-packages/blob/795cd3ae80ba971a6d018b6d31e563c2fec870d3/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp#L156), which constructs a `Builder` object with all the required constraints. This step is where barretenberg's handling of opcodes comes into play: the gates added to the builder depend on the internals of barretenberg and they would be different if we used a different backend. From 70dc8ea5f9574b7692dacdc5d88bcbd2e2678160 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:14:08 +0000 Subject: [PATCH 6/6] Improve lambdas --- .../dsl/acir_format/acir_to_constraint_buf.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 47d19f3690f0..ea138c1e4439 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -41,14 +41,14 @@ bb::fr from_buffer_with_bound_checks(const std::vector& buffer) WitnessOrConstant parse_input(const Acir::FunctionInput& input) { - WitnessOrConstant result = std::visit(overloaded{ [&](const Acir::FunctionInput::Witness& e) { + WitnessOrConstant result = std::visit(overloaded{ [](const Acir::FunctionInput::Witness& e) { return WitnessOrConstant{ .index = e.value.value, .value = bb::fr::zero(), .is_constant = false, }; }, - [&](const Acir::FunctionInput::Constant& e) { + [](const Acir::FunctionInput::Constant& e) { return WitnessOrConstant{ .index = bb::stdlib::IS_CONSTANT, .value = from_buffer_with_bound_checks(e.value), @@ -600,9 +600,9 @@ void add_blackbox_func_call_to_acir_format(Acir::Opcode::BlackBoxFuncCall const& AcirFormat& af, size_t opcode_index) { - auto to_witness_or_constant = [&](const Acir::FunctionInput& e) { return parse_input(e); }; - auto to_witness = [&](const Acir::Witness& e) { return e.value; }; - auto to_witness_from_input = [&](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); }; + auto to_witness_or_constant = [](const Acir::FunctionInput& e) { return parse_input(e); }; + auto to_witness = [](const Acir::Witness& e) { return e.value; }; + auto to_witness_from_input = [](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); }; std::visit( overloaded{ [&](const Acir::BlackBoxFuncCall::AND& arg) { @@ -807,7 +807,7 @@ BlockConstraint memory_init_to_block_constraint(Acir::Opcode::MemoryInit const& void add_memory_op_to_block_constraint(Acir::Opcode::MemoryOp const& mem_op, BlockConstraint& block) { // Lambda to convert an Acir::Expression to a witness index - auto acir_expression_to_witness_or_constant = [&](const Acir::Expression& expr) { + auto acir_expression_to_witness_or_constant = [](const Acir::Expression& expr) { // Noir gives us witnesses or constants for read/write operations. We use the following assertions to ensure // that the data coming from Noir is in the correct form. BB_ASSERT(expr.mul_terms.empty(), "MemoryOp should not have multiplication terms"); @@ -830,7 +830,7 @@ void add_memory_op_to_block_constraint(Acir::Opcode::MemoryOp const& mem_op, Blo }; // Lambda to determine whether a memory operation is a read or write operation - auto is_read_operation = [&](const Acir::Expression& expr) { + auto is_read_operation = [](const Acir::Expression& expr) { BB_ASSERT(expr.mul_terms.empty(), "MemoryOp expression should not have multiplication terms"); BB_ASSERT(expr.linear_combinations.empty(), "MemoryOp expression should not have linear terms");