From cee6505004af826a64bf766a3df84b83993590bf Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 20 Apr 2023 17:50:00 +0200 Subject: [PATCH 01/13] fixed bug where range constraining connected witnesses threw an error --- .../plonk/composer/ultra_composer.cpp | 6 ++-- .../plonk/composer/ultra_composer.hpp | 4 +++ .../plonk/composer/ultra_composer.test.cpp | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index 8782f6ab61..d17939fba1 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -1217,8 +1217,10 @@ void UltraComposer::create_new_range_constraint(const uint32_t variable_index, } auto& list = range_lists[target_range]; - assign_tag(variable_index, list.range_tag); - list.variable_indices.emplace_back(variable_index); + if (real_variable_tags[real_variable_index[variable_index]] != list.range_tag) { + assign_tag(variable_index, list.range_tag); + list.variable_indices.emplace_back(variable_index); + } } void UltraComposer::process_range_list(const RangeList& list) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index b3157639fd..1ce6813d40 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -425,6 +425,10 @@ class UltraComposer : public ComposerBase { void assign_tag(const uint32_t variable_index, const uint32_t tag) { ASSERT(tag <= current_tag); + // If we've already assigned this tag to this variable, return (can happen due to copy constraints) + if (real_variable_tags[real_variable_index[variable_index]] == tag) { + return; + } ASSERT(real_variable_tags[real_variable_index[variable_index]] == DUMMY_TAG); real_variable_tags[real_variable_index[variable_index]] = tag; } diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index eef80fa56c..f3f87047af 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -758,4 +758,39 @@ TYPED_TEST(ultra_composer, ram) TestFixture::prove_and_verify(composer, /*expected_result=*/true); } +TYPED_TEST(ultra_composer, range_checks_on_duplicates) +{ + UltraComposer composer = UltraComposer(); + + uint32_t a = composer.add_variable(100); + uint32_t b = composer.add_variable(100); + uint32_t c = composer.add_variable(100); + uint32_t d = composer.add_variable(100); + + composer.assert_equal(a, b); + composer.assert_equal(a, c); + composer.assert_equal(a, d); + + composer.create_new_range_constraint(a, 1000); + composer.create_new_range_constraint(b, 1000); + composer.create_new_range_constraint(c, 1000); + composer.create_new_range_constraint(d, 1000); + + composer.create_big_add_gate( + { + a, + b, + c, + d, + 0, + 0, + 0, + 0, + 0, + }, + true); + + TestFixture::prove_and_verify(composer, /*expected_result=*/true); +} + } // namespace proof_system::plonk::test_ultra_composer From 4e86e8f4ec48fc1b608f483bfd69a342a4928b57 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 20 Apr 2023 18:03:51 +0200 Subject: [PATCH 02/13] fix --- cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index f3f87047af..8a96d54652 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -788,7 +788,7 @@ TYPED_TEST(ultra_composer, range_checks_on_duplicates) 0, 0, }, - true); + false); TestFixture::prove_and_verify(composer, /*expected_result=*/true); } From a42e10447b40ac17b433caa04887ae189890cbc9 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 20 Apr 2023 18:21:09 +0200 Subject: [PATCH 03/13] bugfix on create_new_range_constraint can now apply multiple overlapping ranges to the same witness (or copies of the witness) --- .../plonk/composer/ultra_composer.cpp | 30 +++++++++++++++++++ .../plonk/composer/ultra_composer.test.cpp | 4 +-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index d17939fba1..2b1cd9ec2c 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -1218,6 +1218,36 @@ void UltraComposer::create_new_range_constraint(const uint32_t variable_index, auto& list = range_lists[target_range]; if (real_variable_tags[real_variable_index[variable_index]] != list.range_tag) { + + if (real_variable_tags[real_variable_index[variable_index]] != DUMMY_TAG) { + const auto existing_tag = real_variable_tags[real_variable_index[variable_index]]; + bool found_tag = false; + // existing range? + for (const auto& r : range_lists) { + if (r.second.range_tag == existing_tag) { + found_tag = true; + if (r.first < target_range) { + // already has a more restrictive range check, skip + return; + } else { + // difficult to remove an existing range check. + // Instead deep-copy the variable and apply a range check to new variable + const uint32_t copied_witness = add_variable(get_variable(variable_index)); + create_add_gate({ .a = variable_index, + .b = copied_witness, + .c = zero_idx, + .a_scaling = 1, + .b_scaling = -1, + .c_scaling = 0, + .const_scaling = 0 }); + // recursve w. new witness that has no tag attached + create_new_range_constraint(copied_witness, target_range, msg); + return; + } + } + } + ASSERT(found_tag == true); + } assign_tag(variable_index, list.range_tag); list.variable_indices.emplace_back(variable_index); } diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index 8a96d54652..75d30ce519 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -772,8 +772,8 @@ TYPED_TEST(ultra_composer, range_checks_on_duplicates) composer.assert_equal(a, d); composer.create_new_range_constraint(a, 1000); - composer.create_new_range_constraint(b, 1000); - composer.create_new_range_constraint(c, 1000); + composer.create_new_range_constraint(b, 1001); + composer.create_new_range_constraint(c, 999); composer.create_new_range_constraint(d, 1000); composer.create_big_add_gate( From a996cdb98b3e686d668e8ead073f3fbcbc638e97 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Sun, 23 Apr 2023 22:39:11 +0200 Subject: [PATCH 04/13] removed blake3s hash from ultraplonk recursive prover UltraComposer will now not create duplicate non-native field multiplication constraints --- .../plonk/composer/ultra_composer.cpp | 192 +++++++++++------- .../plonk/composer/ultra_composer.hpp | 78 ++++++- .../recursion/transcript/transcript.hpp | 41 +++- .../barretenberg/transcript/transcript.cpp | 17 +- .../barretenberg/transcript/transcript.hpp | 1 + 5 files changed, 239 insertions(+), 90 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index 2b1cd9ec2c..f9a707ffc9 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -531,6 +531,7 @@ std::shared_ptr UltraComposer::compute_proving_key() * our circuit is finalised, and we must not to execute these functions again. */ if (!circuit_finalised) { + process_non_native_field_multiplications(); process_ROM_arrays(public_inputs.size()); process_RAM_arrays(public_inputs.size()); process_range_lists(); @@ -1886,8 +1887,6 @@ std::array UltraComposer::evaluate_non_native_field_multiplication( constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; constexpr barretenberg::fr LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); - constexpr barretenberg::fr LIMB_RSHIFT = - barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_RSHIFT_2 = barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); @@ -1936,82 +1935,127 @@ std::array UltraComposer::evaluate_non_native_field_multiplication( range_constrain_two_limbs(input.q[2], input.q[3]); } - // product gate 1 - // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 - create_big_add_gate({ input.q[0], - input.q[1], - input.r[1], - lo_1_idx, - input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, - input.neg_modulus[0] * LIMB_SHIFT, - -LIMB_SHIFT, - -LIMB_SHIFT.sqr(), - 0 }, - true); + // Add witnesses into the multiplication cache + // (when finalising the circuit, we will remove duplicates; several dups produced by biggroup.hpp methods) + cached_non_native_field_multiplication cache{ + .a = input.a, + .b = input.b, + .q = input.q, + .r = input.r, + .cross_terms = { lo_0_idx, lo_1_idx, hi_0_idx, hi_1_idx, hi_2_idx, hi_3_idx }, + .neg_modulus = input.neg_modulus, + }; + cached_non_native_field_multiplications.emplace_back(cache); - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[0]); - w_4.emplace_back(lo_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); - ++num_gates; - w_l.emplace_back(input.a[0]); - w_r.emplace_back(input.b[0]); - w_o.emplace_back(input.a[3]); - w_4.emplace_back(input.b[3]); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); - ++num_gates; - w_l.emplace_back(input.a[2]); - w_r.emplace_back(input.b[2]); - w_o.emplace_back(input.r[3]); - w_4.emplace_back(hi_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); - ++num_gates; - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[2]); - w_4.emplace_back(hi_1_idx); - apply_aux_selectors(AUX_SELECTORS::NONE); - ++num_gates; + return std::array{ lo_1_idx, hi_3_idx }; +} - /** - * product gate 6 - * - * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 - * - **/ - create_big_add_gate( - { - input.q[2], - input.q[3], - lo_1_idx, - hi_1_idx, - -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], - -input.neg_modulus[0] * LIMB_SHIFT, - -1, - -1, - 0, - }, - true); +/** + * @brief Called in `compute_proving_key` when finalizing circuit. + * Iterates over the cached_non_native_field_multiplication objects, + * removes duplicates, and instantiates the remainder as constraints` + */ +void UltraComposer::process_non_native_field_multiplications() +{ + std::sort(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 7 - * - * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b - **/ - create_big_add_gate({ - hi_3_idx, - input.q[0], - input.q[1], - hi_2_idx, - -1, - input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, - input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, - LIMB_RSHIFT_2, - 0, - }); + auto last = + std::unique(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - return std::array{ lo_1_idx, hi_3_idx }; + auto it = cached_non_native_field_multiplications.begin(); + + constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + constexpr barretenberg::fr LIMB_RSHIFT = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); + constexpr barretenberg::fr LIMB_RSHIFT_2 = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); + + // iterate over the cached items and create constraints + while (it != last) { + const auto input = *it; + const auto lo_0_idx = input.cross_terms[0]; + const auto lo_1_idx = input.cross_terms[1]; + const auto hi_0_idx = input.cross_terms[2]; + const auto hi_1_idx = input.cross_terms[3]; + const auto hi_2_idx = input.cross_terms[4]; + const auto hi_3_idx = input.cross_terms[5]; + + // product gate 1 + // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 + create_big_add_gate({ input.q[0], + input.q[1], + input.r[1], + lo_1_idx, + input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, + input.neg_modulus[0] * LIMB_SHIFT, + -LIMB_SHIFT, + -LIMB_SHIFT.sqr(), + 0 }, + true); + + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[0]); + w_4.emplace_back(lo_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); + ++num_gates; + w_l.emplace_back(input.a[0]); + w_r.emplace_back(input.b[0]); + w_o.emplace_back(input.a[3]); + w_4.emplace_back(input.b[3]); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); + ++num_gates; + w_l.emplace_back(input.a[2]); + w_r.emplace_back(input.b[2]); + w_o.emplace_back(input.r[3]); + w_4.emplace_back(hi_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); + ++num_gates; + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[2]); + w_4.emplace_back(hi_1_idx); + apply_aux_selectors(AUX_SELECTORS::NONE); + ++num_gates; + + /** + * product gate 6 + * + * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 + * + **/ + create_big_add_gate( + { + input.q[2], + input.q[3], + lo_1_idx, + hi_1_idx, + -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], + -input.neg_modulus[0] * LIMB_SHIFT, + -1, + -1, + 0, + }, + true); + + /** + * product gate 7 + * + * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b + **/ + create_big_add_gate({ + hi_3_idx, + input.q[0], + input.q[1], + hi_2_idx, + -1, + input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, + input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, + LIMB_RSHIFT_2, + 0, + }); + ++it; + } } /** diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index 1ce6813d40..49471b3bac 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -27,7 +27,8 @@ class UltraComposer : public ComposerBase { static constexpr uint32_t UNINITIALIZED_MEMORY_RECORD = UINT32_MAX; static constexpr size_t NUMBER_OF_GATES_PER_RAM_ACCESS = 2; static constexpr size_t NUMBER_OF_ARITHMETIC_GATES_PER_RAM_ARRAY = 1; - + // number of gates created per non-native field operation in process_non_native_field_multiplications + static constexpr size_t GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC = 7; struct non_native_field_witnesses { // first 4 array elements = limbs // 5th element = prime basis limb @@ -39,6 +40,57 @@ class UltraComposer : public ComposerBase { barretenberg::fr modulus; }; + /** + * @brief Used to store instructions to create non_native_field_multiplication gates. + * We want to cache these (and remove duplicates) as the stdlib code can end up multiplying the same inputs + * repeatedly. + */ + struct cached_non_native_field_multiplication { + std::array a; + std::array b; + std::array q; + std::array r; + std::array cross_terms; + std::array neg_modulus; + + bool operator==(const cached_non_native_field_multiplication& other) const + { + bool valid = true; + for (size_t i = 0; i < 5; ++i) { + valid = valid && (a[i] == other.a[i]); + valid = valid && (b[i] == other.b[i]); + valid = valid && (q[i] == other.q[i]); + valid = valid && (r[i] == other.r[i]); + } + return valid; + } + bool operator<(const cached_non_native_field_multiplication& other) const + { + if (a < other.a) { + return true; + } + if (a == other.a) { + if (b < other.b) { + return true; + } + if (b == other.b) { + if (q < other.q) { + return true; + } + if (q == other.q) { + if (r < other.r) { + return true; + } + } + } + } + return false; + } + }; + + std::vector cached_non_native_field_multiplications; + void process_non_native_field_multiplications(); + enum AUX_SELECTORS { NONE, LIMB_ACCUMULATE_1, @@ -249,11 +301,10 @@ class UltraComposer : public ComposerBase { * @param rangecount return argument, extra gates due to range checks * @param romcount return argument, extra gates due to rom reads * @param ramcount return argument, extra gates due to ram read/writes + * @param nnfcount return argument, extra gates due to queued non native field gates */ - void get_num_gates_split_into_components(size_t& count, - size_t& rangecount, - size_t& romcount, - size_t& ramcount) const + void get_num_gates_split_into_components( + size_t& count, size_t& rangecount, size_t& romcount, size_t& ramcount, size_t& nnfcount) const { count = num_gates; // each ROM gate adds +1 extra gate due to the rom reads being copied to a sorted list set @@ -321,6 +372,13 @@ class UltraComposer : public ComposerBase { rangecount += ram_range_sizes[i]; } } + std::vector nnf_copy(cached_non_native_field_multiplications); + // update nnfcount + std::sort(nnf_copy.begin(), nnf_copy.end()); + + auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + nnf_count = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; } /** @@ -342,8 +400,9 @@ class UltraComposer : public ComposerBase { size_t rangecount = 0; size_t romcount = 0; size_t ramcount = 0; - get_num_gates_split_into_components(count, rangecount, romcount, ramcount); - return count + romcount + ramcount + rangecount; + size_t nnfcount = 0; + get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); + return count + romcount + ramcount + rangecount + nnfcount; } virtual size_t get_total_circuit_size() const override @@ -366,12 +425,13 @@ class UltraComposer : public ComposerBase { size_t rangecount = 0; size_t romcount = 0; size_t ramcount = 0; - + size_t nnfcount = 0; get_num_gates_split_into_components(count, rangecount, romcount, ramcount); size_t total = count + romcount + ramcount + rangecount; std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount - << ", range " << rangecount << "), pubinp = " << public_inputs.size() << std::endl; + << ", range " << rangecount << ", non native field gates " << nnfcount + << "), pubinp = " << public_inputs.size() << std::endl; } void assert_equal_constant(const uint32_t a_idx, diff --git a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp index 73055fb376..cad59ed4a7 100644 --- a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp @@ -253,8 +253,23 @@ template class Transcript { } byte_array compressed_buffer(T0); - byte_array base_hash = stdlib::blake3s(compressed_buffer); - + // TODO(@zac-williamson) make this a Poseidon hash + byte_array base_hash; + if constexpr (Composer::type == ComposerType::PLOOKUP) { + std::vector compression_buffer; + field_pt working_element(context); + size_t byte_counter = 0; + split(working_element, compression_buffer, field_pt(compressed_buffer), byte_counter, 32); + if (byte_counter != 0) { + const uint256_t down_shift = uint256_t(1) << uint256_t((bytes_per_element - byte_counter) * 8); + working_element = working_element / barretenberg::fr(down_shift); + working_element = working_element.normalize(); + compression_buffer.push_back(working_element); + } + base_hash = stdlib::pedersen_plookup_commitment::compress(compression_buffer); + } else { + base_hash = stdlib::blake3s(compressed_buffer); + } byte_array first(field_pt(0), 16); first.write(base_hash.slice(0, 16)); round_challenges.push_back(first); @@ -267,9 +282,25 @@ template class Transcript { for (size_t i = 2; i < num_challenges; i += 2) { byte_array rolling_buffer = base_hash; - rolling_buffer.write(byte_array(field_pt(i / 2), 1)); - byte_array hash_output = stdlib::blake3s(rolling_buffer); - + byte_array hash_output; + if constexpr (Composer::type == ComposerType::PLOOKUP) { + // TODO(@zac-williamson) make this a Poseidon hash not a Pedersen hash + std::vector compression_buffer; + field_pt working_element(context); + size_t byte_counter = 0; + split(working_element, compression_buffer, field_pt(rolling_buffer), byte_counter, 32); + split(working_element, compression_buffer, field_pt(field_pt(i / 2)), byte_counter, 1); + if (byte_counter != 0) { + const uint256_t down_shift = uint256_t(1) << uint256_t((bytes_per_element - byte_counter) * 8); + working_element = working_element / barretenberg::fr(down_shift); + working_element = working_element.normalize(); + compression_buffer.push_back(working_element); + } + hash_output = stdlib::pedersen_plookup_commitment::compress(compression_buffer); + } else { + rolling_buffer.write(byte_array(field_pt(i / 2), 1)); + hash_output = stdlib::blake3s(rolling_buffer); + } byte_array hi(field_pt(0), 16); hi.write(hash_output.slice(0, 16)); round_challenges.push_back(hi); diff --git a/cpp/src/barretenberg/transcript/transcript.cpp b/cpp/src/barretenberg/transcript/transcript.cpp index 91349628b9..9c448949c9 100644 --- a/cpp/src/barretenberg/transcript/transcript.cpp +++ b/cpp/src/barretenberg/transcript/transcript.cpp @@ -54,6 +54,19 @@ std::array Blake3sHasher::hash(std::ve return result; } +std::array Blake3sHasher::hash_plookup(std::vector const& buffer) +{ + // TODO(@zac-williamson) Change to call a Poseidon hash and create a PoseidonHasher + // (not making the name change right now as it will break concurrent work w. getting recursion working in Noir) + // We also need to implement a Poseidon gadget + std::vector compressed_buffer = crypto::pedersen_commitment::lookup::compress_native(buffer); + std::array result; + for (size_t i = 0; i < PRNG_OUTPUT_SIZE; ++i) { + result[i] = compressed_buffer[i]; + } + return result; +} + Transcript::Transcript(const std::vector& input_transcript, const Manifest input_manifest, const HashType hash_type, @@ -219,7 +232,7 @@ void Transcript::apply_fiat_shamir(const std::string& challenge_name /*, const b } case HashType::PlookupPedersenBlake3s: { std::vector compressed_buffer = crypto::pedersen_commitment::lookup::compress_native(buffer); - base_hash = Blake3sHasher::hash(compressed_buffer); + base_hash = Blake3sHasher::hash_plookup(compressed_buffer); break; } default: { @@ -269,7 +282,7 @@ void Transcript::apply_fiat_shamir(const std::string& challenge_name /*, const b break; } case HashType::PlookupPedersenBlake3s: { - hash_output = Blake3sHasher::hash(rolling_buffer); + hash_output = Blake3sHasher::hash_plookup(rolling_buffer); break; } default: { diff --git a/cpp/src/barretenberg/transcript/transcript.hpp b/cpp/src/barretenberg/transcript/transcript.hpp index 43d47e3972..92c73c77e4 100644 --- a/cpp/src/barretenberg/transcript/transcript.hpp +++ b/cpp/src/barretenberg/transcript/transcript.hpp @@ -22,6 +22,7 @@ struct Blake3sHasher { static constexpr size_t PRNG_OUTPUT_SIZE = 32; static std::array hash(std::vector const& input); + static std::array hash_plookup(std::vector const& input); }; enum HashType { Keccak256, PedersenBlake3s, PlookupPedersenBlake3s }; From a24bc4c903e168c8505ca5a6fd222676db53135d Mon Sep 17 00:00:00 2001 From: codygunton Date: Mon, 24 Apr 2023 14:24:12 +0000 Subject: [PATCH 05/13] Fix compilation --- cpp/src/barretenberg/plonk/composer/ultra_composer.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index 49471b3bac..b090b4c729 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -378,7 +378,7 @@ class UltraComposer : public ComposerBase { auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); - nnf_count = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; + nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; } /** @@ -426,7 +426,7 @@ class UltraComposer : public ComposerBase { size_t romcount = 0; size_t ramcount = 0; size_t nnfcount = 0; - get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); size_t total = count + romcount + ramcount + rangecount; std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount From 514c974221ce8dc45327ec711d71561ac0dcf88a Mon Sep 17 00:00:00 2001 From: codygunton Date: Mon, 24 Apr 2023 20:54:42 +0000 Subject: [PATCH 06/13] Propagate new stuff to Honk and splitting_tmp. --- .../splitting_tmp/ultra_plonk_composer.hpp | 34 ++- .../plonk/composer/ultra_composer.cpp | 12 +- .../plonk/composer/ultra_composer.hpp | 6 +- .../ultra_circuit_constructor.cpp | 203 +++++++++++------- .../ultra_circuit_constructor.hpp | 86 +++++++- 5 files changed, 235 insertions(+), 106 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp b/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp index ccc3ece628..1fe28f180c 100644 --- a/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp @@ -133,18 +133,18 @@ class UltraPlonkComposer { // * 1) Current number number of actual gates // * 2) Number of public inputs, as we'll need to add a gate for each of them // * 3) Number of Rom array-associated gates - // * 4) NUmber of range-list associated gates + // * 4) Number of range-list associated gates + // * 5) Number of non-native field multiplication gates. // * // * // * @param count return arument, number of existing gates // * @param rangecount return argument, extra gates due to range checks // * @param romcount return argument, extra gates due to rom reads // * @param ramcount return argument, extra gates due to ram read/writes + // * @param nnfcount return argument, extra gates due to queued non native field gates // */ - // void get_num_gates_split_into_components(size_t& count, - // size_t& rangecount, - // size_t& romcount, - // size_t& ramcount) const + // void get_num_gates_split_into_components( + // size_t& count, size_t& rangecount, size_t& romcount, size_t& ramcount, size_t& nnfcount) const // { // count = num_gates; // // each ROM gate adds +1 extra gate due to the rom reads being copied to a sorted list set @@ -213,17 +213,27 @@ class UltraPlonkComposer { // rangecount += ram_range_sizes[i]; // } // } + // std::vector nnf_copy(cached_non_native_field_multiplications); + // // update nnfcount + // std::sort(nnf_copy.begin(), nnf_copy.end()); + + // auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + // const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + // nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; // } + // // /** // * @brief Get the final number of gates in a circuit, which consists of the sum of: // * 1) Current number number of actual gates // * 2) Number of public inputs, as we'll need to add a gate for each of them // * 3) Number of Rom array-associated gates - // * 4) NUmber of range-list associated gates + // * 4) Number of range-list associated gates + // * 5) Number of non-native field multiplication gates. // * // * @return size_t // */ + // // virtual size_t get_num_gates() const override // { // // if circuit finalised already added extra gates @@ -234,8 +244,9 @@ class UltraPlonkComposer { // size_t rangecount = 0; // size_t romcount = 0; // size_t ramcount = 0; - // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); - // return count + romcount + ramcount + rangecount; + // size_t nnfcount = 0; + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); + // return count + romcount + ramcount + rangecount + nnfcount; // } // virtual void print_num_gates() const override @@ -244,12 +255,13 @@ class UltraPlonkComposer { // size_t rangecount = 0; // size_t romcount = 0; // size_t ramcount = 0; - - // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + // size_t nnfcount = 0; + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); // size_t total = count + romcount + ramcount + rangecount; // std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount - // << ", range " << rangecount << "), pubinp = " << public_inputs.size() << std::endl; + // << ", range " << rangecount << ", non native field gates " << nnfcount + // << "), pubinp = " << public_inputs.size() << std::endl; // } void assert_equal(const uint32_t a_variable_idx, diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index 7349bca519..83c54512f0 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -1847,16 +1847,20 @@ std::array UltraComposer::decompose_non_native_field_double_width_l } /** - * NON NATIVE FIELD MULTIPLICATION CUSTOM GATE SEQUENCE + * @brief Queue up non-native field multiplication data. * - * This method will evaluate the equation (a * b = q * p + r) - * Where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables + * @details The data queued represents a non-native field multiplication identity a * b = q * p + r, + * where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables. + * + * Without this queue some functions, such as proof_system::plonk::stdlib::element::double_montgomery_ladder, would + * duplicate non-native field operations, which can be quite expensive. We queue up these operations, and remove + * duplicates in the circuit finishing stage of the proving key computation. * * The non-native field modulus, p, is a circuit constant * * The return value are the witness indices of the two remainder limbs `lo_1, hi_2` * - * N.B. this method does NOT evaluate the prime field component of non-native field multiplications + * N.B.: This method does NOT evaluate the prime field component of non-native field multiplications. **/ std::array UltraComposer::evaluate_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index b090b4c729..3262e4b533 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -294,7 +294,8 @@ class UltraComposer : public ComposerBase { * 1) Current number number of actual gates * 2) Number of public inputs, as we'll need to add a gate for each of them * 3) Number of Rom array-associated gates - * 4) NUmber of range-list associated gates + * 4) Number of range-list associated gates + * 5) Number of non-native field multiplication gates. * * * @param count return arument, number of existing gates @@ -386,7 +387,8 @@ class UltraComposer : public ComposerBase { * 1) Current number number of actual gates * 2) Number of public inputs, as we'll need to add a gate for each of them * 3) Number of Rom array-associated gates - * 4) NUmber of range-list associated gates + * 4) Number of range-list associated gates + * 5) Number of non-native field multiplication gates. * * @return size_t */ diff --git a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp index d606ab173d..1a2af522d0 100644 --- a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp +++ b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp @@ -33,6 +33,7 @@ void UltraCircuitConstructor::finalize_circuit() * our circuit is finalised, and we must not to execute these functions again. */ if (!circuit_finalised) { + process_non_native_field_multiplications(); process_ROM_arrays(public_inputs.size()); process_RAM_arrays(public_inputs.size()); process_range_lists(); @@ -1236,16 +1237,20 @@ std::array UltraCircuitConstructor::decompose_non_native_field_doub } /** - * NON NATIVE FIELD MULTIPLICATION CUSTOM GATE SEQUENCE + * @brief Queue up non-native field multiplication data. * - * This method will evaluate the equation (a * b = q * p + r) - * Where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables + * @details The data queued represents a non-native field multiplication identity a * b = q * p + r, + * where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables. + * + * Without this queue some functions, such as proof_system::plonk::stdlib::element::double_montgomery_ladder, would + * duplicate non-native field operations, which can be quite expensive. We queue up these operations, and remove + * duplicates in the circuit finishing stage of the proving key computation. * * The non-native field modulus, p, is a circuit constant * * The return value are the witness indices of the two remainder limbs `lo_1, hi_2` * - * N.B. this method does NOT evaluate the prime field component of non-native field multiplications + * N.B.: This method does NOT evaluate the prime field component of non-native field multiplications. **/ std::array UltraCircuitConstructor::evaluate_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) @@ -1279,8 +1284,6 @@ std::array UltraCircuitConstructor::evaluate_non_native_field_multi constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; constexpr barretenberg::fr LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); - constexpr barretenberg::fr LIMB_RSHIFT = - barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_RSHIFT_2 = barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); @@ -1328,83 +1331,127 @@ std::array UltraCircuitConstructor::evaluate_non_native_field_multi range_constrain_two_limbs(input.q[0], input.q[1]); range_constrain_two_limbs(input.q[2], input.q[3]); } + // Add witnesses into the multiplication cache + // (when finalising the circuit, we will remove duplicates; several dups produced by biggroup.hpp methods) + cached_non_native_field_multiplication cache{ + .a = input.a, + .b = input.b, + .q = input.q, + .r = input.r, + .cross_terms = { lo_0_idx, lo_1_idx, hi_0_idx, hi_1_idx, hi_2_idx, hi_3_idx }, + .neg_modulus = input.neg_modulus, + }; + cached_non_native_field_multiplications.emplace_back(cache); - // product gate 1 - // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 - create_big_add_gate({ input.q[0], - input.q[1], - input.r[1], - lo_1_idx, - input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, - input.neg_modulus[0] * LIMB_SHIFT, - -LIMB_SHIFT, - -LIMB_SHIFT.sqr(), - 0 }, - true); + return std::array{ lo_1_idx, hi_3_idx }; +} - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[0]); - w_4.emplace_back(lo_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); - ++num_gates; - w_l.emplace_back(input.a[0]); - w_r.emplace_back(input.b[0]); - w_o.emplace_back(input.a[3]); - w_4.emplace_back(input.b[3]); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); - ++num_gates; - w_l.emplace_back(input.a[2]); - w_r.emplace_back(input.b[2]); - w_o.emplace_back(input.r[3]); - w_4.emplace_back(hi_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); - ++num_gates; - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[2]); - w_4.emplace_back(hi_1_idx); - apply_aux_selectors(AUX_SELECTORS::NONE); - ++num_gates; +/** + * @brief Called in `compute_proving_key` when finalizing circuit. + * Iterates over the cached_non_native_field_multiplication objects, + * removes duplicates, and instantiates the remainder as constraints` + */ +void UltraCircuitConstructor::process_non_native_field_multiplications() +{ + std::sort(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 6 - * - * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 - * - **/ - create_big_add_gate( - { - input.q[2], - input.q[3], - lo_1_idx, - hi_1_idx, - -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], - -input.neg_modulus[0] * LIMB_SHIFT, - -1, - -1, - 0, - }, - true); + auto last = + std::unique(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 7 - * - * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b - **/ - create_big_add_gate({ - hi_3_idx, - input.q[0], - input.q[1], - hi_2_idx, - -1, - input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, - input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, - LIMB_RSHIFT_2, - 0, - }); + auto it = cached_non_native_field_multiplications.begin(); - return std::array{ lo_1_idx, hi_3_idx }; + constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + constexpr barretenberg::fr LIMB_RSHIFT = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); + constexpr barretenberg::fr LIMB_RSHIFT_2 = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); + + // iterate over the cached items and create constraints + while (it != last) { + const auto input = *it; + const auto lo_0_idx = input.cross_terms[0]; + const auto lo_1_idx = input.cross_terms[1]; + const auto hi_0_idx = input.cross_terms[2]; + const auto hi_1_idx = input.cross_terms[3]; + const auto hi_2_idx = input.cross_terms[4]; + const auto hi_3_idx = input.cross_terms[5]; + + // product gate 1 + // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 + create_big_add_gate({ input.q[0], + input.q[1], + input.r[1], + lo_1_idx, + input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, + input.neg_modulus[0] * LIMB_SHIFT, + -LIMB_SHIFT, + -LIMB_SHIFT.sqr(), + 0 }, + true); + + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[0]); + w_4.emplace_back(lo_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); + ++num_gates; + w_l.emplace_back(input.a[0]); + w_r.emplace_back(input.b[0]); + w_o.emplace_back(input.a[3]); + w_4.emplace_back(input.b[3]); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); + ++num_gates; + w_l.emplace_back(input.a[2]); + w_r.emplace_back(input.b[2]); + w_o.emplace_back(input.r[3]); + w_4.emplace_back(hi_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); + ++num_gates; + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[2]); + w_4.emplace_back(hi_1_idx); + apply_aux_selectors(AUX_SELECTORS::NONE); + ++num_gates; + + /** + * product gate 6 + * + * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 + * + **/ + create_big_add_gate( + { + input.q[2], + input.q[3], + lo_1_idx, + hi_1_idx, + -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], + -input.neg_modulus[0] * LIMB_SHIFT, + -1, + -1, + 0, + }, + true); + + /** + * product gate 7 + * + * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b + **/ + create_big_add_gate({ + hi_3_idx, + input.q[0], + input.q[1], + hi_2_idx, + -1, + input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, + input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, + LIMB_RSHIFT_2, + 0, + }); + ++it; + } } /** diff --git a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp index 383dccb6f0..6c810d3c60 100644 --- a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp +++ b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp @@ -29,6 +29,8 @@ static constexpr size_t DEFAULT_NON_NATIVE_FIELD_LIMB_BITS = 68; static constexpr uint32_t UNINITIALIZED_MEMORY_RECORD = UINT32_MAX; static constexpr size_t NUMBER_OF_GATES_PER_RAM_ACCESS = 2; static constexpr size_t NUMBER_OF_ARITHMETIC_GATES_PER_RAM_ARRAY = 1; +// number of gates created per non-native field operation in process_non_native_field_multiplications +static constexpr size_t GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC = 7; struct non_native_field_witnesses { // first 4 array elements = limbs @@ -41,6 +43,54 @@ struct non_native_field_witnesses { barretenberg::fr modulus; }; +/** + * @brief Used to store instructions to create non_native_field_multiplication gates. + * We want to cache these (and remove duplicates) as the stdlib code can end up multiplying the same inputs + * repeatedly. + */ +struct cached_non_native_field_multiplication { + std::array a; + std::array b; + std::array q; + std::array r; + std::array cross_terms; + std::array neg_modulus; + + bool operator==(const cached_non_native_field_multiplication& other) const + { + bool valid = true; + for (size_t i = 0; i < 5; ++i) { + valid = valid && (a[i] == other.a[i]); + valid = valid && (b[i] == other.b[i]); + valid = valid && (q[i] == other.q[i]); + valid = valid && (r[i] == other.r[i]); + } + return valid; + } + bool operator<(const cached_non_native_field_multiplication& other) const + { + if (a < other.a) { + return true; + } + if (a == other.a) { + if (b < other.b) { + return true; + } + if (b == other.b) { + if (q < other.q) { + return true; + } + if (q == other.q) { + if (r < other.r) { + return true; + } + } + } + } + return false; + } +}; + enum AUX_SELECTORS { NONE, LIMB_ACCUMULATE_1, @@ -201,6 +251,10 @@ class UltraCircuitConstructor : public CircuitConstructorBase memory_write_records; + std::vector cached_non_native_field_multiplications; + + void process_non_native_field_multiplications(); + bool circuit_finalised = false; UltraCircuitConstructor(const size_t size_hint = 0) @@ -286,18 +340,18 @@ class UltraCircuitConstructor : public CircuitConstructorBase nnf_copy(cached_non_native_field_multiplications); + // // update nnfcount + // std::sort(nnf_copy.begin(), nnf_copy.end()); + + // auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + // const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + // nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; // } // /** @@ -373,7 +434,8 @@ class UltraCircuitConstructor : public CircuitConstructorBase