diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 486e7f599ad..d92d010a750 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -43,6 +43,33 @@ void build_constraints(Builder& builder, gate_counter.track_diff(constraint_system.gates_per_opcode, constraint_system.original_opcode_indices.quad_constraints.at(i)); } + // Oversize gates are a vector of mul_quad gates. + for (size_t i = 0; i < constraint_system.big_quad_constraints.size(); ++i) { + auto& big_constraint = constraint_system.big_quad_constraints.at(i); + fr next_w4_wire_value = fr(0); + // Define the 4th wire of these mul_quad gates, which is implicitly used by the previous gate. + for (size_t j = 0; j < big_constraint.size() - 1; ++j) { + if (j == 0) { + next_w4_wire_value = builder.get_variable(big_constraint[0].d); + } else { + uint32_t next_w4_wire = builder.add_variable(next_w4_wire_value); + big_constraint[j].d = next_w4_wire; + big_constraint[j].d_scaling = fr(-1); + } + builder.create_big_mul_add_gate(big_constraint[j], true); + next_w4_wire_value = builder.get_variable(big_constraint[j].a) * builder.get_variable(big_constraint[j].b) * + big_constraint[j].mul_scaling + + builder.get_variable(big_constraint[j].a) * big_constraint[j].a_scaling + + builder.get_variable(big_constraint[j].b) * big_constraint[j].b_scaling + + builder.get_variable(big_constraint[j].c) * big_constraint[j].c_scaling + + next_w4_wire_value * big_constraint[j].d_scaling + big_constraint[j].const_scaling; + next_w4_wire_value = -next_w4_wire_value; + } + uint32_t next_w4_wire = builder.add_variable(next_w4_wire_value); + big_constraint.back().d = next_w4_wire; + big_constraint.back().d_scaling = fr(-1); + builder.create_big_mul_add_gate(big_constraint.back(), false); + } // Add logic constraint for (size_t i = 0; i < constraint_system.logic_constraints.size(); ++i) { @@ -492,12 +519,14 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, for (auto [constraint, queue_entry] : zip_view(constraint_system.ivc_recursion_constraints, ivc.stdlib_verification_queue)) { - // Reconstruct complete proof indices from acir constraint data (in which proof is stripped of public inputs) + // Reconstruct complete proof indices from acir constraint data (in which proof is + // stripped of public inputs) std::vector complete_proof_indices = ProofSurgeon::create_indices_for_reconstructed_proof(constraint.proof, constraint.public_inputs); ASSERT(complete_proof_indices.size() == queue_entry.proof.size()); - // Assert equality between the proof indices from the constraint data and those of the internal proof + // Assert equality between the proof indices from the constraint data and those of the + // internal proof for (auto [proof_value, proof_idx] : zip_view(queue_entry.proof, complete_proof_indices)) { circuit.assert_equal(proof_value.get_witness_index(), proof_idx); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 02de8ac8b9e..73acc790bf0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -115,7 +115,13 @@ struct AcirFormat { // for q_M,q_L,q_R,q_O,q_C and indices of three variables taking the role of left, right and output wire // This could be a large vector so use slab allocator, we don't expect the blackbox implementations to be so large. bb::SlabVector poly_triple_constraints; + // A standard ultra plonk arithmetic constraint, of width 4: q_Ma*b+q_A*a+q_B*b+q_C*c+q_d*d+q_const = 0 bb::SlabVector> quad_constraints; + // A vector of vector of mul_quad gates (i.e arithmetic constraints of width 4) + // Each vector of gates represente a 'big' expression (a polynomial of degree 1 or 2 which does not fit inside one + // mul_gate) that has been splitted into multiple mul_gates, using w4_omega (the 4th wire of the next gate), to + // reduce the number of intermediate variables. + bb::SlabVector>> big_quad_constraints; std::vector block_constraints; // Number of gates added to the circuit per original opcode. @@ -153,6 +159,8 @@ struct AcirFormat { avm_recursion_constraints, ivc_recursion_constraints, poly_triple_constraints, + quad_constraints, + big_quad_constraints, block_constraints, bigint_from_le_bytes_constraints, bigint_to_le_bytes_constraints, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index f894d12eed4..0aa7d35021e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -68,6 +68,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .assert_equalities = {}, .poly_triple_constraints = { constraint }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -191,6 +192,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .assert_equalities = {}, .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -282,6 +284,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .q_c = fr::neg_one(), } }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -390,6 +393,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .q_c = fr::neg_one(), } }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -502,6 +506,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .assert_equalities = {}, .poly_triple_constraints = { dummy }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -583,6 +588,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -659,6 +665,7 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) .assert_equalities = {}, .poly_triple_constraints = { first_gate, second_gate }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -669,3 +676,138 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) EXPECT_EQ(constraint_system.gates_per_opcode, std::vector({ 2, 1 })); } + +TEST_F(AcirFormatTests, TestBigAdd) +{ + + WitnessVector witness_values; + witness_values.emplace_back(fr(0)); + + witness_values = { + fr(0), fr(1), fr(2), fr(3), fr(4), fr(5), fr(6), fr(7), fr(8), fr(9), fr(10), fr(11), fr(12), fr(13), fr(-91), + }; + + bb::mul_quad_ quad1 = { + .a = 0, + .b = 1, + .c = 2, + .d = 3, + .mul_scaling = 0, + .a_scaling = fr::one(), + .b_scaling = fr::one(), + .c_scaling = fr::one(), + .d_scaling = fr::one(), + .const_scaling = fr(0), + }; + + bb::mul_quad_ quad2 = { + .a = 4, + .b = 5, + .c = 6, + .d = 0, + .mul_scaling = 0, + .a_scaling = fr::one(), + .b_scaling = fr::one(), + .c_scaling = fr::one(), + .d_scaling = fr(0), + .const_scaling = fr(0), + }; + + bb::mul_quad_ quad3 = { + .a = 7, + .b = 8, + .c = 9, + .d = 0, + .mul_scaling = 0, + .a_scaling = fr::one(), + .b_scaling = fr::one(), + .c_scaling = fr::one(), + .d_scaling = fr(0), + .const_scaling = fr(0), + }; + bb::mul_quad_ quad4 = { + .a = 10, + .b = 11, + .c = 12, + .d = 0, + .mul_scaling = 0, + .a_scaling = fr::one(), + .b_scaling = fr::one(), + .c_scaling = fr::one(), + .d_scaling = fr(0), + .const_scaling = fr(0), + }; + bb::mul_quad_ quad5 = { + .a = 13, + .b = 14, + .c = 0, + .d = 18, + .mul_scaling = 0, + .a_scaling = fr::one(), + .b_scaling = fr::one(), + .c_scaling = fr(0), + .d_scaling = fr(-1), + .const_scaling = fr(0), + }; + + auto res_x = fr(91); + auto assert_equal = poly_triple{ + .a = 14, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = fr::one(), + .q_r = 0, + .q_o = 0, + .q_c = res_x, + }; + auto quad_constraint = { quad1, quad2, quad3, quad4, quad5 }; + size_t num_variables = witness_values.size(); + AcirFormat constraint_system{ + .varnum = static_cast(num_variables + 1), + .recursive = false, + .num_acir_opcodes = 1, + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .aes128_constraints = {}, + .sha256_compression = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, + .multi_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .recursion_constraints = {}, + .honk_recursion_constraints = {}, + .avm_recursion_constraints = {}, + .ivc_recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_to_le_bytes_constraints = {}, + .bigint_operations = {}, + .assert_equalities = {}, + .poly_triple_constraints = { assert_equal }, + .quad_constraints = {}, + .big_quad_constraints = { quad_constraint }, + .block_constraints = {}, + .original_opcode_indices = create_empty_original_opcode_indices(), + }; + mock_opcode_indices(constraint_system); + + auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + + auto composer = Composer(); + auto prover = composer.create_prover(builder); + + auto proof = prover.construct_proof(); + + EXPECT_TRUE(CircuitChecker::check(builder)); + auto verifier = composer.create_verifier(builder); + EXPECT_EQ(verifier.verify_proof(proof), true); +} \ No newline at end of file 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 c5fda604160..defe82a9965 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 @@ -100,6 +100,145 @@ poly_triple serialize_arithmetic_gate(Program::Expression const& arg) pt.q_c = uint256_t(arg.q_c); return pt; } + +/// @brief + +/// @param scaling The scaling factor to apply to the linear term. +/// @note This function is used internally to update the fields of a mul_quad_ gate with a linear term. +/** + * @brief Assigns a linear term to a specific index in a mul_quad_ gate. + * @param gate The mul_quad_ gate to assign the linear term to. + * @param index The index of the linear term to assign (0 for a, 1 for b, 2 for c, 3 for d). + * @param witness_index The witness index to assign to the linear term. + * @return nothing, the input gate is modified in place. + * @note It fails if index is 4 or more. + */ +void assign_linear_term(mul_quad_& gate, int index, uint32_t witness_index, fr const& scaling) +{ + switch (index) { + case 0: + gate.a = witness_index; + gate.a_scaling = scaling; + break; + case 1: + gate.b = witness_index; + gate.b_scaling = scaling; + break; + case 2: + gate.c = witness_index; + gate.c_scaling = scaling; + break; + case 3: + gate.d = witness_index; + gate.d_scaling = scaling; + break; + default: + ASSERT(false); + } +} + +/// Accumulate the input expression into a serie of quad gates +std::vector> split_into_mul_quad_gates(Program::Expression const& arg) +{ + std::vector> result; + auto current_mul_term = arg.mul_terms.begin(); + auto current_linear_term = arg.linear_combinations.begin(); + + // number of wires to use in the intermediate gate + int max_size = 4; + bool done = false; + // the intermediate 'big add' gates. The first one contains the constant term. + mul_quad_ mul_gate = { .a = 0, + .b = 0, + .c = 0, + .d = 0, + .mul_scaling = fr::zero(), + .a_scaling = fr::zero(), + .b_scaling = fr::zero(), + .c_scaling = fr::zero(), + .d_scaling = fr::zero(), + .const_scaling = fr(uint256_t(arg.q_c)) }; + + // list of witnesses that are part of mul terms + std::set all_mul_terms; + for (auto const& term : arg.mul_terms) { + all_mul_terms.insert(std::get<1>(term).value); + all_mul_terms.insert(std::get<2>(term).value); + } + // The 'mul term' witnesses that have been processed + std::set processed_mul_terms; + + while (!done) { + int i = 0; // index of the current free wire in the new intermediate gate + + // we add a mul term (if there are some) to every intermediate gate + if (current_mul_term != arg.mul_terms.end()) { + mul_gate.mul_scaling = fr(uint256_t(std::get<0>(*current_mul_term))); + mul_gate.a = std::get<1>(*current_mul_term).value; + mul_gate.b = std::get<2>(*current_mul_term).value; + mul_gate.a_scaling = fr::zero(); + mul_gate.b_scaling = fr::zero(); + // Try to add corresponding linear terms, only if they were not already added + if (!processed_mul_terms.contains(mul_gate.a) || !processed_mul_terms.contains(mul_gate.b)) { + for (auto lin_term : arg.linear_combinations) { + auto w = std::get<1>(lin_term).value; + if (w == mul_gate.a) { + if (!processed_mul_terms.contains(mul_gate.a)) { + mul_gate.a_scaling = fr(uint256_t(std::get<0>(lin_term))); + processed_mul_terms.insert(w); + } + if (mul_gate.a == mul_gate.b) { + break; + } + } else if (w == mul_gate.b) { + if (!processed_mul_terms.contains(mul_gate.b)) { + mul_gate.b_scaling = fr(uint256_t(std::get<0>(lin_term))); + processed_mul_terms.insert(w); + } + break; + } + } + } + i = 2; // a and b are used because of the mul term + current_mul_term = std::next(current_mul_term); + } + // We need to process all the mul terms before being done. + done = current_mul_term == arg.mul_terms.end(); + + // Assign available wires with the remaining linear terms which are not also a 'mul term' + while (current_linear_term != arg.linear_combinations.end()) { + auto w = std::get<1>(*current_linear_term).value; + if (!all_mul_terms.contains(w)) { + if (i < max_size) { + assign_linear_term(mul_gate, i, w, fr(uint256_t(std::get<0>(*current_linear_term)))); // * fr(-1))); + ++i; + } else { + // No more available wire, but there is still some linear terms; we need another mul_gate + done = false; + break; + } + } + current_linear_term = std::next(current_linear_term); + } + + // Index 4 of the next gate will be used + max_size = 3; + result.push_back(mul_gate); + mul_gate = { .a = 0, + .b = 0, + .c = 0, + .d = 0, + .mul_scaling = fr::zero(), + .a_scaling = fr::zero(), + .b_scaling = fr::zero(), + .c_scaling = fr::zero(), + .d_scaling = fr::zero(), + .const_scaling = fr::zero() }; + } + + return result; +} + mul_quad_ serialize_mul_quad_gate(Program::Expression const& arg) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/816): The initialization of the witness indices a,b,c @@ -135,7 +274,6 @@ mul_quad_ serialize_mul_quad_gate(Program::Expression const& arg) b_set = true; } // If necessary, set values for linears terms q_l * w_l, q_r * w_r and q_o * w_o - ASSERT(arg.linear_combinations.size() <= 4); // We can only accommodate 4 linear terms for (const auto& linear_term : arg.linear_combinations) { fr selector_value(uint256_t(std::get<0>(linear_term))); uint32_t witness_idx = std::get<1>(linear_term).value; @@ -162,7 +300,17 @@ mul_quad_ serialize_mul_quad_gate(Program::Expression const& arg) quad.d_scaling = selector_value; d_set = true; } else { - throw_or_abort("Cannot assign linear term to a constraint of width 4"); + // We cannot assign linear term to a constraint of width 4 + return { .a = 0, + .b = 0, + .c = 0, + .d = 0, + .mul_scaling = 0, + .a_scaling = 0, + .b_scaling = 0, + .c_scaling = 0, + .d_scaling = 0, + .const_scaling = 0 }; } } @@ -203,7 +351,8 @@ std::pair is_assert_equal(Program::Opcode::AssertZero const& void handle_arithmetic(Program::Opcode::AssertZero const& arg, AcirFormat& af, size_t opcode_index) { - if (arg.value.linear_combinations.size() <= 3) { + // If the expression fits in a polytriple, we use it. + if (arg.value.linear_combinations.size() <= 3 && arg.value.mul_terms.size() <= 1) { poly_triple pt = serialize_arithmetic_gate(arg.value); auto assert_equal = is_assert_equal(arg, pt, af); @@ -255,12 +404,29 @@ void handle_arithmetic(Program::Opcode::AssertZero const& arg, AcirFormat& af, s af.original_opcode_indices.poly_triple_constraints.push_back(opcode_index); } } else { - af.quad_constraints.push_back(serialize_mul_quad_gate(arg.value)); - af.original_opcode_indices.quad_constraints.push_back(opcode_index); + std::vector> mul_quads; + // We try to use a single mul_quad gate to represent the expression. + if (arg.value.mul_terms.size() <= 1) { + auto quad = serialize_mul_quad_gate(arg.value); + // add it to the result vector if it worked + if (quad.a != 0 || !(quad.mul_scaling == fr(0)) || !(quad.a_scaling == fr(0))) { + mul_quads.push_back(quad); + } + } + if (mul_quads.empty()) { + // If not, we need to split the expression into multiple gates + mul_quads = split_into_mul_quad_gates(arg.value); + } + if (mul_quads.size() == 1) { + af.quad_constraints.push_back(mul_quads[0]); + af.original_opcode_indices.quad_constraints.push_back(opcode_index); + } + if (mul_quads.size() > 1) { + af.big_quad_constraints.push_back(mul_quads); + } } constrain_witnesses(arg, af); } - uint32_t get_witness_from_function_input(Program::FunctionInput input) { auto input_witness = std::get(input.input.value); @@ -513,9 +679,9 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, auto input_key = get_witness_from_function_input(arg.key_hash); auto proof_type_in = arg.proof_type; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1074): Eventually arg.proof_type will be - // the only means for setting the proof type. use of honk_recursion flag in this context can go away - // once all noir programs (e.g. protocol circuits) are updated to use the new pattern. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1074): Eventually arg.proof_type will + // be the only means for setting the proof type. use of honk_recursion flag in this context can go + // away once all noir programs (e.g. protocol circuits) are updated to use the new pattern. if (honk_recursion && proof_type_in != HONK && proof_type_in != AVM) { proof_type_in = HONK; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 541dbcab37a..7e7cad30430 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -201,6 +201,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -276,6 +277,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -335,6 +337,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -399,6 +402,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -484,6 +488,7 @@ TEST_F(BigIntTests, TestBigIntDIV) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 45dece532ee..10f7d87baea 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -169,6 +169,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -222,6 +223,7 @@ TEST_F(MegaHonk, Databus) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -330,6 +332,7 @@ TEST_F(MegaHonk, DatabusReturn) .assert_equalities = {}, .poly_triple_constraints = { assert_equal }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index f12c39e8f8f..f009fac6fbd 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -91,6 +91,7 @@ TEST_F(EcOperations, TestECOperations) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -229,6 +230,7 @@ TEST_F(EcOperations, TestECMultiScalarMul) .assert_equalities = {}, .poly_triple_constraints = { assert_equal }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 24864b52f6d..09c9cfe63c5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -123,6 +123,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -179,6 +180,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -230,6 +232,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index 657eaa2d316..dc559d9f74c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -157,6 +157,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -215,6 +216,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -271,6 +273,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -323,6 +326,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp index cf3303b557b..37de3ddd6ee 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -119,6 +119,7 @@ class AcirHonkRecursionConstraint : public ::testing::Test { .assert_equalities = {}, .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp index a0adc6331e0..77e818c4d59 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp @@ -91,6 +91,7 @@ TEST_F(MSMTests, TestMSM) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index ec2793c3aca..e1db815ea81 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -71,6 +71,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index ec22ec71fdb..988ab39ddb7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -116,6 +116,7 @@ Builder create_inner_circuit() .assert_equalities = {}, .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d }, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; @@ -279,6 +280,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index 063ad4f62c8..1363df7e31b 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -66,6 +66,7 @@ TEST_F(Sha256Tests, TestSha256Compression) .assert_equalities = {}, .poly_triple_constraints = {}, .quad_constraints = {}, + .big_quad_constraints = {}, .block_constraints = {}, .original_opcode_indices = create_empty_original_opcode_indices(), }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp index 9bae714db18..9ded09c1b66 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp @@ -262,6 +262,40 @@ void UltraCircuitBuilder_::create_add_gate(const add_triple_num_gates; } +/** + * @brief Create a big multiplication-addition gate, where in.a * in.b * in.mul_scaling + in.a * in.a_scaling + in.b * + * in.b_scaling + in.c * in.c_scaling + in.d * in.d_scaling + in.const_scaling = 0. If include_next_gate_w_4 is enabled, + * then this sum also adds the value of the 4-th witness at the next index. + * + * @param in Structure with variable indexes and wire selector values + * @param include_next_gate_w_4 Switches on/off the addition of w_4 at the next index + */ +template +void UltraCircuitBuilder_::create_big_mul_add_gate(const mul_quad_& in, + const bool include_next_gate_w_4) +{ + this->assert_valid_variables({ in.a, in.b, in.c, in.d }); + blocks.arithmetic.populate_wires(in.a, in.b, in.c, in.d); + blocks.arithmetic.q_m().emplace_back(include_next_gate_w_4 ? in.mul_scaling * FF(2) : in.mul_scaling); + blocks.arithmetic.q_1().emplace_back(in.a_scaling); + blocks.arithmetic.q_2().emplace_back(in.b_scaling); + blocks.arithmetic.q_3().emplace_back(in.c_scaling); + blocks.arithmetic.q_c().emplace_back(in.const_scaling); + blocks.arithmetic.q_arith().emplace_back(include_next_gate_w_4 ? 2 : 1); + blocks.arithmetic.q_4().emplace_back(in.d_scaling); + blocks.arithmetic.q_delta_range().emplace_back(0); + blocks.arithmetic.q_lookup_type().emplace_back(0); + blocks.arithmetic.q_elliptic().emplace_back(0); + blocks.arithmetic.q_aux().emplace_back(0); + blocks.arithmetic.q_poseidon2_external().emplace_back(0); + blocks.arithmetic.q_poseidon2_internal().emplace_back(0); + if constexpr (HasAdditionalSelectors) { + blocks.arithmetic.pad_additional(); + } + check_selector_length_consistency(); + ++this->num_gates; +} + /** * @brief Create a big addition gate, where in.a * in.a_scaling + in.b * in.b_scaling + in.c * * in.c_scaling + in.d * in.d_scaling + in.const_scaling = 0. If include_next_gate_w_4 is enabled, then thes sum also diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp index ed3f12aae53..83d036761c1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp @@ -426,7 +426,7 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase& in) override; - + void create_big_mul_add_gate(const mul_quad_& in, const bool use_next_gate_w_4 = false); void create_big_add_gate(const add_quad_& in, const bool use_next_gate_w_4 = false); void create_big_add_gate_with_bit_extraction(const add_quad_& in); void create_big_mul_gate(const mul_quad_& in);