From 9211d8afbd0fe31043ea593675ce5a72c1dc7e4e Mon Sep 17 00:00:00 2001 From: Zachary James Williamson Date: Wed, 30 Oct 2024 12:51:29 -0700 Subject: [PATCH] feat: biggroup_goblin handles points at infinity + 1.8x reduction in ECCVM size (#9366) This PR adds support for biggroup_goblin handling elliptic curve points at infinity This feature is used to optimize the ECCVM instructions created when running a recursive protogalaxy verifier. Instead of performing size-2 scalar multiplications for every witness/commitment, additional random challenges are generated in order to evaluate two large batch multiplications. The technique implemented is described in https://hackmd.io/T1239dufTgO1v8Ie7EExlQ?view In the case of ClientIVCRecursionTests.ClientTubeBase where the number of circuits is set to four, this reduces the size of the ECCVM execution trace size by 45%, which is good enough to reduce the log dyadic size of the ClientIVCVerifier by 1/2. --- .../eccvm/eccvm_builder_types.hpp | 11 ++ .../src/barretenberg/goblin/mock_circuits.hpp | 6 +- .../arithmetization/mega_arithmetization.hpp | 11 +- .../protogalaxy/protogalaxy_verifier.cpp | 9 ++ .../primitives/biggroup/biggroup_goblin.hpp | 69 +++++++++- .../biggroup/biggroup_goblin.test.cpp | 121 +++++++++++++++++- .../biggroup/biggroup_goblin_impl.hpp | 11 +- .../protogalaxy_recursive_verifier.cpp | 109 ++++++++++++++-- .../mega_recursive_flavor.hpp | 2 + .../op_queue/ecc_op_queue.hpp | 19 ++- .../translator_circuit_builder.cpp | 21 +-- .../translator_circuit_builder.test.cpp | 5 +- 12 files changed, 347 insertions(+), 47 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp index 2db0d13abf2..a8158fdeed9 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp @@ -33,6 +33,17 @@ template struct VMOperation { return res; } bool operator==(const VMOperation& other) const = default; + + std::array get_base_point_standard_form() const + { + uint256_t x(base_point.x); + uint256_t y(base_point.y); + if (base_point.is_point_at_infinity()) { + x = 0; + y = 0; + } + return { x, y }; + } }; template struct ScalarMul { uint32_t pc; diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index 73c2ad839fb..2ddcdfb2038 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -57,11 +57,11 @@ class GoblinMockCircuits { PROFILE_THIS(); if (large) { // Results in circuit size 2^19 - stdlib::generate_sha256_test_circuit(builder, 12); + stdlib::generate_sha256_test_circuit(builder, 11); stdlib::generate_ecdsa_verification_test_circuit(builder, 10); stdlib::generate_merkle_membership_test_circuit(builder, 12); } else { // Results in circuit size 2^17 - stdlib::generate_sha256_test_circuit(builder, 9); + stdlib::generate_sha256_test_circuit(builder, 8); stdlib::generate_ecdsa_verification_test_circuit(builder, 2); stdlib::generate_merkle_membership_test_circuit(builder, 10); } @@ -192,7 +192,7 @@ class GoblinMockCircuits { // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make // the kernel "full" within the dyadic size 2^17 - const size_t NUM_MERKLE_CHECKS = 20; + const size_t NUM_MERKLE_CHECKS = 19; const size_t NUM_ECDSA_VERIFICATIONS = 1; const size_t NUM_SHA_HASHES = 1; stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp index b4703c9179f..379602e2463 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp @@ -74,14 +74,14 @@ template class MegaArith { { this->ecc_op = 1 << 10; this->pub_inputs = 1 << 7; - this->busread = 1 << 7; - this->arithmetic = 201000; + this->arithmetic = 198000; this->delta_range = 90000; this->elliptic = 9000; - this->aux = 137000; - this->poseidon2_external = 2500; - this->poseidon2_internal = 11500; + this->aux = 136000; this->lookup = 72000; + this->busread = 1 << 7; + this->poseidon2_external = 2500; + this->poseidon2_internal = 14000; } }; @@ -228,6 +228,7 @@ template class MegaArith { { for (auto block : this->get()) { if (block.size() > block.get_fixed_size()) { + info("WARNING: Num gates in circuit block exceeds the specified fixed size - execution trace will " "not be constructed correctly!"); summarize(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 342286270cb..b0a8a27a6e8 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -134,6 +134,15 @@ std::shared_ptr ProtogalaxyVerifier combination = linear_combination(to_combine, lagranges); } + // generate recursive folding challenges to ensure manifest matches with recursive verifier + // (in recursive verifier we use random challenges to convert Flavor::NUM_FOLDED_ENTITIES muls + // into one large multiscalar multiplication) + std::array args; + for (size_t idx = 0; idx < Flavor::NUM_FOLDED_ENTITIES; ++idx) { + args[idx] = "accumulator_combination_challenges" + std::to_string(idx); + } + transcript->template get_challenges(args); + return next_accumulator; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp index dcb321b3e5c..11d37f67f4b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp @@ -54,9 +54,10 @@ template class goblin_ele static goblin_element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input) { goblin_element out; + // ECCVM requires points at infinity to be represented by 0-value x/y coords if (input.is_point_at_infinity()) { - Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.x); - Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.y); + Fq x = Fq::from_witness(ctx, bb::fq(0)); + Fq y = Fq::from_witness(ctx, bb::fq(0)); out.x = x; out.y = y; } else { @@ -92,6 +93,16 @@ template class goblin_ele return goblin_element(x_fq, y_fq); } + static goblin_element point_at_infinity(Builder* ctx) + { + Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx); + Fq x_fq(zero, zero); + Fq y_fq(zero, zero); + goblin_element result(x_fq, y_fq); + result.set_point_at_infinity(true); + return result; + } + goblin_element checked_unconditional_add(const goblin_element& other) const { return goblin_element::operator+(*this, other); @@ -107,10 +118,58 @@ template class goblin_ele } goblin_element operator-(const goblin_element& other) const { - std::vector points{ *this, other }; - return batch_mul({ *this, other }, { Fr(1), -Fr(1) }); + auto builder = get_context(other); + // Check that the internal accumulator is zero + ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity()); + + // Compute the result natively, and validate that result + other == *this + typename NativeGroup::affine_element result_value = typename NativeGroup::affine_element( + typename NativeGroup::element(get_value()) - typename NativeGroup::element(other.get_value())); + + ecc_op_tuple op_tuple; + op_tuple = builder->queue_ecc_add_accum(other.get_value()); + { + auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi); + x_lo.assert_equal(other.x.limbs[0]); + x_hi.assert_equal(other.x.limbs[1]); + y_lo.assert_equal(other.y.limbs[0]); + y_hi.assert_equal(other.y.limbs[1]); + } + + ecc_op_tuple op_tuple2 = builder->queue_ecc_add_accum(result_value); + auto x_lo = Fr::from_witness_index(builder, op_tuple2.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple2.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple2.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple2.y_hi); + + Fq result_x(x_lo, x_hi); + Fq result_y(y_lo, y_hi); + goblin_element result(result_x, result_y); + + // if the output is at infinity, this is represented by x/y coordinates being zero + // because they are all 136-bit, we can do a cheap zerocheck by first summing the limbs + auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero(); + result.set_point_at_infinity(op2_is_infinity); + { + ecc_op_tuple op_tuple3 = builder->queue_ecc_eq(); + auto x_lo = Fr::from_witness_index(builder, op_tuple3.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple3.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple3.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple3.y_hi); + + x_lo.assert_equal(x.limbs[0]); + x_hi.assert_equal(x.limbs[1]); + y_lo.assert_equal(y.limbs[0]); + y_hi.assert_equal(y.limbs[1]); + } + return result; } - goblin_element operator-() const { return batch_mul({ *this }, { -Fr(1) }); } + + goblin_element operator-() const { return point_at_infinity(get_context()) - *this; } + goblin_element operator+=(const goblin_element& other) { *this = *this + other; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp index 9dd26890fbc..6fc3ec6cbea 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp @@ -42,6 +42,7 @@ template class stdlib_biggroup_goblin : public testing::Test { static void test_goblin_style_batch_mul() { const size_t num_points = 5; + const size_t edge_case_points = 3; Builder builder; std::vector points; @@ -50,10 +51,16 @@ template class stdlib_biggroup_goblin : public testing::Test { points.push_back(affine_element(element::random_element())); scalars.push_back(fr::random_element()); } + points.push_back(g1::affine_point_at_infinity); + scalars.push_back(fr::random_element()); + points.push_back(g1::affine_point_at_infinity); + scalars.push_back(0); + points.push_back(element::random_element()); + scalars.push_back(0); std::vector circuit_points; std::vector circuit_scalars; - for (size_t i = 0; i < num_points; ++i) { + for (size_t i = 0; i < num_points + edge_case_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); } @@ -62,7 +69,7 @@ template class stdlib_biggroup_goblin : public testing::Test { element expected_point = g1::one; expected_point.self_set_infinity(); - for (size_t i = 0; i < num_points; ++i) { + for (size_t i = 0; i < num_points + edge_case_points; ++i) { expected_point += (element(points[i]) * scalars[i]); } @@ -75,13 +82,121 @@ template class stdlib_biggroup_goblin : public testing::Test { EXPECT_CIRCUIT_CORRECTNESS(builder); } + + static void test_goblin_style_batch_mul_to_zero() + { + const size_t num_points = 5; + Builder builder; + + std::vector points; + std::vector scalars; + for (size_t i = 0; i < num_points; ++i) { + points.push_back(affine_element(element::random_element())); + scalars.push_back(fr::random_element()); + } + for (size_t i = 0; i < num_points; ++i) { + points.push_back(points[i]); + scalars.push_back(-scalars[i]); + } + std::vector circuit_points; + std::vector circuit_scalars; + for (size_t i = 0; i < num_points * 2; ++i) { + circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + } + + element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars); + + EXPECT_EQ(result_point.get_value(), g1::affine_point_at_infinity); + EXPECT_CIRCUIT_CORRECTNESS(builder); + } + + /** + * @brief Test goblin-style sub + * @details Check that 1) Goblin-style batch sub returns correct value (esp. when infinities involved), and 2) + * resulting circuit is correct + */ + static void test_goblin_style_sub() + { + Builder builder; + + for (size_t i = 0; i < 100; ++i) { + + affine_element lhs(element::random_element()); + affine_element rhs(element::random_element()); + + affine_element expected = affine_element(element(lhs) - element(rhs)); + + element_ct lhs_ct = element_ct::from_witness(&builder, lhs); + element_ct lhs2_ct = element_ct::from_witness(&builder, lhs); + + element_ct rhs_ct = element_ct::from_witness(&builder, rhs); + element_ct out_ct = lhs_ct - rhs_ct; + EXPECT_EQ(out_ct.get_value(), expected); + + element_ct zero_ct = lhs_ct - lhs_ct; + EXPECT_TRUE(zero_ct.get_value().is_point_at_infinity()); + + element_ct zero_ct2 = lhs_ct - lhs2_ct; + EXPECT_TRUE(zero_ct2.get_value().is_point_at_infinity()); + + element_ct out2_ct = element_ct::point_at_infinity(&builder) - rhs_ct; + EXPECT_EQ(out2_ct.get_value(), -rhs); + + element_ct out3_ct = lhs_ct - element_ct::point_at_infinity(&builder); + EXPECT_EQ(out3_ct.get_value(), lhs); + + auto lhs_infinity_ct = element_ct::point_at_infinity(&builder); + auto rhs_infinity_ct = element_ct::point_at_infinity(&builder); + element_ct out4_ct = lhs_infinity_ct - rhs_infinity_ct; + EXPECT_TRUE(out4_ct.get_value().is_point_at_infinity()); + EXPECT_TRUE(out4_ct.is_point_at_infinity().get_value()); + } + EXPECT_CIRCUIT_CORRECTNESS(builder); + } + + /** + * @brief Check goblin-style negate works as intended, including with points at infinity + */ + static void test_goblin_style_neg() + { + Builder builder; + affine_element lhs(element::random_element()); + + affine_element expected = -lhs; + + element_ct lhs_ct = element_ct::from_witness(&builder, lhs); + + element_ct result_ct = -lhs_ct; + EXPECT_EQ(result_ct.get_value(), expected); + + element_ct infinity = element_ct::point_at_infinity(&builder); + element_ct result2_ct = -infinity; + EXPECT_EQ(result2_ct.get_value(), g1::affine_point_at_infinity); + EXPECT_CIRCUIT_CORRECTNESS(builder); + } }; using TestTypes = testing::Types>; TYPED_TEST_SUITE(stdlib_biggroup_goblin, TestTypes); -HEAVY_TYPED_TEST(stdlib_biggroup_goblin, batch_mul) +TYPED_TEST(stdlib_biggroup_goblin, batch_mul) { TestFixture::test_goblin_style_batch_mul(); } + +TYPED_TEST(stdlib_biggroup_goblin, batch_mul_equals_zero) +{ + TestFixture::test_goblin_style_batch_mul_to_zero(); +} + +TYPED_TEST(stdlib_biggroup_goblin, sub) +{ + TestFixture::test_goblin_style_sub(); +} + +TYPED_TEST(stdlib_biggroup_goblin, neg) +{ + TestFixture::test_goblin_style_neg(); +} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp index 97210ebdd3d..5cd94c27c04 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp @@ -90,11 +90,12 @@ goblin_element goblin_element::batch_mul(const std:: Fq point_y(y_lo, y_hi); goblin_element result = goblin_element(point_x, point_y); - // NOTE: we can have an `if` statement here under the strict assumption that `return_is_infinity` - // is produced from `eq_and_reset` opcode - if (op_tuple.return_is_infinity) { - result.set_point_at_infinity(bool_ct(builder, true)); - }; + // NOTE: this used to be set as a circuit constant from `op_tuple.return_is_infinity + // I do not see how this was secure as it meant a circuit constant could change depending on witness values + // e.g. x*[P] + y*[Q] where `x = y` and `[P] = -[Q]` + // TODO(@zac-williamson) what is op_queue.return_is_infinity actually used for? I don't see its value + auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero(); + result.set_point_at_infinity(op2_is_infinity); return result; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp index 67199413f86..566a8d83397 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp @@ -77,25 +77,98 @@ std::shared_ptr ProtogalaxyRecursiv const FF vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); const std::vector lagranges = { FF(1) - combiner_challenge, combiner_challenge }; + /* + Fold the commitments + Note: we use additional challenges to reduce the amount of elliptic curve work performed by the ECCVM + + For an accumulator commitment [P'] and an instance commitment [P] , we compute folded commitment [P''] where + [P''] = L0(gamma).[P'] + L1(gamma).[P] + For the size-2 case this becomes: + P'' = (1 - gamma).[P'] + gamma.[P] = gamma.[P - P'] + [P'] + + This requires a large number of size-1 scalar muls (about 53) + The ECCVM can perform a size-k MSM in 32 + roundup((k/4)) rows, if each scalar multiplier is <128 bits + i.e. number of ECCVM rows = 53 * 64 = painful + + To optimize, we generate challenges `c_i` for each commitment and evaluate the relation: + + [A] = \sum c_i.[P_i] + [B] = \sum c_i.[P'_i] + [C] = \sum c_i.[P''_i] + and validate + (1 - gamma).[A] + gamma.[B] == [C] + + + This reduces the relation to 3 large MSMs where each commitment requires 3 size-128bit scalar multiplications + For a flavor with 53 instance/witness commitments, this is 53 * 24 rows + + Note: there are more efficient ways to evaluate this relationship if one solely wants to reduce number of scalar + muls, however we must also consider the number of ECCVM operations being executed, as each operation incurs a + cost in the translator circuit Each ECCVM opcode produces 5 rows in the translator circuit, which is approx. + equivalent to 9 ECCVM rows. Something to pay attention to + */ + std::vector accumulator_commitments; + std::vector instance_commitments; + for (const auto& precomputed : keys_to_fold.get_precomputed_commitments()) { + ASSERT(precomputed.size() == 2); + accumulator_commitments.emplace_back(precomputed[0]); + instance_commitments.emplace_back(precomputed[1]); + } + for (const auto& witness : keys_to_fold.get_witness_commitments()) { + ASSERT(witness.size() == 2); + accumulator_commitments.emplace_back(witness[0]); + instance_commitments.emplace_back(witness[1]); + } + + // derive output commitment witnesses + std::vector output_commitments; + for (size_t i = 0; i < accumulator_commitments.size(); ++i) { + const auto lhs_scalar = (FF(1) - combiner_challenge).get_value(); + const auto rhs_scalar = combiner_challenge.get_value(); + + const auto lhs = accumulator_commitments[i].get_value(); + + const auto rhs = instance_commitments[i].get_value(); + const auto output = lhs * lhs_scalar + rhs * rhs_scalar; + output_commitments.emplace_back(Commitment::from_witness(builder, output)); + } + + std::array args; + for (size_t idx = 0; idx < Flavor::NUM_FOLDED_ENTITIES; ++idx) { + args[idx] = "accumulator_combination_challenges" + std::to_string(idx); + } + std::array folding_challenges = transcript->template get_challenges(args); + std::vector scalars(folding_challenges.begin(), folding_challenges.end()); + + Commitment accumulator_sum = Commitment::batch_mul(accumulator_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment instance_sum = Commitment::batch_mul(instance_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment output_sum = Commitment::batch_mul(output_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment folded_sum = Commitment::batch_mul({ accumulator_sum, instance_sum }, + lagranges, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + output_sum.x.assert_equal(folded_sum.x); + output_sum.y.assert_equal(folded_sum.y); + // Compute next folding parameters accumulator->is_accumulator = true; accumulator->target_sum = perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); - // Fold the commitments - for (auto [combination, to_combine] : - zip_view(accumulator->verification_key->get_all(), keys_to_fold.get_precomputed_commitments())) { - combination = Commitment::batch_mul( - to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder); - } - - for (auto [combination, to_combine] : - zip_view(accumulator->witness_commitments.get_all(), keys_to_fold.get_witness_commitments())) { - combination = Commitment::batch_mul( - to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder); - } - // Fold the relation parameters for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) { combination = linear_combination(to_combine, lagranges); @@ -105,6 +178,16 @@ std::shared_ptr ProtogalaxyRecursiv combination = linear_combination(to_combine, lagranges); } + auto accumulator_vkey = accumulator->verification_key->get_all(); + for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) { + accumulator_vkey[i] = output_commitments[i]; + } + + auto accumulator_witnesses = accumulator->witness_commitments.get_all(); + for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) { + accumulator_witnesses[i] = output_commitments[i + accumulator_vkey.size()]; + } + return accumulator; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp index fc310b80e54..ad0d1ccb534 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp @@ -54,6 +54,8 @@ template class MegaRecursiveFlavor_ { static constexpr size_t NUM_PRECOMPUTED_ENTITIES = MegaFlavor::NUM_PRECOMPUTED_ENTITIES; // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = MegaFlavor::NUM_WITNESS_ENTITIES; + // Total number of folded polynomials, which is just all polynomials except the shifts + static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES; // define the tuple of Relations that comprise the Sumcheck relation // Reuse the Relations from Mega diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp index 93ebc0a35d9..bc390f0001c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp @@ -488,6 +488,13 @@ class ECCOpQueue { uint256_t x_256(point.x); uint256_t y_256(point.y); ultra_op.return_is_infinity = point.is_point_at_infinity(); + // if we have a point at infinity, set x/y to zero + // in the biggroup_goblin class we use `assert_equal` statements to validate + // the original in-circuit coordinate values are also zero + if (point.is_point_at_infinity()) { + x_256 = 0; + y_256 = 0; + } ultra_op.x_lo = Fr(x_256.slice(0, CHUNK_SIZE)); ultra_op.x_hi = Fr(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); ultra_op.y_lo = Fr(y_256.slice(0, CHUNK_SIZE)); @@ -497,9 +504,15 @@ class ECCOpQueue { Fr z_1 = 0; Fr z_2 = 0; auto converted = scalar.from_montgomery_form(); - Fr::split_into_endomorphism_scalars(converted, z_1, z_2); - ultra_op.z_1 = z_1.to_montgomery_form(); - ultra_op.z_2 = z_2.to_montgomery_form(); + uint256_t converted_u256(scalar); + if (converted_u256.get_msb() <= 128) { + ultra_op.z_1 = scalar; + ultra_op.z_2 = 0; + } else { + Fr::split_into_endomorphism_scalars(converted, z_1, z_2); + ultra_op.z_1 = z_1.to_montgomery_form(); + ultra_op.z_2 = z_2.to_montgomery_form(); + } append_to_ultra_ops(ultra_op); diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp index 9094e12a561..73a77c2c075 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp @@ -577,12 +577,17 @@ TranslatorCircuitBuilder::AccumulationInput compute_witness_values_for_one_ecc_o Fr p_y_hi(0); // Split P.x and P.y into their representations in bn254 transcript - p_x_lo = Fr(uint256_t(ecc_op.base_point.x).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_x_hi = Fr(uint256_t(ecc_op.base_point.x) - .slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_y_lo = Fr(uint256_t(ecc_op.base_point.y).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_y_hi = Fr(uint256_t(ecc_op.base_point.y) - .slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + // if we have a point at infinity, set x/y to zero + // in the biggroup_goblin class we use `assert_equal` statements to validate + // the original in-circuit coordinate values are also zero + const auto [x_256, y_256] = ecc_op.get_base_point_standard_form(); + + p_x_lo = Fr(uint256_t(x_256).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_x_hi = Fr(uint256_t(x_256).slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, + 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_y_lo = Fr(uint256_t(y_256).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_y_hi = Fr(uint256_t(y_256).slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, + 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); // Generate the full witness values return generate_witness_values(op, @@ -614,9 +619,9 @@ void TranslatorCircuitBuilder::feed_ecc_op_queue_into_circuit(std::shared_ptrget_raw_ops(); for (const auto& ecc_op : raw_ops) { op_accumulator = op_accumulator * x_inv + ecc_op.get_opcode_value(); - p_x_accumulator = p_x_accumulator * x_inv + ecc_op.base_point.x; - p_y_accumulator = p_y_accumulator * x_inv + ecc_op.base_point.y; + const auto [x_u256, y_u256] = ecc_op.get_base_point_standard_form(); + p_x_accumulator = p_x_accumulator * x_inv + x_u256; + p_y_accumulator = p_y_accumulator * x_inv + y_u256; z_1_accumulator = z_1_accumulator * x_inv + ecc_op.z1; z_2_accumulator = z_2_accumulator * x_inv + ecc_op.z2; }