From 3048d0820c89f3bcce38913d3744cf5be1ece14f Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:52:54 -0700 Subject: [PATCH] feat: Goblinize the final ecc ops in ZM (#3741) In the initial implementation of ZM I forgot to goblinize a couple of the final group operations in Zeromorph which was causing the gate count of the recursive verifier to blow up. We fix this. We implement the addition, subtraction, and single scalar multiplication operation inefficiently using the batch_mul methods. We disable some member functions that do not use Goblin when they could, and leave references to https://github.com/AztecProtocol/barretenberg/issues/707 as a future optimization task. --------- Co-authored-by: codygunton --- .../zeromorph/zeromorph.hpp | 23 ++- .../stdlib/primitives/biggroup/biggroup.hpp | 45 ++++-- .../primitives/biggroup/biggroup.test.cpp | 84 +++++++--- .../primitives/biggroup/biggroup_bn254.hpp | 2 + .../primitives/biggroup/biggroup_impl.hpp | 153 +++++++++++------- .../circuit_builders/circuit_builders.hpp | 3 + .../honk/verifier/goblin_verifier.test.cpp | 1 + 7 files changed, 212 insertions(+), 99 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 034bf007d20..0339e7fff1d 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -692,7 +692,16 @@ template class ZeroMorphVerifier_ { concatenation_group_commitments); // Compute commitment C_{\zeta,Z} - auto C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; + Commitment C_zeta_Z; + if constexpr (Curve::is_stdlib_type) { + // Express operation as a batch_mul in order to use Goblinization if available + auto builder = rho.get_context(); + std::vector scalars = { FF(builder, 1), z_challenge }; + std::vector points = { C_zeta_x, C_Z_x }; + C_zeta_Z = Commitment::batch_mul(points, scalars); + } else { + C_zeta_Z = C_zeta_x + C_Z_x * z_challenge; + } // Receive proof commitment \pi auto C_pi = transcript->template receive_from_prover("ZM:PI"); @@ -702,7 +711,17 @@ template class ZeroMorphVerifier_ { // e(C_{\zeta,Z}, [1]_2) = e(pi, [X - x]_2). This can be rearranged (e.g. see the plonk paper) as // e(C_{\zeta,Z} - x*pi, [1]_2) * e(-pi, [X]_2) = 1, or // e(P_0, [1]_2) * e(P_1, [X]_2) = 1 - auto P0 = C_zeta_Z + C_pi * x_challenge; + Commitment P0; + if constexpr (Curve::is_stdlib_type) { + // Express operation as a batch_mul in order to use Goblinization if available + auto builder = rho.get_context(); + std::vector scalars = { FF(builder, 1), x_challenge }; + std::vector points = { C_zeta_Z, C_pi }; + P0 = Commitment::batch_mul(points, scalars); + } else { + P0 = C_zeta_Z + C_pi * x_challenge; + } + auto P1 = -C_pi; return { P0, P1 }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp index ee2a04f956a..f413ce25523 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp @@ -2,18 +2,21 @@ #include "../bigfield/bigfield.hpp" #include "../byte_array/byte_array.hpp" -#include "../field/field.hpp" -#include "barretenberg/ecc/curves/bn254/g1.hpp" - #include "../circuit_builders/circuit_builders_fwd.hpp" +#include "../field/field.hpp" #include "../memory/rom_table.hpp" #include "../memory/twin_rom_table.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" #include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" -namespace proof_system::plonk { -namespace stdlib { +// TODO(https://github.com/AztecProtocol/barretenberg/issues/707) If using a a circuit builder with Goblin, which is +// designed to have efficient barretenberg::g1 operations, a developer might accidentally write inefficient circuits +// using biggroup functions that do not use the OpQueue. We use this concept to prevent compilation of such functions. +template +concept IsNotGoblinInefficiencyTrap = !(IsGoblinBuilder && std::same_as); + +namespace proof_system::plonk::stdlib { // ( ͡° ͜ʖ ͡°) template class element { @@ -154,14 +157,22 @@ template class element { * We can chain repeated point additions together, where we only require 2 non-native field multiplications per * point addition, instead of 3 **/ - static chain_add_accumulator chain_add_start(const element& p1, const element& p2); - static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator); - static element chain_add_end(const chain_add_accumulator& accumulator); - - element montgomery_ladder(const element& other) const; - element montgomery_ladder(const chain_add_accumulator& accumulator); - element multiple_montgomery_ladder(const std::vector& to_add) const; - element quadruple_and_add(const std::vector& to_add) const; + static chain_add_accumulator chain_add_start(const element& p1, const element& p2) + requires(IsNotGoblinInefficiencyTrap); + static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator) + requires(IsNotGoblinInefficiencyTrap); + static element chain_add_end(const chain_add_accumulator& accumulator) + requires(IsNotGoblinInefficiencyTrap); + + element montgomery_ladder(const element& other) const + requires(IsNotGoblinInefficiencyTrap); + element montgomery_ladder(const chain_add_accumulator& accumulator) + requires(IsNotGoblinInefficiencyTrap); + element multiple_montgomery_ladder(const std::vector& to_add) const + requires(IsNotGoblinInefficiencyTrap); + + element quadruple_and_add(const std::vector& to_add) const + requires(IsNotGoblinInefficiencyTrap); typename NativeGroup::affine_element get_value() const { @@ -179,6 +190,9 @@ template class element { static element batch_mul(const std::vector& points, const std::vector& scalars, const size_t max_num_bits = 0); + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) max_num_bits is unused; could implement and use + // this to optimize other operations. static element goblin_batch_mul(const std::vector& points, const std::vector& scalars, const size_t max_num_bits = 0); @@ -196,6 +210,7 @@ template class element { // i.e. for the bn254 curve, the template param is `typename = void` // for any other curve, there is no template param template ::value>> + requires(IsNotGoblinBuilder) // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) static element bn254_endo_batch_mul(const std::vector& big_points, const std::vector& big_scalars, const std::vector& small_points, @@ -203,6 +218,7 @@ template class element { const size_t max_num_small_bits); template ::value>> + requires(IsNotGoblinBuilder) // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) static element bn254_endo_batch_mul_with_generator(const std::vector& big_points, const std::vector& big_scalars, const std::vector& small_points, @@ -889,8 +905,7 @@ inline std::ostream& operator<<(std::ostream& os, element const& v { return os << "{ " << v.x << " , " << v.y << " }"; } -} // namespace stdlib -} // namespace proof_system::plonk +} // namespace proof_system::plonk::stdlib #include "biggroup_batch_mul.hpp" #include "biggroup_bn254.hpp" diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index 3e02c3cb6b0..e80440aaa4f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -1,19 +1,14 @@ -#include "barretenberg/common/test.hpp" -#include - -#include "../bigfield/bigfield.hpp" #include "../biggroup/biggroup.hpp" +#include "../bigfield/bigfield.hpp" #include "../bool/bool.hpp" #include "../field/field.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" - #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" #include "barretenberg/stdlib/primitives/curves/secp256r1.hpp" -#include "barretenberg/numeric/random/engine.hpp" -#include - namespace test_stdlib_biggroup { namespace { auto& engine = numeric::random::get_debug_engine(); @@ -825,12 +820,17 @@ template class stdlib_biggroup : public testing::Test { enum UseBigfield { No, Yes }; using TestTypes = testing::Types, UseBigfield::No>, - TestType, UseBigfield::Yes>>; + TestType, UseBigfield::Yes>, + TestType, UseBigfield::No>>; TYPED_TEST_SUITE(stdlib_biggroup, TestTypes); +template +concept HasGoblinBuilder = IsGoblinBuilder; + TYPED_TEST(stdlib_biggroup, add) { + TestFixture::test_add(); } TYPED_TEST(stdlib_biggroup, sub) @@ -843,7 +843,11 @@ TYPED_TEST(stdlib_biggroup, dbl) } TYPED_TEST(stdlib_biggroup, montgomery_ladder) { - TestFixture::test_montgomery_ladder(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_montgomery_ladder(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, mul) { @@ -851,15 +855,27 @@ HEAVY_TYPED_TEST(stdlib_biggroup, mul) } HEAVY_TYPED_TEST(stdlib_biggroup, twin_mul) { - TestFixture::test_twin_mul(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_twin_mul(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, triple_mul) { - TestFixture::test_triple_mul(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_triple_mul(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, quad_mul) { - TestFixture::test_quad_mul(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_quad_mul(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, one) { @@ -872,12 +888,20 @@ HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul) HEAVY_TYPED_TEST(stdlib_biggroup, chain_add) { - TestFixture::test_chain_add(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_chain_add(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, multiple_montgomery_ladder) { - TestFixture::test_multiple_montgomery_ladder(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_multiple_montgomery_ladder(); + }; } HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf) @@ -897,7 +921,11 @@ HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf) HEAVY_TYPED_TEST(stdlib_biggroup, wnaf_batch_mul) { if constexpr (HasPlookup) { - TestFixture::test_compute_wnaf(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_compute_wnaf(); + }; } else { GTEST_SKIP(); } @@ -921,7 +949,11 @@ HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_short_scalars) if constexpr (TypeParam::use_bigfield) { GTEST_SKIP(); } else { - TestFixture::test_batch_mul_short_scalars(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_batch_mul_short_scalars(); + }; } } HEAVY_TYPED_TEST(stdlib_biggroup, wnaf_batch_mul_128_bit) @@ -929,7 +961,11 @@ HEAVY_TYPED_TEST(stdlib_biggroup, wnaf_batch_mul_128_bit) if constexpr (TypeParam::use_bigfield) { GTEST_SKIP(); } else { - TestFixture::test_wnaf_batch_mul_128_bit(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_wnaf_batch_mul_128_bit(); + }; } } HEAVY_TYPED_TEST(stdlib_biggroup, wnaf_batch_4) @@ -945,7 +981,11 @@ HEAVY_TYPED_TEST(stdlib_biggroup, wnaf_batch_4) HEAVY_TYPED_TEST(stdlib_biggroup, bn254_endo_batch_mul) { if constexpr (TypeParam::Curve::type == proof_system::CurveType::BN254 && !TypeParam::use_bigfield) { - TestFixture::test_bn254_endo_batch_mul(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_bn254_endo_batch_mul(); + }; } else { GTEST_SKIP(); } @@ -953,7 +993,11 @@ HEAVY_TYPED_TEST(stdlib_biggroup, bn254_endo_batch_mul) HEAVY_TYPED_TEST(stdlib_biggroup, mixed_mul_bn254_endo) { if constexpr (TypeParam::Curve::type == proof_system::CurveType::BN254 && !TypeParam::use_bigfield) { - TestFixture::test_mixed_mul_bn254_endo(); + if constexpr (HasGoblinBuilder) { + GTEST_SKIP() << "https://github.com/AztecProtocol/barretenberg/issues/707"; + } else { + TestFixture::test_mixed_mul_bn254_endo(); + }; } else { GTEST_SKIP(); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_bn254.hpp index 1384bc2813c..4f4c655e079 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_bn254.hpp @@ -22,6 +22,7 @@ namespace stdlib { **/ template template + requires(IsNotGoblinBuilder) element element::bn254_endo_batch_mul_with_generator( const std::vector& big_points, const std::vector& big_scalars, @@ -216,6 +217,7 @@ element element::bn254_endo_batch_mul_with_generator **/ template template + requires(IsNotGoblinBuilder) element element::bn254_endo_batch_mul(const std::vector& big_points, const std::vector& big_scalars, const std::vector& small_points, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp index a6503bf1285..95302189a68 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp @@ -1,18 +1,11 @@ #pragma once -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/numeric/uintx/uintx.hpp" -#include - -#include "../circuit_builders/circuit_builders.hpp" - #include "../bit_array/bit_array.hpp" -// #include "../field/field.hpp" +#include "../circuit_builders/circuit_builders.hpp" using namespace barretenberg; -namespace proof_system::plonk { -namespace stdlib { +namespace proof_system::plonk::stdlib { template element::element() @@ -63,6 +56,14 @@ element& element::operator=(element&& other) template element element::operator+(const element& other) const { + if constexpr (IsGoblinBuilder && std::same_as) { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) Optimize + // Current gate count: 6398 + std::vector points{ *this, other }; + std::vector scalars{ 1, 1 }; + return goblin_batch_mul(points, scalars); + } + other.x.assert_is_not_equal(x); const Fq lambda = Fq::div_without_denominator_check({ other.y, -y }, (other.x - x)); const Fq x3 = lambda.sqradd({ -other.x, -x }); @@ -73,6 +74,13 @@ element element::operator+(const element& other) con template element element::operator-(const element& other) const { + if constexpr (IsGoblinBuilder && std::same_as) { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) Optimize + std::vector points{ *this, other }; + std::vector scalars{ 1, -Fr(1) }; + return goblin_batch_mul(points, scalars); + } + other.x.assert_is_not_equal(x); const Fq lambda = Fq::div_without_denominator_check({ other.y, y }, (other.x - x)); const Fq x_3 = lambda.sqradd({ -other.x, -x }); @@ -95,9 +103,14 @@ element element::operator-(const element& other) con * @param other * @return std::array, 2> */ +// TODO(https://github.com/AztecProtocol/barretenberg/issues/657): This function is untested template std::array, 2> element::add_sub(const element& other) const { + if constexpr (IsGoblinBuilder && std::same_as) { + return { *this + other, *this - other }; + } + other.x.assert_is_not_equal(x); const Fq denominator = other.x - x; @@ -115,6 +128,7 @@ std::array, 2> element::add_sub(const elemen template element element::dbl() const { + Fq two_x = x + x; if constexpr (G::has_a) { Fq a(get_context(), uint256_t(G::curve_a)); @@ -150,6 +164,7 @@ template element element template typename element::chain_add_accumulator element::chain_add_start(const element& p1, const element& p2) + requires(IsNotGoblinInefficiencyTrap) { chain_add_accumulator output; output.x1_prev = p1.x; @@ -167,6 +182,7 @@ typename element::chain_add_accumulator element::cha template typename element::chain_add_accumulator element::chain_add(const element& p1, const chain_add_accumulator& acc) + requires(IsNotGoblinInefficiencyTrap) { // use `chain_add_start` to start an addition chain (i.e. if acc has a y-coordinate) if (acc.is_element) { @@ -210,6 +226,7 @@ typename element::chain_add_accumulator element::cha **/ template element element::chain_add_end(const chain_add_accumulator& acc) + requires(IsNotGoblinInefficiencyTrap) { if (acc.is_element) { return element(acc.x3_prev, acc.y3_prev); @@ -263,6 +280,7 @@ element element::chain_add_end(const chain_add_accum **/ template element element::montgomery_ladder(const element& other) const + requires(IsNotGoblinInefficiencyTrap) { other.x.assert_is_not_equal(x); const Fq lambda_1 = Fq::div_without_denominator_check({ other.y - y }, (other.x - x)); @@ -299,6 +317,7 @@ element element::montgomery_ladder(const element& ot **/ template element element::montgomery_ladder(const chain_add_accumulator& to_add) + requires(IsNotGoblinInefficiencyTrap) { if (to_add.is_element) { throw_or_abort("An accumulator expected"); @@ -342,6 +361,7 @@ element element::montgomery_ladder(const chain_add_a */ template element element::quadruple_and_add(const std::vector& to_add) const + requires(IsNotGoblinInefficiencyTrap) { const Fq two_x = x + x; Fq x_1; @@ -437,6 +457,7 @@ element element::quadruple_and_add(const std::vector template element element::multiple_montgomery_ladder( const std::vector& add) const + requires(IsNotGoblinInefficiencyTrap) { struct composite_y { std::vector mul_left; @@ -603,48 +624,50 @@ element element::batch_mul(const std::vector && std::same_as) { return goblin_batch_mul(points, scalars); - } + } else { - const size_t num_points = points.size(); - ASSERT(scalars.size() == num_points); - batch_lookup_table point_table(points); - const size_t num_rounds = (max_num_bits == 0) ? Fr::modulus.get_msb() + 1 : max_num_bits; + const size_t num_points = points.size(); + ASSERT(scalars.size() == num_points); + batch_lookup_table point_table(points); + const size_t num_rounds = (max_num_bits == 0) ? Fr::modulus.get_msb() + 1 : max_num_bits; - std::vector>> naf_entries; - for (size_t i = 0; i < num_points; ++i) { - naf_entries.emplace_back(compute_naf(scalars[i], max_num_bits)); - } - const auto offset_generators = compute_offset_generators(num_rounds); - element accumulator = - element::chain_add_end(element::chain_add(offset_generators.first, point_table.get_chain_initial_entry())); - - constexpr size_t num_rounds_per_iteration = 4; - size_t num_iterations = num_rounds / num_rounds_per_iteration; - num_iterations += ((num_iterations * num_rounds_per_iteration) == num_rounds) ? 0 : 1; - const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration); - for (size_t i = 0; i < num_iterations; ++i) { - - std::vector> nafs(num_points); - std::vector to_add; - const size_t inner_num_rounds = - (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration; - for (size_t j = 0; j < inner_num_rounds; ++j) { - for (size_t k = 0; k < num_points; ++k) { - nafs[k] = (naf_entries[k][i * num_rounds_per_iteration + j + 1]); + std::vector>> naf_entries; + for (size_t i = 0; i < num_points; ++i) { + naf_entries.emplace_back(compute_naf(scalars[i], max_num_bits)); + } + const auto offset_generators = compute_offset_generators(num_rounds); + element accumulator = + element::chain_add_end(element::chain_add(offset_generators.first, point_table.get_chain_initial_entry())); + + constexpr size_t num_rounds_per_iteration = 4; + size_t num_iterations = num_rounds / num_rounds_per_iteration; + num_iterations += ((num_iterations * num_rounds_per_iteration) == num_rounds) ? 0 : 1; + const size_t num_rounds_per_final_iteration = + (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration); + for (size_t i = 0; i < num_iterations; ++i) { + + std::vector> nafs(num_points); + std::vector to_add; + const size_t inner_num_rounds = + (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration; + for (size_t j = 0; j < inner_num_rounds; ++j) { + for (size_t k = 0; k < num_points; ++k) { + nafs[k] = (naf_entries[k][i * num_rounds_per_iteration + j + 1]); + } + to_add.emplace_back(point_table.get_chain_add_accumulator(nafs)); } - to_add.emplace_back(point_table.get_chain_add_accumulator(nafs)); + accumulator = accumulator.multiple_montgomery_ladder(to_add); } - accumulator = accumulator.multiple_montgomery_ladder(to_add); - } - for (size_t i = 0; i < num_points; ++i) { - element skew = accumulator - points[i]; - Fq out_x = accumulator.x.conditional_select(skew.x, naf_entries[i][num_rounds]); - Fq out_y = accumulator.y.conditional_select(skew.y, naf_entries[i][num_rounds]); - accumulator = element(out_x, out_y); - } - accumulator = accumulator - offset_generators.second; + for (size_t i = 0; i < num_points; ++i) { + element skew = accumulator - points[i]; + Fq out_x = accumulator.x.conditional_select(skew.x, naf_entries[i][num_rounds]); + Fq out_y = accumulator.y.conditional_select(skew.y, naf_entries[i][num_rounds]); + accumulator = element(out_x, out_y); + } + accumulator = accumulator - offset_generators.second; - return accumulator; + return accumulator; + } } /** @@ -678,27 +701,33 @@ element element::operator*(const Fr& scalar) const * specifics. * **/ - constexpr uint64_t num_rounds = Fr::modulus.get_msb() + 1; - std::vector> naf_entries = compute_naf(scalar); + if constexpr (IsGoblinBuilder && std::same_as) { + std::vector points{ *this }; + std::vector scalars{ scalar }; + return goblin_batch_mul(points, scalars); + } else { + constexpr uint64_t num_rounds = Fr::modulus.get_msb() + 1; - const auto offset_generators = compute_offset_generators(num_rounds); + std::vector> naf_entries = compute_naf(scalar); - element accumulator = *this + offset_generators.first; + const auto offset_generators = compute_offset_generators(num_rounds); - for (size_t i = 1; i < num_rounds; ++i) { - bool_t predicate = naf_entries[i]; - bigfield y_test = y.conditional_negate(predicate); - element to_add(x, y_test); - accumulator = accumulator.montgomery_ladder(to_add); - } + element accumulator = *this + offset_generators.first; - element skew_output = accumulator - (*this); + for (size_t i = 1; i < num_rounds; ++i) { + bool_t predicate = naf_entries[i]; + bigfield y_test = y.conditional_negate(predicate); + element to_add(x, y_test); + accumulator = accumulator.montgomery_ladder(to_add); + } + + element skew_output = accumulator - (*this); - Fq out_x = accumulator.x.conditional_select(skew_output.x, naf_entries[num_rounds]); - Fq out_y = accumulator.y.conditional_select(skew_output.y, naf_entries[num_rounds]); + Fq out_x = accumulator.x.conditional_select(skew_output.x, naf_entries[num_rounds]); + Fq out_y = accumulator.y.conditional_select(skew_output.y, naf_entries[num_rounds]); - return element(out_x, out_y) - element(offset_generators.second); + return element(out_x, out_y) - element(offset_generators.second); + } } -} // namespace stdlib -} // namespace proof_system::plonk +} // namespace proof_system::plonk::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp index d748baa72a8..045e1445d57 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp @@ -13,6 +13,9 @@ concept HasPlookup = template concept IsGoblinBuilder = proof_system::IsAnyOf; +template +concept IsNotGoblinBuilder = ! +IsGoblinBuilder; #define INSTANTIATE_STDLIB_METHOD(stdlib_method) \ template stdlib_method(proof_system::StandardCircuitBuilder); \ diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/goblin_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/goblin_verifier.test.cpp index 8130ff26e85..22e82dc4824 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/goblin_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/goblin_verifier.test.cpp @@ -187,6 +187,7 @@ template class GoblinRecursiveVerifierTest : public testi OuterBuilder outer_circuit; RecursiveVerifier verifier{ &outer_circuit, instance->verification_key }; auto pairing_points = verifier.verify_proof(inner_proof); + info("Recursive Verifier: num gates = ", outer_circuit.num_gates); // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err();