diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md index fea6cc440d86..ba89e51fccd8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/README.md @@ -16,15 +16,15 @@ 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` @@ -48,28 +48,28 @@ A `MemoryInit` opcode contains a list of witness indices representing the indice 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` 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` 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` `BrilligCall` opcodes are no-ops in barretenberg. They are unconstrained functions in Noir that add witnesses without adding constraints for them. -### `BlackBoxFunctionCall` +### `BlackBoxFuncCall` -`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` 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` -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` 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` 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` 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` 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` 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.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 562e22016322..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 @@ -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,197 @@ 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("acir_format::update_max_witness_index_from_opcode: Call opcode is not supported."); + }, }, opcode.value); } @@ -325,29 +354,26 @@ 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."); + 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); - } 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("acir_format::circuit_serde_to_acir_format: Call opcode is not supported."); + }, }, gate.value); } @@ -363,20 +389,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]); @@ -574,170 +600,177 @@ 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( - [&](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); } @@ -774,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"); @@ -797,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"); 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,