From c3d757a6920b09c4d2a3aa462b547dc16e73f3e6 Mon Sep 17 00:00:00 2001 From: Innokentii Sennovskii Date: Wed, 29 Mar 2023 22:41:15 +0100 Subject: [PATCH] Splitting turbo composer (#266) * Turbo Circuit Constructor working * Turbo!! And also fixed some of the fuzzer compilation issues * Luke: Addressing my own comments and adding minor TODOs where necessary --------- Co-authored-by: ledwards2225 --- .../circuit_constructor_base.cpp | 1 + .../standard_circuit_constructor.hpp | 2 +- .../turbo_circuit_constructor.cpp | 1571 +++++++++++++++++ .../turbo_circuit_constructor.hpp | 101 ++ .../turbo_circuit_constructor.test.cpp | 690 ++++++++ .../composer_helper/composer_helper_lib.cpp | 45 +- .../composer_helper/composer_helper_lib.hpp | 14 +- .../composer_helper/permutation_helper.hpp | 1 + .../standard_plonk_composer_helper.cpp | 77 +- .../standard_plonk_composer_helper.hpp | 13 - .../turbo_plonk_composer_helper.cpp | 180 ++ .../turbo_plonk_composer_helper.hpp | 135 ++ .../honk/composer/standard_honk_composer.hpp | 2 +- .../honk/composer/standard_plonk_composer.hpp | 3 +- .../composer/standard_plonk_composer.test.cpp | 14 +- .../honk/composer/turbo_plonk_composer.hpp | 208 +++ .../composer/turbo_plonk_composer.test.cpp | 1153 ++++++++++++ .../proof_system/flavor/flavor.hpp | 3 +- .../primitives/bigfield/bigfield.fuzzer.hpp | 18 +- .../primitives/bit_array/bit_array.fuzzer.hpp | 13 +- .../stdlib/primitives/bool/bool.fuzzer.hpp | 14 +- .../byte_array/byte_array.fuzzer.hpp | 13 +- .../stdlib/primitives/field/field.fuzzer.hpp | 16 +- .../primitives/safe_uint/safe_uint.fuzzer.hpp | 16 +- .../stdlib/primitives/uint/uint.fuzzer.hpp | 14 +- 25 files changed, 4184 insertions(+), 133 deletions(-) create mode 100644 cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.cpp create mode 100644 cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp create mode 100644 cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.test.cpp create mode 100644 cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.cpp create mode 100644 cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.hpp create mode 100644 cpp/src/barretenberg/honk/composer/turbo_plonk_composer.hpp create mode 100644 cpp/src/barretenberg/honk/composer/turbo_plonk_composer.test.cpp diff --git a/cpp/src/barretenberg/honk/circuit_constructors/circuit_constructor_base.cpp b/cpp/src/barretenberg/honk/circuit_constructors/circuit_constructor_base.cpp index 4f601a92aa..e98c920cc9 100644 --- a/cpp/src/barretenberg/honk/circuit_constructors/circuit_constructor_base.cpp +++ b/cpp/src/barretenberg/honk/circuit_constructors/circuit_constructor_base.cpp @@ -42,4 +42,5 @@ void CircuitConstructorBase::assert_equal(const uint32_t a_varia } // Standard honk/ plonk instantiation template class CircuitConstructorBase<3>; +template class CircuitConstructorBase<4>; } // namespace bonk diff --git a/cpp/src/barretenberg/honk/circuit_constructors/standard_circuit_constructor.hpp b/cpp/src/barretenberg/honk/circuit_constructors/standard_circuit_constructor.hpp index 34f70ec504..66d8389013 100644 --- a/cpp/src/barretenberg/honk/circuit_constructors/standard_circuit_constructor.hpp +++ b/cpp/src/barretenberg/honk/circuit_constructors/standard_circuit_constructor.hpp @@ -11,7 +11,7 @@ inline std::vector standard_selector_names() return result; } -class StandardCircuitConstructor : public CircuitConstructorBase { +class StandardCircuitConstructor : public CircuitConstructorBase { public: // TODO(#216)(Kesha): replace this with Honk enums after we have a verifier and no longer depend on plonk // prover/verifier diff --git a/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.cpp b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.cpp new file mode 100644 index 0000000000..4296b2064c --- /dev/null +++ b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.cpp @@ -0,0 +1,1571 @@ +#include "turbo_circuit_constructor.hpp" +#include "barretenberg/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" + +using namespace barretenberg; + +namespace bonk { + +#define TURBO_SELECTOR_REFS \ + auto& q_m = selectors[TurboSelectors::QM]; \ + auto& q_c = selectors[TurboSelectors::QC]; \ + auto& q_1 = selectors[TurboSelectors::Q1]; \ + auto& q_2 = selectors[TurboSelectors::Q2]; \ + auto& q_3 = selectors[TurboSelectors::Q3]; \ + auto& q_4 = selectors[TurboSelectors::Q4]; \ + auto& q_5 = selectors[TurboSelectors::Q5]; \ + auto& q_arith = selectors[TurboSelectors::QARITH]; \ + auto& q_fixed_base = selectors[TurboSelectors::QFIXED]; \ + auto& q_range = selectors[TurboSelectors::QRANGE]; \ + auto& q_logic = selectors[TurboSelectors::QLOGIC]; + +/** + * Turbo circuit initialization, where you can specify the probable number of gates in your circuit. + * + * @param size_hint Assumed number of gates. Used to allocate space for various member + * vectors during initialization. + * */ +TurboCircuitConstructor::TurboCircuitConstructor(const size_t size_hint) + : CircuitConstructorBase(turbo_selector_names(), TurboSelectors::NUM, size_hint) +{ + w_l.reserve(size_hint); + w_r.reserve(size_hint); + w_o.reserve(size_hint); + w_4.reserve(size_hint); + + zero_idx = put_constant_variable(fr::zero()); +} + +/** + * Create an addition gate. + * The q_m, q_4, q_5, q_fixed_base, q_range, q_logic are zero. + * q_artith is one. w_4 is set to 0-variable index. + * Other parameters are received from the argument. + * + * @param in Specifies addition gate parameters: + * w_l, w_r, w_o, q_1, q_2, q_3, q_c. + * */ +void TurboCircuitConstructor::create_add_gate(const add_triple& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(zero_idx); + q_m.emplace_back(fr::zero()); + q_1.emplace_back(in.a_scaling); + q_2.emplace_back(in.b_scaling); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Create an addition gate that adds 4 variables. + * The q_m, q_5, q_fixed_base, q_range, q_logic are zero. + * q_arith is one. + * Other parameters are received from the argument. + * + * @param in Specifies addition gate parameters: + * w_l, w_r, w_o, w_4, q_1, q_2, q_3, q_4, q_c. + * */ +void TurboCircuitConstructor::create_big_add_gate(const add_quad& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + q_m.emplace_back(fr::zero()); + q_1.emplace_back(in.a_scaling); + q_2.emplace_back(in.b_scaling); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(in.d_scaling); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * @brief Create an addition gate that adds 4 variables with bit extraction. + * The q_m, q_5, q_fixed_base, q_range, q_logic are zero. + * q_arith is 2, so an additional constraint is imposed in the nonlinear terms. + * Other parameters are received from the argument. + * + * @param in Specifies addition gate parameters: + * w_l, w_r, w_o, w_4, q_1, q_2, q_3, q_4, q_c. + * + * @details Impose the constraint + * a_scaling . a + b_scaling . b + c_scaling . c + d_scaling . d + * + 6 * (high bit of c - 4d) == 0. + * @warning This function assumes that c - 4d lies in the set {0, 1, 2, 3}. The circuit writer should take care to + * ensure this assumption is backed by a constraint (e.g., c and d could be accumulators produced using the TurboPLONK + * function `decompose_into_base4_accumulators`). + * */ +void TurboCircuitConstructor::create_big_add_gate_with_bit_extraction(const add_quad& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + q_m.emplace_back(fr::zero()); + q_1.emplace_back(in.a_scaling); + q_2.emplace_back(in.b_scaling); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one() + fr::one()); + q_4.emplace_back(in.d_scaling); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +void TurboCircuitConstructor::create_big_mul_gate(const mul_quad& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + q_m.emplace_back(in.mul_scaling); + q_1.emplace_back(in.a_scaling); + q_2.emplace_back(in.b_scaling); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(in.d_scaling); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * @brief Create an addition constraint with a range constraint on the fourth witness. + * + * @details The constraints imposed by this: + * q_1 * w_l + q_2 * w_r + q_3 * w_o + q_4 * w_4 + q_c == 0 + * and + * w_4 * (w_4 - 1) * (w_4 - 2) == 0 (i.e., w_4 is in {0, 1, 2}). + * + * We use this gate to evaluate additions/subtractions of bounded integers. The purpose is to ensure the prover can use + * the output witness in constraints that require the input to be bounded. For a typical example, look at the function, + * uint::operator+, which calculates addition modulo some number 2**w. This function must + * calculate a long divison by 2**w and return the remainder. Without the constraint on w_4, the prover could lie about + * the remainder. + * + * @warning Even with the constraint on w_3, it is typically necessary to range constrain the wire value that will be + * returned. + */ +void TurboCircuitConstructor::create_balanced_add_gate(const add_quad& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + q_m.emplace_back(fr::zero()); + q_1.emplace_back(in.a_scaling); + q_2.emplace_back(in.b_scaling); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(in.d_scaling); + q_5.emplace_back(fr::one()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Create multiplication gate. + * w_4 is set to the index of zero variable. + * q_1, q_2, q_4, q_4, q_fixed_base, q_range and q_logic are set to zero. + * q_arith is set to 1. + * + * @param in Contains the values for w_l, w_r, w_o, + * q_m, q_3, q_c. + * */ +void TurboCircuitConstructor::create_mul_gate(const mul_triple& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(zero_idx); + q_m.emplace_back(in.mul_scaling); + q_1.emplace_back(fr::zero()); + q_2.emplace_back(fr::zero()); + q_3.emplace_back(in.c_scaling); + q_c.emplace_back(in.const_scaling); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Create a gate contraining the variable to 0 or 1. + * We set selectors in such a way that we get the + * equation x^2-x=0. + * + * @param variable_index The index of the variable. + * */ +void TurboCircuitConstructor::create_bool_gate(const uint32_t variable_index) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ variable_index }); + + w_l.emplace_back(variable_index); + w_r.emplace_back(variable_index); + w_o.emplace_back(variable_index); + w_4.emplace_back(zero_idx); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + + q_m.emplace_back(fr::one()); + q_1.emplace_back(fr::zero()); + q_2.emplace_back(fr::zero()); + q_3.emplace_back(fr::neg_one()); + q_c.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Create poly gate as in standard composer. + * w_4 is set to zero variable. + * q_range, q_logic, q_4, q_5, q_fixed_base are set to 0. + * q_arith is set to 1. + * + * @param in Contains the values for + * w_l, w_r, w_o, q_m, q_1, q_2, q_3, q_c. + * */ +void TurboCircuitConstructor::create_poly_gate(const poly_triple& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(zero_idx); + q_m.emplace_back(in.q_m); + q_1.emplace_back(in.q_l); + q_2.emplace_back(in.q_r); + q_3.emplace_back(in.q_o); + q_c.emplace_back(in.q_c); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + + q_arith.emplace_back(fr::one()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Add a grumpkin point, from a 2-bit lookup table, into an accumulator point. + * + * @param in Witnesses and values of two points. + * */ +void TurboCircuitConstructor::create_fixed_group_add_gate(const fixed_group_add_quad& in) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + + q_arith.emplace_back(fr::zero()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_m.emplace_back(fr::zero()); + q_c.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + + q_1.emplace_back(in.q_x_1); + q_2.emplace_back(in.q_x_2); + q_3.emplace_back(in.q_y_1); + q_fixed_base.emplace_back(in.q_y_2); + ++num_gates; +} + +/** + * Add a grumpkin point into an accumulator, while also initializing the accumulator. + * + * @param in Addition parameters (points and coefficients). + * @param init Initialization parameters (points). + * */ +void TurboCircuitConstructor::create_fixed_group_add_gate_with_init(const fixed_group_add_quad& in, + const fixed_group_init_quad& init) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ in.a, in.b, in.c, in.d }); + + w_l.emplace_back(in.a); + w_r.emplace_back(in.b); + w_o.emplace_back(in.c); + w_4.emplace_back(in.d); + + q_arith.emplace_back(fr::zero()); + q_4.emplace_back(init.q_x_1); + q_5.emplace_back(init.q_x_2); + q_m.emplace_back(init.q_y_1); + q_c.emplace_back(init.q_y_2); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + + q_1.emplace_back(in.q_x_1); + q_2.emplace_back(in.q_x_2); + q_3.emplace_back(in.q_y_1); + q_fixed_base.emplace_back(in.q_y_2); + ++num_gates; +} + +void TurboCircuitConstructor::create_fixed_group_add_gate_final(const add_quad& in) +{ + create_big_add_gate(in); +} + +/** + * Add a gate that will fix the witness (make it public). + * + * @param witness_index Witness variable index. + * @param witness_value Witness variable value. + * */ +void TurboCircuitConstructor::fix_witness(const uint32_t witness_index, const barretenberg::fr& witness_value) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ witness_index }); + + w_l.emplace_back(witness_index); + w_r.emplace_back(zero_idx); + w_o.emplace_back(zero_idx); + w_4.emplace_back(zero_idx); + q_m.emplace_back(fr::zero()); + q_1.emplace_back(fr::one()); + q_2.emplace_back(fr::zero()); + q_3.emplace_back(fr::zero()); + q_c.emplace_back(-witness_value); + q_arith.emplace_back(fr::one()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + q_logic.emplace_back(fr::zero()); + ++num_gates; +} + +/** + * Create a constraint placing the witness in 2^{num_bits} range. + * + * @param witness_index The index of the witness variable to constrain. + * @param num_bits Constraint size. + * + * @return Vector of variable indexes for accumulator variables used in + * the constraint. + * */ +std::vector TurboCircuitConstructor::decompose_into_base4_accumulators(const uint32_t witness_index, + const size_t num_bits, + std::string const& msg) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ witness_index }); + + ASSERT(num_bits > 0); + + /* + * The range constraint accumulates base 4 values into a sum. + * We do this by evaluating a kind of 'raster scan', where we compare adjacent elements + * and validate that their weighted differences lie in a base for value expansion in powers of 4. + * Let's say that we want to perform a 32-bit range constraint on a field element x. + * We can expand x to the desired length via 16 constituent base-4 'quads' {q_0, ..., q_15}: + * + * 15 + * === + * \ i + * x = / q . 4 + * === i + * i = 0 + * + * In program memory, we place an accumulating base-4 sum of x {a_0, ..., a_15}, where + * + * + * i | + * === | a_0 = q_15 + * \ i - j | a_1 = q_15 . 4 + q_14 + * a = / q . 4 | a_2 = q_15 . 4^2 + q_14 . 4 + q_13 + * i === (15 - j) | ... + * j = 0 | a_15 = x + * + * + * From this, we can use our range transition constraint to validate that + * + * + * a - 4 . a ϵ {0, 1, 2, 3} (for the a_i above, we have + * i + 1 i a_{i+1} - 4.a_i = q_{14-i}, for i = 0, ..., 15), + * + * setting a_{-1} = 0. + * + * We place our accumulating sums in program memory in the following sequence: + * + * +-----+-----+-----+-----+ + * | A | B | C | D | + * +-----+-----+-----+-----+ + * | a2 | a1 | a0 | 0 | + * | a6 | a5 | a4 | a3 | + * | a10 | a9 | a8 | a7 | + * | a14 | a13 | a12 | a11 | + * | --- | --- | --- | a15 | + * +-----+-----+-----+-----+ + * + * Our range transition constraint on row 'i' + * performs our base-4 range check on the follwing pairs: + * + * (D_{i}, C_{i}), (C_{i}, B_{i}), (B_{i}, A_{i}), (A_{i}, D_{i+1}) + * + * We need to start our raster scan at zero, so we simplify matters and just force the first value + * to be zero. + * + * We will prepend 0 quads to our sequence of accumulator values so that the final accumulator value (equal to the + * witness value if the range constraint holds) will be in the 4th column of an otherwise unused row. More + * explicitly, in general (num_bits > 0), a_0 will be placed in column: + * - C if num_bits = 7, 8 mod 8 + * - D if num_bits = 1, 2 mod 8 + * - A if num_bits = 3, 4 mod 8 + * - B if num_bits = 5, 6 mod 8 + * + * Examples: + * 7,8-bit 9,10-bit 11,12-bit 13,14-bit + * +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ + * | A | B | C | D | | A | B | C | D | | A | B | C | D | | A | B | C | D | + * +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ + * | a2 | a1 | a0 | 0 | | 0 | 0 | 0 | 0 | | a0 | 0 | 0 | 0 | | a1 | a0 | 0 | 0 | + * | 0 | 0 | 0 | a3 | | a3 | a2 | a1 | a0 | | a4 | a3 | a2 | a1 | | a5 | a4 | a3 | a2 | + * | --- | --- | --- | --- | | 0 | 0 | 0 | a4 | | 0 | 0 | 0 | a5 | | 0 | 0 | 0 | a6 | + * +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+ + * + * + **/ + + const uint256_t witness_value(get_variable(witness_index)); + + if (witness_value.get_msb() >= num_bits && !failed()) { + failure(msg); + } + /* num_quad_gates is the minimum number of gates needed to record num_bits-many bits in a table, putting two-bits (a + * quad) at each position. Since our table has width 4, we can fit 8 bits on a row, hence num_quad_gates is + * num_bits/8 when num_bits is a multiple of 8, and otherwise it is 1 + (num_bits/8). Because we will always pre-pad + * with 0 quads to ensure that the final accumulator is in the fourth column, num_quad_gates is also equal to one + * less than the total number of rows that will be used to record the accumulator values. */ + size_t num_quad_gates = (num_bits >> 3); + num_quad_gates = (num_quad_gates << 3 == num_bits) ? num_quad_gates : num_quad_gates + 1; + + std::vector* wires[4]{ &w_4, &w_o, &w_r, &w_l }; + + // num_quads = the number of accumulators used in the table, not including the output row. + const size_t num_quads = (num_quad_gates << 2); + // (num_quads << 1) is the number of bits in the non-output row, including 0 quads. + // ((num_quads << 1) - num_bits) >> 1 is the number of padding 0 quads. + const size_t forced_zero_threshold = 1 + (((num_quads << 1) - num_bits) >> 1); + + std::vector accumulators; + fr accumulator(0); + uint32_t most_significant_segment = 0; + // iterate through entries of all but final row + for (size_t i = 0; i < num_quads + 1; ++i) { + uint32_t accumulator_index; + // prepend padding 0 quads + if (i < forced_zero_threshold) { + accumulator_index = zero_idx; + } else { + // accumulate quad + const size_t bit_index = (num_quads - i) << 1; + const uint64_t quad = static_cast(witness_value.get_bit(bit_index)) + + 2ULL * static_cast(witness_value.get_bit(bit_index + 1)); + const fr quad_element = fr{ quad, 0, 0, 0 }.to_montgomery_form(); + accumulator += accumulator; + accumulator += accumulator; + accumulator += quad_element; + + accumulator_index = add_variable(accumulator); + accumulators.emplace_back(accumulator_index); + + if (i == forced_zero_threshold) { + // mark this to constrain top bit to 0 in case num_bits is odd + most_significant_segment = accumulator_index; + } + } + + (*(wires + (i & 3)))->emplace_back(accumulator_index); + } + + // we use one additional gate to record the final accumulator value. + size_t used_gates = 1 + num_quad_gates; + for (size_t i = 0; i < used_gates; ++i) { + q_m.emplace_back(0); + q_1.emplace_back(0); + q_2.emplace_back(0); + q_3.emplace_back(0); + q_c.emplace_back(0); + q_arith.emplace_back(0); + q_4.emplace_back(0); + q_5.emplace_back(0); + q_fixed_base.emplace_back(0); + q_logic.emplace_back(0); + q_range.emplace_back(1); + } + + // switch off range widget for final row; fill wire values not in use with zeros + q_range[q_range.size() - 1] = 0; + w_l.emplace_back(zero_idx); + w_r.emplace_back(zero_idx); + w_o.emplace_back(zero_idx); + + assert_equal(witness_index, accumulators[accumulators.size() - 1], msg); + + accumulators[accumulators.size() - 1] = witness_index; + + num_gates += used_gates; + + // constrain top bit of top quad to zero in case num_bits is odd + if ((num_bits & 1ULL) == 1ULL) { + create_bool_gate(most_significant_segment); + } + return accumulators; +} + +/** + * @brief Implements AND and XOR. + * + * @returns A triple of vectors of accumulator values. + * + * @details If T is the returned triple, then the last element of T.left is guaranteed to be + * our input a, regardless of a's relation with the rest of T.left. For instance, if num_bits is + * smaller than the bit length of a, then the constraint that a is reproduced by T.left will fail: + * for u = T.left[T.left.size()-2], u will be too small to express a in the form a = 4u + quad. + * The same holds, mutatis mutandis, for T.right. + */ +accumulator_triple TurboCircuitConstructor::create_logic_constraint(const uint32_t a, + const uint32_t b, + const size_t num_bits, + const bool is_xor_gate) +{ + TURBO_SELECTOR_REFS + assert_valid_variables({ a, b }); + + ASSERT(((num_bits >> 1U) << 1U) == num_bits); // Do not allow constraint for an odd number of bits. + + // one gate accmulates 1 quads, or 2 bits. + // # gates = (bits / 2) + const size_t num_quads = (num_bits >> 1); + + /* + * The LOGIC constraint accumulates 3 base-4 values (a, b, c) into a sum, where c = a & b OR c = a ^ b + * + * In program memory, we place an accumulating base-4 sum of a, b, c {a_0, ..., a_{(num_bits/2)-1}}, where + * i + * === + * \ i - j + * a = / q . 4 + * i === (num_bits/2 - 1 - j) + * j = 0 + * + * Note that a_0 = q_15, a_1 = 4.q_15 + q_14 = 4.a_0 + q_14, and, in general, we have the + * accumulator relation + * + * a = 4 a + q (for i > 0). + * i + 1 i num_bits/2 - 1 -i + * + * We can use our logic transition constraint to validate that + * + * + * a - 4 . a ϵ [0, 1, 2, 3] + * i + 1 i + * + * + * + * + * b - 4 . b ϵ [0, 1, 2, 3] + * i + 1 i + * + * + * + * / \ / \ + * c - 4 . c = | a - 4 . a | (& OR ^) | b - 4 . b | + * i + 1 i \ i + 1 i / \ i + 1 i / + * + * + * We also need the following temporary, w, stored in program memory: + * + * / \ / \ + * w = | a - 4 . a | * | b - 4 . b | + * i \ i + 1 i / \ i + 1 i / + * + * + * w is needed to prevent the degree of our quotient polynomial from blowing up + * + * We place our accumulating sums in program memory in the following sequence: + * + * +-----+-----+-----+-----+ + * | A | B | C | D | + * +-----+-----+-----+-----+ + * | 0 | 0 | w0 | 0 | + * | a0 | b0 | w1 | c0 | + * | a1 | b1 | w2 | c1 | + * | : | : | : | : | + * | aN | bN | 0 | cN | where N = num_bits/2 - 1 (num_bits is assumed even) + * +-----+-----+-----+-----+ + * + * Typically we will set num_bits = max(num_bits(a), num_bits(b)), so that c computes the AND or XOR + * of a and b, depending on the value of is_xor_gate. + * + * Our transition constraint extracts quads by taking the difference between two accumulating sums, + * so we need to start the chain with a row of zeroes + * + * One additional benefit of this constraint, is that both our inputs and output are in 'native' uint32 form. + * This means we *never* have to decompose a uint32 into bits and back in order to chain together + * addition and logic operations. + **/ + + const uint256_t left_witness_value(get_variable(a)); + const uint256_t right_witness_value(get_variable(b)); + + accumulator_triple accumulators; + fr left_accumulator = fr::zero(); + fr right_accumulator = fr::zero(); + fr out_accumulator = fr::zero(); + + // Step 1: populate 1st row accumulators with zero + w_l.emplace_back(zero_idx); + w_r.emplace_back(zero_idx); + w_4.emplace_back(zero_idx); + + // w_l, w_r, w_4 now point to 1 gate ahead of w_o + for (size_t j = 0; j < num_quads; ++j) { + uint32_t left_accumulator_index; + uint32_t right_accumulator_index; + uint32_t out_accumulator_index; + uint32_t product_index; + + const size_t bit_index = (num_quads - 1 - j) << 1; // subscript of q as defined above + // get quad coeffs of 4^{num_quads - 1 - j} in a and b, respectively + const uint64_t left_quad = static_cast(left_witness_value.get_bit(bit_index)) + + 2ULL * static_cast(left_witness_value.get_bit(bit_index + 1)); + const uint64_t right_quad = static_cast(right_witness_value.get_bit(bit_index)) + + 2ULL * static_cast(right_witness_value.get_bit(bit_index + 1)); + + const fr left_quad_element = fr{ left_quad, 0, 0, 0 }.to_montgomery_form(); + const fr right_quad_element = fr{ right_quad, 0, 0, 0 }.to_montgomery_form(); + fr out_quad_element; + if (is_xor_gate) { + out_quad_element = fr{ left_quad ^ right_quad, 0, 0, 0 }.to_montgomery_form(); + } else { + out_quad_element = fr{ left_quad & right_quad, 0, 0, 0 }.to_montgomery_form(); + } + + const fr product_quad_element = fr{ left_quad * right_quad, 0, 0, 0 }.to_montgomery_form(); + + // replace accumulator by 4.accumulator + quad for a, b and c + left_accumulator += left_accumulator; + left_accumulator += left_accumulator; + left_accumulator += left_quad_element; + + right_accumulator += right_accumulator; + right_accumulator += right_accumulator; + right_accumulator += right_quad_element; + + out_accumulator += out_accumulator; + out_accumulator += out_accumulator; + out_accumulator += out_quad_element; + + left_accumulator_index = add_variable(left_accumulator); + accumulators.left.emplace_back(left_accumulator_index); + + right_accumulator_index = add_variable(right_accumulator); + accumulators.right.emplace_back(right_accumulator_index); + + out_accumulator_index = add_variable(out_accumulator); + accumulators.out.emplace_back(out_accumulator_index); + + product_index = add_variable(product_quad_element); + + w_l.emplace_back(left_accumulator_index); + w_r.emplace_back(right_accumulator_index); + w_4.emplace_back(out_accumulator_index); + w_o.emplace_back(product_index); + } + w_o.emplace_back(zero_idx); + + for (size_t i = 0; i < num_quads + 1; ++i) { + q_m.emplace_back(fr::zero()); + q_1.emplace_back(fr::zero()); + q_2.emplace_back(fr::zero()); + q_3.emplace_back(fr::zero()); + q_arith.emplace_back(fr::zero()); + q_4.emplace_back(fr::zero()); + q_5.emplace_back(fr::zero()); + q_fixed_base.emplace_back(fr::zero()); + q_range.emplace_back(fr::zero()); + if (is_xor_gate) { + q_c.emplace_back(fr::neg_one()); + q_logic.emplace_back(fr::neg_one()); + } else { + q_c.emplace_back(fr::one()); + q_logic.emplace_back(fr::one()); + } + } + + q_c[q_c.size() - 1] = fr::zero(); // last gate is a noop + q_logic[q_logic.size() - 1] = fr::zero(); // last gate is a noop + + assert_equal(a, accumulators.left[accumulators.left.size() - 1], "cannot reproduce `a` value using accumulator."); + + accumulators.left[accumulators.left.size() - 1] = a; + + assert_equal(b, accumulators.right[accumulators.right.size() - 1], "cannot reproduce `b` value using accumulator."); + + accumulators.right[accumulators.right.size() - 1] = b; + + num_gates += (num_quads + 1); + + return accumulators; +} + +accumulator_triple TurboCircuitConstructor::create_and_constraint(const uint32_t a, + const uint32_t b, + const size_t num_bits) +{ + return create_logic_constraint(a, b, num_bits, false); +} + +accumulator_triple TurboCircuitConstructor::create_xor_constraint(const uint32_t a, + const uint32_t b, + const size_t num_bits) +{ + return create_logic_constraint(a, b, num_bits, true); +} + +uint32_t TurboCircuitConstructor::put_constant_variable(const barretenberg::fr& variable) +{ + if (constant_variable_indices.contains(variable)) { + return constant_variable_indices.at(variable); + } else { + uint32_t variable_index = add_variable(variable); + fix_witness(variable_index, variable); + constant_variable_indices.insert({ variable, variable_index }); + return variable_index; + } +} +/** + * @brief Just an arithemtic gate zero-equality test, but with base set to 1 + * + * @param gate_index + * @return true Evaluation is zero + * @return false Evaluation is not zero + */ +inline bool TurboCircuitConstructor::lazy_arithmetic_gate_check(const size_t gate_index) +{ + return arithmetic_gate_evaluation(gate_index, fr::one()).is_zero(); +} + +/** + * @brief Separately checks individual conditions that make up the fixed base gate + * + * @param gate_index Gate index + * @return bool + * TODO(luke/kesha): Add some comments explaining in what sense each of these checks are "lazy" + */ +inline bool TurboCircuitConstructor::lazy_fixed_base_gate_check(const size_t gate_index) +{ + ASSERT(gate_index < num_gates); + + constexpr barretenberg::fr grumpkin_curve_b(-17); + constexpr barretenberg::fr nine(9); + auto& q_m = selectors[TurboSelectors::QM]; + auto& q_c = selectors[TurboSelectors::QC]; + auto& q_1 = selectors[TurboSelectors::Q1]; + auto& q_2 = selectors[TurboSelectors::Q2]; + auto& q_3 = selectors[TurboSelectors::Q3]; + auto& q_4 = selectors[TurboSelectors::Q4]; + auto& q_5 = selectors[TurboSelectors::Q5]; + auto& q_fixed_base = selectors[TurboSelectors::QFIXED]; + + // Get witness values + fr wire_1_shifted; + fr wire_2_shifted; + fr wire_3_shifted; + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_1_shifted = get_variable(w_l[gate_index + 1]); + wire_2_shifted = get_variable(w_r[gate_index + 1]); + wire_3_shifted = get_variable(w_o[gate_index + 1]); + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_1_shifted = fr::zero(); + wire_2_shifted = fr::zero(); + wire_3_shifted = fr::zero(); + wire_4_shifted = fr::zero(); + } + + // Get selector values + const fr q_c_value = q_c[gate_index]; + const fr q_fixed_base_value = q_fixed_base[gate_index]; + const fr q_m_value = q_m[gate_index]; + const fr q_1_value = q_1[gate_index]; + const fr q_2_value = q_2[gate_index]; + const fr q_3_value = q_3[gate_index]; + const fr q_4_value = q_4[gate_index]; + const fr q_5_value = q_5[gate_index]; + + // Compute, optimizing multiplications (different fromt the way we used in widgets, since the linearization + // trick is no more) + + fr delta = wire_4_shifted - (wire_4_value + wire_4_value + wire_4_value + wire_4_value); + fr delta_squared = delta.sqr(); + + // accumulator_identity = (δ + 3)(δ + 1)(δ - 1)(δ - 3) + if (delta_squared != nine && delta_squared != fr::one()) { + return false; + } + + // Check x_alpha_identity + if (!(delta_squared * q_1_value + q_2_value - wire_3_shifted).is_zero()) { + return false; + } + + fr T0 = wire_1_shifted + wire_1_value + wire_3_shifted; + fr T1 = (wire_3_shifted - wire_1_value).sqr(); + T0 = T0 * T1; + + T1 = wire_3_shifted.sqr() * wire_3_shifted; + fr T2 = wire_2_value.sqr(); + T1 = T1 + T2; + T1 = -(T1 + grumpkin_curve_b); + + T2 = delta * wire_2_value * q_fixed_base_value; + T2 = T2 + T2; + fr T3_part = delta * wire_3_shifted * q_3_value; + fr T3 = T3_part * wire_2_value; + T3 = T3 + T3; + + // x_accumulator_identity = α^2 * + // [(w_1,ω + w_1 + w_3,ω) * (w_3,ω - w_1)^2 - (b + w_3,ω^3 + w_2^2) + 2δ * w_2 * q_fixed_base] + if (!(T0 + T1 + T2 + T3).is_zero()) { + return false; + } + T0 = (wire_2_shifted + wire_2_value) * (wire_3_shifted - wire_1_value); + T1 = wire_1_value - wire_1_shifted; + T2 = wire_2_value - (q_fixed_base_value * delta); + T1 = T1 * (T2 - T3_part); + + // y_accumulator_identity = α^3 * + // [(w_2,ω + w_2) * (w_3,ω - w_1) + (w_1 - w_1,ω) * (w_2 - q_fixed_base * δ)] + + if (!(T0 + T1).is_zero()) { + return false; + } + + if (!q_c_value.is_zero()) { + T0 = wire_4_value - fr::one(); + T1 = T0 - wire_3_value; + + if (!T0.is_zero() && !T1.is_zero()) { + return false; + } + T0 = wire_3_value * (q_4_value - wire_1_value) + (fr::one() - wire_4_value) * q_5_value; + if (!T0.is_zero()) { + return false; + } + T0 = q_c_value * (fr::one() - wire_4_value); + T1 = wire_2_value * wire_3_value; + fr y_init_identity = (T0 - T1 + q_m_value * wire_3_value); + if (!y_init_identity.is_zero()) { + return false; + } + } + return true; +} + +/** + * @brief Check if the logic gate should pass. (Checks xor or and of values) + * + * @param gate_index Gate index + * @return fr + */ +inline bool TurboCircuitConstructor::lazy_logic_gate_check(const size_t gate_index) +{ + + ASSERT(gate_index < num_gates); + + auto& q_c = selectors[TurboSelectors::QC]; + auto& q_logic = selectors[TurboSelectors::QLOGIC]; + + fr wire_1_shifted; + fr wire_2_shifted; + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_1_shifted = get_variable(w_l[gate_index + 1]); + wire_2_shifted = get_variable(w_r[gate_index + 1]); + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_1_shifted = fr::zero(); + wire_2_shifted = fr::zero(); + wire_4_shifted = fr::zero(); + } + + // Get selector values + const fr q_c_value = q_c[gate_index]; + const fr q_logic_value = q_logic[gate_index]; + constexpr fr two(2); + constexpr fr three(3); + constexpr fr minus_one = -fr::one(); + + fr delta_sum; + fr delta_squared_sum; + fr T0; + fr T1; + fr T2; + fr T3; + fr T4; + fr identity; + + // T0 = a + T0 = wire_1_value + wire_1_value; + T0 += T0; + T0 = wire_1_shifted - T0; + + if (!T0.is_zero() && T0 != fr::one() && T0 != two && T0 != three) { + return false; + } + // T1 = b + T1 = wire_2_value + wire_2_value; + T1 += T1; + T1 = wire_2_shifted - T1; + + if (!T1.is_zero() && T1 != fr::one() && T1 != two && T1 != three) { + return false; + } + + // T2 = c + T2 = wire_4_value + wire_4_value; + T2 += T2; + T2 = wire_4_shifted - T2; + + if (!T2.is_zero() && T2 != fr::one() && T2 != two && T2 != three) { + return false; + } + uint64_t a = uint256_t(T0).data[0]; + uint64_t b = uint256_t(T1).data[0]; + uint64_t c = uint256_t(T2).data[0]; + + if (q_c_value == fr::one() && q_logic_value == fr::one()) { + return (a & b) == c; + } + + if (q_c_value == minus_one && q_logic_value == minus_one) { + return (a ^ b) == c; + } + return false; +} +/** + * @brief Check if the range gate should pass (checks that all the differences are 0,1,2 or 3) + * + * @param gate_index Gate index + * @return bool + */ +inline bool TurboCircuitConstructor::lazy_range_gate_check(const size_t gate_index) +{ + + ASSERT(gate_index < num_gates); + + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_4_shifted = fr::zero(); + } + constexpr barretenberg::fr two(2); + constexpr barretenberg::fr three(3); + + fr delta_1 = wire_4_value + wire_4_value; + delta_1 += delta_1; + delta_1 = (wire_3_value - delta_1).reduce_once(); + if (!delta_1.is_zero() && delta_1 != fr::one() && delta_1 != two && delta_1 != three) { + return false; + } + + fr delta_2 = wire_3_value + wire_3_value; + delta_2 += delta_2; + delta_2 = wire_2_value - delta_2; + + if (!delta_2.is_zero() && delta_2 != fr::one() && delta_2 != two && delta_2 != three) { + return false; + } + fr delta_3 = wire_2_value + wire_2_value; + delta_3 += delta_3; + delta_3 = wire_1_value - delta_3; + + if (!delta_3.is_zero() && delta_3 != fr::one() && delta_3 != two && delta_3 != three) { + return false; + } + fr delta_4 = wire_1_value + wire_1_value; + delta_4 += delta_4; + delta_4 = wire_4_shifted - delta_4; + + if (!delta_4.is_zero() && delta_4 != fr::one() && delta_4 != two && delta_4 != three) { + return false; + } + + return true; +} +/** + * @brief Evaluate the contribution of the arithmetic gate constraint + * + * @param gate_index Gate index + * @param alpha_base The base value that the whole evaluation is multiplied by + * @return fr + */ +inline fr TurboCircuitConstructor::arithmetic_gate_evaluation(const size_t gate_index, const fr alpha_base) +{ + ASSERT(gate_index < num_gates); + + auto& q_m = selectors[TurboSelectors::QM]; + auto& q_c = selectors[TurboSelectors::QC]; + auto& q_1 = selectors[TurboSelectors::Q1]; + auto& q_2 = selectors[TurboSelectors::Q2]; + auto& q_3 = selectors[TurboSelectors::Q3]; + auto& q_4 = selectors[TurboSelectors::Q4]; + auto& q_5 = selectors[TurboSelectors::Q5]; + auto& q_arith = selectors[TurboSelectors::QARITH]; + + constexpr barretenberg::fr minus_seven(-7); + + constexpr fr two = fr::one() + fr::one(); + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + + // T2 = Δ + fr T2 = wire_4_value + wire_4_value; + T2 += T2; + T2 = wire_3_value - T2; + + // T3 = 2Δ^2 + fr T3 = T2.sqr(); + T3 += T3; + + // T4 = 9.Δ + fr T4 = T2 + T2; + T4 += T2; + // // T5 = 6.Δ + fr T5 = T4 + T4; + T4 += T5; + + // T4 = 9.Δ - 2.Δ^2 - 7 + T4 -= T3; + T4 += minus_seven; + + // T2 = 9.Δ^2 - 2.Δ^3 - 7.Δ + T2 *= T4; + + return alpha_base * q_arith[gate_index] * + (wire_1_value * (q_m[gate_index] * wire_2_value + q_1[gate_index]) + q_2[gate_index] * wire_2_value + + q_3[gate_index] * wire_3_value + + wire_4_value * (q_4[gate_index] + q_5[gate_index] * (wire_4_value - two) * (wire_4_value - fr::one())) + + q_c[gate_index] + (q_arith[gate_index] - 1) * T2); +} +/** + * @brief Evaluate the contribution of the range gate constraint + * + * @param gate_index Gate index + * @param alpha_base The base value that the whole evaluation is multiplied by + * @param alpha An element used as a separator of individual subrelations + * @return fr + */ +inline fr TurboCircuitConstructor::range_gate_evaluation(const size_t gate_index, const fr alpha_base, const fr alpha) +{ + + ASSERT(gate_index < num_gates); + + auto& q_range = selectors[TurboSelectors::QRANGE]; + + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_4_shifted = fr::zero(); + } + constexpr barretenberg::fr minus_two(-2); + constexpr barretenberg::fr minus_three(-3); + fr alpha_a = alpha_base; + fr alpha_b = alpha_a * alpha; + fr alpha_c = alpha_b * alpha; + fr alpha_d = alpha_c * alpha; + + fr delta_1 = wire_4_value + wire_4_value; + delta_1 += delta_1; + delta_1 = wire_3_value - delta_1; + + fr delta_2 = wire_3_value + wire_3_value; + delta_2 += delta_2; + delta_2 = wire_2_value - delta_2; + + fr delta_3 = wire_2_value + wire_2_value; + delta_3 += delta_3; + delta_3 = wire_1_value - delta_3; + + fr delta_4 = wire_1_value + wire_1_value; + delta_4 += delta_4; + delta_4 = wire_4_shifted - delta_4; + + // D(D - 1)(D - 2)(D - 3).alpha + fr T0 = delta_1.sqr(); + T0 -= delta_1; + fr T1 = delta_1 + minus_two; + T0 *= T1; + T1 = delta_1 + minus_three; + T0 *= T1; + fr range_accumulator = T0 * alpha_a; + + T0 = delta_2.sqr(); + T0 -= delta_2; + T1 = delta_2 + minus_two; + T0 *= T1; + T1 = delta_2 + minus_three; + T0 *= T1; + T0 *= alpha_b; + range_accumulator += T0; + + T0 = delta_3.sqr(); + T0 -= delta_3; + T1 = delta_3 + minus_two; + T0 *= T1; + T1 = delta_3 + minus_three; + T0 *= T1; + T0 *= alpha_c; + range_accumulator += T0; + + T0 = delta_4.sqr(); + T0 -= delta_4; + T1 = delta_4 + minus_two; + T0 *= T1; + T1 = delta_4 + minus_three; + T0 *= T1; + T0 *= alpha_d; + range_accumulator += T0; + + return q_range[gate_index] * range_accumulator; +} +/** + * @brief Evaluate the contribution of the logic gate constraint + * + * @param gate_index Gate index + * @param alpha_base The base value the whole evaluation is multiplied by + * @param alpha The element used as separator for individual subrelations + * @return fr + */ +inline fr TurboCircuitConstructor::logic_gate_evaluation(const size_t gate_index, const fr alpha_base, const fr alpha) +{ + + ASSERT(gate_index < num_gates); + + auto& q_c = selectors[TurboSelectors::QC]; + auto& q_logic = selectors[TurboSelectors::QLOGIC]; + + fr wire_1_shifted; + fr wire_2_shifted; + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_1_shifted = get_variable(w_l[gate_index + 1]); + wire_2_shifted = get_variable(w_r[gate_index + 1]); + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_1_shifted = fr::zero(); + wire_2_shifted = fr::zero(); + wire_4_shifted = fr::zero(); + } + + // Get selector values + const fr q_c_value = q_c[gate_index]; + constexpr fr six(6); + constexpr fr eighty_one(81); + constexpr fr eighty_three(83); + + fr delta_sum; + fr delta_squared_sum; + fr T0; + fr T1; + fr T2; + fr T3; + fr T4; + fr identity; + + T0 = wire_1_value + wire_1_value; + T0 += T0; + T0 = wire_1_shifted - T0; + + // T1 = b + T1 = wire_2_value + wire_2_value; + T1 += T1; + T1 = wire_2_shifted - T1; + + // delta_sum = a + b + delta_sum = T0 + T1; + + // T2 = a^2, T3 = b^2 + T2 = T0.sqr(); + T3 = T1.sqr(); + + delta_squared_sum = T2 + T3; + + // identity = a^2 + b^2 + 2ab + identity = delta_sum.sqr(); + // identity = 2ab + identity -= delta_squared_sum; + + // identity = 2(ab - w) + T4 = wire_3_value + wire_3_value; + identity -= T4; + identity *= alpha; + + // T4 = 4w + T4 += T4; + + // T2 = a^2 - a + T2 -= T0; + + // T0 = a^2 - 5a + 6 + T0 += T0; + T0 += T0; + T0 = T2 - T0; + T0 += six; + + // identity = (identity + a(a - 1)(a - 2)(a - 3)) * alpha + T0 *= T2; + identity += T0; + identity *= alpha; + + // T3 = b^2 - b + T3 -= T1; + + // T1 = b^2 - 5b + 6 + T1 += T1; + T1 += T1; + T1 = T3 - T1; + T1 += six; + + // identity = (identity + b(b - 1)(b - 2)(b - 3)) * alpha + T1 *= T3; + identity += T1; + identity *= alpha; + + // T0 = 3(a + b) + T0 = delta_sum + delta_sum; + T0 += delta_sum; + + // T1 = 9(a + b) + T1 = T0 + T0; + T1 += T0; + + // delta_sum = 18(a + b) + delta_sum = T1 + T1; + + // T1 = 81(a + b) + T2 = delta_sum + delta_sum; + T2 += T2; + T1 += T2; + + // delta_squared_sum = 18(a^2 + b^2) + T2 = delta_squared_sum + delta_squared_sum; + T2 += delta_squared_sum; + delta_squared_sum = T2 + T2; + delta_squared_sum += T2; + delta_squared_sum += delta_squared_sum; + + // delta_sum = w(4w - 18(a + b) + 81) + delta_sum = T4 - delta_sum; + delta_sum += eighty_one; + delta_sum *= wire_3_value; + + // T1 = 18(a^2 + b^2) - 81(a + b) + 83 + T1 = delta_squared_sum - T1; + T1 += eighty_three; + + // delta_sum = w ( w ( 4w - 18(a + b) + 81) + 18(a^2 + b^2) - 81(a + b) + 83) + delta_sum += T1; + delta_sum *= wire_3_value; + + // T2 = 3c + T2 = wire_4_value + wire_4_value; + T2 += T2; + T2 = wire_4_shifted - T2; + T3 = T2 + T2; + T2 += T3; + + // T3 = 9c + T3 = T2 + T2; + T3 += T2; + + // T3 = q_c * (9c - 3(a + b)) + T3 -= T0; + T3 *= q_c_value; + + // T2 = 3c + 3(a + b) - 2 * delta_sum + T2 += T0; + delta_sum += delta_sum; + T2 -= delta_sum; + + // T2 = T2 + T3 + T2 += T3; + + // identity = q_logic * alpha_base * (identity + T2) + identity += T2; + identity *= alpha_base; + return identity * q_logic[gate_index]; +} + +/** + * @brief Evaluates the contribution of the fixed_base constraint + * + * @param gate_index Gate index + * @param alpha_powers A vector with alpha_base and alpha_base*α^{i} for enough i + * @return fr + */ +inline fr TurboCircuitConstructor::fixed_base_gate_evaluation(const size_t gate_index, + const std::vector& alpha_powers) +{ + ASSERT(gate_index < num_gates); + + constexpr barretenberg::fr grumpkin_curve_b(-17); + constexpr barretenberg::fr three(3); + auto& q_m = selectors[TurboSelectors::QM]; + auto& q_c = selectors[TurboSelectors::QC]; + auto& q_1 = selectors[TurboSelectors::Q1]; + auto& q_2 = selectors[TurboSelectors::Q2]; + auto& q_3 = selectors[TurboSelectors::Q3]; + auto& q_4 = selectors[TurboSelectors::Q4]; + auto& q_5 = selectors[TurboSelectors::Q5]; + auto& q_fixed_base = selectors[TurboSelectors::QFIXED]; + + // Get witness values + fr wire_1_shifted; + fr wire_2_shifted; + fr wire_3_shifted; + fr wire_4_shifted; + const fr wire_1_value = get_variable(w_l[gate_index]); + const fr wire_2_value = get_variable(w_r[gate_index]); + const fr wire_3_value = get_variable(w_o[gate_index]); + const fr wire_4_value = get_variable(w_4[gate_index]); + if ((gate_index + 1) < num_gates) { + wire_1_shifted = get_variable(w_l[gate_index + 1]); + wire_2_shifted = get_variable(w_r[gate_index + 1]); + wire_3_shifted = get_variable(w_o[gate_index + 1]); + wire_4_shifted = get_variable(w_4[gate_index + 1]); + } else { + wire_1_shifted = fr::zero(); + wire_2_shifted = fr::zero(); + wire_3_shifted = fr::zero(); + wire_4_shifted = fr::zero(); + } + + // Get selector values + const fr q_c_value = q_c[gate_index]; + const fr q_fixed_base_value = q_fixed_base[gate_index]; + const fr q_m_value = q_m[gate_index]; + const fr q_1_value = q_1[gate_index]; + const fr q_2_value = q_2[gate_index]; + const fr q_3_value = q_3[gate_index]; + const fr q_4_value = q_4[gate_index]; + const fr q_5_value = q_5[gate_index]; + + // Compute, optimizing multiplications (different from the way we used in widgets, since the linearization + // trick is no more) + + fr delta = wire_4_shifted - (wire_4_value + wire_4_value + wire_4_value + wire_4_value); + fr delta_squared = delta.sqr(); + + // accumulator_identity = (δ + 3)(δ + 1)(δ - 1)(δ - 3) + fr result = (delta - three) * (delta - fr::one()) * (delta + fr::one()) * (delta + three) * alpha_powers[0]; + + // Originaly q_1 and q_2 multiplicands with x_alpha_identity + result += (delta_squared * q_1_value + q_2_value - wire_3_shifted) * alpha_powers[1]; + fr T1_part = wire_2_value * alpha_powers[2]; + // Added q_3 multiplicand + result += + ((T1_part + T1_part) + (wire_1_shifted - wire_1_value) * alpha_powers[3]) * delta * wire_3_shifted * q_3_value; + + fr T0 = wire_1_shifted + wire_1_value + wire_3_shifted; + fr T1 = (wire_3_shifted - wire_1_value).sqr(); + T0 = T0 * T1; + + T1 = wire_3_shifted.sqr() * wire_3_shifted; + fr T2 = wire_2_value.sqr(); + T1 = T1 + T2; + T1 = -(T1 + grumpkin_curve_b); + + T2 = delta * wire_2_value * q_fixed_base_value; + T2 = T2 + T2; + + // x_accumulator_identity = α^2 * + // [(w_1,ω + w_1 + w_3,ω) * (w_3,ω - w_1)^2 - (b + w_3,ω^3 + w_2^2) + 2δ * w_2 * q_fixed_base] + result += (T0 + T1 + T2) * alpha_powers[2]; + + T0 = (wire_2_shifted + wire_2_value) * (wire_3_shifted - wire_1_value); + T1 = wire_1_value - wire_1_shifted; + T2 = wire_2_value - (q_fixed_base_value * delta); + T1 = T1 * T2; + + // y_accumulator_identity = α^3 * + // [(w_2,ω + w_2) * (w_3,ω - w_1) + (w_1 - w_1,ω) * (w_2 - q_fixed_base * δ)] + + result += (T0 + T1) * alpha_powers[3]; + + T0 = wire_4_value - fr::one(); + T1 = T0 - wire_3_value; + fr accumulator_init_identity = T0 * T1 * alpha_powers[4]; + + // q_4 and q_5 + result += (((wire_3_value * q_4_value + (fr::one() - wire_4_value) * q_5_value) * alpha_powers[5]) + + (q_m_value * wire_3_value * alpha_powers[6])) * + q_c_value; + + // x_init_identity = -α^5 * w_1 * w_3 + fr x_init_identity = -(wire_1_value * wire_3_value) * alpha_powers[5]; + + // y_init_identity = α^6 * (q_c * (1 - w_4) - w_2 * w_3) + T0 = fr::one() - wire_4_value; + T0 = T0 * q_c_value; + T1 = wire_2_value * wire_3_value; + fr y_init_identity = (T0 - T1) * alpha_powers[6]; + + result += (accumulator_init_identity + x_init_identity + y_init_identity) * q_c_value; + return result * q_fixed_base_value; +} +/** + * Check if the circuit constraints hold. + * + * @return true if circuit is correct, false if not. + * */ +bool TurboCircuitConstructor::check_circuit() +{ +//#define LAZY_CIRCUIT_CHECKS +#ifdef LAZY_CIRCUIT_CHECKS + for (size_t i = 0; i < num_gates; i++) { + if (!q_arith[i].is_zero() && !lazy_arithmetic_gate_check(i)) { + return false; + } + if (!q_fixed_base[i].is_zero() && !lazy_fixed_base_gate_check(i)) { + return false; + } + if (!q_range[i].is_zero() && !lazy_range_gate_check(i)) { + return false; + } + if (!q_logic[i].is_zero() && !lazy_logic_gate_check(i)) { + return false; + } + } + return true; +#else + // Initialize each of the kernels + const fr alpha_base = fr::random_element(); + const fr alpha = fr::random_element(); + std::vector alpha_powers; + alpha_powers.push_back(alpha_base); + for (size_t i = 1; i < 7; i++) { + alpha_powers.push_back(alpha_powers[i] * alpha); + } + + for (size_t i = 0; i < get_num_gates(); i++) { + if (!arithmetic_gate_evaluation(i, alpha_base).is_zero()) { +#ifndef FUZZING + info("Arithmetic gate ", i, " failed"); +#endif + return false; + } + + if (!logic_gate_evaluation(i, alpha_base, alpha).is_zero()) { +#ifndef FUZZING + info("Logic gate ", i, " failed"); +#endif + return false; + } + + if (!range_gate_evaluation(i, alpha_base, alpha).is_zero()) { +#ifndef FUZZING + info("Range gate ", i, " failed"); +#endif + return false; + } + + if (!fixed_base_gate_evaluation(i, alpha_powers).is_zero()) { +#ifndef FUZZING + info("Arithmetic gate ", i, " failed"); +#endif + return false; + } + } + return true; + +#endif +} + +} // namespace bonk diff --git a/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp new file mode 100644 index 0000000000..18c5243a97 --- /dev/null +++ b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp @@ -0,0 +1,101 @@ +#pragma once +#include "circuit_constructor_base.hpp" +#include "barretenberg/plonk/proof_system/constants.hpp" +#include "barretenberg/proof_system/flavor/flavor.hpp" + +namespace bonk { + +inline std::vector turbo_selector_names() +{ + std::vector result{ "q_m", "q_c", "q_1", "q_2", "q_3", "q_4", + "q_5", "q_arith", "q_fixed_base", "q_range", "q_logic" }; + return result; +} +class TurboCircuitConstructor : public CircuitConstructorBase { + + enum TurboSelectors { QM, QC, Q1, Q2, Q3, Q4, Q5, QARITH, QFIXED, QRANGE, QLOGIC, NUM }; + + public: + static constexpr plonk::ComposerType type = plonk::ComposerType::TURBO; + static constexpr size_t UINT_LOG2_BASE = 2; + + TurboCircuitConstructor(const size_t size_hint = 0); + TurboCircuitConstructor(TurboCircuitConstructor&& other) = default; + TurboCircuitConstructor& operator=(TurboCircuitConstructor&& other) = default; + ~TurboCircuitConstructor() {} + + void create_add_gate(const add_triple& in); + + void create_big_add_gate(const add_quad& in); + void create_big_add_gate_with_bit_extraction(const add_quad& in); + void create_big_mul_gate(const mul_quad& in); + void create_balanced_add_gate(const add_quad& in); + + void create_mul_gate(const mul_triple& in); + void create_bool_gate(const uint32_t a); + void create_poly_gate(const poly_triple& in); + void create_fixed_group_add_gate(const fixed_group_add_quad& in); + void create_fixed_group_add_gate_with_init(const fixed_group_add_quad& in, const fixed_group_init_quad& init); + void create_fixed_group_add_gate_final(const add_quad& in); + void fix_witness(const uint32_t witness_index, const barretenberg::fr& witness_value); + + barretenberg::fr arithmetic_gate_evaluation(const size_t index, const barretenberg::fr alpha_base); + barretenberg::fr fixed_base_gate_evaluation(const size_t index, const std::vector& alpha_powers); + barretenberg::fr logic_gate_evaluation(const size_t index, + const barretenberg::fr alpha_bas, + const barretenberg::fr alpha); + barretenberg::fr range_gate_evaluation(const size_t index, + const barretenberg::fr alpha_bas, + const barretenberg::fr alpha); + + bool lazy_arithmetic_gate_check(const size_t gate_index); + bool lazy_fixed_base_gate_check(const size_t gate_index); + bool lazy_logic_gate_check(const size_t gate_index); + bool lazy_range_gate_check(const size_t gate_index); + + bool check_circuit(); + + std::vector decompose_into_base4_accumulators(const uint32_t witness_index, + const size_t num_bits, + std::string const& msg); + + void create_range_constraint(const uint32_t variable_index, const size_t num_bits, std::string const& msg) + { + decompose_into_base4_accumulators(variable_index, num_bits, msg); + } + + accumulator_triple create_logic_constraint(const uint32_t a, + const uint32_t b, + const size_t num_bits, + bool is_xor_gate); + accumulator_triple create_and_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); + accumulator_triple create_xor_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); + + uint32_t put_constant_variable(const barretenberg::fr& variable); + + size_t get_num_constant_gates() const { return 0; } + + void assert_equal_constant(const uint32_t a_idx, + const barretenberg::fr& b, + std::string const& msg = "assert_equal_constant") + { + if (variables[a_idx] != b && !failed()) { + failure(msg); + } + auto b_idx = put_constant_variable(b); + assert_equal(a_idx, b_idx, msg); + } + + /** + * For any type other than uint32_t (presumed to be a witness index), we call normalize first. + */ + template + void assert_equal_constant(T const& in, const barretenberg::fr& b, std::string const& msg = "assert_equal_constant") + { + assert_equal_constant(in.normalize().witness_index, b, msg); + } + + // these are variables that we have used a gate on, to enforce that they are equal to a defined value + std::map constant_variable_indices; +}; +} // namespace bonk \ No newline at end of file diff --git a/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.test.cpp b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.test.cpp new file mode 100644 index 0000000000..3be6bbea1e --- /dev/null +++ b/cpp/src/barretenberg/honk/circuit_constructors/turbo_circuit_constructor.test.cpp @@ -0,0 +1,690 @@ +#include "turbo_circuit_constructor.hpp" +#include "barretenberg/crypto/pedersen/pedersen.hpp" +#include + +using namespace barretenberg; +using namespace crypto::pedersen; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} +namespace bonk { +TEST(turbo_circuit_constructor, base_case) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + fr a = fr::one(); + circuit_constructor.add_public_variable(a); + bool result = circuit_constructor.check_circuit(); + EXPECT_EQ(result, true); +} + +TEST(turbo_circuit_constructor, test_add_gate_proofs) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + fr a = fr::one(); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t a_idx = circuit_constructor.add_variable(a); + uint32_t b_idx = circuit_constructor.add_variable(b); + uint32_t c_idx = circuit_constructor.add_variable(c); + uint32_t d_idx = circuit_constructor.add_variable(d); + + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ b_idx, a_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + + // TODO: proof fails if one wire contains all zeros. Should we support this? + uint32_t zero_idx = circuit_constructor.add_variable(fr::zero()); + + circuit_constructor.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, a_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = circuit_constructor.check_circuit(); + EXPECT_EQ(result, true); +} + +TEST(turbo_circuit_constructor, test_mul_gate_proofs) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + fr q[7]{ fr::random_element(), fr::random_element(), fr::random_element(), fr::random_element(), + fr::random_element(), fr::random_element(), fr::random_element() }; + fr q_inv[7]{ + q[0].invert(), q[1].invert(), q[2].invert(), q[3].invert(), q[4].invert(), q[5].invert(), q[6].invert(), + }; + + fr a = fr::random_element(); + fr b = fr::random_element(); + fr c = -((((q[0] * a) + (q[1] * b)) + q[3]) * q_inv[2]); + fr d = -((((q[4] * (a * b)) + q[6]) * q_inv[5])); + + uint32_t a_idx = circuit_constructor.add_public_variable(a); + uint32_t b_idx = circuit_constructor.add_variable(b); + uint32_t c_idx = circuit_constructor.add_variable(c); + uint32_t d_idx = circuit_constructor.add_variable(d); + + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + circuit_constructor.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + circuit_constructor.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + uint32_t zero_idx = circuit_constructor.add_variable(fr::zero()); + uint32_t one_idx = circuit_constructor.add_variable(fr::one()); + circuit_constructor.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + uint32_t e_idx = circuit_constructor.add_variable(a - fr::one()); + circuit_constructor.create_add_gate({ e_idx, b_idx, c_idx, q[0], q[1], q[2], (q[3] + q[0]) }); + + bool result = circuit_constructor.check_circuit(); + EXPECT_EQ(result, true); +} + +TEST(turbo_circuit_constructor, small_scalar_multipliers) +{ + constexpr size_t num_bits = 63; + constexpr size_t num_quads_base = (num_bits - 1) >> 1; + constexpr size_t num_quads = ((num_quads_base << 1) + 1 < num_bits) ? num_quads_base + 1 : num_quads_base; + constexpr size_t num_wnaf_bits = (num_quads << 1) + 1; + constexpr size_t initial_exponent = ((num_bits & 1) == 1) ? num_bits - 1 : num_bits; + constexpr uint64_t bit_mask = (1ULL << num_bits) - 1UL; + auto gen_data = crypto::pedersen::get_generator_data(DEFAULT_GEN_1); + const crypto::pedersen::fixed_base_ladder* ladder = gen_data.get_ladder(num_bits); + grumpkin::g1::affine_element generator = gen_data.generator; + + grumpkin::g1::element origin_points[2]; + origin_points[0] = grumpkin::g1::element(ladder[0].one); + origin_points[1] = origin_points[0] + generator; + origin_points[1] = origin_points[1].normalize(); + + grumpkin::fr scalar_multiplier_entropy = grumpkin::fr::random_element(); + grumpkin::fr scalar_multiplier_base{ scalar_multiplier_entropy.data[0] & bit_mask, 0, 0, 0 }; + // scalar_multiplier_base.data[0] = scalar_multiplier_base.data[0] | (1ULL); + scalar_multiplier_base.data[0] = scalar_multiplier_base.data[0] & (~1ULL); + grumpkin::fr scalar_multiplier = scalar_multiplier_base; + + uint64_t wnaf_entries[num_quads + 1] = { 0 }; + if ((scalar_multiplier_base.data[0] & 1) == 0) { + scalar_multiplier_base.data[0] -= 2; + } + bool skew = false; + barretenberg::wnaf::fixed_wnaf(&scalar_multiplier_base.data[0], &wnaf_entries[0], skew, 0); + + fr accumulator_offset = (fr::one() + fr::one()).pow(static_cast(initial_exponent)).invert(); + fr origin_accumulators[2]{ fr::one(), accumulator_offset + fr::one() }; + + grumpkin::g1::element* multiplication_transcript = + static_cast(aligned_alloc(64, sizeof(grumpkin::g1::element) * (num_quads + 1))); + fr* accumulator_transcript = static_cast(aligned_alloc(64, sizeof(fr) * (num_quads + 1))); + + if (skew) { + multiplication_transcript[0] = origin_points[1]; + accumulator_transcript[0] = origin_accumulators[1]; + } else { + multiplication_transcript[0] = origin_points[0]; + accumulator_transcript[0] = origin_accumulators[0]; + } + + fr one = fr::one(); + fr three = ((one + one) + one); + for (size_t i = 0; i < num_quads; ++i) { + uint64_t entry = wnaf_entries[i + 1] & crypto::pedersen::WNAF_MASK; + fr prev_accumulator = accumulator_transcript[i] + accumulator_transcript[i]; + prev_accumulator = prev_accumulator + prev_accumulator; + + grumpkin::g1::affine_element point_to_add = (entry == 1) ? ladder[i + 1].three : ladder[i + 1].one; + fr scalar_to_add = (entry == 1) ? three : one; + uint64_t predicate = (wnaf_entries[i + 1] >> 31U) & 1U; + if (predicate) { + point_to_add = -point_to_add; + scalar_to_add.self_neg(); + } + accumulator_transcript[i + 1] = prev_accumulator + scalar_to_add; + multiplication_transcript[i + 1] = multiplication_transcript[i] + point_to_add; + } + grumpkin::g1::element::batch_normalize(&multiplication_transcript[0], num_quads + 1); + + fixed_group_init_quad init_quad{ origin_points[0].x, + (origin_points[0].x - origin_points[1].x), + origin_points[0].y, + (origin_points[0].y - origin_points[1].y) }; + + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + fr x_alpha = accumulator_offset; + for (size_t i = 0; i < num_quads; ++i) { + fixed_group_add_quad round_quad; + round_quad.d = circuit_constructor.add_variable(accumulator_transcript[i]); + round_quad.a = circuit_constructor.add_variable(multiplication_transcript[i].x); + round_quad.b = circuit_constructor.add_variable(multiplication_transcript[i].y); + round_quad.c = circuit_constructor.add_variable(x_alpha); + if ((wnaf_entries[i + 1] & 0xffffffU) == 0) { + x_alpha = ladder[i + 1].one.x; + } else { + x_alpha = ladder[i + 1].three.x; + } + round_quad.q_x_1 = ladder[i + 1].q_x_1; + round_quad.q_x_2 = ladder[i + 1].q_x_2; + round_quad.q_y_1 = ladder[i + 1].q_y_1; + round_quad.q_y_2 = ladder[i + 1].q_y_2; + + if (i > 0) { + circuit_constructor.create_fixed_group_add_gate(round_quad); + } else { + circuit_constructor.create_fixed_group_add_gate_with_init(round_quad, init_quad); + } + } + + add_quad add_quad{ circuit_constructor.add_variable(multiplication_transcript[num_quads].x), + circuit_constructor.add_variable(multiplication_transcript[num_quads].y), + circuit_constructor.add_variable(x_alpha), + circuit_constructor.add_variable(accumulator_transcript[num_quads]), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero() }; + circuit_constructor.create_big_add_gate(add_quad); + + grumpkin::g1::element expected_point = + grumpkin::g1::element(generator * scalar_multiplier.to_montgomery_form()).normalize(); + EXPECT_EQ((multiplication_transcript[num_quads].x == expected_point.x), true); + EXPECT_EQ((multiplication_transcript[num_quads].y == expected_point.y), true); + + uint64_t result_accumulator = accumulator_transcript[num_quads].from_montgomery_form().data[0]; + uint64_t expected_accumulator = scalar_multiplier.data[0]; + EXPECT_EQ(result_accumulator, expected_accumulator); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, true); + + free(multiplication_transcript); + free(accumulator_transcript); +} + +TEST(turbo_circuit_constructor, large_scalar_multipliers) +{ + constexpr size_t num_bits = 254; + constexpr size_t num_quads_base = (num_bits - 1) >> 1; + constexpr size_t num_quads = ((num_quads_base << 1) + 1 < num_bits) ? num_quads_base + 1 : num_quads_base; + constexpr size_t num_wnaf_bits = (num_quads << 1) + 1; + + constexpr size_t initial_exponent = num_bits; // ((num_bits & 1) == 1) ? num_bits - 1 : num_bits; + auto gen_data = crypto::pedersen::get_generator_data(DEFAULT_GEN_1); + const crypto::pedersen::fixed_base_ladder* ladder = gen_data.get_ladder(num_bits); + grumpkin::g1::affine_element generator = gen_data.generator; + + grumpkin::g1::element origin_points[2]; + origin_points[0] = grumpkin::g1::element(ladder[0].one); + origin_points[1] = origin_points[0] + generator; + origin_points[1] = origin_points[1].normalize(); + + grumpkin::fr scalar_multiplier_base = grumpkin::fr::random_element(); + + grumpkin::fr scalar_multiplier = scalar_multiplier_base.from_montgomery_form(); + + if ((scalar_multiplier.data[0] & 1) == 0) { + grumpkin::fr two = grumpkin::fr::one() + grumpkin::fr::one(); + scalar_multiplier_base = scalar_multiplier_base - two; + } + scalar_multiplier_base = scalar_multiplier_base.from_montgomery_form(); + uint64_t wnaf_entries[num_quads + 1] = { 0 }; + + bool skew = false; + barretenberg::wnaf::fixed_wnaf(&scalar_multiplier_base.data[0], &wnaf_entries[0], skew, 0); + + fr accumulator_offset = (fr::one() + fr::one()).pow(static_cast(initial_exponent)).invert(); + fr origin_accumulators[2]{ fr::one(), accumulator_offset + fr::one() }; + + grumpkin::g1::element* multiplication_transcript = + static_cast(aligned_alloc(64, sizeof(grumpkin::g1::element) * (num_quads + 1))); + fr* accumulator_transcript = static_cast(aligned_alloc(64, sizeof(fr) * (num_quads + 1))); + + if (skew) { + multiplication_transcript[0] = origin_points[1]; + accumulator_transcript[0] = origin_accumulators[1]; + } else { + multiplication_transcript[0] = origin_points[0]; + accumulator_transcript[0] = origin_accumulators[0]; + } + + fr one = fr::one(); + fr three = ((one + one) + one); + for (size_t i = 0; i < num_quads; ++i) { + uint64_t entry = wnaf_entries[i + 1] & crypto::pedersen::WNAF_MASK; + fr prev_accumulator = accumulator_transcript[i] + accumulator_transcript[i]; + prev_accumulator = prev_accumulator + prev_accumulator; + + grumpkin::g1::affine_element point_to_add = (entry == 1) ? ladder[i + 1].three : ladder[i + 1].one; + fr scalar_to_add = (entry == 1) ? three : one; + uint64_t predicate = (wnaf_entries[i + 1] >> 31U) & 1U; + if (predicate) { + point_to_add = -point_to_add; + scalar_to_add.self_neg(); + } + accumulator_transcript[i + 1] = prev_accumulator + scalar_to_add; + multiplication_transcript[i + 1] = multiplication_transcript[i] + point_to_add; + } + grumpkin::g1::element::batch_normalize(&multiplication_transcript[0], num_quads + 1); + + fixed_group_init_quad init_quad{ origin_points[0].x, + (origin_points[0].x - origin_points[1].x), + origin_points[0].y, + (origin_points[0].y - origin_points[1].y) }; + + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + fr x_alpha = accumulator_offset; + for (size_t i = 0; i < num_quads; ++i) { + fixed_group_add_quad round_quad; + round_quad.d = circuit_constructor.add_variable(accumulator_transcript[i]); + round_quad.a = circuit_constructor.add_variable(multiplication_transcript[i].x); + round_quad.b = circuit_constructor.add_variable(multiplication_transcript[i].y); + round_quad.c = circuit_constructor.add_variable(x_alpha); + if ((wnaf_entries[i + 1] & 0xffffffU) == 0) { + x_alpha = ladder[i + 1].one.x; + } else { + x_alpha = ladder[i + 1].three.x; + } + round_quad.q_x_1 = ladder[i + 1].q_x_1; + round_quad.q_x_2 = ladder[i + 1].q_x_2; + round_quad.q_y_1 = ladder[i + 1].q_y_1; + round_quad.q_y_2 = ladder[i + 1].q_y_2; + + if (i > 0) { + circuit_constructor.create_fixed_group_add_gate(round_quad); + } else { + circuit_constructor.create_fixed_group_add_gate_with_init(round_quad, init_quad); + } + } + + add_quad add_quad{ circuit_constructor.add_variable(multiplication_transcript[num_quads].x), + circuit_constructor.add_variable(multiplication_transcript[num_quads].y), + circuit_constructor.add_variable(x_alpha), + circuit_constructor.add_variable(accumulator_transcript[num_quads]), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero() }; + circuit_constructor.create_big_add_gate(add_quad); + + grumpkin::g1::element expected_point = + grumpkin::g1::element(generator * scalar_multiplier.to_montgomery_form()).normalize(); + EXPECT_EQ((multiplication_transcript[num_quads].x == expected_point.x), true); + EXPECT_EQ((multiplication_transcript[num_quads].y == expected_point.y), true); + + fr result_accumulator = (accumulator_transcript[num_quads]); + fr expected_accumulator = + fr{ scalar_multiplier.data[0], scalar_multiplier.data[1], scalar_multiplier.data[2], scalar_multiplier.data[3] } + .to_montgomery_form(); + EXPECT_EQ((result_accumulator == expected_accumulator), true); + + bool result = circuit_constructor.check_circuit(); + EXPECT_EQ(result, true); + + free(multiplication_transcript); + free(accumulator_transcript); +} + +TEST(turbo_circuit_constructor, range_constraint) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + for (size_t i = 0; i < 10; ++i) { + uint32_t value = engine.get_random_uint32(); + fr witness_value = fr{ value, 0, 0, 0 }.to_montgomery_form(); + uint32_t witness_index = circuit_constructor.add_variable(witness_value); + + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + std::vector accumulators = circuit_constructor.decompose_into_base4_accumulators( + witness_index, 32 + extra_bits, "constraint in test range_constraint fails"); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t result = (value >> (30U - (2 * j))); + fr source = circuit_constructor.get_variable(accumulators[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t expected = static_cast(source.data[0]); + EXPECT_EQ(result, expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (value >> (30U - (2 * j))); + uint32_t right = (value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = circuit_constructor.add_variable(fr::zero()); + uint32_t one_idx = circuit_constructor.add_variable(fr::one()); + circuit_constructor.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, true); +} + +TEST(turbo_circuit_constructor, range_constraint_fail) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + uint64_t value = 0xffffff; + uint32_t witness_index = circuit_constructor.add_variable(fr(value)); + + circuit_constructor.decompose_into_base4_accumulators(witness_index, 23, "yay, range constraint fails"); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, false); +} + +/** + * @brief Test that the `AND` constraint fails when constraining too few bits. + * + */ +TEST(turbo_circuit_constructor, and_constraint_failure) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + uint32_t left_value = 4; + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = circuit_constructor.add_variable(left_witness_value); + + uint32_t right_value = 5; + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = circuit_constructor.add_variable(right_witness_value); + + // 4 && 5 is 4, so 3 bits are needed, but we only constrain 2 + accumulator_triple accumulators = + circuit_constructor.create_and_constraint(left_witness_index, right_witness_index, 2); + + bool result = circuit_constructor.check_circuit(); + + if (circuit_constructor.failed()) { + info("circuit_constructor failed; ", circuit_constructor.err()); + } + + EXPECT_EQ(result, false); +} + +TEST(turbo_circuit_constructor, and_constraint) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + for (size_t i = 0; i < /*10*/ 1; ++i) { + uint32_t left_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = circuit_constructor.add_variable(left_witness_value); + + uint32_t right_value = engine.get_random_uint32(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = circuit_constructor.add_variable(right_witness_value); + + uint32_t out_value = left_value & right_value; + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + accumulator_triple accumulators = + circuit_constructor.create_and_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + // circuit_constructor.create_and_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t left_expected = (left_value >> (30U - (2 * j))); + uint32_t right_expected = (right_value >> (30U - (2 * j))); + uint32_t out_expected = left_expected & right_expected; + + fr left_source = + circuit_constructor.get_variable(accumulators.left[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t left_result = static_cast(left_source.data[0]); + + fr right_source = + circuit_constructor.get_variable(accumulators.right[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t right_result = static_cast(right_source.data[0]); + + fr out_source = + circuit_constructor.get_variable(accumulators.out[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t out_result = static_cast(out_source.data[0]); + + EXPECT_EQ(left_result, left_expected); + EXPECT_EQ(right_result, right_expected); + EXPECT_EQ(out_result, out_expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (left_value >> (30U - (2 * j))); + uint32_t right = (left_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (right_value >> (30U - (2 * j))); + right = (right_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (out_value >> (30U - (2 * j))); + right = (out_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = circuit_constructor.add_variable(fr::zero()); + uint32_t one_idx = circuit_constructor.add_variable(fr::one()); + circuit_constructor.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, true); +} + +/** + * @brief Test that the `XOR` constraint fails when constraining too few bits. + * + */ +TEST(turbo_circuit_constructor, xor_constraint_failure) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + uint32_t left_value = 4; + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = circuit_constructor.add_variable(left_witness_value); + + uint32_t right_value = 1; + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = circuit_constructor.add_variable(right_witness_value); + + // 4 && 1 is 5, so 3 bits are needed, but we only constrain 2 + accumulator_triple accumulators = + circuit_constructor.create_and_constraint(left_witness_index, right_witness_index, 2); + + bool result = circuit_constructor.check_circuit(); + + if (circuit_constructor.failed()) { + info("circuit_constructor failed; ", circuit_constructor.err()); + } + + EXPECT_EQ(result, false); +} + +TEST(turbo_circuit_constructor, xor_constraint) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + for (size_t i = 0; i < /*10*/ 1; ++i) { + uint32_t left_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = circuit_constructor.add_variable(left_witness_value); + + uint32_t right_value = engine.get_random_uint32(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = circuit_constructor.add_variable(right_witness_value); + + uint32_t out_value = left_value ^ right_value; + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + accumulator_triple accumulators = + circuit_constructor.create_xor_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t left_expected = (left_value >> (30U - (2 * j))); + uint32_t right_expected = (right_value >> (30U - (2 * j))); + uint32_t out_expected = left_expected ^ right_expected; + + fr left_source = + circuit_constructor.get_variable(accumulators.left[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t left_result = static_cast(left_source.data[0]); + + fr right_source = + circuit_constructor.get_variable(accumulators.right[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t right_result = static_cast(right_source.data[0]); + + fr out_source = + circuit_constructor.get_variable(accumulators.out[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t out_result = static_cast(out_source.data[0]); + + EXPECT_EQ(left_result, left_expected); + EXPECT_EQ(right_result, right_expected); + EXPECT_EQ(out_result, out_expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (left_value >> (30U - (2 * j))); + uint32_t right = (left_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (right_value >> (30U - (2 * j))); + right = (right_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (out_value >> (30U - (2 * j))); + right = (out_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = circuit_constructor.add_variable(fr::zero()); + uint32_t one_idx = circuit_constructor.add_variable(fr::one()); + circuit_constructor.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, true); +} + +TEST(turbo_circuit_constructor, big_add_gate_with_bit_extract) +{ + TurboCircuitConstructor circuit_constructor = TurboCircuitConstructor(); + + const auto generate_constraints = [&circuit_constructor](uint32_t quad_value) { + uint32_t quad_accumulator_left = + (engine.get_random_uint32() & 0x3fffffff) - quad_value; // make sure this won't overflow + uint32_t quad_accumulator_right = (4 * quad_accumulator_left) + quad_value; + + uint32_t left_idx = circuit_constructor.add_variable(uint256_t(quad_accumulator_left)); + uint32_t right_idx = circuit_constructor.add_variable(uint256_t(quad_accumulator_right)); + + uint32_t input = engine.get_random_uint32(); + uint32_t output = input + (quad_value > 1 ? 1 : 0); + + add_quad gate{ circuit_constructor.add_variable(uint256_t(input)), + circuit_constructor.add_variable(uint256_t(output)), + right_idx, + left_idx, + fr(6), + -fr(6), + fr::zero(), + fr::zero(), + fr::zero() }; + + circuit_constructor.create_big_add_gate_with_bit_extraction(gate); + }; + + generate_constraints(0); + generate_constraints(1); + generate_constraints(2); + generate_constraints(3); + + bool result = circuit_constructor.check_circuit(); + + EXPECT_EQ(result, true); +} + +} // namespace bonk diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.cpp index fbaff76a2d..8cbf8df51f 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.cpp @@ -7,6 +7,7 @@ #include "composer_helper_lib.hpp" #include "barretenberg/honk/pcs/commitment_key.hpp" #include "barretenberg/honk/circuit_constructors/standard_circuit_constructor.hpp" +#include "barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp" namespace bonk { /** @@ -68,12 +69,31 @@ void construct_lagrange_selector_forms(const CircuitConstructor& circuit_constru // this does not clash with any other values we want to place at the end of of the witness vectors. In later // iterations of the Sumcheck, we will be able to efficiently cancel out any checks in the last 2^k rows, so any // randomness or unique values should be placed there. - circuit_proving_key->polynomial_store.put(circuit_constructor.selector_names_[j] + "_lagrange", std::move(selector_poly_lagrange)); } } +/** + * @brief Fill the last index of each selector polynomial in lagrange form with a non-zero value + * + * @tparam CircuitConstructor The class holding the circuit + * @param circuit_constructor The object holding the circuit + * @param key Pointer to the proving key + */ +template +void enforce_nonzero_polynomial_selectors(const CircuitConstructor& circuit_constructor, + bonk::proving_key* circuit_proving_key) +{ + for (size_t j = 0; j < circuit_constructor.num_selectors; ++j) { + auto current_selector = + circuit_proving_key->polynomial_store.get(circuit_constructor.selector_names_[j] + "_lagrange"); + current_selector[current_selector.size() - 1] = j + 1; + circuit_proving_key->polynomial_store.put(circuit_constructor.selector_names_[j] + "_lagrange", + std::move(current_selector)); + } +} + /** * @brief Retrieve lagrange forms of selector polynomials and compute monomial and coset-monomial forms and put into * cache @@ -102,7 +122,6 @@ void compute_monomial_and_coset_selector_forms(bonk::proving_key* circuit_provin // Remove the selector lagrange forms since they will not be needed beyond this point circuit_proving_key->polynomial_store.remove(selector_properties[i].name + "_lagrange"); - circuit_proving_key->polynomial_store.put(selector_properties[i].name, std::move(selector_poly)); circuit_proving_key->polynomial_store.put(selector_properties[i].name + "_fft", std::move(selector_poly_fft)); } @@ -175,7 +194,7 @@ std::vector compute_witness_base(const CircuitConstruc * (1) commitments to the selector, permutation, and lagrange (first/last) polynomials, * (2) sets the polynomial manifest using the data from proving key. */ -std::shared_ptr compute_verification_key_base_common( +std::shared_ptr compute_verification_key_common( std::shared_ptr const& proving_key, std::shared_ptr const& vrs) { auto circuit_verification_key = std::make_shared( @@ -206,12 +225,18 @@ std::shared_ptr compute_verification_key_base_common( return circuit_verification_key; } - -template std::shared_ptr initialize_proving_key( - const StandardCircuitConstructor&, bonk::ReferenceStringFactory*, const size_t, const size_t, plonk::ComposerType); -template void construct_lagrange_selector_forms(const StandardCircuitConstructor&, - bonk::proving_key*); -template std::vector compute_witness_base( - const StandardCircuitConstructor&, const size_t, const size_t); +// Ensure we compile all versions so that there are no issues during linkage +#define COMPILE_FOR_CIRCUIT_CONSTRUCTOR(circuit_constructor) \ + template std::shared_ptr initialize_proving_key( \ + const circuit_constructor&, bonk::ReferenceStringFactory*, const size_t, const size_t, plonk::ComposerType); \ + template void construct_lagrange_selector_forms(const circuit_constructor&, \ + bonk::proving_key*); \ + template std::vector compute_witness_base( \ + const circuit_constructor&, const size_t, const size_t); \ + template void enforce_nonzero_polynomial_selectors(const circuit_constructor& constructor, \ + bonk::proving_key* circuit_proving_key); + +COMPILE_FOR_CIRCUIT_CONSTRUCTOR(StandardCircuitConstructor) +COMPILE_FOR_CIRCUIT_CONSTRUCTOR(TurboCircuitConstructor) } // namespace bonk diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.hpp b/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.hpp index 096b62588e..7387270fb5 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.hpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/composer_helper_lib.hpp @@ -27,7 +27,7 @@ std::shared_ptr initialize_proving_key(const CircuitConstruct plonk::ComposerType composer_type); /** - * @brief Construct lagrange selector polynomials from ciruit selector information and put into polynomial cache + * @brief Construct lagrange selector polynomials from circuit selector information and put into polynomial cache * * @tparam CircuitConstructor The class holding the circuit * @param circuit_constructor The object holding the circuit @@ -36,6 +36,16 @@ std::shared_ptr initialize_proving_key(const CircuitConstruct template void construct_lagrange_selector_forms(const CircuitConstructor& circuit_constructor, bonk::proving_key* key); +/** + * @brief Fill the last index of each selector polynomial in lagrange form with a non-zero value + * + * @tparam CircuitConstructor The class holding the circuit + * @param circuit_constructor The object holding the circuit + * @param key Pointer to the proving key + */ +template +void enforce_nonzero_polynomial_selectors(const CircuitConstructor& circuit_constructor, bonk::proving_key* key); + /** * @brief Retrieve lagrange forms of selector polynomials and compute monomial and coset-monomial forms and put into * cache @@ -65,7 +75,7 @@ std::vector compute_witness_base(const CircuitConstruc * (1) commitments to the selector, permutation, and lagrange (first/last) polynomials, * (2) sets the polynomial manifest using the data from proving key. */ -std::shared_ptr compute_verification_key_base_common( +std::shared_ptr compute_verification_key_common( std::shared_ptr const& proving_key, std::shared_ptr const& vrs); } // namespace bonk diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/permutation_helper.hpp b/cpp/src/barretenberg/honk/composer/composer_helper/permutation_helper.hpp index 51f02c84a7..92bf892055 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/permutation_helper.hpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/permutation_helper.hpp @@ -397,6 +397,7 @@ void compute_standard_honk_id_polynomials(auto key) // proving_key* and shared_p * @param circuit_constructor * @param key */ +// TODO(#293): Update this (and all similar functions) to take a smart pointer. template void compute_standard_honk_sigma_permutations(CircuitConstructor& circuit_constructor, bonk::proving_key* key) { diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.cpp index 18926d0591..6bd7c532d9 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.cpp @@ -13,51 +13,6 @@ namespace bonk { -/** - * Compute proving key base. - * - * 1. Load crs. - * 2. Initialize this.circuit_proving_key. - * 3. Create constraint selector polynomials from each of this composer's `selectors` vectors and add them to the - * proving key. - * - * N.B. Need to add the fix for coefficients - * - * @param minimum_circuit_size Used as the total number of gates when larger than n + count of public inputs. - * @param num_reserved_gates The number of reserved gates. - * @return Pointer to the initialized proving key updated with selector polynomials. - * */ -template -std::shared_ptr StandardPlonkComposerHelper::compute_proving_key_base( - const CircuitConstructor& constructor, const size_t minimum_circuit_size, const size_t num_randomized_gates) -{ - - // Initialize circuit_proving_key - // TODO(#229)(Kesha): replace composer types. - circuit_proving_key = initialize_proving_key( - constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, plonk::ComposerType::STANDARD); - // Compute lagrange selectors - construct_lagrange_selector_forms(constructor, circuit_proving_key.get()); - // Compute selectors in monomial form - compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), standard_selector_properties()); - - return circuit_proving_key; -} - -/** - * @brief Computes the verification key by computing the: - * (1) commitments to the selector, permutation, and lagrange (first/last) polynomials, - * (2) sets the polynomial manifest using the data from proving key. - */ - -template -std::shared_ptr StandardPlonkComposerHelper::compute_verification_key_base( - std::shared_ptr const& proving_key, std::shared_ptr const& vrs) -{ - - return compute_verification_key_base_common(proving_key, vrs); -} - /** * Compute witness polynomials (w_1, w_2, w_3, w_4). * @@ -87,12 +42,16 @@ void StandardPlonkComposerHelper::compute_witness(const Circ } /** - * Compute proving key. - * Compute the polynomials q_l, q_r, etc. and sigma polynomial. + * Compute proving key + * + * 1. Load crs. + * 2. Initialize this.circuit_proving_key. + * 3. Create constraint selector polynomials from each of this composer's `selectors` vectors and add them to the + * proving key. + * 4. Compute sigma polynomial * - * @return Proving key with saved computed polynomials. + * @return Pointer to the initialized proving key updated with selector polynomials. * */ - template std::shared_ptr StandardPlonkComposerHelper::compute_proving_key( const CircuitConstructor& circuit_constructor) @@ -100,8 +59,21 @@ std::shared_ptr StandardPlonkComposerHelper(circuit_constructor, @@ -130,8 +102,7 @@ std::shared_ptr StandardPlonkComposerHelperget_verifier_crs()); + circuit_verification_key = compute_verification_key_common(circuit_proving_key, crs_factory_->get_verifier_crs()); circuit_verification_key->composer_type = circuit_proving_key->composer_type; circuit_verification_key->recursive_proof_public_input_indices = std::vector(recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.hpp b/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.hpp index 29df317602..df366c7ff3 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.hpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/standard_plonk_composer_helper.hpp @@ -8,7 +8,6 @@ #include "barretenberg/honk/pcs/commitment_key.hpp" #include "barretenberg/proof_system/verification_key/verification_key.hpp" #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" -#include "barretenberg/proof_system/composer/composer_base.hpp" #include "composer_helper_lib.hpp" #include "permutation_helper.hpp" @@ -79,18 +78,6 @@ template class StandardPlonkComposerHelper { plonk::Verifier create_verifier(const CircuitConstructor& circuit_constructor); plonk::Prover create_prover(const CircuitConstructor& circuit_constructor); - // TODO(#216)(Adrian): Seems error prone to provide the number of randomized gates - // Cody: Where should this go? In the flavor (or whatever that becomes)? - std::shared_ptr compute_proving_key_base( - const CircuitConstructor& circuit_constructor, - const size_t minimum_ciricut_size = 0, - const size_t num_randomized_gates = NUM_RANDOMIZED_GATES); - // This needs to be static as it may be used only to compute the selector commitments. - - static std::shared_ptr compute_verification_key_base( - std::shared_ptr const& proving_key, - std::shared_ptr const& vrs); - void compute_witness(const CircuitConstructor& circuit_constructor, const size_t minimum_circuit_size = 0); /** * Create a manifest, which specifies proof rounds, elements and who supplies them. diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.cpp new file mode 100644 index 0000000000..7c62536d4d --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.cpp @@ -0,0 +1,180 @@ +#include "turbo_plonk_composer_helper.hpp" +#include "barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp" +#include "barretenberg/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/plonk/proof_system/widgets/random_widgets/permutation_widget.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/turbo_arithmetic_widget.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/fixed_base_widget.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/turbo_logic_widget.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/turbo_range_widget.hpp" +#include "barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/transition_widget.hpp" +#include "barretenberg/plonk/proof_system/widgets/transition_widgets/turbo_arithmetic_widget.hpp" +#include "permutation_helper.hpp" +using namespace barretenberg; + +namespace bonk { + +/** + * Compute proving key + * + * 1. Load crs. + * 2. Initialize this.circuit_proving_key. + * 3. Create constraint selector polynomials from each of this composer's `selectors` vectors and add them to the + * proving key. + * 4. Compute sigma polynomial + * + * @return Pointer to the initialized proving key updated with selector polynomials. + * */ +template +std::shared_ptr TurboPlonkComposerHelper::compute_proving_key( + const CircuitConstructor& circuit_constructor) +{ + if (circuit_proving_key) { + return circuit_proving_key; + } + const size_t minimum_circuit_size = 0; + const size_t num_randomized_gates = NUM_RANDOMIZED_GATES; + // Initialize circuit_proving_key + // TODO(#229)(Kesha): replace composer types. + circuit_proving_key = initialize_proving_key(circuit_constructor, + crs_factory_.get(), + minimum_circuit_size, + num_randomized_gates, + plonk::ComposerType::TURBO); + + construct_lagrange_selector_forms(circuit_constructor, circuit_proving_key.get()); + + enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get()); + + compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), turbo_selector_properties()); + + // Compute sigma polynomials (TODO(kesha): we should update that late) + bonk::compute_standard_plonk_sigma_permutations(circuit_constructor, + circuit_proving_key.get()); + circuit_proving_key->recursive_proof_public_input_indices = + std::vector(recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); + circuit_proving_key->contains_recursive_proof = contains_recursive_proof; + return circuit_proving_key; +} + +/** + * Compute verification key consisting of selector precommitments. + * + * @return Pointer to created circuit verification key. + * */ +template +std::shared_ptr TurboPlonkComposerHelper::compute_verification_key( + const CircuitConstructor& circuit_constructor) +{ + if (circuit_verification_key) { + return circuit_verification_key; + } + if (!circuit_proving_key) { + compute_proving_key(circuit_constructor); + } + + circuit_verification_key = compute_verification_key_common(circuit_proving_key, crs_factory_->get_verifier_crs()); + circuit_verification_key->composer_type = circuit_proving_key->composer_type; + circuit_verification_key->recursive_proof_public_input_indices = + std::vector(recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); + circuit_verification_key->contains_recursive_proof = contains_recursive_proof; + + return circuit_verification_key; +} +/** + * Compute witness polynomials (w_1, w_2, w_3, w_4). + * + * @details Fills 3 or 4 witness polynomials w_1, w_2, w_3, w_4 with the values of in-circuit variables. The beginning + * of w_1, w_2 polynomials is filled with public_input values. + * @return Witness with computed witness polynomials. + * + * @tparam Program settings needed to establish if w_4 is being used. + * */ +template +void TurboPlonkComposerHelper::compute_witness(const CircuitConstructor& circuit_constructor, + const size_t minimum_circuit_size) +{ + + if (computed_witness) { + return; + } + auto wire_polynomial_evaluations = + compute_witness_base(circuit_constructor, minimum_circuit_size, NUM_RANDOMIZED_GATES); + + for (size_t j = 0; j < program_width; ++j) { + std::string index = std::to_string(j + 1); + circuit_proving_key->polynomial_store.put("w_" + index + "_lagrange", + std::move(wire_polynomial_evaluations[j])); + } + computed_witness = true; +} +/** + * Create prover. + * 1. Compute the starting polynomials (q_l, etc, sigma, witness polynomials). + * 2. Initialize StandardProver with them. + * 3. Add Permutation and arithmetic widgets to the prover. + * 4. Add KateCommitmentScheme to the prover. + * + * @return Initialized prover. + * */ +template +plonk::TurboProver TurboPlonkComposerHelper::create_prover( + const CircuitConstructor& circuit_constructor) +{ + // Compute q_l, etc. and sigma polynomials. + compute_proving_key(circuit_constructor); + + // Compute witness polynomials. + compute_witness(circuit_constructor); + + plonk::TurboProver output_state(circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size())); + + auto permutation_widget = std::make_unique>(circuit_proving_key.get()); + + auto arithmetic_widget = std::make_unique>(circuit_proving_key.get()); + auto fixed_base_widget = std::make_unique>(circuit_proving_key.get()); + auto range_widget = std::make_unique>(circuit_proving_key.get()); + auto logic_widget = std::make_unique>(circuit_proving_key.get()); + + output_state.random_widgets.emplace_back(std::move(permutation_widget)); + + output_state.transition_widgets.emplace_back(std::move(arithmetic_widget)); + output_state.transition_widgets.emplace_back(std::move(fixed_base_widget)); + output_state.transition_widgets.emplace_back(std::move(range_widget)); + output_state.transition_widgets.emplace_back(std::move(logic_widget)); + + std::unique_ptr> kate_commitment_scheme = + std::make_unique>(); + + output_state.commitment_scheme = std::move(kate_commitment_scheme); + + return output_state; +} + +/** + * Create verifier: compute verification key, + * initialize verifier with it and an initial manifest and initialize commitment_scheme. + * + * @return The verifier. + * */ +// TODO(Cody): This should go away altogether. +template +plonk::TurboVerifier TurboPlonkComposerHelper::create_verifier( + const CircuitConstructor& circuit_constructor) +{ + auto verification_key = compute_verification_key(circuit_constructor); + + plonk::TurboVerifier output_state(circuit_verification_key, + create_manifest(circuit_constructor.public_inputs.size())); + + std::unique_ptr> kate_commitment_scheme = + std::make_unique>(); + + output_state.commitment_scheme = std::move(kate_commitment_scheme); + + return output_state; +} + +template class TurboPlonkComposerHelper; +} // namespace bonk diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.hpp b/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.hpp new file mode 100644 index 0000000000..60c9355aac --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/composer_helper/turbo_plonk_composer_helper.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include "composer_helper_lib.hpp" +#include "barretenberg/srs/reference_string/file_reference_string.hpp" +#include "barretenberg/proof_system/proving_key/proving_key.hpp" +#include "barretenberg/plonk/proof_system/prover/prover.hpp" +#include "barretenberg/plonk/proof_system/verifier/verifier.hpp" + +namespace bonk { +template class TurboPlonkComposerHelper { + public: + static constexpr size_t NUM_RANDOMIZED_GATES = 2; // equal to the number of multilinear evaluations leaked + static constexpr size_t program_width = CircuitConstructor::program_width; + static constexpr ComposerType type = ComposerType::TURBO; + static constexpr MerkleHashType merkle_hash_type = MerkleHashType::FIXED_BASE_PEDERSEN; + static constexpr size_t UINT_LOG2_BASE = 2; + std::shared_ptr circuit_proving_key; + std::shared_ptr circuit_verification_key; + + // TODO(#218)(kesha): we need to put this into the commitment key, so that the composer doesn't have to handle srs + // at all + std::shared_ptr crs_factory_; + + std::vector recursive_proof_public_input_indices; + bool contains_recursive_proof = false; + + bool computed_witness = false; + TurboPlonkComposerHelper() + : TurboPlonkComposerHelper( + std::shared_ptr(new bonk::FileReferenceStringFactory("../srs_db/ignition"))) + {} + + TurboPlonkComposerHelper(std::shared_ptr crs_factory) + : crs_factory_(std::move(crs_factory)) + {} + TurboPlonkComposerHelper(std::unique_ptr&& crs_factory) + : crs_factory_(std::move(crs_factory)) + {} + TurboPlonkComposerHelper(std::shared_ptr p_key, std::shared_ptr v_key) + : circuit_proving_key(std::move(p_key)) + , circuit_verification_key(std::move(v_key)) + {} + TurboPlonkComposerHelper(TurboPlonkComposerHelper&& other) = default; + TurboPlonkComposerHelper& operator=(TurboPlonkComposerHelper&& other) noexcept = default; + ~TurboPlonkComposerHelper() {} + + std::shared_ptr compute_proving_key(const CircuitConstructor& circuit_constructor); + std::shared_ptr compute_verification_key(const CircuitConstructor& circuit_constructor); + void compute_witness(const CircuitConstructor& circuit_constructor, const size_t minimum_circuit_size = 0); + + TurboProver create_prover(const CircuitConstructor& circuit_constructor); + TurboVerifier create_verifier(const CircuitConstructor& circuit_constructor); + inline std::vector turbo_selector_properties() + { + const std::vector result{ + { "q_m", false }, { "q_c", false }, { "q_1", false }, { "q_2", false }, + { "q_3", false }, { "q_4", false }, { "q_5", false }, { "q_arith", false }, + { "q_fixed_base", false }, { "q_range", false }, { "q_logic", false }, + }; + return result; + } + void add_recursive_proof(CircuitConstructor& circuit_constructor, + const std::vector& proof_output_witness_indices) + { + + if (contains_recursive_proof) { + circuit_constructor.failure("added recursive proof when one already exists"); + } + contains_recursive_proof = true; + + for (const auto& idx : proof_output_witness_indices) { + circuit_constructor.set_public_input(idx); + recursive_proof_public_input_indices.push_back((uint32_t)(circuit_constructor.public_inputs.size() - 1)); + } + } + static transcript::Manifest create_manifest(const size_t num_public_inputs) + { + // add public inputs.... + constexpr size_t g1_size = 64; + constexpr size_t fr_size = 32; + const size_t public_input_size = fr_size * num_public_inputs; + const transcript::Manifest output = transcript::Manifest( + { transcript::Manifest::RoundManifest( + { { "circuit_size", 4, true }, { "public_input_size", 4, true } }, "init", 1), + + transcript::Manifest::RoundManifest({}, "eta", 0), + + transcript::Manifest::RoundManifest( + { + { "public_inputs", public_input_size, false }, + { "W_1", g1_size, false }, + { "W_2", g1_size, false }, + { "W_3", g1_size, false }, + { "W_4", g1_size, false }, + }, + "beta", + 2), + transcript::Manifest::RoundManifest({ { "Z_PERM", g1_size, false } }, "alpha", 1), + transcript::Manifest::RoundManifest( + { + { "T_1", g1_size, false }, + { "T_2", g1_size, false }, + { "T_3", g1_size, false }, + { "T_4", g1_size, false }, + }, + "z", + 1), + + transcript::Manifest::RoundManifest( + { + { "t", fr_size, true, -1 }, { "w_1", fr_size, false, 0 }, + { "w_2", fr_size, false, 1 }, { "w_3", fr_size, false, 2 }, + { "w_4", fr_size, false, 3 }, { "sigma_1", fr_size, false, 4 }, + { "sigma_2", fr_size, false, 5 }, { "sigma_3", fr_size, false, 6 }, + { "sigma_4", fr_size, false, 7 }, { "q_1", fr_size, false, 8 }, + { "q_2", fr_size, false, 9 }, { "q_3", fr_size, false, 10 }, + { "q_4", fr_size, false, 11 }, { "q_5", fr_size, false, 12 }, + { "q_m", fr_size, false, 13 }, { "q_c", fr_size, false, 14 }, + { "q_arith", fr_size, false, 15 }, { "q_logic", fr_size, false, 16 }, + { "q_range", fr_size, false, 17 }, { "q_fixed_base", fr_size, false, 18 }, + { "z_perm", fr_size, false, 19 }, { "z_perm_omega", fr_size, false, 19 }, + { "w_1_omega", fr_size, false, 0 }, { "w_2_omega", fr_size, false, 1 }, + { "w_3_omega", fr_size, false, 2 }, { "w_4_omega", fr_size, false, 3 }, + }, + "nu", + TURBO_MANIFEST_SIZE, + true), + + transcript::Manifest::RoundManifest( + { { "PI_Z", g1_size, false }, { "PI_Z_OMEGA", g1_size, false } }, "separator", 3) }); + + return output; + } +}; +} // namespace bonk diff --git a/cpp/src/barretenberg/honk/composer/standard_honk_composer.hpp b/cpp/src/barretenberg/honk/composer/standard_honk_composer.hpp index 7b69ef834c..ec3cbb9ee0 100644 --- a/cpp/src/barretenberg/honk/composer/standard_honk_composer.hpp +++ b/cpp/src/barretenberg/honk/composer/standard_honk_composer.hpp @@ -28,7 +28,7 @@ class StandardHonkComposer { // Leaving it in for now just in case bool contains_recursive_proof = false; - static constexpr size_t program_width = STANDARD_HONK_WIDTH; + static constexpr size_t program_width = STANDARD_WIDTH; /**Standard methods*/ diff --git a/cpp/src/barretenberg/honk/composer/standard_plonk_composer.hpp b/cpp/src/barretenberg/honk/composer/standard_plonk_composer.hpp index 12e5b6bf56..9932139354 100644 --- a/cpp/src/barretenberg/honk/composer/standard_plonk_composer.hpp +++ b/cpp/src/barretenberg/honk/composer/standard_plonk_composer.hpp @@ -29,7 +29,7 @@ class StandardPlonkComposer { // Leaving it in for now just in case bool contains_recursive_proof = false; - static constexpr size_t program_width = STANDARD_HONK_WIDTH; + static constexpr size_t program_width = STANDARD_WIDTH; /**Standard methods*/ @@ -187,7 +187,6 @@ class StandardPlonkComposer { * * @return A new initialized prover. */ - plonk::Prover preprocess() { return composer_helper.create_prover(circuit_constructor); }; plonk::Prover create_prover() { return composer_helper.create_prover(circuit_constructor); }; static transcript::Manifest create_manifest(const size_t num_public_inputs) diff --git a/cpp/src/barretenberg/honk/composer/standard_plonk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_plonk_composer.test.cpp index 86d0dfb6b4..9241c94fa5 100644 --- a/cpp/src/barretenberg/honk/composer/standard_plonk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_plonk_composer.test.cpp @@ -102,7 +102,7 @@ TEST(standard_composer, test_add_gate_proofs) composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -181,7 +181,7 @@ TEST(standard_composer, test_mul_gate_proofs) composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -223,7 +223,7 @@ TEST(standard_composer, range_constraint) composer.create_big_add_gate( { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -314,7 +314,7 @@ TEST(standard_composer, and_constraint) composer.create_big_add_gate( { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -384,7 +384,7 @@ TEST(standard_composer, xor_constraint) composer.create_big_add_gate( { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -428,7 +428,7 @@ TEST(standard_composer, big_add_gate_with_bit_extract) generate_constraints(2); generate_constraints(3); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); @@ -445,7 +445,7 @@ TEST(standard_composer, test_range_constraint_fail) uint32_t witness_index = composer.add_variable(fr::neg_one()); composer.decompose_into_base4_accumulators(witness_index, 32); - plonk::Prover prover = composer.preprocess(); + plonk::Prover prover = composer.create_prover(); plonk::Verifier verifier = composer.create_verifier(); diff --git a/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.hpp b/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.hpp new file mode 100644 index 0000000000..6f79cbaa71 --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.hpp @@ -0,0 +1,208 @@ +#pragma once +#include "composer_helper/turbo_plonk_composer_helper.hpp" +#include "barretenberg/honk/circuit_constructors/turbo_circuit_constructor.hpp" +using namespace bonk; +namespace plonk { +/** + * @brief Standard Plonk Composer has everything required to construct a prover and verifier, just as the legacy + * classes. + * + * @details However, it has a lot of its logic separated into subclasses and simply proxies the calls. + * + */ +class TurboPlonkComposer { + public: + static constexpr plonk::ComposerType type = plonk::ComposerType::STANDARD; + + static constexpr size_t UINT_LOG2_BASE = 2; + + // An instantiation of the circuit constructor that only depends on arithmetization, not on the proof system + TurboCircuitConstructor circuit_constructor; + // Composer helper contains all proof-related material that is separate from circuit creation such as: + // 1) Proving and verification keys + // 2) CRS + // 3) Converting variables to witness vectors/polynomials + TurboPlonkComposerHelper composer_helper; + size_t& num_gates; + std::vector& variables; + + // Leaving it in for now just in case + bool contains_recursive_proof = false; + static constexpr size_t program_width = TurboCircuitConstructor::program_width; + + /**Standard methods*/ + + TurboPlonkComposer(const size_t size_hint = 0) + : circuit_constructor(size_hint) + , num_gates(circuit_constructor.num_gates) + , variables(circuit_constructor.variables){}; + + TurboPlonkComposer(std::string const& crs_path, const size_t size_hint = 0) + : TurboPlonkComposer( + std::unique_ptr(new bonk::FileReferenceStringFactory(crs_path)), + size_hint){}; + + TurboPlonkComposer(std::shared_ptr const& crs_factory, const size_t size_hint = 0) + : circuit_constructor(size_hint) + , composer_helper(crs_factory) + , num_gates(circuit_constructor.num_gates) + , variables(circuit_constructor.variables){}; + + TurboPlonkComposer(std::unique_ptr&& crs_factory, const size_t size_hint = 0) + : circuit_constructor(size_hint) + , composer_helper(std::move(crs_factory)) + , num_gates(circuit_constructor.num_gates) + , variables(circuit_constructor.variables){}; + + TurboPlonkComposer(std::shared_ptr const& p_key, + std::shared_ptr const& v_key, + size_t size_hint = 0) + : circuit_constructor(size_hint) + , composer_helper(p_key, v_key) + , num_gates(circuit_constructor.num_gates) + , variables(circuit_constructor.variables){}; + + TurboPlonkComposer(const TurboPlonkComposer& other) = delete; + TurboPlonkComposer(TurboPlonkComposer&& other) = default; + TurboPlonkComposer& operator=(const TurboPlonkComposer& other) = delete; + // TODO(#230)(Cody): This constructor started to be implicitly deleted when I added `n` and `variables` members. + // This is a temporary measure until we can rewrite Plonk and all tests using circuit builder methods in place of + // composer methods, where appropriate. TurboPlonkComposer& operator=(TurboPlonkComposer&& other) = default; + ~TurboPlonkComposer() = default; + + /**Methods related to circuit construction + * They simply get proxied to the circuit constructor + */ + + size_t get_num_gates() const { return circuit_constructor.get_num_gates(); } + + void assert_equal(const uint32_t a_variable_idx, const uint32_t b_variable_idx, std::string const& msg) + { + circuit_constructor.assert_equal(a_variable_idx, b_variable_idx, msg); + } + void assert_equal_constant(uint32_t const a_idx, + barretenberg::fr const& b, + std::string const& msg = "assert equal constant") + { + circuit_constructor.assert_equal_constant(a_idx, b, msg); + } + + void create_add_gate(const add_triple& in) { circuit_constructor.create_add_gate(in); } + void create_mul_gate(const mul_triple& in) { circuit_constructor.create_mul_gate(in); } + void create_bool_gate(const uint32_t a) { circuit_constructor.create_bool_gate(a); } + void create_poly_gate(const poly_triple& in) { circuit_constructor.create_poly_gate(in); } + void create_fixed_group_add_gate(const fixed_group_add_quad& in) + { + circuit_constructor.create_fixed_group_add_gate(in); + } + void create_fixed_group_add_gate_with_init(const fixed_group_add_quad& in, const fixed_group_init_quad& init) + { + circuit_constructor.create_fixed_group_add_gate_with_init(in, init); + } + void create_fixed_group_add_gate_final(const add_quad& in) + { + circuit_constructor.create_fixed_group_add_gate_final(in); + } + void create_big_add_gate(const add_quad& in) { circuit_constructor.create_big_add_gate(in); } + void create_big_add_gate_with_bit_extraction(const add_quad& in) + { + circuit_constructor.create_big_add_gate_with_bit_extraction(in); + } + void create_big_mul_gate(const mul_quad& in) { circuit_constructor.create_big_mul_gate(in); } + void create_balanced_add_gate(const add_quad& in) { circuit_constructor.create_balanced_add_gate(in); } + + void fix_witness(const uint32_t witness_index, const barretenberg::fr& witness_value) + { + circuit_constructor.fix_witness(witness_index, witness_value); + } + + std::vector decompose_into_base4_accumulators(const uint32_t witness_index, + + const size_t num_bits, + std::string const& msg = "create_range_constraint") + { + return circuit_constructor.decompose_into_base4_accumulators(witness_index, num_bits, msg); + } + + void create_range_constraint(const uint32_t variable_index, + const size_t num_bits, + std::string const& msg = "create_range_constraint") + { + circuit_constructor.create_range_constraint(variable_index, num_bits, msg); + } + + accumulator_triple create_logic_constraint(const uint32_t a, + const uint32_t b, + const size_t num_bits, + bool is_xor_gate) + { + return circuit_constructor.create_logic_constraint(a, b, num_bits, is_xor_gate); + } + + accumulator_triple create_and_constraint(const uint32_t a, const uint32_t b, const size_t num_bits) + { + return circuit_constructor.create_and_constraint(a, b, num_bits); + } + + accumulator_triple create_xor_constraint(const uint32_t a, const uint32_t b, const size_t num_bits) + { + return circuit_constructor.create_xor_constraint(a, b, num_bits); + } + uint32_t add_variable(const barretenberg::fr& in) { return circuit_constructor.add_variable(in); } + + uint32_t add_public_variable(const barretenberg::fr& in) { return circuit_constructor.add_public_variable(in); } + + virtual void set_public_input(const uint32_t witness_index) + { + return circuit_constructor.set_public_input(witness_index); + } + + uint32_t put_constant_variable(const barretenberg::fr& variable) + { + return circuit_constructor.put_constant_variable(variable); + } + + size_t get_num_constant_gates() const { return circuit_constructor.get_num_constant_gates(); } + + bool check_circuit() { return circuit_constructor.check_circuit(); } + + barretenberg::fr get_variable(const uint32_t index) const { return circuit_constructor.get_variable(index); } + /**Proof and verification-related methods*/ + + std::shared_ptr compute_proving_key() + { + return composer_helper.compute_proving_key(circuit_constructor); + } + + std::shared_ptr compute_verification_key() + { + return composer_helper.compute_verification_key(circuit_constructor); + } + + uint32_t zero_idx = 0; + + void compute_witness() { composer_helper.compute_witness(circuit_constructor); }; + // TODO(#230)(Cody): This will not be needed, but maybe something is required for ComposerHelper to be generic? + plonk::TurboVerifier create_verifier() { return composer_helper.create_verifier(circuit_constructor); } + /** + * Preprocess the circuit. Delegates to create_prover. + * + * @return A new initialized prover. + */ + /** + * Preprocess the circuit. Delegates to create_unrolled_prover. + * + * @return A new initialized prover. + */ + plonk::TurboProver create_prover() { return composer_helper.create_prover(circuit_constructor); }; + + static transcript::Manifest create_manifest(const size_t num_public_inputs) + { + return TurboPlonkComposerHelper::create_manifest(num_public_inputs); + } + + bool failed() const { return circuit_constructor.failed(); }; + const std::string& err() const { return circuit_constructor.err(); }; + void failure(std::string msg) { circuit_constructor.failure(msg); } +}; +} // namespace plonk diff --git a/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.test.cpp new file mode 100644 index 0000000000..9a336c3472 --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/turbo_plonk_composer.test.cpp @@ -0,0 +1,1153 @@ +#include "turbo_plonk_composer.hpp" +#include "barretenberg/crypto/pedersen/pedersen.hpp" +#include +#include "barretenberg/proof_system/proving_key/serialize.hpp" + +using namespace barretenberg; +using namespace bonk; +using namespace crypto::pedersen; + +namespace { +auto& engine = numeric::random::get_debug_engine(); +} +TEST(turbo_plonk_composer, base_case) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr a = fr::one(); + composer.add_public_variable(a); + + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, composer_from_serialized_keys) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr a = fr::one(); + composer.add_public_variable(a); + + auto pk_buf = to_buffer(*composer.compute_proving_key()); + auto vk_buf = to_buffer(*composer.compute_verification_key()); + auto pk_data = from_buffer(pk_buf); + auto vk_data = from_buffer(vk_buf); + + auto crs = std::make_unique("../srs_db/ignition"); + auto proving_key = + std::make_shared(std::move(pk_data), crs->get_prover_crs(pk_data.circuit_size + 1)); + auto verification_key = std::make_shared(std::move(vk_data), crs->get_verifier_crs()); + + TurboPlonkComposer composer2 = TurboPlonkComposer(proving_key, verification_key); + composer2.add_public_variable(a); + + auto prover = composer2.create_prover(); + auto verifier = composer2.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, test_add_gate_proofs) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr a = fr::one(); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t a_idx = composer.add_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ b_idx, a_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + + // TODO: proof fails if one wire contains all zeros. Should we support this? + uint32_t zero_idx = composer.add_variable(fr::zero()); + + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, a_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, test_mul_gate_proofs) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr q[7]{ fr::random_element(), fr::random_element(), fr::random_element(), fr::random_element(), + fr::random_element(), fr::random_element(), fr::random_element() }; + fr q_inv[7]{ + q[0].invert(), q[1].invert(), q[2].invert(), q[3].invert(), q[4].invert(), q[5].invert(), q[6].invert(), + }; + + fr a = fr::random_element(); + fr b = fr::random_element(); + fr c = -((((q[0] * a) + (q[1] * b)) + q[3]) * q_inv[2]); + fr d = -((((q[4] * (a * b)) + q[6]) * q_inv[5])); + + uint32_t a_idx = composer.add_public_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + uint32_t e_idx = composer.add_variable(a - fr::one()); + composer.create_add_gate({ e_idx, b_idx, c_idx, q[0], q[1], q[2], (q[3] + q[0]) }); + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, small_scalar_multipliers) +{ + constexpr size_t num_bits = 63; + constexpr size_t num_quads_base = (num_bits - 1) >> 1; + constexpr size_t num_quads = ((num_quads_base << 1) + 1 < num_bits) ? num_quads_base + 1 : num_quads_base; + constexpr size_t num_wnaf_bits = (num_quads << 1) + 1; + constexpr size_t initial_exponent = ((num_bits & 1) == 1) ? num_bits - 1 : num_bits; + constexpr uint64_t bit_mask = (1ULL << num_bits) - 1UL; + auto gen_data = crypto::pedersen::get_generator_data(DEFAULT_GEN_1); + const crypto::pedersen::fixed_base_ladder* ladder = gen_data.get_ladder(num_bits); + grumpkin::g1::affine_element generator = gen_data.generator; + + grumpkin::g1::element origin_points[2]; + origin_points[0] = grumpkin::g1::element(ladder[0].one); + origin_points[1] = origin_points[0] + generator; + origin_points[1] = origin_points[1].normalize(); + + grumpkin::fr scalar_multiplier_entropy = grumpkin::fr::random_element(); + grumpkin::fr scalar_multiplier_base{ scalar_multiplier_entropy.data[0] & bit_mask, 0, 0, 0 }; + // scalar_multiplier_base.data[0] = scalar_multiplier_base.data[0] | (1ULL); + scalar_multiplier_base.data[0] = scalar_multiplier_base.data[0] & (~1ULL); + grumpkin::fr scalar_multiplier = scalar_multiplier_base; + + uint64_t wnaf_entries[num_quads + 1] = { 0 }; + if ((scalar_multiplier_base.data[0] & 1) == 0) { + scalar_multiplier_base.data[0] -= 2; + } + bool skew = false; + barretenberg::wnaf::fixed_wnaf(&scalar_multiplier_base.data[0], &wnaf_entries[0], skew, 0); + + fr accumulator_offset = (fr::one() + fr::one()).pow(static_cast(initial_exponent)).invert(); + fr origin_accumulators[2]{ fr::one(), accumulator_offset + fr::one() }; + + grumpkin::g1::element* multiplication_transcript = + static_cast(aligned_alloc(64, sizeof(grumpkin::g1::element) * (num_quads + 1))); + fr* accumulator_transcript = static_cast(aligned_alloc(64, sizeof(fr) * (num_quads + 1))); + + if (skew) { + multiplication_transcript[0] = origin_points[1]; + accumulator_transcript[0] = origin_accumulators[1]; + } else { + multiplication_transcript[0] = origin_points[0]; + accumulator_transcript[0] = origin_accumulators[0]; + } + + fr one = fr::one(); + fr three = ((one + one) + one); + for (size_t i = 0; i < num_quads; ++i) { + uint64_t entry = wnaf_entries[i + 1] & crypto::pedersen::WNAF_MASK; + fr prev_accumulator = accumulator_transcript[i] + accumulator_transcript[i]; + prev_accumulator = prev_accumulator + prev_accumulator; + + grumpkin::g1::affine_element point_to_add = (entry == 1) ? ladder[i + 1].three : ladder[i + 1].one; + fr scalar_to_add = (entry == 1) ? three : one; + uint64_t predicate = (wnaf_entries[i + 1] >> 31U) & 1U; + if (predicate) { + point_to_add = -point_to_add; + scalar_to_add.self_neg(); + } + accumulator_transcript[i + 1] = prev_accumulator + scalar_to_add; + multiplication_transcript[i + 1] = multiplication_transcript[i] + point_to_add; + } + grumpkin::g1::element::batch_normalize(&multiplication_transcript[0], num_quads + 1); + + fixed_group_init_quad init_quad{ origin_points[0].x, + (origin_points[0].x - origin_points[1].x), + origin_points[0].y, + (origin_points[0].y - origin_points[1].y) }; + + TurboPlonkComposer composer = TurboPlonkComposer(); + + fr x_alpha = accumulator_offset; + for (size_t i = 0; i < num_quads; ++i) { + fixed_group_add_quad round_quad; + round_quad.d = composer.add_variable(accumulator_transcript[i]); + round_quad.a = composer.add_variable(multiplication_transcript[i].x); + round_quad.b = composer.add_variable(multiplication_transcript[i].y); + round_quad.c = composer.add_variable(x_alpha); + if ((wnaf_entries[i + 1] & 0xffffffU) == 0) { + x_alpha = ladder[i + 1].one.x; + } else { + x_alpha = ladder[i + 1].three.x; + } + round_quad.q_x_1 = ladder[i + 1].q_x_1; + round_quad.q_x_2 = ladder[i + 1].q_x_2; + round_quad.q_y_1 = ladder[i + 1].q_y_1; + round_quad.q_y_2 = ladder[i + 1].q_y_2; + + if (i > 0) { + composer.create_fixed_group_add_gate(round_quad); + } else { + composer.create_fixed_group_add_gate_with_init(round_quad, init_quad); + } + } + + add_quad add_quad{ composer.add_variable(multiplication_transcript[num_quads].x), + composer.add_variable(multiplication_transcript[num_quads].y), + composer.add_variable(x_alpha), + composer.add_variable(accumulator_transcript[num_quads]), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero() }; + composer.create_big_add_gate(add_quad); + + grumpkin::g1::element expected_point = + grumpkin::g1::element(generator * scalar_multiplier.to_montgomery_form()).normalize(); + EXPECT_EQ((multiplication_transcript[num_quads].x == expected_point.x), true); + EXPECT_EQ((multiplication_transcript[num_quads].y == expected_point.y), true); + + uint64_t result_accumulator = accumulator_transcript[num_quads].from_montgomery_form().data[0]; + uint64_t expected_accumulator = scalar_multiplier.data[0]; + EXPECT_EQ(result_accumulator, expected_accumulator); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); + + free(multiplication_transcript); + free(accumulator_transcript); +} + +TEST(turbo_plonk_composer, large_scalar_multipliers) +{ + constexpr size_t num_bits = 254; + constexpr size_t num_quads_base = (num_bits - 1) >> 1; + constexpr size_t num_quads = ((num_quads_base << 1) + 1 < num_bits) ? num_quads_base + 1 : num_quads_base; + constexpr size_t num_wnaf_bits = (num_quads << 1) + 1; + + constexpr size_t initial_exponent = num_bits; // ((num_bits & 1) == 1) ? num_bits - 1 : num_bits; + auto gen_data = crypto::pedersen::get_generator_data(DEFAULT_GEN_1); + const crypto::pedersen::fixed_base_ladder* ladder = gen_data.get_ladder(num_bits); + grumpkin::g1::affine_element generator = gen_data.generator; + + grumpkin::g1::element origin_points[2]; + origin_points[0] = grumpkin::g1::element(ladder[0].one); + origin_points[1] = origin_points[0] + generator; + origin_points[1] = origin_points[1].normalize(); + + grumpkin::fr scalar_multiplier_base = grumpkin::fr::random_element(); + + grumpkin::fr scalar_multiplier = scalar_multiplier_base.from_montgomery_form(); + + if ((scalar_multiplier.data[0] & 1) == 0) { + grumpkin::fr two = grumpkin::fr::one() + grumpkin::fr::one(); + scalar_multiplier_base = scalar_multiplier_base - two; + } + scalar_multiplier_base = scalar_multiplier_base.from_montgomery_form(); + uint64_t wnaf_entries[num_quads + 1] = { 0 }; + + bool skew = false; + barretenberg::wnaf::fixed_wnaf(&scalar_multiplier_base.data[0], &wnaf_entries[0], skew, 0); + + fr accumulator_offset = (fr::one() + fr::one()).pow(static_cast(initial_exponent)).invert(); + fr origin_accumulators[2]{ fr::one(), accumulator_offset + fr::one() }; + + grumpkin::g1::element* multiplication_transcript = + static_cast(aligned_alloc(64, sizeof(grumpkin::g1::element) * (num_quads + 1))); + fr* accumulator_transcript = static_cast(aligned_alloc(64, sizeof(fr) * (num_quads + 1))); + + if (skew) { + multiplication_transcript[0] = origin_points[1]; + accumulator_transcript[0] = origin_accumulators[1]; + } else { + multiplication_transcript[0] = origin_points[0]; + accumulator_transcript[0] = origin_accumulators[0]; + } + + fr one = fr::one(); + fr three = ((one + one) + one); + for (size_t i = 0; i < num_quads; ++i) { + uint64_t entry = wnaf_entries[i + 1] & crypto::pedersen::WNAF_MASK; + fr prev_accumulator = accumulator_transcript[i] + accumulator_transcript[i]; + prev_accumulator = prev_accumulator + prev_accumulator; + + grumpkin::g1::affine_element point_to_add = (entry == 1) ? ladder[i + 1].three : ladder[i + 1].one; + fr scalar_to_add = (entry == 1) ? three : one; + uint64_t predicate = (wnaf_entries[i + 1] >> 31U) & 1U; + if (predicate) { + point_to_add = -point_to_add; + scalar_to_add.self_neg(); + } + accumulator_transcript[i + 1] = prev_accumulator + scalar_to_add; + multiplication_transcript[i + 1] = multiplication_transcript[i] + point_to_add; + } + grumpkin::g1::element::batch_normalize(&multiplication_transcript[0], num_quads + 1); + + fixed_group_init_quad init_quad{ origin_points[0].x, + (origin_points[0].x - origin_points[1].x), + origin_points[0].y, + (origin_points[0].y - origin_points[1].y) }; + + TurboPlonkComposer composer = TurboPlonkComposer(); + + fr x_alpha = accumulator_offset; + for (size_t i = 0; i < num_quads; ++i) { + fixed_group_add_quad round_quad; + round_quad.d = composer.add_variable(accumulator_transcript[i]); + round_quad.a = composer.add_variable(multiplication_transcript[i].x); + round_quad.b = composer.add_variable(multiplication_transcript[i].y); + round_quad.c = composer.add_variable(x_alpha); + if ((wnaf_entries[i + 1] & 0xffffffU) == 0) { + x_alpha = ladder[i + 1].one.x; + } else { + x_alpha = ladder[i + 1].three.x; + } + round_quad.q_x_1 = ladder[i + 1].q_x_1; + round_quad.q_x_2 = ladder[i + 1].q_x_2; + round_quad.q_y_1 = ladder[i + 1].q_y_1; + round_quad.q_y_2 = ladder[i + 1].q_y_2; + + if (i > 0) { + composer.create_fixed_group_add_gate(round_quad); + } else { + composer.create_fixed_group_add_gate_with_init(round_quad, init_quad); + } + } + + add_quad add_quad{ composer.add_variable(multiplication_transcript[num_quads].x), + composer.add_variable(multiplication_transcript[num_quads].y), + composer.add_variable(x_alpha), + composer.add_variable(accumulator_transcript[num_quads]), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero() }; + composer.create_big_add_gate(add_quad); + + grumpkin::g1::element expected_point = + grumpkin::g1::element(generator * scalar_multiplier.to_montgomery_form()).normalize(); + EXPECT_EQ((multiplication_transcript[num_quads].x == expected_point.x), true); + EXPECT_EQ((multiplication_transcript[num_quads].y == expected_point.y), true); + + fr result_accumulator = (accumulator_transcript[num_quads]); + fr expected_accumulator = + fr{ scalar_multiplier.data[0], scalar_multiplier.data[1], scalar_multiplier.data[2], scalar_multiplier.data[3] } + .to_montgomery_form(); + EXPECT_EQ((result_accumulator == expected_accumulator), true); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); + + free(multiplication_transcript); + free(accumulator_transcript); +} + +TEST(turbo_plonk_composer, range_constraint) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + for (size_t i = 0; i < 10; ++i) { + uint32_t value = engine.get_random_uint32(); + fr witness_value = fr{ value, 0, 0, 0 }.to_montgomery_form(); + uint32_t witness_index = composer.add_variable(witness_value); + + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + std::vector accumulators = composer.decompose_into_base4_accumulators( + witness_index, 32 + extra_bits, "constraint in test range_constraint fails"); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t result = (value >> (30U - (2 * j))); + fr source = composer.get_variable(accumulators[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t expected = static_cast(source.data[0]); + EXPECT_EQ(result, expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (value >> (30U - (2 * j))); + uint32_t right = (value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, range_constraint_fail) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + uint64_t value = 0xffffff; + uint32_t witness_index = composer.add_variable(fr(value)); + + composer.decompose_into_base4_accumulators(witness_index, 23, "yay, range constraint fails"); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + plonk::proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, false); +} + +/** + * @brief Test that the `AND` constraint fails when constraining too few bits. + * + */ +TEST(turbo_plonk_composer, and_constraint_failure) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + uint32_t left_value = 4; + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = composer.add_variable(left_witness_value); + + uint32_t right_value = 5; + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + // 4 && 5 is 4, so 3 bits are needed, but we only constrain 2 + accumulator_triple accumulators = composer.create_and_constraint(left_witness_index, right_witness_index, 2); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + if (composer.failed()) { + info("Composer failed; ", composer.err()); + } + + EXPECT_EQ(result, false); +} + +TEST(turbo_plonk_composer, and_constraint) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + for (size_t i = 0; i < /*10*/ 1; ++i) { + uint32_t left_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = composer.add_variable(left_witness_value); + + uint32_t right_value = engine.get_random_uint32(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + uint32_t out_value = left_value & right_value; + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + accumulator_triple accumulators = + composer.create_and_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + // composer.create_and_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t left_expected = (left_value >> (30U - (2 * j))); + uint32_t right_expected = (right_value >> (30U - (2 * j))); + uint32_t out_expected = left_expected & right_expected; + + fr left_source = composer.get_variable(accumulators.left[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t left_result = static_cast(left_source.data[0]); + + fr right_source = composer.get_variable(accumulators.right[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t right_result = static_cast(right_source.data[0]); + + fr out_source = composer.get_variable(accumulators.out[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t out_result = static_cast(out_source.data[0]); + + EXPECT_EQ(left_result, left_expected); + EXPECT_EQ(right_result, right_expected); + EXPECT_EQ(out_result, out_expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (left_value >> (30U - (2 * j))); + uint32_t right = (left_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (right_value >> (30U - (2 * j))); + right = (right_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (out_value >> (30U - (2 * j))); + right = (out_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); +} + +/** + * @brief Test that the `XOR` constraint fails when constraining too few bits. + * + */ +TEST(turbo_plonk_composer, xor_constraint_failure) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + uint32_t left_value = 4; + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = composer.add_variable(left_witness_value); + + uint32_t right_value = 1; + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + // 4 && 1 is 5, so 3 bits are needed, but we only constrain 2 + accumulator_triple accumulators = composer.create_and_constraint(left_witness_index, right_witness_index, 2); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + if (composer.failed()) { + info("Composer failed; ", composer.err()); + } + + EXPECT_EQ(result, false); +} + +TEST(turbo_plonk_composer, xor_constraint) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + for (size_t i = 0; i < /*10*/ 1; ++i) { + uint32_t left_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = composer.add_variable(left_witness_value); + + uint32_t right_value = engine.get_random_uint32(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + uint32_t out_value = left_value ^ right_value; + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + accumulator_triple accumulators = + composer.create_xor_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + + for (uint32_t j = 0; j < 16; ++j) { + uint32_t left_expected = (left_value >> (30U - (2 * j))); + uint32_t right_expected = (right_value >> (30U - (2 * j))); + uint32_t out_expected = left_expected ^ right_expected; + + fr left_source = composer.get_variable(accumulators.left[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t left_result = static_cast(left_source.data[0]); + + fr right_source = composer.get_variable(accumulators.right[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t right_result = static_cast(right_source.data[0]); + + fr out_source = composer.get_variable(accumulators.out[j + (extra_bits >> 1)]).from_montgomery_form(); + uint32_t out_result = static_cast(out_source.data[0]); + + EXPECT_EQ(left_result, left_expected); + EXPECT_EQ(right_result, right_expected); + EXPECT_EQ(out_result, out_expected); + } + for (uint32_t j = 1; j < 16; ++j) { + uint32_t left = (left_value >> (30U - (2 * j))); + uint32_t right = (left_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (right_value >> (30U - (2 * j))); + right = (right_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + + left = (out_value >> (30U - (2 * j))); + right = (out_value >> (30U - (2 * (j - 1)))); + EXPECT_EQ(left - 4 * right < 4, true); + } + } + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, big_add_gate_with_bit_extract) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + const auto generate_constraints = [&composer](uint32_t quad_value) { + uint32_t quad_accumulator_left = + (engine.get_random_uint32() & 0x3fffffff) - quad_value; // make sure this won't overflow + uint32_t quad_accumulator_right = (4 * quad_accumulator_left) + quad_value; + + uint32_t left_idx = composer.add_variable(uint256_t(quad_accumulator_left)); + uint32_t right_idx = composer.add_variable(uint256_t(quad_accumulator_right)); + + uint32_t input = engine.get_random_uint32(); + uint32_t output = input + (quad_value > 1 ? 1 : 0); + + add_quad gate{ composer.add_variable(uint256_t(input)), + composer.add_variable(uint256_t(output)), + right_idx, + left_idx, + fr(6), + -fr(6), + fr::zero(), + fr::zero(), + fr::zero() }; + + composer.create_big_add_gate_with_bit_extraction(gate); + }; + + generate_constraints(0); + generate_constraints(1); + generate_constraints(2); + generate_constraints(3); + + auto prover = composer.create_prover(); + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, validate_copy_constraints) +{ + for (size_t m = 0; m < 2; ++m) { + for (size_t k = 0; k < 4; ++k) { + for (size_t j = 0; j < 4; ++j) { + if (m == 0 && (j > 0 || k > 0)) { + continue; + } + TurboPlonkComposer composer = TurboPlonkComposer(); + + barretenberg::fr variables[4]{ + barretenberg::fr::random_element(), + barretenberg::fr::random_element(), + barretenberg::fr::random_element(), + barretenberg::fr::random_element(), + }; + + uint32_t indices[4]{ + composer.add_variable(variables[0]), + composer.add_variable(variables[1]), + composer.add_variable(variables[2]), + composer.add_variable(variables[3]), + }; + + for (size_t i = 0; i < 4; ++i) { + composer.create_big_add_gate({ + indices[0], + indices[1], + indices[2], + indices[3], + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + }); + + composer.create_big_add_gate({ + indices[3], + indices[2], + indices[1], + indices[0], + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + barretenberg::fr(0), + }); + } + + auto prover = composer.create_prover(); + + if (m > 0) { + ((barretenberg::polynomial&)prover.key->polynomial_store.get( + "w_" + std::to_string(k + 1) + "_lagrange"))[j] = barretenberg::fr::random_element(); + } + + auto verifier = composer.create_verifier(); + + proof proof = prover.construct_proof(); + + bool result = verifier.verify_proof(proof); + + bool expected = (m == 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(turbo_plonk_composer, test_check_circuit_add_gate_proofs_correct) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr a = fr::one(); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t a_idx = composer.add_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + + // TODO: proof fails if one wire contains all zeros. Should we support this? + uint32_t zero_idx = composer.add_variable(fr::zero()); + + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, a_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = composer.check_circuit(); + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, test_check_circuit_add_gate_proofs_broken) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr a = fr::one(); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t a_idx = composer.add_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c + 1); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + + // TODO: proof fails if one wire contains all zeros. Should we support this? + uint32_t zero_idx = composer.add_variable(fr::zero()); + + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, a_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = composer.check_circuit(); + EXPECT_EQ(result, false); +} +TEST(turbo_plonk_composer, test_check_circuit_mul_gate_proofs_correct) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr q[7]{ fr::random_element(), fr::random_element(), fr::random_element(), fr::random_element(), + fr::random_element(), fr::random_element(), fr::random_element() }; + fr q_inv[7]{ + q[0].invert(), q[1].invert(), q[2].invert(), q[3].invert(), q[4].invert(), q[5].invert(), q[6].invert(), + }; + + fr a = fr::random_element(); + fr b = fr::random_element(); + fr c = -((((q[0] * a) + (q[1] * b)) + q[3]) * q_inv[2]); + fr d = -((((q[4] * (a * b)) + q[6]) * q_inv[5])); + + uint32_t a_idx = composer.add_public_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + uint32_t e_idx = composer.add_variable(a - fr::one()); + composer.create_add_gate({ e_idx, b_idx, c_idx, q[0], q[1], q[2], (q[3] + q[0]) }); + + bool result = composer.check_circuit(); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, test_check_circuit_mul_gate_proofs_broken) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + fr q[7]{ fr::random_element(), fr::random_element(), fr::random_element(), fr::random_element(), + fr::random_element(), fr::random_element(), fr::random_element() }; + fr q_inv[7]{ + q[0].invert(), q[1].invert(), q[2].invert(), q[3].invert(), q[4].invert(), q[5].invert(), q[6].invert(), + }; + + fr a = fr::random_element(); + fr b = fr::random_element(); + fr c = -((((q[0] * a) + (q[1] * b)) + q[3]) * q_inv[2]); + fr d = -((((q[4] * (a * b)) + q[6]) * q_inv[5])); + + uint32_t a_idx = composer.add_public_variable(a); + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c + 1); + uint32_t d_idx = composer.add_variable(d); + + composer.create_add_gate({ a_idx, b_idx, c_idx, q[0], q[1], q[2], q[3] }); + composer.create_mul_gate({ a_idx, b_idx, d_idx, q[4], q[5], q[6] }); + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + uint32_t e_idx = composer.add_variable(a - fr::one()); + composer.create_add_gate({ e_idx, b_idx, c_idx, q[0], q[1], q[2], (q[3] + q[0]) }); + + bool result = composer.check_circuit(); + + EXPECT_EQ(result, false); +} +TEST(turbo_plonk_composer, test_check_circuit_fixed_group) +{ + constexpr size_t num_bits = 254; + constexpr size_t num_quads_base = (num_bits - 1) >> 1; + constexpr size_t num_quads = ((num_quads_base << 1) + 1 < num_bits) ? num_quads_base + 1 : num_quads_base; + constexpr size_t num_wnaf_bits = (num_quads << 1) + 1; + + constexpr size_t initial_exponent = num_bits; // ((num_bits & 1) == 1) ? num_bits - 1 : num_bits; + auto gen_data = crypto::pedersen::get_generator_data(DEFAULT_GEN_1); + const crypto::pedersen::fixed_base_ladder* ladder = gen_data.get_ladder(num_bits); + grumpkin::g1::affine_element generator = gen_data.generator; + + grumpkin::g1::element origin_points[2]; + origin_points[0] = grumpkin::g1::element(ladder[0].one); + origin_points[1] = origin_points[0] + generator; + origin_points[1] = origin_points[1].normalize(); + + grumpkin::fr scalar_multiplier_base = grumpkin::fr::random_element(); + + grumpkin::fr scalar_multiplier = scalar_multiplier_base.from_montgomery_form(); + + if ((scalar_multiplier.data[0] & 1) == 0) { + grumpkin::fr two = grumpkin::fr::one() + grumpkin::fr::one(); + scalar_multiplier_base = scalar_multiplier_base - two; + } + scalar_multiplier_base = scalar_multiplier_base.from_montgomery_form(); + uint64_t wnaf_entries[num_quads + 1] = { 0 }; + + bool skew = false; + barretenberg::wnaf::fixed_wnaf(&scalar_multiplier_base.data[0], &wnaf_entries[0], skew, 0); + + fr accumulator_offset = (fr::one() + fr::one()).pow(static_cast(initial_exponent)).invert(); + fr origin_accumulators[2]{ fr::one(), accumulator_offset + fr::one() }; + + grumpkin::g1::element* multiplication_transcript = + static_cast(aligned_alloc(64, sizeof(grumpkin::g1::element) * (num_quads + 1))); + fr* accumulator_transcript = static_cast(aligned_alloc(64, sizeof(fr) * (num_quads + 1))); + + if (skew) { + multiplication_transcript[0] = origin_points[1]; + accumulator_transcript[0] = origin_accumulators[1]; + } else { + multiplication_transcript[0] = origin_points[0]; + accumulator_transcript[0] = origin_accumulators[0]; + } + + fr one = fr::one(); + fr three = ((one + one) + one); + for (size_t i = 0; i < num_quads; ++i) { + uint64_t entry = wnaf_entries[i + 1] & 0xffffff; + fr prev_accumulator = accumulator_transcript[i] + accumulator_transcript[i]; + prev_accumulator = prev_accumulator + prev_accumulator; + + grumpkin::g1::affine_element point_to_add = (entry == 1) ? ladder[i + 1].three : ladder[i + 1].one; + fr scalar_to_add = (entry == 1) ? three : one; + uint64_t predicate = (wnaf_entries[i + 1] >> 31U) & 1U; + if (predicate) { + point_to_add = -point_to_add; + scalar_to_add.self_neg(); + } + accumulator_transcript[i + 1] = prev_accumulator + scalar_to_add; + multiplication_transcript[i + 1] = multiplication_transcript[i] + point_to_add; + } + grumpkin::g1::element::batch_normalize(&multiplication_transcript[0], num_quads + 1); + + fixed_group_init_quad init_quad{ origin_points[0].x, + (origin_points[0].x - origin_points[1].x), + origin_points[0].y, + (origin_points[0].y - origin_points[1].y) }; + + TurboPlonkComposer composer = TurboPlonkComposer(); + + fr x_alpha = accumulator_offset; + for (size_t i = 0; i < num_quads; ++i) { + fixed_group_add_quad round_quad; + round_quad.d = composer.add_variable(accumulator_transcript[i]); + round_quad.a = composer.add_variable(multiplication_transcript[i].x); + round_quad.b = composer.add_variable(multiplication_transcript[i].y); + round_quad.c = composer.add_variable(x_alpha); + if ((wnaf_entries[i + 1] & 0xffffffU) == 0) { + x_alpha = ladder[i + 1].one.x; + } else { + x_alpha = ladder[i + 1].three.x; + } + round_quad.q_x_1 = ladder[i + 1].q_x_1; + round_quad.q_x_2 = ladder[i + 1].q_x_2; + round_quad.q_y_1 = ladder[i + 1].q_y_1; + round_quad.q_y_2 = ladder[i + 1].q_y_2; + + if (i > 0) { + composer.create_fixed_group_add_gate(round_quad); + } else { + composer.create_fixed_group_add_gate_with_init(round_quad, init_quad); + } + } + + add_quad add_quad{ composer.add_variable(multiplication_transcript[num_quads].x), + composer.add_variable(multiplication_transcript[num_quads].y), + composer.add_variable(x_alpha), + composer.add_variable(accumulator_transcript[num_quads]), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero(), + fr::zero() }; + composer.create_big_add_gate(add_quad); + + grumpkin::g1::element expected_point = + grumpkin::g1::element(generator * scalar_multiplier.to_montgomery_form()).normalize(); + EXPECT_EQ((multiplication_transcript[num_quads].x == expected_point.x), true); + EXPECT_EQ((multiplication_transcript[num_quads].y == expected_point.y), true); + + fr result_accumulator = (accumulator_transcript[num_quads]); + fr expected_accumulator = + fr{ scalar_multiplier.data[0], scalar_multiplier.data[1], scalar_multiplier.data[2], scalar_multiplier.data[3] } + .to_montgomery_form(); + EXPECT_EQ((result_accumulator == expected_accumulator), true); + + bool result = composer.check_circuit(); + + EXPECT_EQ(result, true); + + free(multiplication_transcript); + free(accumulator_transcript); +} + +TEST(turbo_plonk_composer, test_check_circuit_range_constraint) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + for (size_t i = 0; i < 10; ++i) { + uint32_t value = engine.get_random_uint32(); + fr witness_value = fr{ value, 0, 0, 0 }.to_montgomery_form(); + uint32_t witness_index = composer.add_variable(witness_value); + + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + std::vector accumulators = composer.decompose_into_base4_accumulators( + witness_index, 32 + extra_bits, "range constraint fails in test_check_circuit_range_constraint"); + } + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = composer.check_circuit(); + + EXPECT_EQ(result, true); +} + +TEST(turbo_plonk_composer, test_check_circuit_xor) +{ + TurboPlonkComposer composer = TurboPlonkComposer(); + + for (size_t i = 0; i < /*10*/ 1; ++i) { + uint32_t left_value = engine.get_random_uint32(); + + fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t left_witness_index = composer.add_variable(left_witness_value); + + uint32_t right_value = engine.get_random_uint32(); + fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + uint32_t right_witness_index = composer.add_variable(right_witness_value); + + // include non-nice numbers of bits, that will bleed over gate boundaries + size_t extra_bits = 2 * (i % 4); + + accumulator_triple accumulators = + composer.create_xor_constraint(left_witness_index, right_witness_index, 32 + extra_bits); + } + + uint32_t zero_idx = composer.add_variable(fr::zero()); + uint32_t one_idx = composer.add_variable(fr::one()); + composer.create_big_add_gate( + { zero_idx, zero_idx, zero_idx, one_idx, fr::one(), fr::one(), fr::one(), fr::one(), fr::neg_one() }); + + bool result = composer.check_circuit(); + + EXPECT_EQ(result, true); +} diff --git a/cpp/src/barretenberg/proof_system/flavor/flavor.hpp b/cpp/src/barretenberg/proof_system/flavor/flavor.hpp index b929f9fb52..e7047e1e73 100644 --- a/cpp/src/barretenberg/proof_system/flavor/flavor.hpp +++ b/cpp/src/barretenberg/proof_system/flavor/flavor.hpp @@ -4,7 +4,8 @@ #include "barretenberg/common/log.hpp" #include "barretenberg/transcript/manifest.hpp" -#define STANDARD_HONK_WIDTH 3 +#define STANDARD_WIDTH 3 +#define TURBO_WIDTH 4 // TODO(Cody): "bonk" is short for "both plonk and honk". Just need a short and non-vague temporary name. namespace bonk { struct StandardArithmetization { diff --git a/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp index 610b5b2abd..b7ede3f00b 100644 --- a/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp @@ -2,7 +2,9 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" #include "barretenberg/ecc/curves/bn254/fq.hpp" - +#pragma clang diagnostic push +// TODO(luke/kesha): Add a comment explaining why we need this ignore and what the solution is. +#pragma clang diagnostic ignored "-Wc99-designator" // This is a global variable, so that the execution handling class could alter it and signal to the input tester // that the input should fail bool circuit_should_fail = false; @@ -234,9 +236,7 @@ template class BigFieldBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -362,8 +362,7 @@ template class BigFieldBase { * @return Mutated element */ template - inline static fq mutateFieldElement(fq e, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static fq mutateFieldElement(fq e, T& rng, HavocSettings& havoc_config) requires SimpleRng { // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This // has merit, since the computation is performed in montgomery form and comparisons are often performed @@ -459,8 +458,9 @@ template class BigFieldBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { #define PUT_RANDOM_BYTE_IF_LUCKY(variable) \ if (rng.next() & 1) { \ @@ -1969,3 +1969,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/bit_array/bit_array.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/bit_array/bit_array.fuzzer.hpp index d12d096965..2f1a1f6536 100644 --- a/cpp/src/barretenberg/stdlib/primitives/bit_array/bit_array.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/bit_array/bit_array.fuzzer.hpp @@ -1,5 +1,7 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/bit_array/bit_array.hpp" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" #define MAX_ARRAY_SIZE 128 @@ -168,9 +170,7 @@ template class BitArrayFuzzBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -229,8 +229,9 @@ template class BitArrayFuzzBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { (void)rng; (void)havoc_config; @@ -920,3 +921,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/bool/bool.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/bool/bool.fuzzer.hpp index bf3a04f803..3b9e9c501e 100644 --- a/cpp/src/barretenberg/stdlib/primitives/bool/bool.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/bool/bool.fuzzer.hpp @@ -1,6 +1,7 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/bit_array/bit_array.hpp" - +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail bool circuit_should_fail = false; @@ -71,9 +72,7 @@ template class BoolFuzzBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -130,8 +129,9 @@ template class BoolFuzzBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { (void)rng; (void)havoc_config; @@ -840,3 +840,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.fuzzer.hpp index 09a0430340..d0bef67e52 100644 --- a/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.fuzzer.hpp @@ -1,6 +1,8 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" #include "barretenberg/stdlib/primitives/safe_uint/safe_uint.hpp" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" #define MAX_ARRAY_SIZE 128 @@ -104,9 +106,7 @@ template class ByteArrayFuzzBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -176,8 +176,9 @@ template class ByteArrayFuzzBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { (void)rng; (void)havoc_config; @@ -968,3 +969,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/field/field.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/field/field.fuzzer.hpp index 239d06edda..612349149f 100644 --- a/cpp/src/barretenberg/stdlib/primitives/field/field.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/field/field.fuzzer.hpp @@ -4,6 +4,8 @@ #include "barretenberg/stdlib/primitives/bool/bool.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" // This is a global variable, so that the execution handling class could alter it and signal to the input tester // that the input should fail @@ -234,9 +236,7 @@ template class FieldBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -335,8 +335,7 @@ template class FieldBase { * @return Mutated element */ template - inline static fr mutateFieldElement(fr e, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static fr mutateFieldElement(fr e, T& rng, HavocSettings& havoc_config) requires SimpleRng { // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This // has merit, since the computation is performed in montgomery form and comparisons are often performed @@ -432,8 +431,9 @@ template class FieldBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { #define PUT_RANDOM_BYTE_IF_LUCKY(variable) \ if (rng.next() & 1) { \ @@ -2019,3 +2019,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp index 3f3b02f7d1..a5d706dc14 100644 --- a/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp @@ -2,6 +2,8 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/safe_uint/safe_uint.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail @@ -186,9 +188,7 @@ template class SafeUintFuzzBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -283,8 +283,7 @@ template class SafeUintFuzzBase { * @return Mutated element */ template - inline static fr mutateFieldElement(fr e, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static fr mutateFieldElement(fr e, T& rng, HavocSettings& havoc_config) requires SimpleRng { // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This has // merit, since the computation is performed in montgomery form and comparisons are often performed in it, @@ -377,8 +376,9 @@ template class SafeUintFuzzBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { #define PUT_RANDOM_BYTE_IF_LUCKY(variable) \ if (rng.next() & 1) { \ @@ -1457,3 +1457,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/uint/uint.fuzzer.hpp b/cpp/src/barretenberg/stdlib/primitives/uint/uint.fuzzer.hpp index 4b2c7fb385..ba7dcaad1a 100644 --- a/cpp/src/barretenberg/stdlib/primitives/uint/uint.fuzzer.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/uint/uint.fuzzer.hpp @@ -3,6 +3,9 @@ #include "barretenberg/stdlib/primitives/field/field.hpp" #include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" #include "barretenberg/stdlib/primitives/bool/bool.hpp" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" + // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail bool circuit_should_fail = false; @@ -101,9 +104,7 @@ template class UintFuzzBase { * @param rng PRNG used * @return A random instruction */ - template - inline static Instruction generateRandom(T& rng) - requires SimpleRng + template inline static Instruction generateRandom(T& rng) requires SimpleRng { // Choose which instruction we are going to generate OPCODE instruction_opcode = static_cast(rng.next() % (OPCODE::_LAST)); @@ -163,8 +164,9 @@ template class UintFuzzBase { * @return Mutated instruction */ template - inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config) - requires SimpleRng + inline static Instruction mutateInstruction(Instruction instruction, + T& rng, + HavocSettings& havoc_config) requires SimpleRng { (void)rng; (void)havoc_config; @@ -1583,3 +1585,5 @@ extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) RunWithComposers(Data, Size, VarianceRNG); return 0; } + +#pragma clang diagnostic pop \ No newline at end of file