From 35475e22f207573369352e629ac636bfb6130b1d Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Tue, 29 Oct 2024 19:14:26 +0000 Subject: [PATCH 01/43] wip --- .../stdlib/primitives/group/cycle_group.cpp | 114 +++++++++++------- .../stdlib/primitives/group/cycle_group.hpp | 22 ++-- 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 789c5eb76275..7fbf698d594a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -203,7 +203,7 @@ template cycle_group cycle_group::get_stand * @return cycle_group */ template -cycle_group cycle_group::dbl() const +cycle_group cycle_group::dbl([[maybe_unused]] const std::optional /*unused*/) const requires IsNotUltraArithmetic { auto modified_y = field_t::conditional_assign(is_point_at_infinity(), 1, y); @@ -220,29 +220,36 @@ cycle_group cycle_group::dbl() const * @return cycle_group */ template -cycle_group cycle_group::dbl() const +cycle_group cycle_group::dbl(const std::optional hint) const requires IsUltraArithmetic { // ensure we use a value of y that is not zero. (only happens if point at infinity) // this costs 0 gates if `is_infinity` is a circuit constant auto modified_y = field_t::conditional_assign(is_point_at_infinity(), 1, y).normalize(); - auto x1 = x.get_value(); - auto y1 = modified_y.get_value(); - - // N.B. the formula to derive the witness value for x3 mirrors the formula in elliptic_relation.hpp - // Specifically, we derive x^4 via the Short Weierstrass curve formula `y^2 = x^3 + b` - // i.e. x^4 = x * (y^2 - b) - // We must follow this pattern exactly to support the edge-case where the input is the point at infinity. - auto y_pow_2 = y1.sqr(); - auto x_pow_4 = x1 * (y_pow_2 - Group::curve_b); - auto lambda_squared = (x_pow_4 * 9) / (y_pow_2 * 4); - auto lambda = (x1 * x1 * 3) / (y1 + y1); - auto x3 = lambda_squared - x1 - x1; - auto y3 = lambda * (x1 - x3) - y1; + + cycle_group result; + if (hint.has_value()) { + result = hint.value(); + } else { + auto x1 = x.get_value(); + auto y1 = modified_y.get_value(); + + // N.B. the formula to derive the witness value for x3 mirrors the formula in elliptic_relation.hpp + // Specifically, we derive x^4 via the Short Weierstrass curve formula `y^2 = x^3 + b` + // i.e. x^4 = x * (y^2 - b) + // We must follow this pattern exactly to support the edge-case where the input is the point at infinity. + auto y_pow_2 = y1.sqr(); + auto x_pow_4 = x1 * (y_pow_2 - Group::curve_b); + auto lambda_squared = (x_pow_4 * 9) / (y_pow_2 * 4); + auto lambda = (x1 * x1 * 3) / (y1 + y1); + auto x3 = lambda_squared - x1 - x1; + auto y3 = lambda * (x1 - x3) - y1; + + result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); + } if (is_constant()) { - return cycle_group(x3, y3, is_point_at_infinity().get_value()); + return result; } - cycle_group result(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); context->create_ecc_dbl_gate(bb::ecc_dbl_gate_{ .x1 = x.get_witness_index(), .y1 = modified_y.normalize().get_witness_index(), @@ -263,7 +270,8 @@ cycle_group cycle_group::dbl() const * @return cycle_group */ template -cycle_group cycle_group::unconditional_add(const cycle_group& other) const +cycle_group cycle_group::unconditional_add( + const cycle_group& other, [[maybe_unused]] const std::optional /*unused*/ /*unused*/) const requires IsNotUltraArithmetic { auto x_diff = other.x - x; @@ -288,7 +296,8 @@ cycle_group cycle_group::unconditional_add(const cycle_group& * @return cycle_group */ template -cycle_group cycle_group::unconditional_add(const cycle_group& other) const +cycle_group cycle_group::unconditional_add(const cycle_group& other, + const std::optional hint) const requires IsUltraArithmetic { auto context = get_context(other); @@ -303,17 +312,21 @@ cycle_group cycle_group::unconditional_add(const cycle_group& auto rhs = cycle_group::from_constant_witness(context, other.get_value()); return unconditional_add(rhs); } + cycle_group result; + if (hint.has_value()) { + result = hint.value(); + } else { - const auto p1 = get_value(); - const auto p2 = other.get_value(); - AffineElement p3(Element(p1) + Element(p2)); + const auto p1 = get_value(); + const auto p2 = other.get_value(); + AffineElement p3(Element(p1) + Element(p2)); + field_t r_x(witness_t(context, p3.x)); + field_t r_y(witness_t(context, p3.y)); + result = cycle_group(r_x, r_y, false); + } if (lhs_constant && rhs_constant) { - return cycle_group(p3); + return result; } - field_t r_x(witness_t(context, p3.x)); - field_t r_y(witness_t(context, p3.y)); - cycle_group result(r_x, r_y, false); - bb::ecc_add_gate_ add_gate{ .x1 = x.get_witness_index(), .y1 = y.get_witness_index(), @@ -338,10 +351,11 @@ cycle_group cycle_group::unconditional_add(const cycle_group& * @return cycle_group */ template -cycle_group cycle_group::unconditional_subtract(const cycle_group& other) const +cycle_group cycle_group::unconditional_subtract(const cycle_group& other, + const std::optional hint) const { if constexpr (!IS_ULTRA) { - return unconditional_add(-other); + return unconditional_add(-other, hint); } else { auto context = get_context(other); @@ -350,22 +364,28 @@ cycle_group cycle_group::unconditional_subtract(const cycle_gr if (lhs_constant && !rhs_constant) { auto lhs = cycle_group::from_constant_witness(context, get_value()); - return lhs.unconditional_subtract(other); + return lhs.unconditional_subtract(other, hint); } if (!lhs_constant && rhs_constant) { auto rhs = cycle_group::from_constant_witness(context, other.get_value()); - return unconditional_subtract(rhs); + return unconditional_subtract(rhs, hint); + } + cycle_group result; + if (hint.has_value()) { + result = hint.value(); + } else { + + auto p1 = get_value(); + auto p2 = other.get_value(); + AffineElement p3(Element(p1) - Element(p2)); + + field_t r_x(witness_t(context, p3.x)); + field_t r_y(witness_t(context, p3.y)); + result = cycle_group(r_x, r_y, false); } - auto p1 = get_value(); - auto p2 = other.get_value(); - AffineElement p3(Element(p1) - Element(p2)); if (lhs_constant && rhs_constant) { - return cycle_group(p3); + return result; } - field_t r_x(witness_t(context, p3.x)); - field_t r_y(witness_t(context, p3.y)); - cycle_group result(r_x, r_y, false); - bb::ecc_add_gate_ add_gate{ .x1 = x.get_witness_index(), .y1 = y.get_witness_index(), @@ -394,11 +414,12 @@ cycle_group cycle_group::unconditional_subtract(const cycle_gr * @return cycle_group */ template -cycle_group cycle_group::checked_unconditional_add(const cycle_group& other) const +cycle_group cycle_group::checked_unconditional_add(const cycle_group& other, + const std::optional hint) const { field_t x_delta = x - other.x; x_delta.assert_is_not_zero("cycle_group::checked_unconditional_add, x-coordinate collision"); - return unconditional_add(other); + return unconditional_add(other, hint); } /** @@ -414,11 +435,12 @@ cycle_group cycle_group::checked_unconditional_add(const cycle * @return cycle_group */ template -cycle_group cycle_group::checked_unconditional_subtract(const cycle_group& other) const +cycle_group cycle_group::checked_unconditional_subtract(const cycle_group& other, + const std::optional hint) const { field_t x_delta = x - other.x; x_delta.assert_is_not_zero("cycle_group::checked_unconditional_subtract, x-coordinate collision"); - return unconditional_subtract(other); + return unconditional_subtract(other, hint); } /** @@ -870,7 +892,8 @@ template cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, const cycle_group& base_point, const cycle_group& offset_generator, - size_t table_bits) + size_t table_bits, + std::optional> hints) : _table_bits(table_bits) , _context(context) { @@ -891,7 +914,8 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y); cycle_group modded_base_point(modded_x, modded_y, false); for (size_t i = 1; i < table_size; ++i) { - auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point); + std::optional hint = hints.has_value() ? hints.value()[i - 1] : std::optional(); + auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); point_table[i] = cycle_group(x, y, false); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp index 8a9b7ff06203..d50f8fcc851c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp @@ -152,7 +152,8 @@ template class cycle_group { straus_lookup_table(Builder* context, const cycle_group& base_point, const cycle_group& offset_generator, - size_t table_bits); + size_t table_bits, + std::optional> hints = std::optional>()); cycle_group read(const field_t& index); size_t _table_bits; Builder* _context; @@ -186,17 +187,22 @@ template class cycle_group { void set_point_at_infinity(const bool_t& is_infinity) { _is_infinity = is_infinity; } cycle_group get_standard_form() const; void validate_is_on_curve() const; - cycle_group dbl() const + cycle_group dbl(const std::optional hint = std::optional()) const requires IsUltraArithmetic; - cycle_group dbl() const + cycle_group dbl(const std::optional hint = std::optional()) const requires IsNotUltraArithmetic; - cycle_group unconditional_add(const cycle_group& other) const + cycle_group unconditional_add(const cycle_group& other, + const std::optional hint = std::optional()) const requires IsUltraArithmetic; - cycle_group unconditional_add(const cycle_group& other) const + cycle_group unconditional_add(const cycle_group& other, + const std::optional hint = std::optional()) const requires IsNotUltraArithmetic; - cycle_group unconditional_subtract(const cycle_group& other) const; - cycle_group checked_unconditional_add(const cycle_group& other) const; - cycle_group checked_unconditional_subtract(const cycle_group& other) const; + cycle_group unconditional_subtract(const cycle_group& other, + const std::optional hint = std::optional()) const; + cycle_group checked_unconditional_add(const cycle_group& other, + const std::optional hint = std::optional()) const; + cycle_group checked_unconditional_subtract( + const cycle_group& other, const std::optional hint = std::optional()) const; cycle_group operator+(const cycle_group& other) const; cycle_group operator-(const cycle_group& other) const; cycle_group operator-() const; From 8cb78b58a716347a802a929e251cd6fb4db8d894 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 00:55:14 +0000 Subject: [PATCH 02/43] cycle_group::batch_mul batches modular inversions during witness generation --- .../stdlib/primitives/group/cycle_group.cpp | 263 ++++++++++++++---- .../stdlib/primitives/group/cycle_group.hpp | 24 +- 2 files changed, 226 insertions(+), 61 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 7fbf698d594a..3312a9baaf5e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -203,7 +203,7 @@ template cycle_group cycle_group::get_stand * @return cycle_group */ template -cycle_group cycle_group::dbl([[maybe_unused]] const std::optional /*unused*/) const +cycle_group cycle_group::dbl([[maybe_unused]] const std::optional /*unused*/) const requires IsNotUltraArithmetic { auto modified_y = field_t::conditional_assign(is_point_at_infinity(), 1, y); @@ -220,7 +220,7 @@ cycle_group cycle_group::dbl([[maybe_unused]] const std::optio * @return cycle_group */ template -cycle_group cycle_group::dbl(const std::optional hint) const +cycle_group cycle_group::dbl(const std::optional hint) const requires IsUltraArithmetic { // ensure we use a value of y that is not zero. (only happens if point at infinity) @@ -229,7 +229,31 @@ cycle_group cycle_group::dbl(const std::optional cycle_group result; if (hint.has_value()) { - result = hint.value(); + auto x3 = hint.value().x; + auto y3 = hint.value().y; + if (is_constant()) { + result = cycle_group(x3, y3, is_point_at_infinity()); + return result; + } + + result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); + auto x1 = x.get_value(); + auto y1 = modified_y.get_value(); + + // N.B. the formula to derive the witness value for x3 mirrors the formula in elliptic_relation.hpp + // Specifically, we derive x^4 via the Short Weierstrass curve formula `y^2 = x^3 + b` + // i.e. x^4 = x * (y^2 - b) + // We must follow this pattern exactly to support the edge-case where the input is the point at infinity. + auto y_pow_2 = y1.sqr(); + auto x_pow_4 = x1 * (y_pow_2 - Group::curve_b); + auto lambda_squared = (x_pow_4 * 9) / (y_pow_2 * 4); + auto lambda = (x1 * x1 * 3) / (y1 + y1); + auto xx3 = lambda_squared - x1 - x1; + auto yy3 = lambda * (x1 - xx3) - y1; + + ASSERT(xx3 == result.get_value().x); + ASSERT(yy3 == result.get_value().y); + } else { auto x1 = x.get_value(); auto y1 = modified_y.get_value(); @@ -244,12 +268,13 @@ cycle_group cycle_group::dbl(const std::optional auto lambda = (x1 * x1 * 3) / (y1 + y1); auto x3 = lambda_squared - x1 - x1; auto y3 = lambda * (x1 - x3) - y1; + if (is_constant()) { + return cycle_group(x3, y3, is_point_at_infinity().get_value()); + } result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); } - if (is_constant()) { - return result; - } + context->create_ecc_dbl_gate(bb::ecc_dbl_gate_{ .x1 = x.get_witness_index(), .y1 = modified_y.normalize().get_witness_index(), @@ -271,7 +296,7 @@ cycle_group cycle_group::dbl(const std::optional */ template cycle_group cycle_group::unconditional_add( - const cycle_group& other, [[maybe_unused]] const std::optional /*unused*/ /*unused*/) const + const cycle_group& other, [[maybe_unused]] const std::optional /*unused*/) const requires IsNotUltraArithmetic { auto x_diff = other.x - x; @@ -297,7 +322,7 @@ cycle_group cycle_group::unconditional_add( */ template cycle_group cycle_group::unconditional_add(const cycle_group& other, - const std::optional hint) const + const std::optional hint) const requires IsUltraArithmetic { auto context = get_context(other); @@ -306,27 +331,38 @@ cycle_group cycle_group::unconditional_add(const cycle_group& const bool rhs_constant = other.is_constant(); if (lhs_constant && !rhs_constant) { auto lhs = cycle_group::from_constant_witness(context, get_value()); - return lhs.unconditional_add(other); + return lhs.unconditional_add(other, hint); } if (!lhs_constant && rhs_constant) { auto rhs = cycle_group::from_constant_witness(context, other.get_value()); - return unconditional_add(rhs); + return unconditional_add(rhs, hint); } cycle_group result; if (hint.has_value()) { - result = hint.value(); - } else { + auto x3 = hint.value().x; + auto y3 = hint.value().y; + if (lhs_constant && rhs_constant) { + return cycle_group(x3, y3, false); + } + result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); + + const auto p1 = get_value(); + const auto p2 = other.get_value(); + AffineElement p3(Element(p1) + Element(p2)); + ASSERT(p3.x == result.get_value().x); + ASSERT(p3.y == result.get_value().y); + } else { const auto p1 = get_value(); const auto p2 = other.get_value(); AffineElement p3(Element(p1) + Element(p2)); + if (lhs_constant && rhs_constant) { + return cycle_group(p3); + } field_t r_x(witness_t(context, p3.x)); field_t r_y(witness_t(context, p3.y)); result = cycle_group(r_x, r_y, false); } - if (lhs_constant && rhs_constant) { - return result; - } bb::ecc_add_gate_ add_gate{ .x1 = x.get_witness_index(), .y1 = y.get_witness_index(), @@ -352,7 +388,7 @@ cycle_group cycle_group::unconditional_add(const cycle_group& */ template cycle_group cycle_group::unconditional_subtract(const cycle_group& other, - const std::optional hint) const + const std::optional hint) const { if constexpr (!IS_ULTRA) { return unconditional_add(-other, hint); @@ -372,20 +408,23 @@ cycle_group cycle_group::unconditional_subtract(const cycle_gr } cycle_group result; if (hint.has_value()) { - result = hint.value(); + auto x3 = hint.value().x; + auto y3 = hint.value().y; + if (lhs_constant && rhs_constant) { + return cycle_group(x3, y3, false); + } + result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); } else { - auto p1 = get_value(); auto p2 = other.get_value(); AffineElement p3(Element(p1) - Element(p2)); - + if (lhs_constant && rhs_constant) { + return cycle_group(p3); + } field_t r_x(witness_t(context, p3.x)); field_t r_y(witness_t(context, p3.y)); result = cycle_group(r_x, r_y, false); } - if (lhs_constant && rhs_constant) { - return result; - } bb::ecc_add_gate_ add_gate{ .x1 = x.get_witness_index(), .y1 = y.get_witness_index(), @@ -415,7 +454,7 @@ cycle_group cycle_group::unconditional_subtract(const cycle_gr */ template cycle_group cycle_group::checked_unconditional_add(const cycle_group& other, - const std::optional hint) const + const std::optional hint) const { field_t x_delta = x - other.x; x_delta.assert_is_not_zero("cycle_group::checked_unconditional_add, x-coordinate collision"); @@ -436,7 +475,7 @@ cycle_group cycle_group::checked_unconditional_add(const cycle */ template cycle_group cycle_group::checked_unconditional_subtract(const cycle_group& other, - const std::optional hint) const + const std::optional hint) const { field_t x_delta = x - other.x; x_delta.assert_is_not_zero("cycle_group::checked_unconditional_subtract, x-coordinate collision"); @@ -798,7 +837,10 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, // convert an input cycle_scalar object into a vector of slices, each containing `table_bits` bits. // this also performs an implicit range check on the input slices const auto slice_scalar = [&](const field_t& scalar, const size_t num_bits) { - std::vector result; + std::pair, std::vector> result; + result.first.reserve(1ULL << table_bits); + result.second.reserve(1ULL << table_bits); + if (num_bits == 0) { return result; } @@ -808,11 +850,21 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, uint256_t raw_value = scalar.get_value(); for (size_t i = 0; i < num_slices; ++i) { uint64_t slice_v = static_cast(raw_value.data[0]) & table_mask; - result.push_back(field_t(slice_v)); + result.first.push_back(field_t(slice_v)); + result.second.push_back(slice_v); raw_value = raw_value >> table_bits; } return result; } + uint256_t raw_value = scalar.get_value(); + const uint64_t table_mask = (1ULL << table_bits) - 1ULL; + const size_t num_slices = (num_bits + table_bits - 1) / table_bits; + for (size_t i = 0; i < num_slices; ++i) { + uint64_t slice_v = static_cast(raw_value.data[0]) & table_mask; + result.second.push_back(slice_v); + raw_value = raw_value >> table_bits; + } + if constexpr (IS_ULTRA) { const auto slice_indices = context->decompose_into_default_range(scalar.normalize().get_witness_index(), @@ -820,26 +872,22 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, table_bits, "straus_scalar_slice decompose_into_default_range"); for (auto& idx : slice_indices) { - result.emplace_back(field_t::from_witness_index(context, idx)); + result.first.emplace_back(field_t::from_witness_index(context, idx)); } } else { - uint256_t raw_value = scalar.get_value(); - const uint64_t table_mask = (1ULL << table_bits) - 1ULL; - const size_t num_slices = (num_bits + table_bits - 1) / table_bits; for (size_t i = 0; i < num_slices; ++i) { - uint64_t slice_v = static_cast(raw_value.data[0]) & table_mask; + uint64_t slice_v = result.second[i]; field_t slice(witness_t(context, slice_v)); context->create_range_constraint( slice.get_witness_index(), table_bits, "straus_scalar_slice create_range_constraint"); - result.emplace_back(slice); - raw_value = raw_value >> table_bits; + result.first.push_back(slice); } std::vector linear_elements; FF scaling_factor = 1; for (size_t i = 0; i < num_slices; ++i) { - linear_elements.emplace_back(result[i] * scaling_factor); + linear_elements.emplace_back(result.first[i] * scaling_factor); scaling_factor += scaling_factor; } field_t::accumulate(linear_elements).assert_equal(scalar); @@ -852,8 +900,10 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, auto hi_slices = slice_scalar(scalar.hi, hi_bits); auto lo_slices = slice_scalar(scalar.lo, lo_bits); - std::copy(lo_slices.begin(), lo_slices.end(), std::back_inserter(slices)); - std::copy(hi_slices.begin(), hi_slices.end(), std::back_inserter(slices)); + std::copy(lo_slices.first.begin(), lo_slices.first.end(), std::back_inserter(slices)); + std::copy(hi_slices.first.begin(), hi_slices.first.end(), std::back_inserter(slices)); + std::copy(lo_slices.second.begin(), lo_slices.second.end(), std::back_inserter(slices_native)); + std::copy(hi_slices.second.begin(), hi_slices.second.end(), std::back_inserter(slices_native)); } /** @@ -874,6 +924,31 @@ std::optional> cycle_group::straus_scalar_slice::read( return slices[index]; } +template +std::vector::Element> cycle_group< + Builder>::straus_lookup_table::compute_straus_lookup_table_hints(const Element& base_point, + const Element& offset_generator, + size_t table_bits) +{ + const size_t table_size = 1UL << table_bits; + Element base = base_point.is_point_at_infinity() ? Group::one : base_point; + std::vector hints; + hints.emplace_back(offset_generator); + std::vector t; + t.emplace_back(offset_generator); + for (size_t i = 1; i < table_size; ++i) { + auto add_output = t[i - 1] + base; + hints.emplace_back(add_output); + // hints.emplace_back(hints[i - 1] + base); + if (base_point.is_point_at_infinity()) { + t.emplace_back(offset_generator); + } else { + t.emplace_back(add_output); + } + } + return hints; +} + /** * @brief Construct a new cycle group::straus lookup table::straus lookup table object * @@ -893,12 +968,13 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, const cycle_group& base_point, const cycle_group& offset_generator, size_t table_bits, - std::optional> hints) + std::optional> hints) : _table_bits(table_bits) , _context(context) { const size_t table_size = 1UL << table_bits; point_table.resize(table_size); + x_coordinate_checks.resize(table_size - 1); point_table[0] = offset_generator; // We want to support the case where input points are points at infinity. @@ -914,8 +990,10 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y); cycle_group modded_base_point(modded_x, modded_y, false); for (size_t i = 1; i < table_size; ++i) { - std::optional hint = hints.has_value() ? hints.value()[i - 1] : std::optional(); - auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); + std::optional hint = + hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; + x_coordinate_checks[i - 1] = { point_table[i - 1].x, modded_base_point.x }; + auto add_output = point_table[i - 1].unconditional_add(modded_base_point, hint); field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); point_table[i] = cycle_group(x, y, false); @@ -1022,13 +1100,66 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ const size_t num_points = scalars.size(); std::vector scalar_slices; + + std::vector operation_transcript; + std::vector> native_straus_tables; + Element offset_generator_accumulator = offset_generators[0]; + { + for (size_t i = 0; i < num_points; ++i) { + std::vector native_straus_table; + native_straus_table.emplace_back(offset_generators[i + 1]); + size_t table_size = 1ULL << TABLE_BITS; + for (size_t j = 1; j < table_size; ++j) { + native_straus_table.emplace_back(native_straus_table[j - 1] + base_points[i].get_value()); + } + native_straus_tables.emplace_back(native_straus_table); + } + for (size_t i = 0; i < num_points; ++i) { + scalar_slices.emplace_back(straus_scalar_slice(context, scalars[i], TABLE_BITS)); + + auto table_transcript = straus_lookup_table::compute_straus_lookup_table_hints( + base_points[i].get_value(), offset_generators[i + 1], TABLE_BITS); + std::copy(table_transcript.begin() + 1, table_transcript.end(), std::back_inserter(operation_transcript)); + } + Element accumulator = offset_generators[0]; + + for (size_t i = 0; i < num_rounds; ++i) { + if (i != 0) { + for (size_t j = 0; j < TABLE_BITS; ++j) { + // offset_generator_accuulator is a regular Element, so dbl() won't add constraints + accumulator = accumulator.dbl(); + operation_transcript.emplace_back(accumulator); + offset_generator_accumulator = offset_generator_accumulator.dbl(); + } + } + for (size_t j = 0; j < num_points; ++j) { + + const Element point = native_straus_tables[j][scalar_slices[j].slices_native[num_rounds - i - 1]]; + + accumulator += point; + + operation_transcript.emplace_back(accumulator); + offset_generator_accumulator = offset_generator_accumulator + Element(offset_generators[j + 1]); + } + } + } + + Element::batch_normalize(&operation_transcript[0], operation_transcript.size()); + + std::vector operation_hints; + operation_hints.reserve(operation_transcript.size()); + for (auto& element : operation_transcript) { + operation_hints.emplace_back(AffineElement(element.x, element.y)); + } + std::vector point_tables; + const size_t hints_per_table = (1ULL << TABLE_BITS) - 1; for (size_t i = 0; i < num_points; ++i) { - scalar_slices.emplace_back(straus_scalar_slice(context, scalars[i], TABLE_BITS)); + std::span table_hints(&operation_hints[i * hints_per_table], hints_per_table); point_tables.emplace_back(straus_lookup_table(context, base_points[i], offset_generators[i + 1], TABLE_BITS)); } - Element offset_generator_accumulator = offset_generators[0]; + AffineElement* hint_ptr = &operation_hints[num_points * hints_per_table]; cycle_group accumulator = offset_generators[0]; // populate the set of points we are going to add into our accumulator, *before* we do any ECC operations @@ -1039,44 +1170,56 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ for (size_t i = 0; i < num_rounds; ++i) { for (size_t j = 0; j < num_points; ++j) { const std::optional scalar_slice = scalar_slices[j].read(num_rounds - i - 1); - // if we are doing a batch mul over scalars of different bit-lengths, we may not have any scalar bits for a - // given round and a given scalar + // if we are doing a batch mul over scalars of different bit-lengths, we may not have any scalar + // bits for a given round and a given scalar if (scalar_slice.has_value()) { const cycle_group point = point_tables[j].read(scalar_slice.value()); points_to_add.emplace_back(point); } } } + std::vector> x_coordinate_checks; size_t point_counter = 0; + if (!unconditional_add) { + for (size_t i = 0; i < num_points; ++i) { + std::copy(point_tables[i].x_coordinate_checks.begin(), + point_tables[i].x_coordinate_checks.end(), + std::back_inserter(x_coordinate_checks)); + } + } for (size_t i = 0; i < num_rounds; ++i) { if (i != 0) { for (size_t j = 0; j < TABLE_BITS; ++j) { - // offset_generator_accuulator is a regular Element, so dbl() won't add constraints - accumulator = accumulator.dbl(); - offset_generator_accumulator = offset_generator_accumulator.dbl(); + accumulator = accumulator.dbl(*hint_ptr); + hint_ptr++; } } for (size_t j = 0; j < num_points; ++j) { const std::optional scalar_slice = scalar_slices[j].read(num_rounds - i - 1); - // if we are doing a batch mul over scalars of different bit-lengths, we may not have a bit slice for a - // given round and a given scalar + // if we are doing a batch mul over scalars of different bit-lengths, we may not have a bit slice + // for a given round and a given scalar + ASSERT(scalar_slice.value().get_value() == scalar_slices[j].slices_native[num_rounds - i - 1]); if (scalar_slice.has_value()) { const auto& point = points_to_add[point_counter++]; if (!unconditional_add) { x_coordinate_checks.push_back({ accumulator.x, point.x }); } - accumulator = accumulator.unconditional_add(point); - offset_generator_accumulator = offset_generator_accumulator + Element(offset_generators[j + 1]); + accumulator = accumulator.unconditional_add(point, *hint_ptr); + hint_ptr++; } } } + // U cannot be zero + field_t coordinate_check_product = 1; for (auto& [x1, x2] : x_coordinate_checks) { auto x_diff = x2 - x1; - x_diff.assert_is_not_zero("_variable_base_batch_mul_internal x-coordinate collision"); + coordinate_check_product *= x_diff; } + coordinate_check_product.assert_is_not_zero("_variable_base_batch_mul_internal x-coordinate collision"); + /** * offset_generator_accumulator represents the sum of all the offset generator terms present in `accumulator`. * We don't subtract off yet, as we may be able to combine `offset_generator_accumulator` with other constant terms @@ -1143,12 +1286,28 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ ASSERT(offset_1.has_value()); offset_generator_accumulator += offset_1.value(); } + + std::vector operation_transcript; + { + Element accumulator = lookup_points[0].get_value(); + for (size_t i = 1; i < lookup_points.size(); ++i) { + accumulator = accumulator + (lookup_points[i].get_value()); + operation_transcript.emplace_back(accumulator); + } + } + Element::batch_normalize(&operation_transcript[0], operation_transcript.size()); + std::vector operation_hints; + operation_hints.reserve(operation_transcript.size()); + for (auto& element : operation_transcript) { + operation_hints.emplace_back(AffineElement(element.x, element.y)); + } + cycle_group accumulator = lookup_points[0]; // Perform all point additions sequentially. The Ultra ecc_addition relation costs 1 gate iff additions are chained // and output point of previous addition = input point of current addition. // If this condition is not met, the addition relation costs 2 gates. So it's good to do these sequentially! for (size_t i = 1; i < lookup_points.size(); ++i) { - accumulator = accumulator.unconditional_add(lookup_points[i]); + accumulator = accumulator.unconditional_add(lookup_points[i], operation_hints[i - 1]); } /** * offset_generator_accumulator represents the sum of all the offset generator terms present in `accumulator`. diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp index d50f8fcc851c..35709bb5b8dc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp @@ -119,6 +119,7 @@ template class cycle_group { std::optional read(size_t index); size_t _table_bits; std::vector slices; + std::vector slices_native; }; /** @@ -148,16 +149,21 @@ template class cycle_group { */ struct straus_lookup_table { public: + static std::vector compute_straus_lookup_table_hints(const Element& base_point, + const Element& offset_generator, + size_t table_bits); + straus_lookup_table() = default; straus_lookup_table(Builder* context, const cycle_group& base_point, const cycle_group& offset_generator, size_t table_bits, - std::optional> hints = std::optional>()); + std::optional> hints = std::nullopt); cycle_group read(const field_t& index); size_t _table_bits; Builder* _context; std::vector point_table; + std::vector> x_coordinate_checks; size_t rom_id = 0; }; @@ -187,22 +193,22 @@ template class cycle_group { void set_point_at_infinity(const bool_t& is_infinity) { _is_infinity = is_infinity; } cycle_group get_standard_form() const; void validate_is_on_curve() const; - cycle_group dbl(const std::optional hint = std::optional()) const + cycle_group dbl(const std::optional hint = std::nullopt) const requires IsUltraArithmetic; - cycle_group dbl(const std::optional hint = std::optional()) const + cycle_group dbl(const std::optional hint = std::nullopt) const requires IsNotUltraArithmetic; cycle_group unconditional_add(const cycle_group& other, - const std::optional hint = std::optional()) const + const std::optional hint = std::nullopt) const requires IsUltraArithmetic; cycle_group unconditional_add(const cycle_group& other, - const std::optional hint = std::optional()) const + const std::optional hint = std::nullopt) const requires IsNotUltraArithmetic; cycle_group unconditional_subtract(const cycle_group& other, - const std::optional hint = std::optional()) const; + const std::optional hint = std::nullopt) const; cycle_group checked_unconditional_add(const cycle_group& other, - const std::optional hint = std::optional()) const; - cycle_group checked_unconditional_subtract( - const cycle_group& other, const std::optional hint = std::optional()) const; + const std::optional hint = std::nullopt) const; + cycle_group checked_unconditional_subtract(const cycle_group& other, + const std::optional hint = std::nullopt) const; cycle_group operator+(const cycle_group& other) const; cycle_group operator-(const cycle_group& other) const; cycle_group operator-() const; From 7b80a032e3364fede0ce70b290d305f827c7922f Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 01:02:10 +0000 Subject: [PATCH 03/43] removed x_coordinate_check from straus_lookup_table --- .../stdlib/primitives/group/cycle_group.cpp | 11 +---------- .../stdlib/primitives/group/cycle_group.hpp | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 3312a9baaf5e..2622825f572d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -974,7 +974,6 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, { const size_t table_size = 1UL << table_bits; point_table.resize(table_size); - x_coordinate_checks.resize(table_size - 1); point_table[0] = offset_generator; // We want to support the case where input points are points at infinity. @@ -992,8 +991,7 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, for (size_t i = 1; i < table_size; ++i) { std::optional hint = hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; - x_coordinate_checks[i - 1] = { point_table[i - 1].x, modded_base_point.x }; - auto add_output = point_table[i - 1].unconditional_add(modded_base_point, hint); + auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); point_table[i] = cycle_group(x, y, false); @@ -1181,13 +1179,6 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ std::vector> x_coordinate_checks; size_t point_counter = 0; - if (!unconditional_add) { - for (size_t i = 0; i < num_points; ++i) { - std::copy(point_tables[i].x_coordinate_checks.begin(), - point_tables[i].x_coordinate_checks.end(), - std::back_inserter(x_coordinate_checks)); - } - } for (size_t i = 0; i < num_rounds; ++i) { if (i != 0) { for (size_t j = 0; j < TABLE_BITS; ++j) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp index 35709bb5b8dc..da5b07bf66b6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp @@ -163,7 +163,6 @@ template class cycle_group { size_t _table_bits; Builder* _context; std::vector point_table; - std::vector> x_coordinate_checks; size_t rom_id = 0; }; From 850db1bdff0cd88c32f30f623c02033981f39dfa Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 01:09:27 +0000 Subject: [PATCH 04/43] removed debug code --- .../stdlib/primitives/group/cycle_group.cpp | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 2622825f572d..d8ba6701196d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -237,23 +237,6 @@ cycle_group cycle_group::dbl(const std::optional cycle_group::unconditional_add(const cycle_group& return cycle_group(x3, y3, false); } result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); - - const auto p1 = get_value(); - const auto p2 = other.get_value(); - AffineElement p3(Element(p1) + Element(p2)); - - ASSERT(p3.x == result.get_value().x); - ASSERT(p3.y == result.get_value().y); } else { const auto p1 = get_value(); const auto p2 = other.get_value(); From ca6da54a011b73e07301071aceb1c5771f8a44e4 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 01:20:32 +0000 Subject: [PATCH 05/43] minor optimization to remove 12 gates per mul in `batch_mul` when the base point is constant --- .../stdlib/primitives/group/cycle_group.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index d8ba6701196d..1ad718cca8e2 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -964,14 +964,27 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, field_t modded_x = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.x, base_point.x); field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y); cycle_group modded_base_point(modded_x, modded_y, false); + const bool is_checked = !modded_base_point.is_constant(); + // if the input point is constant, it is cheaper to fix the point as a witness and then derive the table, than it is + // to derive the table and fix its witnesses to be constant! (due to group additions = 1 gate, and fixing x/y coords + // to be constant = 2 gates) + if (modded_base_point.is_constant()) { + modded_base_point = cycle_group::from_constant_witness(_context, modded_base_point.get_value()); + } for (size_t i = 1; i < table_size; ++i) { std::optional hint = hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; - auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); + cycle_group add_output; + if (is_checked) { + add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); + } else { + add_output = point_table[i - 1].unconditional_add(modded_base_point, hint); + } field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); point_table[i] = cycle_group(x, y, false); } + if constexpr (IS_ULTRA) { rom_id = context->create_ROM_array(table_size); for (size_t i = 0; i < table_size; ++i) { From a6f433579489ac9d77bdcee1f3203aa01302fede Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 02:17:41 +0000 Subject: [PATCH 06/43] wip --- .../commitment_schemes/ipa/ipa.hpp | 4 ++ .../eccvm_recursive_verifier.test.cpp | 2 +- .../stdlib/primitives/group/cycle_group.cpp | 37 ++++++++++++------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 859bb7876fff..f6654a74af89 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -486,7 +486,9 @@ template class IPA { // Step 5. // Compute C₀ = C' + ∑_{j ∈ [k]} u_j^{-1}L_j + ∑_{j ∈ [k]} u_jR_j + std::cout << "about to batch mul LR sums" << std::endl; GroupElement LR_sums = GroupElement::batch_mul(msm_elements, msm_scalars); + std::cout << "done with sums" << std::endl; GroupElement C_zero = C_prime + LR_sums; @@ -527,7 +529,9 @@ template class IPA { // Compute G₀ // Unlike the native verification function, the verifier commitment key only containts the SRS so we can apply // batch_mul directly on it. + std::cout << "about to batch mul G sums" << std::endl; Commitment G_zero = Commitment::batch_mul(srs_elements, s_vec); + std::cout << "done with G sums" << std::endl; // Step 9. // Receive a₀ from the prover diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp index 5dc17cb76b50..43e18c956a51 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp @@ -84,7 +84,7 @@ template class ECCVMRecursiveTests : public ::testing OuterBuilder outer_circuit; RecursiveVerifier verifier{ &outer_circuit, verification_key }; verifier.verify_proof(proof); - info("Recursive Verifier: num gates = ", outer_circuit.num_gates); + info("Recursive Verifier: num gates = ", outer_circuit.get_estimated_num_finalized_gates()); // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 1ad718cca8e2..2da63624f3d8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -236,6 +236,7 @@ cycle_group cycle_group::dbl(const std::optional cycle_group::unconditional_add(const cycle_group& if (lhs_constant && rhs_constant) { return cycle_group(x3, y3, false); } - result = cycle_group(witness_t(context, x3), witness_t(context, y3), is_point_at_infinity()); + ASSERT(is_point_at_infinity().is_constant() == true); + result = cycle_group(witness_t(context, x3), witness_t(context, y3), false); } else { const auto p1 = get_value(); const auto p2 = other.get_value(); @@ -964,22 +966,28 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, field_t modded_x = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.x, base_point.x); field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y); cycle_group modded_base_point(modded_x, modded_y, false); - const bool is_checked = !modded_base_point.is_constant(); - // if the input point is constant, it is cheaper to fix the point as a witness and then derive the table, than it is - // to derive the table and fix its witnesses to be constant! (due to group additions = 1 gate, and fixing x/y coords - // to be constant = 2 gates) - if (modded_base_point.is_constant()) { - modded_base_point = cycle_group::from_constant_witness(_context, modded_base_point.get_value()); - } + // const bool is_checked = !modded_base_point.is_constant(); + // std::cout << "is checked = " << is_checked << ", is infinity constant = " << + // base_point.is_point_at_infinity().is_constant() << std::endl; if the input point is constant, it is cheaper to + // fix the point as a witness and then derive the table, than it is to derive the table and fix its witnesses to be + // constant! (due to group additions = 1 gate, and fixing x/y coords to be constant = 2 gates) if + // (modded_base_point.is_constant() && !base_point.is_point_at_infinity().get_value()) { + // modded_base_point = cycle_group::from_constant_witness(_context, modded_base_point.get_value()); + // point_table[0] = cycle_group::from_constant_witness(_context, offset_generator.get_value()); + // for (size_t i = 1; i < table_size; ++i) { + // std::optional hint = + // hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; + // cycle_group add_output; + // point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, hint); + // } + // } + // else + + // A + B = C for (size_t i = 1; i < table_size; ++i) { std::optional hint = hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; - cycle_group add_output; - if (is_checked) { - add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); - } else { - add_output = point_table[i - 1].unconditional_add(modded_base_point, hint); - } + auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); point_table[i] = cycle_group(x, y, false); @@ -989,6 +997,7 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, rom_id = context->create_ROM_array(table_size); for (size_t i = 0; i < table_size; ++i) { if (point_table[i].is_constant()) { + std::cout << "fixing point table" << std::endl; auto element = point_table[i].get_value(); point_table[i] = cycle_group::from_constant_witness(_context, element); point_table[i].x.assert_equal(element.x); From 1e1668f81bc17163442f1cfcd745a99d691b7c8a Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 30 Oct 2024 02:47:33 +0000 Subject: [PATCH 07/43] minor efficiency improvements for straus_lookup_table constructor --- .../commitment_schemes/ipa/ipa.hpp | 1 - .../stdlib/primitives/group/cycle_group.cpp | 73 +++++++++---------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 917059de8869..f877488330e3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -539,7 +539,6 @@ template class IPA { // batch_mul directly on it. const std::vector srs_elements = vk->get_monomial_points(); Commitment G_zero = Commitment::batch_mul(srs_elements, s_vec); - std::cout << "done with G sums" << std::endl; // Step 8. // Compute R = C' + ∑_{j ∈ [k]} u_j^{-1}L_j + ∑_{j ∈ [k]} u_jR_j - G₀ * a₀ - (f(\beta) + a₀ * b₀) ⋅ U diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index d9c1edf7ee9d..04772fcdec3c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -999,17 +999,8 @@ std::vector::Element> cycle_group< Element base = base_point.is_point_at_infinity() ? Group::one : base_point; std::vector hints; hints.emplace_back(offset_generator); - std::vector t; - t.emplace_back(offset_generator); for (size_t i = 1; i < table_size; ++i) { - auto add_output = t[i - 1] + base; - hints.emplace_back(add_output); - // hints.emplace_back(hints[i - 1] + base); - if (base_point.is_point_at_infinity()) { - t.emplace_back(offset_generator); - } else { - t.emplace_back(add_output); - } + hints.emplace_back(hints[i - 1] + base); } return hints; } @@ -1053,38 +1044,46 @@ cycle_group::straus_lookup_table::straus_lookup_table(Builder* context, field_t modded_x = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.x, base_point.x); field_t modded_y = field_t::conditional_assign(base_point.is_point_at_infinity(), fallback_point.y, base_point.y); cycle_group modded_base_point(modded_x, modded_y, false); - // const bool is_checked = !modded_base_point.is_constant(); - // std::cout << "is checked = " << is_checked << ", is infinity constant = " << - // base_point.is_point_at_infinity().is_constant() << std::endl; if the input point is constant, it is cheaper to - // fix the point as a witness and then derive the table, than it is to derive the table and fix its witnesses to be - // constant! (due to group additions = 1 gate, and fixing x/y coords to be constant = 2 gates) if - // (modded_base_point.is_constant() && !base_point.is_point_at_infinity().get_value()) { - // modded_base_point = cycle_group::from_constant_witness(_context, modded_base_point.get_value()); - // point_table[0] = cycle_group::from_constant_witness(_context, offset_generator.get_value()); - // for (size_t i = 1; i < table_size; ++i) { - // std::optional hint = - // hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; - // cycle_group add_output; - // point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, hint); - // } - // } - // else - - // A + B = C - for (size_t i = 1; i < table_size; ++i) { - std::optional hint = - hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; - auto add_output = point_table[i - 1].checked_unconditional_add(modded_base_point, hint); - field_t x = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.x, add_output.x); - field_t y = field_t::conditional_assign(base_point.is_point_at_infinity(), offset_generator.y, add_output.y); - point_table[i] = cycle_group(x, y, false); - } + // if the input point is constant, it is cheaper to fix the point as a witness and then derive the table, than it is + // to derive the table and fix its witnesses to be constant! (due to group additions = 1 gate, and fixing x/y coords + // to be constant = 2 gates) + if (modded_base_point.is_constant() && !base_point.is_point_at_infinity().get_value()) { + modded_base_point = cycle_group::from_constant_witness(_context, modded_base_point.get_value()); + point_table[0] = cycle_group::from_constant_witness(_context, offset_generator.get_value()); + for (size_t i = 1; i < table_size; ++i) { + std::optional hint = + hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; + point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, hint); + } + } else { + std::vector> x_coordinate_checks; + // ensure all of the ecc add gates are lined up so that we can pay 1 gate per add and not 2 + for (size_t i = 1; i < table_size; ++i) { + std::optional hint = + hints.has_value() ? std::optional(hints.value()[i - 1]) : std::nullopt; + x_coordinate_checks.emplace_back(point_table[i - 1].x, modded_base_point.x); + point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, hint); + } + + // batch the x-coordinate checks together + // because `assert_is_not_zero` witness generation needs a modular inversion (expensive) + field_t coordinate_check_product = 1; + for (auto& [x1, x2] : x_coordinate_checks) { + auto x_diff = x2 - x1; + coordinate_check_product *= x_diff; + } + coordinate_check_product.assert_is_not_zero("straus_lookup_table x-coordinate collision"); + + for (size_t i = 1; i < table_size; ++i) { + point_table[i] = + cycle_group::conditional_assign(base_point.is_point_at_infinity(), offset_generator, point_table[i]); + } + } if constexpr (IS_ULTRA) { rom_id = context->create_ROM_array(table_size); for (size_t i = 0; i < table_size; ++i) { if (point_table[i].is_constant()) { - std::cout << "fixing point table" << std::endl; auto element = point_table[i].get_value(); point_table[i] = cycle_group::from_constant_witness(_context, element); point_table[i].x.assert_equal(element.x); From 96cedff7550c9d95eb77b52fa6a7e75ae04660ff Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Tue, 5 Nov 2024 23:28:25 +0000 Subject: [PATCH 08/43] disabled join split circuit change test --- .../examples/join_split/join_split.test.cpp | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp b/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp index 1ef0dfe3305e..9d6a9f54ea7a 100644 --- a/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp +++ b/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp @@ -19,12 +19,12 @@ using namespace bb::crypto::merkle_tree; /* Old join-split tests below. The value of having all of these logic tests is unclear, but we'll leave them around, at least for a while. */ -#ifdef CI -constexpr bool CIRCUIT_CHANGE_EXPECTED = false; -#else +// #ifdef CI +// constexpr bool CIRCUIT_CHANGE_EXPECTED = false; +// #else // During development, if the circuit vk hash/gate count is expected to change, set the following to true. -constexpr bool CIRCUIT_CHANGE_EXPECTED = false; -#endif +// constexpr bool CIRCUIT_CHANGE_EXPECTED = false; +// #endif using namespace bb; using namespace bb::stdlib; @@ -688,32 +688,31 @@ TEST_F(join_split_tests, test_withdraw_but_different_input_note_2_asset_id_fails // Input note combinations. Deposit/Send/Withdraw. // ************************************************************************************************************* -TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) -{ - join_split_tx tx = zero_input_setup(); - tx.proof_id = proof_ids::DEPOSIT; - tx.public_value = 30; - tx.public_owner = fr::random_element(); - tx.output_note[0].value = 30; +// TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) +// { +// join_split_tx tx = zero_input_setup(); +// tx.proof_id = proof_ids::DEPOSIT; +// tx.public_value = 30; +// tx.public_owner = fr::random_element(); +// tx.output_note[0].value = 30; - auto result = sign_and_verify_logic(tx, user.owner); +// auto result = sign_and_verify_logic(tx, user.owner); - EXPECT_TRUE(result.valid); +// EXPECT_TRUE(result.valid); - // The below part detects any changes in the join-split circuit - constexpr size_t DYADIC_CIRCUIT_SIZE = 1 << 16; +// // The below part detects any changes in the join-split circuit - constexpr uint256_t CIRCUIT_HASH("0x2b30566e4d921ea9b0c76802d86ea5b8381ffa78ef143af1b0d0e3045862cb6b"); +// constexpr uint256_t CIRCUIT_HASH("0x2b30566e4d921ea9b0c76802d86ea5b8381ffa78ef143af1b0d0e3045862cb6b"); - const uint256_t circuit_hash = circuit.hash_circuit(); - // circuit is finalized now - if (!CIRCUIT_CHANGE_EXPECTED) { - EXPECT_LT(circuit.get_estimated_num_finalized_gates(), DYADIC_CIRCUIT_SIZE) - << "The gate count for the join-split circuit has crossed a power-of-two boundary."; - EXPECT_EQ(circuit_hash, CIRCUIT_HASH) - << "The circuit hash for the join_split circuit has changed to: " << circuit_hash; - } -} +// const uint256_t circuit_hash = circuit.hash_circuit(); +// // circuit is finalized now +// if (!CIRCUIT_CHANGE_EXPECTED) { +// EXPECT_LT(circuit.get_estimated_num_finalized_gates(), DYADIC_CIRCUIT_SIZE) +// << "The gate count for the join-split circuit has crossed a power-of-two boundary."; +// EXPECT_EQ(circuit_hash, CIRCUIT_HASH) +// << "The circuit hash for the join_split circuit has changed to: " << circuit_hash; +// } +// } // Bespoke test seeking bug. TEST_F(join_split_tests, test_0_input_notes_create_dupicate_output_notes_fails) From 2dcde3009f4fc9ffa95f705254f95cd77d95f1ab Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 6 Nov 2024 03:46:39 +0000 Subject: [PATCH 09/43] compiler fix --- .../src/barretenberg/stdlib/primitives/group/cycle_group.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 04772fcdec3c..67e0ac8ddf46 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -903,8 +903,8 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, // this also performs an implicit range check on the input slices const auto slice_scalar = [&](const field_t& scalar, const size_t num_bits) { std::pair, std::vector> result; - result.first.reserve(1ULL << table_bits); - result.second.reserve(1ULL << table_bits); + result.first.reserve(static_cast(1ULL) << table_bits); + result.second.reserve(static_cast(1ULL) << table_bits); if (num_bits == 0) { return result; From f841ba03a1210b4ac7313cb0e7836f2bd34bf58f Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 11 Dec 2024 15:35:12 +0000 Subject: [PATCH 10/43] fixed wasm compiler error --- .../src/barretenberg/stdlib/primitives/group/cycle_group.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index d4c7a9615338..51d849ebece7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -1271,7 +1271,8 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ } for (size_t j = 0; j < num_points; ++j) { - const Element point = native_straus_tables[j][scalar_slices[j].slices_native[num_rounds - i - 1]]; + const Element point = + native_straus_tables[j][static_cast(scalar_slices[j].slices_native[num_rounds - i - 1])]; accumulator += point; From 267d8ad44cef2f642b0d956c97f5485508ed8dd8 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 12 Dec 2024 12:52:57 +0000 Subject: [PATCH 11/43] added comments and added back in join_split_test --- .../examples/join_split/join_split.test.cpp | 51 ++++++++++--------- .../stdlib/primitives/group/cycle_group.cpp | 42 +++++++++++++-- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp b/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp index 4b80b679f0ed..bac099cc407a 100644 --- a/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp +++ b/barretenberg/cpp/src/barretenberg/examples/join_split/join_split.test.cpp @@ -19,12 +19,12 @@ using namespace bb::crypto::merkle_tree; /* Old join-split tests below. The value of having all of these logic tests is unclear, but we'll leave them around, at least for a while. */ -// #ifdef CI -// constexpr bool CIRCUIT_CHANGE_EXPECTED = false; -// #else +#ifdef CI +constexpr bool CIRCUIT_CHANGE_EXPECTED = false; +#else // During development, if the circuit vk hash/gate count is expected to change, set the following to true. -// constexpr bool CIRCUIT_CHANGE_EXPECTED = false; -// #endif +constexpr bool CIRCUIT_CHANGE_EXPECTED = false; +#endif using namespace bb; using namespace bb::stdlib; @@ -688,31 +688,32 @@ TEST_F(join_split_tests, test_withdraw_but_different_input_note_2_asset_id_fails // Input note combinations. Deposit/Send/Withdraw. // ************************************************************************************************************* -// TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) -// { -// join_split_tx tx = zero_input_setup(); -// tx.proof_id = proof_ids::DEPOSIT; -// tx.public_value = 30; -// tx.public_owner = fr::random_element(); -// tx.output_note[0].value = 30; +TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) +{ + join_split_tx tx = zero_input_setup(); + tx.proof_id = proof_ids::DEPOSIT; + tx.public_value = 30; + tx.public_owner = fr::random_element(); + tx.output_note[0].value = 30; -// auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, user.owner); -// EXPECT_TRUE(result.valid); + EXPECT_TRUE(result.valid); -// // The below part detects any changes in the join-split circuit + // The below part detects any changes in the join-split circuit + constexpr size_t DYADIC_CIRCUIT_SIZE = 1 << 16; -// constexpr uint256_t CIRCUIT_HASH("0x2b30566e4d921ea9b0c76802d86ea5b8381ffa78ef143af1b0d0e3045862cb6b"); + constexpr uint256_t CIRCUIT_HASH("0x727924c3ea2266dc2b6a38f335019c26a941911d5991f25adb3ce9c288af7751"); -// const uint256_t circuit_hash = circuit.hash_circuit(); -// // circuit is finalized now -// if (!CIRCUIT_CHANGE_EXPECTED) { -// EXPECT_LT(circuit.get_estimated_num_finalized_gates(), DYADIC_CIRCUIT_SIZE) -// << "The gate count for the join-split circuit has crossed a power-of-two boundary."; -// EXPECT_EQ(circuit_hash, CIRCUIT_HASH) -// << "The circuit hash for the join_split circuit has changed to: " << circuit_hash; -// } -// } + const uint256_t circuit_hash = circuit.hash_circuit(); + // circuit is finalized now + if (!CIRCUIT_CHANGE_EXPECTED) { + EXPECT_LT(circuit.get_estimated_num_finalized_gates(), DYADIC_CIRCUIT_SIZE) + << "The gate count for the join-split circuit has crossed a power-of-two boundary."; + EXPECT_EQ(circuit_hash, CIRCUIT_HASH) + << "The circuit hash for the join_split circuit has changed to: " << circuit_hash; + } +} // Bespoke test seeking bug. TEST_F(join_split_tests, test_0_input_notes_create_dupicate_output_notes_fails) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index 51d849ebece7..cb356bfce93b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -314,6 +314,7 @@ cycle_group cycle_group::unconditional_add( * * @tparam Builder * @param other + * @param hint : value of output point witness, if known ahead of time (used to avoid modular inversions during witgen) * @return cycle_group */ template @@ -383,6 +384,7 @@ cycle_group cycle_group::unconditional_add(const cycle_group& * * @tparam Builder * @param other + * @param hint : value of output point witness, if known ahead of time (used to avoid modular inversions during witgen) * @return cycle_group */ template @@ -458,6 +460,7 @@ cycle_group cycle_group::unconditional_subtract(const cycle_gr * * @tparam Builder * @param other + * @param hint : value of output point witness, if known ahead of time (used to avoid modular inversions during witgen) * @return cycle_group */ template @@ -479,6 +482,7 @@ cycle_group cycle_group::checked_unconditional_add(const cycle * * @tparam Builder * @param other + * @param hint : value of output point witness, if known ahead of time (used to avoid modular inversions during witgen) * @return cycle_group */ template @@ -943,6 +947,9 @@ cycle_group::straus_scalar_slice::straus_scalar_slice(Builder* context, // convert an input cycle_scalar object into a vector of slices, each containing `table_bits` bits. // this also performs an implicit range check on the input slices const auto slice_scalar = [&](const field_t& scalar, const size_t num_bits) { + // we record the scalar slices both as field_t circuit elements and u64 values + // (u64 values are used to index arrays and we don't want to repeatedly cast a stdlib value to a numeric + // primitive as this gets expensive when repeated enough times) std::pair, std::vector> result; result.first.reserve(static_cast(1ULL) << table_bits); result.second.reserve(static_cast(1ULL) << table_bits); @@ -1036,6 +1043,19 @@ std::optional> cycle_group::straus_scalar_slice::read( return slices[index]; } +/** + * @brief Compute the output points generated when computing the Straus lookup table + * @details When performing an MSM, we first compute all the witness values as Element types (with a Z-coordinate), + * and then we batch-convert the points into affine representation `AffineElement` + * This avoids the need to compute a modular inversion for every group operation, + * which dramatically cuts witness generation times + * + * @tparam Builder + * @param base_point + * @param offset_generator + * @param table_bits + * @return std::vector::Element> + */ template std::vector::Element> cycle_group< Builder>::straus_lookup_table::compute_straus_lookup_table_hints(const Element& base_point, @@ -1238,6 +1258,12 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ std::vector scalar_slices; + /** + * Compute the witness values of the batch_mul algorithm natively, as Element types with a Z-coordinate. + * We then batch-convert to AffineElement types, and feed this points as "hints" into the cycle_group methods. + * This avoids the need to compute modular inversions for every group operation, which dramatically reduces witness + * generation times + */ std::vector operation_transcript; std::vector> native_straus_tables; Element offset_generator_accumulator = offset_generators[0]; @@ -1282,6 +1308,7 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ } } + // Normalize the computed witness points and convert into AffineElement type Element::batch_normalize(&operation_transcript[0], operation_transcript.size()); std::vector operation_hints; @@ -1312,8 +1339,8 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ for (size_t i = 0; i < num_rounds; ++i) { for (size_t j = 0; j < num_points; ++j) { const std::optional scalar_slice = scalar_slices[j].read(num_rounds - i - 1); - // if we are doing a batch mul over scalars of different bit-lengths, we may not have any scalar - // bits for a given round and a given scalar + // if we are doing a batch mul over scalars of different bit-lengths, we may not have any scalar bits for a + // given round and a given scalar if (scalar_slice.has_value()) { const cycle_group point = point_tables[j].read(scalar_slice.value()); points_to_add.emplace_back(point); @@ -1347,7 +1374,9 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ } } - // U cannot be zero + // validate that none of the x-coordinate differences are zero + // we batch the x-coordinate checks together + // because `assert_is_not_zero` witness generation needs a modular inversion (expensive) field_t coordinate_check_product = 1; for (auto& [x1, x2] : x_coordinate_checks) { auto x_diff = x2 - x1; @@ -1427,7 +1456,12 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ ASSERT(offset_1.has_value()); offset_generator_accumulator += offset_1.value(); } - + /** + * Compute the witness values of the batch_mul algorithm natively, as Element types with a Z-coordinate. + * We then batch-convert to AffineElement types, and feed this points as "hints" into the cycle_group methods. + * This avoids the need to compute modular inversions for every group operation, which dramatically reduces witness + * generation times + */ std::vector operation_transcript; { Element accumulator = lookup_points[0].get_value(); From a9f0629b85c66428d51ce70c6c7a68a2ccfd6da5 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 12 Dec 2024 12:58:18 +0000 Subject: [PATCH 12/43] added missing hint description --- .../src/barretenberg/stdlib/primitives/group/cycle_group.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index cb356bfce93b..a037cd41c182 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -202,6 +202,7 @@ template cycle_group cycle_group::get_stand * @brief Evaluates a doubling. Does not use Ultra double gate * * @tparam Builder + * @param unused param is due to interface-compatibility with the UltraArithmetic version of `dbl` * @return cycle_group */ template @@ -219,6 +220,7 @@ cycle_group cycle_group::dbl([[maybe_unused]] const std::optio * @brief Evaluates a doubling. Uses Ultra double gate * * @tparam Builder + * @param hint : value of output point witness, if known ahead of time (used to avoid modular inversions during witgen) * @return cycle_group */ template From 197cf5d693aa2c9b63992f085162bc753dcb4dbc Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 12 Dec 2024 13:00:31 +0000 Subject: [PATCH 13/43] grammar --- .../src/barretenberg/stdlib/primitives/group/cycle_group.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index a037cd41c182..e0bcea1f14eb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -1262,7 +1262,7 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ /** * Compute the witness values of the batch_mul algorithm natively, as Element types with a Z-coordinate. - * We then batch-convert to AffineElement types, and feed this points as "hints" into the cycle_group methods. + * We then batch-convert to AffineElement types, and feed these points as "hints" into the cycle_group methods. * This avoids the need to compute modular inversions for every group operation, which dramatically reduces witness * generation times */ @@ -1460,7 +1460,7 @@ typename cycle_group::batch_mul_internal_output cycle_group::_ } /** * Compute the witness values of the batch_mul algorithm natively, as Element types with a Z-coordinate. - * We then batch-convert to AffineElement types, and feed this points as "hints" into the cycle_group methods. + * We then batch-convert to AffineElement types, and feed these points as "hints" into the cycle_group methods. * This avoids the need to compute modular inversions for every group operation, which dramatically reduces witness * generation times */ From 4379bfb16a1ac48a696bf62e13488da9f193b1e4 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Fri, 20 Dec 2024 15:31:51 +0000 Subject: [PATCH 14/43] PR comments --- .../src/barretenberg/stdlib/primitives/group/cycle_group.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp index e0bcea1f14eb..080e5a200877 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.cpp @@ -243,7 +243,6 @@ cycle_group cycle_group::dbl(const std::optional cycle_group::unconditional_add(const cycle_group& if (lhs_constant && rhs_constant) { return cycle_group(x3, y3, false); } - ASSERT(is_point_at_infinity().is_constant() == true); result = cycle_group(witness_t(context, x3), witness_t(context, y3), false); } else { const auto p1 = get_value(); From 3d7bc01420fc493a7fdb8ec38427f357a1b55f2a Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 5 Feb 2025 11:14:56 +0000 Subject: [PATCH 15/43] wip --- .../cpp/src/barretenberg/flavor/flavor.hpp | 3 +- .../protogalaxy/protogalaxy_prover_impl.hpp | 42 +- .../ultra_recursive_verifier.cpp | 12 + .../stdlib_circuit_builders/mega_flavor.hpp | 1 + .../cpp/src/barretenberg/vm/CMakeLists.txt | 2 +- .../vm/avm/generated/recursive_verifier.cpp | 1 + .../goblin_avm_recursive_verifier.hpp | 43 + .../vm/avm/tests/recursive_verifier.test.cpp | 739 +++++++++++++++++- 8 files changed, 817 insertions(+), 26 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index eb3fa6a59948..a3308d1d2054 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -394,7 +394,8 @@ concept IsRecursiveFlavor = IsAnyOf, TranslatorRecursiveFlavor_, ECCVMRecursiveFlavor_, - AvmRecursiveFlavor_>; + AvmRecursiveFlavor_, + AvmRecursiveFlavor_>; template concept IsECCVMRecursiveFlavor = IsAnyOf>; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index 6fbd2e47dc0a..ca42c4b036f4 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -144,16 +144,42 @@ FoldingResult ProtogalaxyProver_proving_key.log_circuit_size, keys[1]->proving_key.log_circuit_size); } - // Fold the proving key polynomials - for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) { - poly *= lagranges[0]; - } - for (size_t key_idx = 1; key_idx < DeciderProvingKeys::NUM; key_idx++) { - for (auto [acc_poly, key_poly] : zip_view(result.accumulator->proving_key.polynomials.get_unshifted(), - keys[key_idx]->proving_key.polynomials.get_unshifted())) { - acc_poly.add_scaled(key_poly, lagranges[key_idx]); + ASSERT(DeciderProvingKeys::NUM == 2); // this mechanism is not supported for the folding of multiple keys + auto accumulator_polys = result.accumulator->proving_key.polynomials.get_unshifted(); + auto key_polys = keys[1]->proving_key.polynomials.get_unshifted(); + std::cout << "acc size = " << accumulator_polys.size() << std::endl; + std::cout << "key size = " << key_polys.size() << std::endl; + for (size_t i = 0; i < accumulator_polys.size(); ++i) { + PolynomialSpan acc = static_cast>(accumulator_polys[i]); + PolynomialSpan key = static_cast>(key_polys[i]); + + // acc *= lagranges[0]; + // acc.add_scaled(key, lagranges[1]); + + // *= calls coefficients_.data() + // add_scaled calls at() which calls coefficients_::operator[] which does not have bounds checks + std::cout << "acc poly size = " << acc.size() << std::endl; + std::cout << "key poly size = " << key.size() << std::endl; + std::cout << "acc start index " << acc.start_index << std::endl; + std::cout << "key start index " << key.start_index << std::endl; + const size_t size = std::min(acc.size() - acc.start_index, key.size() - key.start_index); + for (size_t j = 0; j < size; ++j) { + acc[j + acc.start_index] *= lagranges[0]; + acc[j + acc.start_index] += key[j + key.start_index] * lagranges[1]; } } + // // Fold the proving key polynomials + // for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) { + // poly *= lagranges[0]; + // } + + // // acc poly = acc[i] * lagranges[0] + key[i] * lagranges[1] + // for (size_t key_idx = 1; key_idx < DeciderProvingKeys::NUM; key_idx++) { + // for (auto [acc_poly, key_poly] : zip_view(result.accumulator->proving_key.polynomials.get_unshifted(), + // keys[key_idx]->proving_key.polynomials.get_unshifted())) { + // acc_poly.add_scaled(key_poly, lagranges[key_idx]); + // } + // } // Evaluate the combined batching α_i univariate at challenge to obtain next α_i and send it to the // verifier, where i ∈ {0,...,NUM_SUBRELATIONS - 1} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index c8170f4373c3..c7f0f216066a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -46,10 +46,12 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ using VerifierCommitments = typename Flavor::VerifierCommitments; using Transcript = typename Flavor::Transcript; + std::cout << "XA" << std::endl; transcript = std::make_shared(proof); auto verification_key = std::make_shared(builder, key); OinkVerifier oink_verifier{ builder, verification_key, transcript }; oink_verifier.verify(); + std::cout << "XB" << std::endl; VerifierCommitments commitments{ key, verification_key->witness_commitments }; @@ -61,14 +63,21 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // Parse out the aggregation object using the key->pairing_point_accumulator_public_input_indices AggregationObject nested_agg_obj; size_t idx = 0; + std::array nested_pairing_points; for (size_t i = 0; i < 2; i++) { std::array base_field_vals; for (size_t j = 0; j < 2; j++) { std::array bigfield_limbs; for (size_t k = 0; k < 4; k++) { + std::cout << "k = " << k << std::endl; + std::cout << "ultra recursive verifier public input indices [" << idx + << "] = " << key->pairing_point_accumulator_public_input_indices[idx] << std::endl; + std::cout << "x" << std::endl; + std::cout << "vkey pub inputs size = " << verification_key->public_inputs.size() << std::endl; bigfield_limbs[k] = verification_key->public_inputs[key->pairing_point_accumulator_public_input_indices[idx]]; + std::cout << "y" << std::endl; idx++; } base_field_vals[j] = Curve::BaseField::construct_from_limbs( @@ -106,6 +115,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ if constexpr (Flavor::HasZK) { libra_evaluations = std::move(sumcheck_output.claimed_libra_evaluations); } + std::cout << "XC" << std::endl; // Execute Shplemini to produce a batch opening claim subsequently verified by a univariate PCS const BatchOpeningClaim opening_claim = @@ -129,6 +139,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ agg_obj.aggregate(pairing_points, recursion_separator); Output output; output.agg_obj = std::move(agg_obj); + std::cout << "XD" << std::endl; // Extract the IPA claim from the public inputs // Parse out the nested IPA claim using key->ipa_claim_public_input_indices and runs the native IPA verifier. @@ -165,6 +176,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ output.ipa_opening_claim = std::move(ipa_claim); } } + std::cout << "XE" << std::endl; return output; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index 062929736f9a..2acc9b69e73a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -359,6 +359,7 @@ class MegaFlavor { /* offset */ 1 }; } // catch-all with fully formed polynomials + for (auto& poly : get_unshifted()) { if (poly.is_empty()) { // Not set above diff --git a/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt index 2b697200bb01..9d311809be64 100644 --- a/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt @@ -1,3 +1,3 @@ if(NOT DISABLE_AZTEC_VM) - barretenberg_module(vm sumcheck stdlib_honk_verifier) + barretenberg_module(vm sumcheck stdlib_circuit_builders stdlib_honk_verifier stdlib_eccvm_verifier stdlib_translator_vm_verifier stdlib_honk_verifier goblin stdlib_goblin_verifier) endif() \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp index 4c88da4c3ebe..d20819cac6a6 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp @@ -170,5 +170,6 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: } template class AvmRecursiveVerifier_>; +template class AvmRecursiveVerifier_>; } // namespace bb::avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp new file mode 100644 index 000000000000..f7019b259f29 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -0,0 +1,43 @@ +// #pragma once +// #include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" +// #include "barretenberg/sumcheck/sumcheck.hpp" +// #include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" + +// namespace bb::avm { + +// template class GoblinAvmRecursiveVerifier_ { +// using FF = typename Flavor::FF; +// using BF = typename Flavor::BF; +// using Curve = typename Flavor::Curve; +// using Commitment = typename Flavor::Commitment; +// using CommitmentLabels = typename Flavor::CommitmentLabels; +// using RelationSeparator = typename Flavor::RelationSeparator; +// using VerificationKey = typename Flavor::VerificationKey; +// using NativeVerificationKey = typename Flavor::NativeVerificationKey; +// using Builder = typename Flavor::CircuitBuilder; +// using PCS = typename Flavor::PCS; +// using Transcript = bb::BaseTranscript>; +// using VerifierCommitments = typename Flavor::VerifierCommitments; +// using AggregationObject = bb::stdlib::recursion::aggregation_state; + +// public: +// explicit GoblinAvmRecursiveVerifier_(Builder* builder, +// const std::shared_ptr& native_verification_key); +// explicit GoblinAvmRecursiveVerifier_(Builder* builder, const std::shared_ptr& vkey); + +// AggregationObject verify_proof(const HonkProof& proof, +// const std::vector>& public_inputs_vec_nt, +// AggregationObject agg_obj); +// AggregationObject verify_proof(const StdlibProof& stdlib_proof, +// const std::vector>& public_inputs, +// AggregationObject agg_obj); + +// std::shared_ptr key; +// Builder* builder; +// std::shared_ptr transcript; + +// private: +// FF evaluate_public_input_column(const std::vector& points, const std::vector& challenges); +// }; + +// } // namespace bb::avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 23b23c204357..d849122dc0bc 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -1,7 +1,10 @@ #include "barretenberg/vm/avm/recursion/recursive_verifier.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/goblin/goblin.hpp" #include "barretenberg/numeric/random/engine.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" +#include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" +#include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.hpp" +#include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -15,14 +18,373 @@ #include "barretenberg/vm/avm/trace/trace.hpp" #include +namespace bb::stdlib::recursion::honk { + +class AVMGoblin { + + public: + using Builder = UltraCircuitBuilder; + using MergeVerifier = goblin::MergeRecursiveVerifier_; + + using TranslatorFlavor = TranslatorRecursiveFlavor_; + using TranslatorVerifier = TranslatorRecursiveVerifier_; + using TranslationEvaluations = TranslatorVerifier::TranslationEvaluations; + using TranslatorBF = TranslatorFlavor::BF; + using VerifierInput = GoblinVerifier::VerifierInput; + + using ECCVMFlavor = ECCVMRecursiveFlavor_; + using ECCVMVerifier = ECCVMRecursiveVerifier_; + using OpQueue = bb::ECCOpQueue; + using ECCVMBuilder = bb::ECCVMCircuitBuilder; + using ECCVMNativeFlavor = bb::ECCVMFlavor; + using ECCVMProvingKey = ECCVMNativeFlavor::ProvingKey; + using NativeTranscript = NativeTranscript; + using TranslatorBuilder = bb::TranslatorCircuitBuilder; + + // Builder* builder; + + GoblinProof proof; + + ECCVMProof eccvm_proof; + HonkProof translator_proof; + ECCVMProver::TranslationEvaluations translation_evaluations; + std::shared_ptr op_queue = std::make_shared(); + std::unique_ptr eccvm_prover; + std::shared_ptr eccvm_key; + std::unique_ptr translator_prover; + std::shared_ptr> commitment_key; + + AVMGoblin(const std::shared_ptr>& bn254_commitment_key = nullptr) + { // Mocks the interaction of a first circuit with the op queue due to the inability to currently handle zero + // commitments (https://github.com/AztecProtocol/barretenberg/issues/871) which would otherwise appear in the + // first round of the merge protocol. To be removed once the issue has been resolved. + commitment_key = bn254_commitment_key ? bn254_commitment_key : nullptr; + } + + void recursive_goblin_prove() + { + auto eccvm_builder = std::make_unique(op_queue); + eccvm_prover = std::make_unique(*eccvm_builder); + + PROFILE_THIS_NAME("Construct ECCVM Proof"); + + eccvm_proof = eccvm_prover->construct_proof(); + + PROFILE_THIS_NAME("Assign Translation Evaluations"); + + translation_evaluations = eccvm_prover->translation_evaluations; + + { + fq translation_batching_challenge_v = eccvm_prover->translation_batching_challenge_v; + fq evaluation_challenge_x = eccvm_prover->evaluation_challenge_x; + std::shared_ptr transcript = eccvm_prover->transcript; + eccvm_key = eccvm_prover->key; + eccvm_prover = nullptr; + { + + PROFILE_THIS_NAME("Create TranslatorBuilder and TranslatorProver"); + + auto translator_builder = std::make_unique( + translation_batching_challenge_v, evaluation_challenge_x, op_queue); + translator_prover = std::make_unique(*translator_builder, transcript, commitment_key); + } + + { + + PROFILE_THIS_NAME("Construct Translator Proof"); + + translator_proof = translator_prover->construct_proof(); + } + } + } + GoblinRecursiveVerifierOutput recursive_goblin_verify(UltraCircuitBuilder* builder) + + { + // Run the ECCVM recursive verifier + std::cout << "AA" << std::endl; + const std::shared_ptr eccvm_vk = + std::make_shared(eccvm_key); + + const std::shared_ptr translator_vk = + std::make_shared(translator_prover->key); + + ECCVMVerifier eccvm_verifier{ builder, eccvm_vk }; + std::cout << "BB" << std::endl; + + auto [opening_claim, ipa_transcript] = eccvm_verifier.verify_proof(eccvm_proof); + std::cout << "CC" << std::endl; + + TranslatorVerifier translator_verifier{ builder, translator_vk, eccvm_verifier.transcript }; + // TODO TAKE THE PAIRING POINT OUTPUTS AND ACCUMULATE + std::cout << "DD" << std::endl; + + translator_verifier.verify_proof(translator_proof); + std::cout << "EE" << std::endl; + + // Verify the consistency between the ECCVM and Translator transcript polynomial evaluations + // In reality the Goblin Proof is going to already be a stdlib proof and this conversion is not going to happen + // here (see https://github.com/AztecProtocol/barretenberg/issues/991) + auto native_translation_evaluations = translation_evaluations; + auto translation_evaluations = + TranslationEvaluations{ TranslatorBF::from_witness(builder, native_translation_evaluations.op), + TranslatorBF::from_witness(builder, native_translation_evaluations.Px), + TranslatorBF::from_witness(builder, native_translation_evaluations.Py), + TranslatorBF::from_witness(builder, native_translation_evaluations.z1), + TranslatorBF::from_witness(builder, native_translation_evaluations.z2) + + }; + std::cout << "FF" << std::endl; + + translator_verifier.verify_translation(translation_evaluations); + std::cout << "GG" << std::endl; + return { opening_claim, ipa_transcript }; + } + + using RecursiveFlavor = AvmRecursiveFlavor_; + using RecursiveVerificationKey = RecursiveFlavor::VerificationKey; + using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + + using InnerFlavor = typename RecursiveFlavor::NativeFlavor; + using InnerBuilder = bb::avm::AvmCircuitBuilder; + using InnerProver = bb::avm::AvmProver; + using InnerVerifier = bb::avm::AvmVerifier; + using InnerComposer = bb::avm::AvmComposer; + using InnerG1 = InnerFlavor::Commitment; + using InnerFF = InnerFlavor::FF; + using OuterFF = RecursiveFlavor::FF; + using FinalRecursiveFlavor = MegaRecursiveFlavor_; + + using FinalRecursiveVerifier = UltraRecursiveVerifier_; + using RecursiveAVMDeciderProvingKey = DeciderProvingKey_; + + using FinalFF = FinalRecursiveFlavor::FF; + + // TODO propagate the ultra public inputs into mega public inputs + auto compute_full_avm_recursion( + UltraCircuitBuilder* builder, + const std::vector& proof_fields, + const std::vector>& public_inputs_vec, + const std::shared_ptr& recursive_verification_key) + { + + std::cout << "A" << std::endl; + // bool verified = verifier.verify_proof(proof, public_inputs_vec); + // ASSERT_TRUE(verified) << "native proof verification failed"; + + // Create the outer verifier, to verify the proof + + MegaCircuitBuilder outer_circuit(op_queue); + auto input_agg_obj = + stdlib::recursion::init_default_aggregation_state( + outer_circuit); + + RecursiveVerifier recursive_verifier{ &outer_circuit, recursive_verification_key }; + + std::cout << "B" << std::endl; + + // TODO: do this properly + std::vector proof_fields_mega; + for (auto& x : proof_fields) { + proof_fields_mega.push_back(OuterFF::from_witness(&outer_circuit, x.get_value())); + } + std::vector> public_inputs_mega; + for (auto& x : public_inputs_vec) { + std::vector vec; + for (auto& y : x) { + vec.push_back(OuterFF::from_witness(&outer_circuit, y.get_value())); + } + public_inputs_mega.push_back(vec); + } + std::cout << "C" << std::endl; + + // TODO propagate into public inputs + [[maybe_unused]] auto output_agg_object = + recursive_verifier.verify_proof(proof_fields_mega, public_inputs_mega, input_agg_obj); + + std::cout << "D" << std::endl; + recursive_goblin_prove(); + std::cout << "E" << std::endl; + + GoblinRecursiveVerifierOutput goblin_output = recursive_goblin_verify(builder); + std::cout << "F" << std::endl; + + auto avm_recursion_proving_key = std::make_shared(outer_circuit); + UltraProver_ outer_prover(avm_recursion_proving_key); + auto verification_key = + std::make_shared(avm_recursion_proving_key->proving_key); + auto outer_proof = outer_prover.construct_proof(); + std::cout << "G" << std::endl; + + FinalRecursiveVerifier verifier{ builder, verification_key }; + + auto dummy_agg_object = + stdlib::recursion::init_default_aggregation_state(*builder); + + std::cout << "GB" << std::endl; + auto final_agg_object = verifier.verify_proof(outer_proof, dummy_agg_object); + std::cout << "H" << std::endl; + + // TODO also return the goblin plonk claims + return final_agg_object; + } +}; +} // namespace bb::stdlib::recursion::honk + namespace tests_avm { using namespace bb; using namespace bb::avm_trace; +// class AVMRecursion { +// public: +// using Builder = bb::GoblinProver::Builder; +// using RecursiveFlavor = AvmRecursiveFlavor_; +// using AVMRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; +// using InnerFlavor = typename RecursiveFlavor::NativeFlavor; +// using InnerBuilder = bb::avm::AvmCircuitBuilder; +// using InnerProver = bb::avm::AvmProver; +// using InnerVerifier = bb::avm::AvmVerifier; +// using InnerComposer = bb::avm::AvmComposer; +// using InnerG1 = InnerFlavor::Commitment; +// using InnerFF = InnerFlavor::FF; + +// using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; +// using OuterProver = UltraProver; +// using OuterVerifier = UltraVerifier; +// using OuterDeciderProvingKey = DeciderProvingKey_; +// using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; +// using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; + +// void verify_goblin_proof(HonkProof& proof, +// std::vector>& public_inputs_vec, +// const std::shared_ptr verification_key) const +// { +// GoblinProver goblin; +// OuterBuilder outer_circuit(goblin.op_queue); +// AVMRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + +// auto agg_object = +// stdlib::recursion::init_default_aggregation_state( +// outer_circuit); + +// auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + +// bool agg_output_valid = +// verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), +// agg_output.P1.get_value()); + +// ASSERT(agg_output_valid); +// // At this point we have a circuit that verifies an AVM proof +// // We need the following: +// // 1: a proof of the AVM circuit +// // 2: an eccvm proof of the transcript from the AVM circuit builder +// // 3: a translator proof of the transcript from the AVM circuit builder + +// // 1. Make a proof of the verification of an AVM proof +// const size_t srs_size = 1 << 23; +// auto ultra_instance = std::make_shared( +// outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + +// OuterProver ultra_prover(ultra_instance); +// auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); +// OuterVerifier ultra_verifier(ultra_verification_key); + +// auto recursion_proof = ultra_prover.construct_proof(); +// bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); +// ASSERT(recursion_verified); +// // 2. eccvm +// GoblinProof g_proof = goblin.prove(); + +// // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from +// their +// // respective proving keys) +// auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); +// auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); +// GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; +// bool verified = goblin_verifier.verify(g_proof); +// ASSERT(verified); +// } +// }; +// class AVMGoblinProver : public bb::GoblinProver { +// public: +// using RecursiveFlavor = AvmRecursiveFlavor_; +// using AVMRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; +// using InnerFlavor = typename RecursiveFlavor::NativeFlavor; +// using InnerBuilder = bb::avm::AvmCircuitBuilder; +// using InnerProver = bb::avm::AvmProver; +// using InnerVerifier = bb::avm::AvmVerifier; +// using InnerComposer = bb::avm::AvmComposer; +// using InnerG1 = InnerFlavor::Commitment; +// using InnerFF = InnerFlavor::FF; + +// using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; +// using OuterProver = UltraProver_; +// using OuterVerifier = UltraVerifier_; +// using OuterDeciderProvingKey = DeciderProvingKey_; + +// PairingPoints verify_goblin_merge(Builder& outer_circuit, +// MergeProof& proof, +// const std::shared_ptr verification_key, +// std::vector>& public_inputs_vec) const +// { +// // we are verifying an inner proof +// // blah blah blah +// AVMRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; +// auto agg_object = +// stdlib::recursion::init_default_aggregation_state( +// outer_circuit); + +// auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + +// // ooook so... +// // we need to do 2 things +// // 1: perform a "standard" recursive verification where ecc ops get shoved into a circuit builder queue +// // 2: call merge verifier on the ecc builder queue +// // see client_ivc.cpp ClientIVC::complete_kernel_circuit_logic + +// // this is where we need to change things? we can't return pairing points we need to return data blob + +// // ok soooo here's what I think so far. +// // we actually keep the recursive verification the same as original AVM, including returning the agg_output +// // However we ALSO include a merge verifier step +// // and create a combined accumulator object +// return agg_output; +// PROFILE_THIS_NAME("Goblin::merge"); +// // const std::shared_ptr verification_key = verifier.key; +// //. OuterBuilder outer_circuit; +// // RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + +// // Make a proof of the verification of an AVM proof +// //. const size_t srs_size = 1 << 23; +// // auto ultra_instance = std::make_shared( +// // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + +// // OuterProver ultra_prover(ultra_instance); +// // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + +// // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); +// // OuterVerifier ultra_verifier(ultra_verification_key); + +// // AVMRecursiveVerifier merge_verifier(ultra_verification_key); +// // return merge_verifier.verify_proof(proof); +// }; + +// void avm_merge(Builder& circuit_builder) +// { +// // Append a recursive merge verification of the merge proof +// if (merge_proof_exists) { +// [[maybe_unused]] auto pairing_points = verify_goblin_merge(circuit_builder, merge_proof); +// } + +// // Construct a merge proof for the present circuit +// merge_proof = prove_merge(circuit_builder); +// }; +// }; + class AvmRecursiveTests : public ::testing::Test { public: - using RecursiveFlavor = AvmRecursiveFlavor_; + using RecursiveFlavor = AvmRecursiveFlavor_; using InnerFlavor = typename RecursiveFlavor::NativeFlavor; using InnerBuilder = bb::avm::AvmCircuitBuilder; @@ -36,12 +398,39 @@ class AvmRecursiveTests : public ::testing::Test { using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; - using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; + // In the goblin example, we have a GoblinProver object that takes in an inner circuit. we call `merge` method on + // GoblinProver taking the inner circuit as argument. And then we make a goblin proof. The `merge` method invokes a + // MergeRecursiveVerifier_ object and calls `verify_proof` + + // In the AVM context, that MergeRecursiveVerifier_ object needs to be an AvmRecursiveFlavor_ object + // There are two ways we can do this. + + // First way is the template param the goblin prover to take in an inner recursive verifier type + // 2nd way is add explicit new methods into goblin + // fastest might be to get option 2 working and then refactor into option 1 + using OuterBuilder = UltraCircuitBuilder; using OuterProver = UltraProver; using OuterVerifier = UltraVerifier; using OuterDeciderProvingKey = DeciderProvingKey_; - static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + using OuterRecursiveFlavor = MegaRecursiveFlavor_; + using OuterFF = OuterRecursiveFlavor::FF; + using OuterRecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_; + + using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; + using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; + using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; + using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; + + using MegaProver = UltraProver_; + using MegaVerifier = UltraVerifier_; + using MegaDeciderProvingKey = DeciderProvingKey_; + + static void SetUpTestSuite() + { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } AvmPublicInputs public_inputs; @@ -71,9 +460,9 @@ class AvmRecursiveTests : public ::testing::Test { TEST_F(AvmRecursiveTests, recursion) { - if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - GTEST_SKIP(); - } + // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + // GTEST_SKIP(); + // } InnerBuilder circuit_builder = generate_avm_circuit(); InnerComposer composer = InnerComposer(); @@ -102,11 +491,14 @@ TEST_F(AvmRecursiveTests, recursion) // Create the outer verifier, to verify the proof const std::shared_ptr verification_key = verifier.key; - OuterBuilder outer_circuit; + + GoblinProver goblin; + MegaCircuitBuilder outer_circuit(goblin.op_queue); RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; auto agg_object = - stdlib::recursion::init_default_aggregation_state(outer_circuit); + stdlib::recursion::init_default_aggregation_state( + outer_circuit); auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); @@ -119,8 +511,10 @@ TEST_F(AvmRecursiveTests, recursion) bool outer_circuit_checked = CircuitChecker::check(outer_circuit); ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; + std::cout << "A" << std::endl; auto manifest = verifier.transcript->get_manifest(); auto recursive_manifest = recursive_verifier.transcript->get_manifest(); + std::cout << "B" << std::endl; EXPECT_EQ(manifest.size(), recursive_manifest.size()); for (size_t i = 0; i < recursive_manifest.size(); ++i) { @@ -134,18 +528,331 @@ TEST_F(AvmRecursiveTests, recursion) EXPECT_EQ(verifier.key->circuit_size, recursive_verifier.key->circuit_size); EXPECT_EQ(verifier.key->num_public_inputs, recursive_verifier.key->num_public_inputs); - // Make a proof of the verification of an AVM proof - const size_t srs_size = 1 << 23; - auto ultra_instance = std::make_shared( - outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - OuterProver ultra_prover(ultra_instance); - auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - OuterVerifier ultra_verifier(ultra_verification_key); + // in goblin test... + // there are several mega circuits being generated + // goblin proof is then constructed from the mega circuits + + // what is a goblin proof? + // eccvm proof, translator proof and a "merge" proof + // what is the merge proof? + + // `goblin.verify_merge` is called using a client builder input + // input is a mega circuit + // eww + // The next step is... + // An UltraBuilder is created + // The UltraBuilder is used to create a circuit that verifies the goblin proof + std::cout << "C" << std::endl; + goblin.merge(outer_circuit); + + GoblinProof g_proof = goblin.prove(); + std::cout << "D" << std::endl; + + // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their + // respective proving keys) + auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + std::cout << "E" << std::endl; + + UltraCircuitBuilder builder; + GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), + std::make_shared(goblin.get_translator_proving_key()) }; + bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &builder, goblin_vinput }; + std::cout << "F" << std::endl; + + // next step fails likely because of a lack of a merge proof + + gverifier.verify(g_proof); + std::cout << "G" << std::endl; + + EXPECT_EQ(builder.failed(), false) << builder.err(); + EXPECT_TRUE(CircuitChecker::check(builder)); + // Construct and verify a proof for the Goblin Recursive Verifier circuit + { + auto proving_key = std::make_shared(builder); + std::cout << "H" << std::endl; + OuterProver prover(proving_key); + std::cout << "I" << std::endl; + auto verification_key = std::make_shared(proving_key->proving_key); + std::cout << "J" << std::endl; + OuterVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } + + // const size_t srs_size = 1 << 23; + auto ultra_instance = std::make_shared(outer_circuit); + MegaProver ultra_prover(ultra_instance); + auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + MegaVerifier ultra_verifier(ultra_verification_key); vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); auto recursion_proof = ultra_prover.construct_proof(); bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + + // Make a proof of the verification of an AVM proof + // const size_t srs_size = 1 << 23; + // auto ultra_instance = std::make_shared( + // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + // auto ultra_instance = std::make_shared(outer_circuit); + + // OuterProver ultra_prover(ultra_instance); + // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + // OuterVerifier ultra_verifier(ultra_verification_key); + + // // // auto proving_key = std::make_shared(builder); + + // // OuterProver outer_prover(ultra_instance); + + // // goblin.merge(outer_circit); + // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + // // GoblinVerifier ultra_verifier(ultra_verification_key); + + // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + + // auto recursion_proof = ultra_prover.construct_proof(); + // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); + // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + + // GoblinProof g_proof = goblin.prove(); + + // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their + // // respective proving keys) + // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + // GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; + // bool g_verified = goblin_verifier.verify(g_proof); + // EXPECT_TRUE(g_verified); +} + +TEST_F(AvmRecursiveTests, recursion2) +{ + // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + // GTEST_SKIP(); + // } + + InnerBuilder circuit_builder = generate_avm_circuit(); + InnerComposer composer = InnerComposer(); + InnerProver prover = composer.create_prover(circuit_builder); + InnerVerifier verifier = composer.create_verifier(circuit_builder); + + HonkProof proof = prover.construct_proof(); + + // We just pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + + std::vector> public_inputs{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs + }; + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata + }; + + bool verified = verifier.verify_proof(proof, public_inputs_vec); + ASSERT_TRUE(verified) << "native proof verification failed"; + + // Create the outer verifier, to verify the proof + const std::shared_ptr verification_key = verifier.key; + + bb::stdlib::recursion::honk::AVMGoblin avmgoblin; + + UltraCircuitBuilder builder; + + // auto fields_from_witnesses = [&](std::vector const& input) { + // std::vector result; + // result.reserve(input.size()); + // for (const auto& idx : input) { + // auto field = OuterFF::from_witness_index(&builder, idx); + // result.emplace_back(field); + // } + // return result; + // }; + + // const auto proof_fields = fields_from_witnesses(proof); + // const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); + + // auto it = public_inputs_flattened.begin(); + // VmPublicInputs vm_public_inputs = + // avm_trace::convert_public_inputs(std::vector(it, it + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH)); + // it += PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH; + // std::vector calldata(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); + // it += AVM_PUBLIC_COLUMN_MAX_SIZE; + // std::vector return_data(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); + + // auto public_inputs_vectors = avm_trace::copy_public_inputs_columns(vm_public_inputs, calldata, return_data); + + UltraCircuitBuilder outer_circuit; + + std::vector proof_fields; + for (auto& field : proof) { + proof_fields.emplace_back(OuterFF::from_witness(&outer_circuit, field)); + } + std::vector> public_inputs_vec_fields; + for (auto& x : public_inputs_vec) { + std::vector vec; + for (auto& y : x) { + vec.emplace_back(OuterFF::from_witness(&outer_circuit, y)); + } + public_inputs_vec_fields.emplace_back(vec); + } + // std::vector public_inputs_fields; + + avmgoblin.compute_full_avm_recursion(&outer_circuit, proof_fields, public_inputs_vec_fields, verification_key); + + std::cout << "I" << std::endl; + { + auto proving_key = std::make_shared(builder); + OuterProver prover(proving_key); + auto verification_key = std::make_shared(proving_key->proving_key); + OuterVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + EXPECT_TRUE(verified); + } + + // ASSERT(verified); + // GoblinProver goblin; + // MegaCircuitBuilder outer_circuit(goblin.op_queue); + // RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + + // auto agg_object = + // stdlib::recursion::init_default_aggregation_state( + // outer_circuit); + + // auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + + // bool agg_output_valid = + // verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); + + // ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; + // ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; + + // bool outer_circuit_checked = CircuitChecker::check(outer_circuit); + // ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; + + // std::cout << "A" << std::endl; + // auto manifest = verifier.transcript->get_manifest(); + // auto recursive_manifest = recursive_verifier.transcript->get_manifest(); + // std::cout << "B" << std::endl; + + // EXPECT_EQ(manifest.size(), recursive_manifest.size()); + // for (size_t i = 0; i < recursive_manifest.size(); ++i) { + // EXPECT_EQ(recursive_manifest[i], manifest[i]); + // } + + // for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { + // EXPECT_EQ(key_el, rec_key_el.get_value()); + // } + + // EXPECT_EQ(verifier.key->circuit_size, recursive_verifier.key->circuit_size); + // EXPECT_EQ(verifier.key->num_public_inputs, recursive_verifier.key->num_public_inputs); + + // // in goblin test... + // // there are several mega circuits being generated + // // goblin proof is then constructed from the mega circuits + + // // what is a goblin proof? + // // eccvm proof, translator proof and a "merge" proof + // // what is the merge proof? + + // // `goblin.verify_merge` is called using a client builder input + // // input is a mega circuit + // // eww + // // The next step is... + // // An UltraBuilder is created + // // The UltraBuilder is used to create a circuit that verifies the goblin proof + // std::cout << "C" << std::endl; + // goblin.merge(outer_circuit); + + // GoblinProof g_proof = goblin.prove(); + // std::cout << "D" << std::endl; + + // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their + // // respective proving keys) + // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + // std::cout << "E" << std::endl; + + // UltraCircuitBuilder builder; + // GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), + // std::make_shared(goblin.get_translator_proving_key()) + // }; + // bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &builder, goblin_vinput }; + // std::cout << "F" << std::endl; + + // // next step fails likely because of a lack of a merge proof + + // gverifier.verify(g_proof); + // std::cout << "G" << std::endl; + + // EXPECT_EQ(builder.failed(), false) << builder.err(); + // EXPECT_TRUE(CircuitChecker::check(builder)); + // // Construct and verify a proof for the Goblin Recursive Verifier circuit + // { + // auto proving_key = std::make_shared(builder); + // OuterProver prover(proving_key); + // auto verification_key = std::make_shared(proving_key->proving_key); + // OuterVerifier verifier(verification_key); + // auto proof = prover.construct_proof(); + // bool verified = verifier.verify_proof(proof); + + // ASSERT(verified); + // } + + // // const size_t srs_size = 1 << 23; + // auto ultra_instance = std::make_shared(outer_circuit); + // MegaProver ultra_prover(ultra_instance); + // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + // MegaVerifier ultra_verifier(ultra_verification_key); + + // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + + // auto recursion_proof = ultra_prover.construct_proof(); + // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); + // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + + // // Make a proof of the verification of an AVM proof + // // const size_t srs_size = 1 << 23; + // // auto ultra_instance = std::make_shared( + // // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + // // auto ultra_instance = std::make_shared(outer_circuit); + + // // OuterProver ultra_prover(ultra_instance); + // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + // // OuterVerifier ultra_verifier(ultra_verification_key); + + // // // // auto proving_key = std::make_shared(builder); + + // // // OuterProver outer_prover(ultra_instance); + + // // // goblin.merge(outer_circit); + // // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + // // // GoblinVerifier ultra_verifier(ultra_verification_key); + + // // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + + // // auto recursion_proof = ultra_prover.construct_proof(); + // // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); + // // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + + // // GoblinProof g_proof = goblin.prove(); + + // // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from + // their + // // // respective proving keys) + // // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + // // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + // // GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; + // // bool g_verified = goblin_verifier.verify(g_proof); + // // EXPECT_TRUE(g_verified); } } // namespace tests_avm From 555c705d61e27437a3c49e86c3e6c4fc0fa9c6c9 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Fri, 7 Mar 2025 22:56:04 +0000 Subject: [PATCH 16/43] working complete (I think) circuit that takes in AVM proof and spits out an ultra proof + IPA accumulator --- .../protogalaxy/protogalaxy_prover_impl.hpp | 24 - .../ultra_recursive_verifier.cpp | 3 - .../stdlib_circuit_builders/mega_flavor.hpp | 1 - .../vm/avm/tests/recursive_verifier.test.cpp | 756 +++++------------- 4 files changed, 182 insertions(+), 602 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index ca42c4b036f4..06462e181f25 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -147,39 +147,15 @@ FoldingResult ProtogalaxyProver_proving_key.polynomials.get_unshifted(); auto key_polys = keys[1]->proving_key.polynomials.get_unshifted(); - std::cout << "acc size = " << accumulator_polys.size() << std::endl; - std::cout << "key size = " << key_polys.size() << std::endl; for (size_t i = 0; i < accumulator_polys.size(); ++i) { PolynomialSpan acc = static_cast>(accumulator_polys[i]); PolynomialSpan key = static_cast>(key_polys[i]); - - // acc *= lagranges[0]; - // acc.add_scaled(key, lagranges[1]); - - // *= calls coefficients_.data() - // add_scaled calls at() which calls coefficients_::operator[] which does not have bounds checks - std::cout << "acc poly size = " << acc.size() << std::endl; - std::cout << "key poly size = " << key.size() << std::endl; - std::cout << "acc start index " << acc.start_index << std::endl; - std::cout << "key start index " << key.start_index << std::endl; const size_t size = std::min(acc.size() - acc.start_index, key.size() - key.start_index); for (size_t j = 0; j < size; ++j) { acc[j + acc.start_index] *= lagranges[0]; acc[j + acc.start_index] += key[j + key.start_index] * lagranges[1]; } } - // // Fold the proving key polynomials - // for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) { - // poly *= lagranges[0]; - // } - - // // acc poly = acc[i] * lagranges[0] + key[i] * lagranges[1] - // for (size_t key_idx = 1; key_idx < DeciderProvingKeys::NUM; key_idx++) { - // for (auto [acc_poly, key_poly] : zip_view(result.accumulator->proving_key.polynomials.get_unshifted(), - // keys[key_idx]->proving_key.polynomials.get_unshifted())) { - // acc_poly.add_scaled(key_poly, lagranges[key_idx]); - // } - // } // Evaluate the combined batching α_i univariate at challenge to obtain next α_i and send it to the // verifier, where i ∈ {0,...,NUM_SUBRELATIONS - 1} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index c7f0f216066a..dbba8d11a4e3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -46,12 +46,10 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ using VerifierCommitments = typename Flavor::VerifierCommitments; using Transcript = typename Flavor::Transcript; - std::cout << "XA" << std::endl; transcript = std::make_shared(proof); auto verification_key = std::make_shared(builder, key); OinkVerifier oink_verifier{ builder, verification_key, transcript }; oink_verifier.verify(); - std::cout << "XB" << std::endl; VerifierCommitments commitments{ key, verification_key->witness_commitments }; @@ -63,7 +61,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // Parse out the aggregation object using the key->pairing_point_accumulator_public_input_indices AggregationObject nested_agg_obj; size_t idx = 0; - std::array nested_pairing_points; for (size_t i = 0; i < 2; i++) { std::array base_field_vals; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index 2acc9b69e73a..062929736f9a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -359,7 +359,6 @@ class MegaFlavor { /* offset */ 1 }; } // catch-all with fully formed polynomials - for (auto& poly : get_unshifted()) { if (poly.is_empty()) { // Not set above diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index d849122dc0bc..2b9f70f0f40f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -3,8 +3,10 @@ #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" +#include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp" #include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.hpp" #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -18,369 +20,137 @@ #include "barretenberg/vm/avm/trace/trace.hpp" #include -namespace bb::stdlib::recursion::honk { +namespace bb::stdlib::recursion::honk {} -class AVMGoblin { - - public: - using Builder = UltraCircuitBuilder; - using MergeVerifier = goblin::MergeRecursiveVerifier_; - - using TranslatorFlavor = TranslatorRecursiveFlavor_; - using TranslatorVerifier = TranslatorRecursiveVerifier_; - using TranslationEvaluations = TranslatorVerifier::TranslationEvaluations; - using TranslatorBF = TranslatorFlavor::BF; - using VerifierInput = GoblinVerifier::VerifierInput; - - using ECCVMFlavor = ECCVMRecursiveFlavor_; - using ECCVMVerifier = ECCVMRecursiveVerifier_; - using OpQueue = bb::ECCOpQueue; - using ECCVMBuilder = bb::ECCVMCircuitBuilder; - using ECCVMNativeFlavor = bb::ECCVMFlavor; - using ECCVMProvingKey = ECCVMNativeFlavor::ProvingKey; - using NativeTranscript = NativeTranscript; - using TranslatorBuilder = bb::TranslatorCircuitBuilder; - - // Builder* builder; - - GoblinProof proof; - - ECCVMProof eccvm_proof; - HonkProof translator_proof; - ECCVMProver::TranslationEvaluations translation_evaluations; - std::shared_ptr op_queue = std::make_shared(); - std::unique_ptr eccvm_prover; - std::shared_ptr eccvm_key; - std::unique_ptr translator_prover; - std::shared_ptr> commitment_key; - - AVMGoblin(const std::shared_ptr>& bn254_commitment_key = nullptr) - { // Mocks the interaction of a first circuit with the op queue due to the inability to currently handle zero - // commitments (https://github.com/AztecProtocol/barretenberg/issues/871) which would otherwise appear in the - // first round of the merge protocol. To be removed once the issue has been resolved. - commitment_key = bn254_commitment_key ? bn254_commitment_key : nullptr; - } - - void recursive_goblin_prove() - { - auto eccvm_builder = std::make_unique(op_queue); - eccvm_prover = std::make_unique(*eccvm_builder); - - PROFILE_THIS_NAME("Construct ECCVM Proof"); - - eccvm_proof = eccvm_prover->construct_proof(); - - PROFILE_THIS_NAME("Assign Translation Evaluations"); +namespace tests_avm { - translation_evaluations = eccvm_prover->translation_evaluations; +using namespace bb; +using namespace bb::avm_trace; - { - fq translation_batching_challenge_v = eccvm_prover->translation_batching_challenge_v; - fq evaluation_challenge_x = eccvm_prover->evaluation_challenge_x; - std::shared_ptr transcript = eccvm_prover->transcript; - eccvm_key = eccvm_prover->key; - eccvm_prover = nullptr; - { +void test_recursive_avm_gobble_gobble(const HonkProof& proof, bb::avm::AvmVerifier& verifier) +{ + using AvmRecursiveFlavor = AvmRecursiveFlavor_; - PROFILE_THIS_NAME("Create TranslatorBuilder and TranslatorProver"); + using InnerFlavor = typename AvmRecursiveFlavor::NativeFlavor; + using InnerFF = InnerFlavor::FF; + using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; + using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; + using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; + using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; + using MegaProver = UltraProver_; + using MegaDeciderProvingKey = DeciderProvingKey_; + using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; + using UltraRecursiveVerifier = + bb::stdlib::recursion::honk::UltraRecursiveVerifier_; + using UltraRollupProver = bb::UltraProver_; + using UltraRollupVerifier = bb::UltraVerifier_; - auto translator_builder = std::make_unique( - translation_batching_challenge_v, evaluation_challenge_x, op_queue); - translator_prover = std::make_unique(*translator_builder, transcript, commitment_key); - } + // Pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + std::vector> public_inputs{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs + }; + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata + }; + // ASSERT_TRUE(verifier.verify_proof(proof, public_inputs_vec)) << "native proof verification failed"; - { + const std::shared_ptr verification_key = verifier.key; - PROFILE_THIS_NAME("Construct Translator Proof"); + GoblinProver goblin; + MegaCircuitBuilder inner_builder(goblin.op_queue); + RecursiveVerifier recursive_verifier{ &inner_builder, verification_key }; + auto agg_object = + stdlib::recursion::init_default_aggregation_state( + inner_builder); + [[maybe_unused]] auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - translator_proof = translator_prover->construct_proof(); - } - } - } - GoblinRecursiveVerifierOutput recursive_goblin_verify(UltraCircuitBuilder* builder) + /* + ASSERT_TRUE( + verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), *agg_output.P1.get_value())) + << "Pairing points (aggregation state) are not valid."; + */ - { - // Run the ECCVM recursive verifier - std::cout << "AA" << std::endl; - const std::shared_ptr eccvm_vk = - std::make_shared(eccvm_key); - - const std::shared_ptr translator_vk = - std::make_shared(translator_prover->key); - - ECCVMVerifier eccvm_verifier{ builder, eccvm_vk }; - std::cout << "BB" << std::endl; - - auto [opening_claim, ipa_transcript] = eccvm_verifier.verify_proof(eccvm_proof); - std::cout << "CC" << std::endl; - - TranslatorVerifier translator_verifier{ builder, translator_vk, eccvm_verifier.transcript }; - // TODO TAKE THE PAIRING POINT OUTPUTS AND ACCUMULATE - std::cout << "DD" << std::endl; - - translator_verifier.verify_proof(translator_proof); - std::cout << "EE" << std::endl; - - // Verify the consistency between the ECCVM and Translator transcript polynomial evaluations - // In reality the Goblin Proof is going to already be a stdlib proof and this conversion is not going to happen - // here (see https://github.com/AztecProtocol/barretenberg/issues/991) - auto native_translation_evaluations = translation_evaluations; - auto translation_evaluations = - TranslationEvaluations{ TranslatorBF::from_witness(builder, native_translation_evaluations.op), - TranslatorBF::from_witness(builder, native_translation_evaluations.Px), - TranslatorBF::from_witness(builder, native_translation_evaluations.Py), - TranslatorBF::from_witness(builder, native_translation_evaluations.z1), - TranslatorBF::from_witness(builder, native_translation_evaluations.z2) - - }; - std::cout << "FF" << std::endl; - - translator_verifier.verify_translation(translation_evaluations); - std::cout << "GG" << std::endl; - return { opening_claim, ipa_transcript }; - } + goblin.merge(inner_builder); + inner_builder.add_pairing_point_accumulator( + stdlib::recursion::init_default_agg_obj_indices(inner_builder)); - using RecursiveFlavor = AvmRecursiveFlavor_; - using RecursiveVerificationKey = RecursiveFlavor::VerificationKey; - using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + std::shared_ptr ultra_instance = std::make_shared(inner_builder); - using InnerFlavor = typename RecursiveFlavor::NativeFlavor; - using InnerBuilder = bb::avm::AvmCircuitBuilder; - using InnerProver = bb::avm::AvmProver; - using InnerVerifier = bb::avm::AvmVerifier; - using InnerComposer = bb::avm::AvmComposer; - using InnerG1 = InnerFlavor::Commitment; - using InnerFF = InnerFlavor::FF; - using OuterFF = RecursiveFlavor::FF; - using FinalRecursiveFlavor = MegaRecursiveFlavor_; + MegaProver ultra_prover(ultra_instance); + auto recursion_proof = ultra_prover.construct_proof(); - using FinalRecursiveVerifier = UltraRecursiveVerifier_; - using RecursiveAVMDeciderProvingKey = DeciderProvingKey_; + GoblinProof g_proof = goblin.prove(); - using FinalFF = FinalRecursiveFlavor::FF; + auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - // TODO propagate the ultra public inputs into mega public inputs - auto compute_full_avm_recursion( - UltraCircuitBuilder* builder, - const std::vector& proof_fields, - const std::vector>& public_inputs_vec, - const std::shared_ptr& recursive_verification_key) - { + UltraCircuitBuilder outer_builder; + GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), + std::make_shared(goblin.get_translator_proving_key()) }; + bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &outer_builder, goblin_vinput }; - std::cout << "A" << std::endl; - // bool verified = verifier.verify_proof(proof, public_inputs_vec); - // ASSERT_TRUE(verified) << "native proof verification failed"; - - // Create the outer verifier, to verify the proof - - MegaCircuitBuilder outer_circuit(op_queue); - auto input_agg_obj = - stdlib::recursion::init_default_aggregation_state( - outer_circuit); - - RecursiveVerifier recursive_verifier{ &outer_circuit, recursive_verification_key }; - - std::cout << "B" << std::endl; - - // TODO: do this properly - std::vector proof_fields_mega; - for (auto& x : proof_fields) { - proof_fields_mega.push_back(OuterFF::from_witness(&outer_circuit, x.get_value())); - } - std::vector> public_inputs_mega; - for (auto& x : public_inputs_vec) { - std::vector vec; - for (auto& y : x) { - vec.push_back(OuterFF::from_witness(&outer_circuit, y.get_value())); - } - public_inputs_mega.push_back(vec); - } - std::cout << "C" << std::endl; - - // TODO propagate into public inputs - [[maybe_unused]] auto output_agg_object = - recursive_verifier.verify_proof(proof_fields_mega, public_inputs_mega, input_agg_obj); - - std::cout << "D" << std::endl; - recursive_goblin_prove(); - std::cout << "E" << std::endl; - - GoblinRecursiveVerifierOutput goblin_output = recursive_goblin_verify(builder); - std::cout << "F" << std::endl; - - auto avm_recursion_proving_key = std::make_shared(outer_circuit); - UltraProver_ outer_prover(avm_recursion_proving_key); - auto verification_key = - std::make_shared(avm_recursion_proving_key->proving_key); - auto outer_proof = outer_prover.construct_proof(); - std::cout << "G" << std::endl; - - FinalRecursiveVerifier verifier{ builder, verification_key }; - - auto dummy_agg_object = - stdlib::recursion::init_default_aggregation_state(*builder); - - std::cout << "GB" << std::endl; - auto final_agg_object = verifier.verify_proof(outer_proof, dummy_agg_object); - std::cout << "H" << std::endl; + // next step fails likely because of a lack of a merge proof - // TODO also return the goblin plonk claims - return final_agg_object; - } + bb::stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); + + // ASSERT(current_aggregation_object.size() && "Aggregation object should not be empty"); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive + // verifiers + PairingPointAccumulatorIndices current_aggregation_object = + stdlib::recursion::init_default_agg_obj_indices(outer_builder); + // This is currently just setting the aggregation object to the default one. + outer_builder.add_pairing_point_accumulator(current_aggregation_object); + + // The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof + outer_builder.add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); + outer_builder.ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); + ASSERT(outer_builder.ipa_proof.size() && "IPA proof should not be empty"); + + auto native_outer_vk = std::make_shared(ultra_instance->proving_key); + auto outer_vk = + std::make_shared(&outer_builder, native_outer_vk); + + UltraRecursiveVerifier outer_verifier(&outer_builder, outer_vk); + // Dummy aggregation object until we do proper aggregation + bb::stdlib::recursion::aggregation_state agg_obj = + bb::stdlib::recursion::init_default_aggregation_state( + outer_builder); + + // NOTE: this returns an output - should we do something with this? add + outer_verifier.verify_proof(recursion_proof, agg_obj); + outer_builder.add_pairing_point_accumulator( + stdlib::recursion::init_default_agg_obj_indices(outer_builder)); + + auto outer_proving_key = std::make_shared>(outer_builder); + + UltraRollupProver outer_prover(outer_proving_key); + + auto outer_proof = outer_prover.construct_proof(); + auto outer_verification_key = + std::make_shared(outer_proving_key->proving_key); + auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); + UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); + + // WHAT WE NEED IS TO MODIFY THE AVM SO THAT: + /* + We have an avm_recursion_constraint that spits out this puppy: + struct HonkRecursionConstraintOutput { + PairingPointAccumulatorIndices agg_obj_indices; + OpeningClaim> ipa_claim; + StdlibProof ipa_proof; }; -} // namespace bb::stdlib::recursion::honk - -namespace tests_avm { - -using namespace bb; -using namespace bb::avm_trace; - -// class AVMRecursion { -// public: -// using Builder = bb::GoblinProver::Builder; -// using RecursiveFlavor = AvmRecursiveFlavor_; -// using AVMRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; -// using InnerFlavor = typename RecursiveFlavor::NativeFlavor; -// using InnerBuilder = bb::avm::AvmCircuitBuilder; -// using InnerProver = bb::avm::AvmProver; -// using InnerVerifier = bb::avm::AvmVerifier; -// using InnerComposer = bb::avm::AvmComposer; -// using InnerG1 = InnerFlavor::Commitment; -// using InnerFF = InnerFlavor::FF; - -// using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; -// using OuterProver = UltraProver; -// using OuterVerifier = UltraVerifier; -// using OuterDeciderProvingKey = DeciderProvingKey_; -// using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; -// using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; - -// void verify_goblin_proof(HonkProof& proof, -// std::vector>& public_inputs_vec, -// const std::shared_ptr verification_key) const -// { -// GoblinProver goblin; -// OuterBuilder outer_circuit(goblin.op_queue); -// AVMRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; - -// auto agg_object = -// stdlib::recursion::init_default_aggregation_state( -// outer_circuit); - -// auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - -// bool agg_output_valid = -// verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), -// agg_output.P1.get_value()); - -// ASSERT(agg_output_valid); -// // At this point we have a circuit that verifies an AVM proof -// // We need the following: -// // 1: a proof of the AVM circuit -// // 2: an eccvm proof of the transcript from the AVM circuit builder -// // 3: a translator proof of the transcript from the AVM circuit builder - -// // 1. Make a proof of the verification of an AVM proof -// const size_t srs_size = 1 << 23; -// auto ultra_instance = std::make_shared( -// outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - -// OuterProver ultra_prover(ultra_instance); -// auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); -// OuterVerifier ultra_verifier(ultra_verification_key); - -// auto recursion_proof = ultra_prover.construct_proof(); -// bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); -// ASSERT(recursion_verified); -// // 2. eccvm -// GoblinProof g_proof = goblin.prove(); - -// // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from -// their -// // respective proving keys) -// auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); -// auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); -// GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; -// bool verified = goblin_verifier.verify(g_proof); -// ASSERT(verified); -// } -// }; -// class AVMGoblinProver : public bb::GoblinProver { -// public: -// using RecursiveFlavor = AvmRecursiveFlavor_; -// using AVMRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; -// using InnerFlavor = typename RecursiveFlavor::NativeFlavor; -// using InnerBuilder = bb::avm::AvmCircuitBuilder; -// using InnerProver = bb::avm::AvmProver; -// using InnerVerifier = bb::avm::AvmVerifier; -// using InnerComposer = bb::avm::AvmComposer; -// using InnerG1 = InnerFlavor::Commitment; -// using InnerFF = InnerFlavor::FF; - -// using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; -// using OuterProver = UltraProver_; -// using OuterVerifier = UltraVerifier_; -// using OuterDeciderProvingKey = DeciderProvingKey_; - -// PairingPoints verify_goblin_merge(Builder& outer_circuit, -// MergeProof& proof, -// const std::shared_ptr verification_key, -// std::vector>& public_inputs_vec) const -// { -// // we are verifying an inner proof -// // blah blah blah -// AVMRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; -// auto agg_object = -// stdlib::recursion::init_default_aggregation_state( -// outer_circuit); - -// auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - -// // ooook so... -// // we need to do 2 things -// // 1: perform a "standard" recursive verification where ecc ops get shoved into a circuit builder queue -// // 2: call merge verifier on the ecc builder queue -// // see client_ivc.cpp ClientIVC::complete_kernel_circuit_logic - -// // this is where we need to change things? we can't return pairing points we need to return data blob - -// // ok soooo here's what I think so far. -// // we actually keep the recursive verification the same as original AVM, including returning the agg_output -// // However we ALSO include a merge verifier step -// // and create a combined accumulator object -// return agg_output; -// PROFILE_THIS_NAME("Goblin::merge"); -// // const std::shared_ptr verification_key = verifier.key; -// //. OuterBuilder outer_circuit; -// // RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; - -// // Make a proof of the verification of an AVM proof -// //. const size_t srs_size = 1 << 23; -// // auto ultra_instance = std::make_shared( -// // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - -// // OuterProver ultra_prover(ultra_instance); -// // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - -// // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); -// // OuterVerifier ultra_verifier(ultra_verification_key); - -// // AVMRecursiveVerifier merge_verifier(ultra_verification_key); -// // return merge_verifier.verify_proof(proof); -// }; - -// void avm_merge(Builder& circuit_builder) -// { -// // Append a recursive merge verification of the merge proof -// if (merge_proof_exists) { -// [[maybe_unused]] auto pairing_points = verify_goblin_merge(circuit_builder, merge_proof); -// } - -// // Construct a merge proof for the present circuit -// merge_proof = prove_merge(circuit_builder); -// }; -// }; + */ + ASSERT(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof) && + "UltraRollupVerifier should accept"); +} class AvmRecursiveTests : public ::testing::Test { public: @@ -458,6 +228,18 @@ class AvmRecursiveTests : public ::testing::Test { } }; +TEST_F(AvmRecursiveTests, RecursionLatest) +{ + + InnerBuilder circuit_builder = generate_avm_circuit(); + InnerComposer composer = InnerComposer(); + InnerProver prover = composer.create_prover(circuit_builder); + InnerVerifier verifier = composer.create_verifier(circuit_builder); + + HonkProof proof = prover.construct_proof(); + + test_recursive_avm_gobble_gobble(proof, verifier); +} TEST_F(AvmRecursiveTests, recursion) { // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { @@ -492,6 +274,44 @@ TEST_F(AvmRecursiveTests, recursion) // Create the outer verifier, to verify the proof const std::shared_ptr verification_key = verifier.key; + // WTF is happening here? + + // 1. we make a goblin prover + // 2. we construct the "outer" circuit as a MegaCircuit + // previously the outer circuit was bb::avm::AvmRecursiveVerifier_::CircuitBuilder + // NOTE: why different? + // RecursiveFlavor = AvmRecursiveFlavor_ so...RecursiveFlavor::CircuitBuilder is Mega? + // 3. We create a bb::avm::AvmRecursiveVerifier_ to verify the outer circuit + // 4. `agg_output` is the result of `recurisve_verifier.verify_proof` + // 5. We apply `goblin.merge` on the outer_circuit + // 6. We generate a goblin proof of the merge circuit + // 7. We construct a GoblinRecursiveVerifier out of an UltraCircuitBuilder and a GoblinVerifier::VerifierInput + // GoblinVerifier::VerifierInput is generated from the eccvm proving key and the translator proving key + // OK so something weird going on is that when we recursively verify the goblin proof, we don't use the output? + // output needs to be spat up to next recursive layer (tube?) + // 8. The UltraCircuitBuilder that we run the goblin verifier on...we generate a proving/verification key for it + // and generate + verify a proof. Note: this is a native verifier + // 9. We create a MegaDeciderProvingKey out of the `outer_circuit` (the original circuit that verifies the AVM) + // 10. We generate a proof of outer_circuit + + // OK What are we expecting out of this? + + // Tier 1: Original AVM proof + // Tier 2: A Goblin-ified Mega circuit that verifies the AVM Proof - we get a proof of this (PI0) + // Tier 3: An ECCVM + Translator proof of the transcript in PI0 (PI1, PI2) + // Tier 4: An Ultra circuit that verifies PI0, PI1, PI2 + + // What do we currently do? + + // `outer_circuit` runs folding verifier on AVM proof `recursive_verifier.verify_proof` + // This spits out a transcript + // We then call `goblin.merge` on `outer_circuit`. Can we do this? If merge is no longer represented as commitments + // thne we can + + // OuterCircuit: [verifies AVM Pi0], [produces transcript via goblin.merge] + // Builder verifies goblin plonk proofs generated from OuterCircuit + // Builder needs to also verify OuterCircuit + // `outer_circuit` runs GoblinProver goblin; MegaCircuitBuilder outer_circuit(goblin.op_queue); RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; @@ -562,7 +382,21 @@ TEST_F(AvmRecursiveTests, recursion) // next step fails likely because of a lack of a merge proof - gverifier.verify(g_proof); + auto goblin_verifier_output = gverifier.verify(g_proof); + + // Validate natively that `goblin_verifier_output` produces valid IPA claim + { + auto crs_factory = std::make_shared>("../srs_db/grumpkin", + 1 << CONST_ECCVM_LOG_N); + auto grumpkin_verifier_commitment_key = + std::make_shared>(1 << CONST_ECCVM_LOG_N, crs_factory); + OpeningClaim native_claim = goblin_verifier_output.opening_claim.get_native_opening_claim(); + auto native_ipa_transcript = std::make_shared( + convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data)); + + EXPECT_TRUE( + IPA::reduce_verify(grumpkin_verifier_commitment_key, native_claim, native_ipa_transcript)); + } std::cout << "G" << std::endl; EXPECT_EQ(builder.failed(), false) << builder.err(); @@ -629,230 +463,4 @@ TEST_F(AvmRecursiveTests, recursion) // EXPECT_TRUE(g_verified); } -TEST_F(AvmRecursiveTests, recursion2) -{ - // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - // GTEST_SKIP(); - // } - - InnerBuilder circuit_builder = generate_avm_circuit(); - InnerComposer composer = InnerComposer(); - InnerProver prover = composer.create_prover(circuit_builder); - InnerVerifier verifier = composer.create_verifier(circuit_builder); - - HonkProof proof = prover.construct_proof(); - - // We just pad all the public inputs with the right number of zeroes - std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); - std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector calldata{ {} }; - std::vector returndata{ {} }; - - std::vector> public_inputs{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs - }; - std::vector> public_inputs_vec{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata - }; - - bool verified = verifier.verify_proof(proof, public_inputs_vec); - ASSERT_TRUE(verified) << "native proof verification failed"; - - // Create the outer verifier, to verify the proof - const std::shared_ptr verification_key = verifier.key; - - bb::stdlib::recursion::honk::AVMGoblin avmgoblin; - - UltraCircuitBuilder builder; - - // auto fields_from_witnesses = [&](std::vector const& input) { - // std::vector result; - // result.reserve(input.size()); - // for (const auto& idx : input) { - // auto field = OuterFF::from_witness_index(&builder, idx); - // result.emplace_back(field); - // } - // return result; - // }; - - // const auto proof_fields = fields_from_witnesses(proof); - // const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); - - // auto it = public_inputs_flattened.begin(); - // VmPublicInputs vm_public_inputs = - // avm_trace::convert_public_inputs(std::vector(it, it + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH)); - // it += PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH; - // std::vector calldata(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); - // it += AVM_PUBLIC_COLUMN_MAX_SIZE; - // std::vector return_data(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); - - // auto public_inputs_vectors = avm_trace::copy_public_inputs_columns(vm_public_inputs, calldata, return_data); - - UltraCircuitBuilder outer_circuit; - - std::vector proof_fields; - for (auto& field : proof) { - proof_fields.emplace_back(OuterFF::from_witness(&outer_circuit, field)); - } - std::vector> public_inputs_vec_fields; - for (auto& x : public_inputs_vec) { - std::vector vec; - for (auto& y : x) { - vec.emplace_back(OuterFF::from_witness(&outer_circuit, y)); - } - public_inputs_vec_fields.emplace_back(vec); - } - // std::vector public_inputs_fields; - - avmgoblin.compute_full_avm_recursion(&outer_circuit, proof_fields, public_inputs_vec_fields, verification_key); - - std::cout << "I" << std::endl; - { - auto proving_key = std::make_shared(builder); - OuterProver prover(proving_key); - auto verification_key = std::make_shared(proving_key->proving_key); - OuterVerifier verifier(verification_key); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - EXPECT_TRUE(verified); - } - - // ASSERT(verified); - // GoblinProver goblin; - // MegaCircuitBuilder outer_circuit(goblin.op_queue); - // RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; - - // auto agg_object = - // stdlib::recursion::init_default_aggregation_state( - // outer_circuit); - - // auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - - // bool agg_output_valid = - // verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); - - // ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; - // ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; - - // bool outer_circuit_checked = CircuitChecker::check(outer_circuit); - // ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; - - // std::cout << "A" << std::endl; - // auto manifest = verifier.transcript->get_manifest(); - // auto recursive_manifest = recursive_verifier.transcript->get_manifest(); - // std::cout << "B" << std::endl; - - // EXPECT_EQ(manifest.size(), recursive_manifest.size()); - // for (size_t i = 0; i < recursive_manifest.size(); ++i) { - // EXPECT_EQ(recursive_manifest[i], manifest[i]); - // } - - // for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { - // EXPECT_EQ(key_el, rec_key_el.get_value()); - // } - - // EXPECT_EQ(verifier.key->circuit_size, recursive_verifier.key->circuit_size); - // EXPECT_EQ(verifier.key->num_public_inputs, recursive_verifier.key->num_public_inputs); - - // // in goblin test... - // // there are several mega circuits being generated - // // goblin proof is then constructed from the mega circuits - - // // what is a goblin proof? - // // eccvm proof, translator proof and a "merge" proof - // // what is the merge proof? - - // // `goblin.verify_merge` is called using a client builder input - // // input is a mega circuit - // // eww - // // The next step is... - // // An UltraBuilder is created - // // The UltraBuilder is used to create a circuit that verifies the goblin proof - // std::cout << "C" << std::endl; - // goblin.merge(outer_circuit); - - // GoblinProof g_proof = goblin.prove(); - // std::cout << "D" << std::endl; - - // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their - // // respective proving keys) - // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - // std::cout << "E" << std::endl; - - // UltraCircuitBuilder builder; - // GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), - // std::make_shared(goblin.get_translator_proving_key()) - // }; - // bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &builder, goblin_vinput }; - // std::cout << "F" << std::endl; - - // // next step fails likely because of a lack of a merge proof - - // gverifier.verify(g_proof); - // std::cout << "G" << std::endl; - - // EXPECT_EQ(builder.failed(), false) << builder.err(); - // EXPECT_TRUE(CircuitChecker::check(builder)); - // // Construct and verify a proof for the Goblin Recursive Verifier circuit - // { - // auto proving_key = std::make_shared(builder); - // OuterProver prover(proving_key); - // auto verification_key = std::make_shared(proving_key->proving_key); - // OuterVerifier verifier(verification_key); - // auto proof = prover.construct_proof(); - // bool verified = verifier.verify_proof(proof); - - // ASSERT(verified); - // } - - // // const size_t srs_size = 1 << 23; - // auto ultra_instance = std::make_shared(outer_circuit); - // MegaProver ultra_prover(ultra_instance); - // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - // MegaVerifier ultra_verifier(ultra_verification_key); - - // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); - - // auto recursion_proof = ultra_prover.construct_proof(); - // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); - // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; - - // // Make a proof of the verification of an AVM proof - // // const size_t srs_size = 1 << 23; - // // auto ultra_instance = std::make_shared( - // // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - // // auto ultra_instance = std::make_shared(outer_circuit); - - // // OuterProver ultra_prover(ultra_instance); - // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - // // OuterVerifier ultra_verifier(ultra_verification_key); - - // // // // auto proving_key = std::make_shared(builder); - - // // // OuterProver outer_prover(ultra_instance); - - // // // goblin.merge(outer_circit); - // // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - // // // GoblinVerifier ultra_verifier(ultra_verification_key); - - // // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); - - // // auto recursion_proof = ultra_prover.construct_proof(); - // // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); - // // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; - - // // GoblinProof g_proof = goblin.prove(); - - // // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from - // their - // // // respective proving keys) - // // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - // // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - // // GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; - // // bool g_verified = goblin_verifier.verify(g_proof); - // // EXPECT_TRUE(g_verified); -} -} // namespace tests_avm +} // namespace tests_avm \ No newline at end of file From dc50c48aa98903eb4de33e2fe905e2eca0656f91 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Wed, 19 Mar 2025 17:37:36 +0000 Subject: [PATCH 17/43] moved recursive verifier into separate file --- .../goblin_avm_recursive_verifier.hpp | 196 ++++++++++++++---- .../vm/avm/tests/recursive_verifier.test.cpp | 129 +----------- 2 files changed, 156 insertions(+), 169 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index f7019b259f29..b1cf330404ae 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -1,43 +1,153 @@ -// #pragma once -// #include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" -// #include "barretenberg/sumcheck/sumcheck.hpp" -// #include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" - -// namespace bb::avm { - -// template class GoblinAvmRecursiveVerifier_ { -// using FF = typename Flavor::FF; -// using BF = typename Flavor::BF; -// using Curve = typename Flavor::Curve; -// using Commitment = typename Flavor::Commitment; -// using CommitmentLabels = typename Flavor::CommitmentLabels; -// using RelationSeparator = typename Flavor::RelationSeparator; -// using VerificationKey = typename Flavor::VerificationKey; -// using NativeVerificationKey = typename Flavor::NativeVerificationKey; -// using Builder = typename Flavor::CircuitBuilder; -// using PCS = typename Flavor::PCS; -// using Transcript = bb::BaseTranscript>; -// using VerifierCommitments = typename Flavor::VerifierCommitments; -// using AggregationObject = bb::stdlib::recursion::aggregation_state; - -// public: -// explicit GoblinAvmRecursiveVerifier_(Builder* builder, -// const std::shared_ptr& native_verification_key); -// explicit GoblinAvmRecursiveVerifier_(Builder* builder, const std::shared_ptr& vkey); - -// AggregationObject verify_proof(const HonkProof& proof, -// const std::vector>& public_inputs_vec_nt, -// AggregationObject agg_obj); -// AggregationObject verify_proof(const StdlibProof& stdlib_proof, -// const std::vector>& public_inputs, -// AggregationObject agg_obj); - -// std::shared_ptr key; -// Builder* builder; -// std::shared_ptr transcript; - -// private: -// FF evaluate_public_input_column(const std::vector& points, const std::vector& challenges); -// }; - -// } // namespace bb::avm \ No newline at end of file +#pragma once + +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/numeric/random/engine.hpp" +#include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" +#include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.hpp" +#include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" +#include "barretenberg/ultra_honk/decider_proving_key.hpp" +#include "barretenberg/ultra_honk/ultra_prover.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" +#include "barretenberg/vm/avm/generated/circuit_builder.hpp" +#include "barretenberg/vm/avm/generated/composer.hpp" +#include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" +#include "barretenberg/vm/avm/recursion/recursive_verifier.hpp" +#include "barretenberg/vm/avm/trace/common.hpp" +#include "barretenberg/vm/avm/trace/helper.hpp" +#include "barretenberg/vm/avm/trace/public_inputs.hpp" +#include "barretenberg/vm/avm/trace/trace.hpp" + +namespace bb { +class RecursiveAvm { + public: + static void test_recursive_avm(const HonkProof& proof, avm::AvmVerifier& verifier) + { + using AvmRecursiveFlavor = AvmRecursiveFlavor_; + + using InnerFlavor = typename AvmRecursiveFlavor::NativeFlavor; + using InnerFF = InnerFlavor::FF; + using RecursiveVerifier = avm::AvmRecursiveVerifier_; + using ECCVMVerificationKey = ECCVMFlavor::VerificationKey; + using TranslatorVerificationKey = TranslatorFlavor::VerificationKey; + using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; + using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; + using MegaProver = UltraProver_; + using MegaDeciderProvingKey = DeciderProvingKey_; + using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; + using UltraRecursiveVerifier = + stdlib::recursion::honk::UltraRecursiveVerifier_; + using UltraRollupProver = UltraProver_; + using UltraRollupVerifier = UltraVerifier_; + + // Pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + std::vector> public_inputs{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs + }; + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, + returndata + }; + // ASSERT_TRUE(verifier.verify_proof(proof, public_inputs_vec)) << "native proof verification failed"; + + const std::shared_ptr verification_key = verifier.key; + + GoblinProver goblin; + MegaCircuitBuilder inner_builder(goblin.op_queue); + RecursiveVerifier recursive_verifier{ &inner_builder, verification_key }; + auto agg_object = + stdlib::recursion::init_default_aggregation_state( + inner_builder); + [[maybe_unused]] auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + + /* + ASSERT_TRUE( + verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), + *agg_output.P1.get_value())) + << "Pairing points (aggregation state) are not valid."; + */ + + goblin.merge(inner_builder); + inner_builder.add_pairing_point_accumulator( + stdlib::recursion::init_default_agg_obj_indices(inner_builder)); + + std::shared_ptr ultra_instance = std::make_shared(inner_builder); + + MegaProver ultra_prover(ultra_instance); + auto recursion_proof = ultra_prover.construct_proof(); + + GoblinProof g_proof = goblin.prove(); + + auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); + auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + + UltraCircuitBuilder outer_builder; + GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), + std::make_shared( + goblin.get_translator_proving_key()) }; + stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &outer_builder, goblin_vinput }; + + // next step fails likely because of a lack of a merge proof + + stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); + + // ASSERT(current_aggregation_object.size() && "Aggregation object should not be empty"); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive + // verifiers + PairingPointAccumulatorIndices current_aggregation_object = + stdlib::recursion::init_default_agg_obj_indices(outer_builder); + // This is currently just setting the aggregation object to the default one. + outer_builder.add_pairing_point_accumulator(current_aggregation_object); + + // The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof + outer_builder.add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); + outer_builder.ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); + ASSERT(outer_builder.ipa_proof.size() && "IPA proof should not be empty"); + + auto native_outer_vk = std::make_shared(ultra_instance->proving_key); + auto outer_vk = + std::make_shared(&outer_builder, native_outer_vk); + + UltraRecursiveVerifier outer_verifier(&outer_builder, outer_vk); + // Dummy aggregation object until we do proper aggregation + stdlib::recursion::aggregation_state agg_obj = + stdlib::recursion::init_default_aggregation_state( + outer_builder); + + // NOTE: this returns an output - should we do something with this? add + outer_verifier.verify_proof(recursion_proof, agg_obj); + outer_builder.add_pairing_point_accumulator( + stdlib::recursion::init_default_agg_obj_indices(outer_builder)); + + auto outer_proving_key = std::make_shared>(outer_builder); + + UltraRollupProver outer_prover(outer_proving_key); + + auto outer_proof = outer_prover.construct_proof(); + auto outer_verification_key = + std::make_shared(outer_proving_key->proving_key); + auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); + UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); + + // WHAT WE NEED IS TO MODIFY THE AVM SO THAT: + /* + We have an avm_recursion_constraint that spits out this puppy: + struct HonkRecursionConstraintOutput { + PairingPointAccumulatorIndices agg_obj_indices; + OpeningClaim> ipa_claim; + StdlibProof ipa_proof; + }; + */ + ASSERT(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof) && + "UltraRollupVerifier should accept"); + } +}; +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 2b9f70f0f40f..612d960a08db 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -12,12 +12,14 @@ #include "barretenberg/ultra_honk/ultra_verifier.hpp" #include "barretenberg/vm/avm/generated/circuit_builder.hpp" #include "barretenberg/vm/avm/generated/composer.hpp" +#include "barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp" #include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" #include "barretenberg/vm/avm/tests/helpers.test.hpp" #include "barretenberg/vm/avm/trace/common.hpp" #include "barretenberg/vm/avm/trace/helper.hpp" #include "barretenberg/vm/avm/trace/public_inputs.hpp" #include "barretenberg/vm/avm/trace/trace.hpp" + #include namespace bb::stdlib::recursion::honk {} @@ -27,131 +29,6 @@ namespace tests_avm { using namespace bb; using namespace bb::avm_trace; -void test_recursive_avm_gobble_gobble(const HonkProof& proof, bb::avm::AvmVerifier& verifier) -{ - using AvmRecursiveFlavor = AvmRecursiveFlavor_; - - using InnerFlavor = typename AvmRecursiveFlavor::NativeFlavor; - using InnerFF = InnerFlavor::FF; - using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; - using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; - using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; - using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; - using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; - using MegaProver = UltraProver_; - using MegaDeciderProvingKey = DeciderProvingKey_; - using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; - using UltraRecursiveVerifier = - bb::stdlib::recursion::honk::UltraRecursiveVerifier_; - using UltraRollupProver = bb::UltraProver_; - using UltraRollupVerifier = bb::UltraVerifier_; - - // Pad all the public inputs with the right number of zeroes - std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); - std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector calldata{ {} }; - std::vector returndata{ {} }; - std::vector> public_inputs{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs - }; - std::vector> public_inputs_vec{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata - }; - // ASSERT_TRUE(verifier.verify_proof(proof, public_inputs_vec)) << "native proof verification failed"; - - const std::shared_ptr verification_key = verifier.key; - - GoblinProver goblin; - MegaCircuitBuilder inner_builder(goblin.op_queue); - RecursiveVerifier recursive_verifier{ &inner_builder, verification_key }; - auto agg_object = - stdlib::recursion::init_default_aggregation_state( - inner_builder); - [[maybe_unused]] auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - - /* - ASSERT_TRUE( - verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), *agg_output.P1.get_value())) - << "Pairing points (aggregation state) are not valid."; - */ - - goblin.merge(inner_builder); - inner_builder.add_pairing_point_accumulator( - stdlib::recursion::init_default_agg_obj_indices(inner_builder)); - - std::shared_ptr ultra_instance = std::make_shared(inner_builder); - - MegaProver ultra_prover(ultra_instance); - auto recursion_proof = ultra_prover.construct_proof(); - - GoblinProof g_proof = goblin.prove(); - - auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - - UltraCircuitBuilder outer_builder; - GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), - std::make_shared(goblin.get_translator_proving_key()) }; - bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &outer_builder, goblin_vinput }; - - // next step fails likely because of a lack of a merge proof - - bb::stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); - - // ASSERT(current_aggregation_object.size() && "Aggregation object should not be empty"); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive - // verifiers - PairingPointAccumulatorIndices current_aggregation_object = - stdlib::recursion::init_default_agg_obj_indices(outer_builder); - // This is currently just setting the aggregation object to the default one. - outer_builder.add_pairing_point_accumulator(current_aggregation_object); - - // The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof - outer_builder.add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); - outer_builder.ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); - ASSERT(outer_builder.ipa_proof.size() && "IPA proof should not be empty"); - - auto native_outer_vk = std::make_shared(ultra_instance->proving_key); - auto outer_vk = - std::make_shared(&outer_builder, native_outer_vk); - - UltraRecursiveVerifier outer_verifier(&outer_builder, outer_vk); - // Dummy aggregation object until we do proper aggregation - bb::stdlib::recursion::aggregation_state agg_obj = - bb::stdlib::recursion::init_default_aggregation_state( - outer_builder); - - // NOTE: this returns an output - should we do something with this? add - outer_verifier.verify_proof(recursion_proof, agg_obj); - outer_builder.add_pairing_point_accumulator( - stdlib::recursion::init_default_agg_obj_indices(outer_builder)); - - auto outer_proving_key = std::make_shared>(outer_builder); - - UltraRollupProver outer_prover(outer_proving_key); - - auto outer_proof = outer_prover.construct_proof(); - auto outer_verification_key = - std::make_shared(outer_proving_key->proving_key); - auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); - UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); - - // WHAT WE NEED IS TO MODIFY THE AVM SO THAT: - /* - We have an avm_recursion_constraint that spits out this puppy: - struct HonkRecursionConstraintOutput { - PairingPointAccumulatorIndices agg_obj_indices; - OpeningClaim> ipa_claim; - StdlibProof ipa_proof; -}; - */ - ASSERT(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof) && - "UltraRollupVerifier should accept"); -} - class AvmRecursiveTests : public ::testing::Test { public: using RecursiveFlavor = AvmRecursiveFlavor_; @@ -238,7 +115,7 @@ TEST_F(AvmRecursiveTests, RecursionLatest) HonkProof proof = prover.construct_proof(); - test_recursive_avm_gobble_gobble(proof, verifier); + RecursiveAvm::test_recursive_avm(proof, verifier); } TEST_F(AvmRecursiveTests, recursion) { From dd65108cbd6fd8b89ac6d719fefbf723ed4bd950 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 20 Mar 2025 00:02:42 +0000 Subject: [PATCH 18/43] working tests + new dsl method for avm recursion constraint --- .../acir_format/avm_recursion_constraint.cpp | 92 ++++++ .../acir_format/avm_recursion_constraint.hpp | 7 +- .../goblin_avm_recursive_verifier.hpp | 267 ++++++++++++------ .../vm/avm/tests/recursive_verifier.test.cpp | 244 +++------------- 4 files changed, 319 insertions(+), 291 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 6a76981b0ee1..103433dde081 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -6,8 +6,10 @@ #include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" +#include "barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp" #include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" #include "barretenberg/vm/avm/recursion/recursive_verifier.hpp" + #include "barretenberg/vm/avm/trace/common.hpp" #include "barretenberg/vm/avm/trace/helper.hpp" #include "barretenberg/vm/aztec_constants.hpp" @@ -209,5 +211,95 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints( return output_agg_object.get_witness_indices(); } +// NEW VERSION THAT USES GOBLIN PLONK IN VERIFICATION ALGO +HonkRecursionConstraintOutput create_honk_recursion_constraints( + Builder& builder, + const RecursionConstraint& input, + PairingPointAccumulatorIndices input_aggregation_object_indices, + bool has_valid_witness_assignments) +{ + // using Flavor = AvmRecursiveFlavor_; + // using RecursiveVerificationKey = Flavor::VerificationKey; + using RecursiveVerifier = bb::avm::AvmGoblinRecursiveVerifier; + + ASSERT(input.proof_type == AVM); + + // Construct an in-circuit representation of the verification key. + std::vector key_fields; + key_fields.reserve(input.key.size()); + for (const auto& idx : input.key) { + auto field = field_ct::from_witness_index(&builder, idx); + key_fields.emplace_back(field); + } + + auto fields_from_witnesses = [&](std::vector const& input) { + std::vector result; + result.reserve(input.size()); + for (const auto& idx : input) { + auto field = field_ct::from_witness_index(&builder, idx); + result.emplace_back(field); + } + return result; + }; + + const auto proof_fields = fields_from_witnesses(input.proof); + const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); + + auto it = public_inputs_flattened.begin(); + VmPublicInputs vm_public_inputs = + avm_trace::convert_public_inputs(std::vector(it, it + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH)); + it += PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH; + std::vector calldata(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); + it += AVM_PUBLIC_COLUMN_MAX_SIZE; + std::vector return_data(it, it + AVM_PUBLIC_COLUMN_MAX_SIZE); + + auto public_inputs_vectors = avm_trace::copy_public_inputs_columns(vm_public_inputs, calldata, return_data); + + // Populate the key fields and proof fields with dummy values to prevent issues (e.g. points must be on curve). + if (!has_valid_witness_assignments) { + create_dummy_vkey_and_proof(builder, input.proof.size(), input.public_inputs.size(), key_fields, proof_fields); + } + + // Recursively verify the proof + // auto vkey = std::make_shared(builder, key_fields); + RecursiveVerifier verifier(&builder, key_fields); + aggregation_state_ct input_agg_obj = bb::stdlib::recursion::convert_witness_indices_to_agg_obj( + builder, input_aggregation_object_indices); + auto output_agg_object = verifier.verify_proof(proof_fields, public_inputs_vectors, input_agg_obj); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public + // inputs is important, like what the plonk recursion constraint does. + + /* + HonkRecursionConstraintOutput output; + if (is_rollup_honk_recursion_constraint) { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1168): Add formula to flavor + const size_t HONK_PROOF_LENGTH = 469; + // The extra calculation is for the IPA proof length. + ASSERT(input.proof.size() == HONK_PROOF_LENGTH + 1 + 4 * (CONST_ECCVM_LOG_N) + 2 + 2); + ASSERT(proof_fields.size() == HONK_PROOF_LENGTH + 65 + input.public_inputs.size()); + // split out the ipa proof + const std::ptrdiff_t honk_proof_with_pub_inputs_length = + static_cast(HONK_PROOF_LENGTH + input.public_inputs.size()); + output.ipa_proof = + StdlibProof(honk_proof.begin() + honk_proof_with_pub_inputs_length, honk_proof.end()); + honk_proof = StdlibProof(honk_proof.begin(), honk_proof.end() + honk_proof_with_pub_inputs_length); + } + UltraRecursiveVerifierOutput verifier_output = verifier.verify_proof(honk_proof, input_agg_obj); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs + // is important, like what the plonk recursion constraint does. + + output.agg_obj_indices = verifier_output.agg_obj.get_witness_indices(); + if (is_rollup_honk_recursion_constraint) { + ASSERT(HasIPAAccumulator); + output.ipa_claim = verifier_output.ipa_opening_claim; + } + + */ + HonkRecursionConstraintOutput result{ .agg_obj_indices = output_agg_object.aggregation_object.get_witness_indices(), + .ipa_claim = output_agg_object.ipa_claim, + .ipa_proof = output_agg_object.ipa_proof }; + return result; +} + } // namespace acir_format #endif // DISABLE_AZTEC_VM diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp index 178cf97fb8d4..5bae5709fd7d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp @@ -1,8 +1,8 @@ #ifndef DISABLE_AZTEC_VM #pragma once +#include "barretenberg/dsl/acir_format/honk_recursion_constraint.hpp" #include "barretenberg/dsl/acir_format/recursion_constraint.hpp" #include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" - namespace acir_format { using Builder = bb::UltraCircuitBuilder; @@ -13,5 +13,10 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints(Builder& builder PairingPointAccumulatorIndices input_aggregation_object, bool has_valid_witness_assignments); +HonkRecursionConstraintOutput create_avm_recursion_constraints2(Builder& builder, + const RecursionConstraint& input, + PairingPointAccumulatorIndices input_aggregation_object, + bool has_valid_witness_assignments); + } // namespace acir_format #endif // DISABLE_AZTEC_VM \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index b1cf330404ae..923c6b266abf 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -4,9 +4,11 @@ #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" +#include "barretenberg/stdlib/hash/poseidon2/poseidon2.hpp" #include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.hpp" #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -18,16 +20,54 @@ #include "barretenberg/vm/avm/trace/helper.hpp" #include "barretenberg/vm/avm/trace/public_inputs.hpp" #include "barretenberg/vm/avm/trace/trace.hpp" +namespace bb::avm { -namespace bb { -class RecursiveAvm { +class AvmGoblinRecursiveVerifier { public: - static void test_recursive_avm(const HonkProof& proof, avm::AvmVerifier& verifier) + using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; + struct RecursiveAvmGoblinOutput { + using UltraBuilder = UltraRollupRecursiveFlavor::CircuitBuilder; + using UltraFF = UltraRollupRecursiveFlavor::Curve::ScalarField; + std::vector ipa_proof; + OpeningClaim> ipa_claim; + stdlib::recursion::aggregation_state> aggregation_object; + }; + using Builder = typename UltraRollupRecursiveFlavor::CircuitBuilder; + using AggregationObject = bb::stdlib::recursion::aggregation_state>; + + using RecursiveFlavor = AvmRecursiveFlavor_; + + using Transcript = bb::BaseTranscript>; + using UltraFF = UltraRollupRecursiveFlavor::Curve::ScalarField; + + using OuterAvmKey = AvmRecursiveFlavor_::VerificationKey; + std::vector outer_key_fields; + + Builder* builder; + std::shared_ptr transcript; + + // Note: instead of passing in a native verification key we pass in a vector of field elements whose type equals + // Builder::FF + // Reason is a bit of a workaround. In avm_recursion_constraint.cpp we construct an AVM verification key in the + // current UltraRollupCircuit context. + // However, the UltraRollupCircuit doesn't verify the AVM - it verifies a MegaCircuit that verifies the AVM. + // We therefore need the vkey both present in the top-level UltraRollupCircuit *and* the downstream MegaCircuit. + // Initial plan was to take a vkey in the UltraRollupCircuit context as an input, and then use `key.to_fields()` to + // construct a MegaCircuit vkey context. However `to_fields` seems bugged when used in a recursive vkey setting. + explicit AvmGoblinRecursiveVerifier(Builder* builder, const std::vector& outer_key_fields) + : outer_key_fields(outer_key_fields) + , builder(builder) + {} + + RecursiveAvmGoblinOutput verify_proof( + const StdlibProof& stdlib_proof, + const std::vector>& public_inputs, + AggregationObject agg_obj) const { - using AvmRecursiveFlavor = AvmRecursiveFlavor_; - using InnerFlavor = typename AvmRecursiveFlavor::NativeFlavor; - using InnerFF = InnerFlavor::FF; + using AvmRecursiveFlavor = AvmRecursiveFlavor_; + using FF = AvmRecursiveFlavor::FF; + using UltraFF = UltraRollupRecursiveFlavor::FF; using RecursiveVerifier = avm::AvmRecursiveVerifier_; using ECCVMVerificationKey = ECCVMFlavor::VerificationKey; using TranslatorVerificationKey = TranslatorFlavor::VerificationKey; @@ -38,116 +78,167 @@ class RecursiveAvm { using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; using UltraRecursiveVerifier = stdlib::recursion::honk::UltraRecursiveVerifier_; - using UltraRollupProver = UltraProver_; - using UltraRollupVerifier = UltraVerifier_; - - // Pad all the public inputs with the right number of zeroes - std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); - std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector calldata{ {} }; - std::vector returndata{ {} }; - std::vector> public_inputs{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs - }; - std::vector> public_inputs_vec{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, - returndata - }; - // ASSERT_TRUE(verifier.verify_proof(proof, public_inputs_vec)) << "native proof verification failed"; - - const std::shared_ptr verification_key = verifier.key; + /* + Here's an attempt at a flow chat that describes what this function is doing. + We start with an AVMProof and end up with an UltraProof and an IPA proof. + The goal is to verify the AVMProof using a goblin-enhanced MegaCircuit, + and then "blackbox" the goblin plonk component by recursively verifying both + the MegaCircuit proof AND the goblin plonk proofs + in an UltraRecursiveRollupCircuit + + AVMProof (this is `stdlib_proof`) + | + | + v + MegaCircuit (verifies AVMProof) + | + | + v + MergeProof + MegaProof + | | + | ----------------- + v | + ECCVMCircuit + TranslatorCircuit | + | | | + | | | + v v | + ECCVMProof TranslatorProof | + | | | + | | | + v v v + ####UltraRecursiveRollupCircuit##### (this is `builder`) + | + | + v + UltraProof + IPA Proof + + */ + + // STEP 1: + // Take the UltraBuilder proof inputs and convert into MegaBuilder proof inputs + // (later on we must validate that the AVM proof fed into the MegaCircuit matches the one fed into the upstream + // UltraCircuit) + StdlibProof mega_stdlib_proof; + std::vector> mega_public_inputs; GoblinProver goblin; MegaCircuitBuilder inner_builder(goblin.op_queue); - RecursiveVerifier recursive_verifier{ &inner_builder, verification_key }; - auto agg_object = + + std::vector input_hash; + std::vector upstream_hash; + for (auto& element : stdlib_proof) { + FF val = FF::from_witness(&inner_builder, element.get_value()); + mega_stdlib_proof.emplace_back(val); + input_hash.emplace_back(val); + upstream_hash.emplace_back(element); + } + for (auto& input_vec : public_inputs) { + std::vector inner_vec; + for (auto& input : input_vec) { + FF val = FF::from_witness(&inner_builder, input.get_value()); + inner_vec.emplace_back(val); + input_hash.emplace_back(val); + upstream_hash.emplace_back(input); + } + mega_public_inputs.emplace_back(inner_vec); + } + + // Step 1.5 Convert the UltraRollupCircuit representation of the AVM verification key into a MegaCircuit + // representation + std::vector key_fields; + for (const auto& f : outer_key_fields) { + FF val = FF::from_witness(&inner_builder, f.get_value()); + key_fields.emplace_back(val); + input_hash.emplace_back(val); + upstream_hash.emplace_back(f); + } + auto stdlib_key = std::make_shared::VerificationKey>( + inner_builder, std::span(key_fields)); + + // we use the hash of the proof + public inputs to validate that we're correctly transferring data between the + // Mega and Ultra circuits + auto mega_input_hash = stdlib::poseidon2::hash(inner_builder, input_hash); + // NOTE: there doesn't seem to be an easy way to know *which* public input index will map to mega_input_hash + // which is troublesome + mega_input_hash.set_public(); + + // Step 2: Verify the AVM proof + // NOTICE!!!! We don't currently propagate the aggregation object which we need to for this to be sound! + RecursiveVerifier recursive_verifier{ &inner_builder, stdlib_key }; + auto mega_agg_object = stdlib::recursion::init_default_aggregation_state( inner_builder); - [[maybe_unused]] auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - - /* - ASSERT_TRUE( - verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), - *agg_output.P1.get_value())) - << "Pairing points (aggregation state) are not valid."; - */ + [[maybe_unused]] auto agg_output = + recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, mega_agg_object); + // Step 3: run the goblin merge protocol goblin.merge(inner_builder); inner_builder.add_pairing_point_accumulator( stdlib::recursion::init_default_agg_obj_indices(inner_builder)); + // Step 4: generate a proof of the above MegaCircuit std::shared_ptr ultra_instance = std::make_shared(inner_builder); - MegaProver ultra_prover(ultra_instance); auto recursion_proof = ultra_prover.construct_proof(); + // Step 5: make a goblin proof and construct a GoblinRecursiveVerifier GoblinProof g_proof = goblin.prove(); auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - - UltraCircuitBuilder outer_builder; GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), std::make_shared( goblin.get_translator_proving_key()) }; - stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &outer_builder, goblin_vinput }; - - // next step fails likely because of a lack of a merge proof + // Step 6: In our UltraCircuit, recursively verify the goblin proof + stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ builder, goblin_vinput }; stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); - // ASSERT(current_aggregation_object.size() && "Aggregation object should not be empty"); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive - // verifiers + // NOTE: I think this part is wrong. What do we initialize the builder's agg object to be? PairingPointAccumulatorIndices current_aggregation_object = - stdlib::recursion::init_default_agg_obj_indices(outer_builder); + stdlib::recursion::init_default_agg_obj_indices(*builder); // This is currently just setting the aggregation object to the default one. - outer_builder.add_pairing_point_accumulator(current_aggregation_object); + builder->add_pairing_point_accumulator(current_aggregation_object); - // The tube only calls an IPA recursive verifier once, so we can just add this IPA claim and proof - outer_builder.add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); - outer_builder.ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); - ASSERT(outer_builder.ipa_proof.size() && "IPA proof should not be empty"); + // We only calls the IPA recursive verifier once, so we can just add this IPA claim and proof + builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); + builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); + ASSERT(builder->ipa_proof.size() && "IPA proof should not be empty"); + // Step 7: Compute the verification key to recursively verify the MegaProof + // NOTE: this part could be precomputed to save time auto native_outer_vk = std::make_shared(ultra_instance->proving_key); - auto outer_vk = - std::make_shared(&outer_builder, native_outer_vk); - - UltraRecursiveVerifier outer_verifier(&outer_builder, outer_vk); - // Dummy aggregation object until we do proper aggregation - stdlib::recursion::aggregation_state agg_obj = - stdlib::recursion::init_default_aggregation_state( - outer_builder); - - // NOTE: this returns an output - should we do something with this? add - outer_verifier.verify_proof(recursion_proof, agg_obj); - outer_builder.add_pairing_point_accumulator( - stdlib::recursion::init_default_agg_obj_indices(outer_builder)); - - auto outer_proving_key = std::make_shared>(outer_builder); - - UltraRollupProver outer_prover(outer_proving_key); - - auto outer_proof = outer_prover.construct_proof(); - auto outer_verification_key = - std::make_shared(outer_proving_key->proving_key); - auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); - UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); - - // WHAT WE NEED IS TO MODIFY THE AVM SO THAT: - /* - We have an avm_recursion_constraint that spits out this puppy: - struct HonkRecursionConstraintOutput { - PairingPointAccumulatorIndices agg_obj_indices; - OpeningClaim> ipa_claim; - StdlibProof ipa_proof; - }; - */ - ASSERT(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof) && - "UltraRollupVerifier should accept"); + auto outer_vk = std::make_shared(builder, native_outer_vk); + + // Step 8: In our UltraCirfcuit, recursively verify the mega proof + UltraRecursiveVerifier outer_verifier(builder, outer_vk); + StdlibProof stdlib_recursion_proof = bb::convert_native_proof_to_stdlib(builder, recursion_proof); + auto outer_verifier_output = outer_verifier.verify_proof(stdlib_recursion_proof, agg_obj); + + // Step 9: Validate that both `builder` and `inner_builder` use the same AVM proof data and AVM public inputs + // Note: we don't seem to have a nice way of finding out where within a public input space a given value is that + // we call `set_public` on. So we scan manually here to find the index :/ + size_t mega_input_hash_public_input_index = 0; + for (const auto& proof_ele : stdlib_recursion_proof) { + if (proof_ele.get_value() == mega_input_hash.get_value()) { + break; + } + mega_input_hash_public_input_index += 1; + } + auto ultra_output_hash = + stdlib::poseidon2::hash(*builder, upstream_hash); + stdlib_recursion_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); + + // Step 10: gather up the ipa proof, ipa claim and output aggregation object produced from verifying the mega + // proof + goblin proof, and return them + auto ipa_proof_output = goblin_verifier_output.ipa_transcript->proof_data; + auto ipa_claim_output = outer_verifier_output.ipa_opening_claim; + auto pairing_accumulator_output = outer_verifier_output.agg_obj; + + RecursiveAvmGoblinOutput result{ .ipa_proof = ipa_proof_output, + .ipa_claim = ipa_claim_output, + .aggregation_object = pairing_accumulator_output }; + return result; } }; -} // namespace bb \ No newline at end of file +} // namespace bb::avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 612d960a08db..c9726461f207 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -105,32 +105,38 @@ class AvmRecursiveTests : public ::testing::Test { } }; -TEST_F(AvmRecursiveTests, RecursionLatest) +TEST_F(AvmRecursiveTests, GoblinRecursion) { InnerBuilder circuit_builder = generate_avm_circuit(); InnerComposer composer = InnerComposer(); InnerProver prover = composer.create_prover(circuit_builder); - InnerVerifier verifier = composer.create_verifier(circuit_builder); + InnerVerifier inner_verifier = composer.create_verifier(circuit_builder); HonkProof proof = prover.construct_proof(); - RecursiveAvm::test_recursive_avm(proof, verifier); -} -TEST_F(AvmRecursiveTests, recursion) -{ - // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - // GTEST_SKIP(); - // } + UltraCircuitBuilder outer_builder; + using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; + using UltraFF = UltraRollupRecursiveFlavor::FF; - InnerBuilder circuit_builder = generate_avm_circuit(); - InnerComposer composer = InnerComposer(); - InnerProver prover = composer.create_prover(circuit_builder); - InnerVerifier verifier = composer.create_verifier(circuit_builder); + std::shared_ptr::VerificationKey> avm_key = + std::make_shared::VerificationKey>( + &outer_builder, inner_verifier.key); - HonkProof proof = prover.construct_proof(); + auto key_fields_native = inner_verifier.key->to_field_elements(); + std::vector outer_key_fields; + for (const auto& f : key_fields_native) { + UltraFF val = UltraFF::from_witness(&outer_builder, f); + outer_key_fields.push_back(val); + } + avm::AvmGoblinRecursiveVerifier verifier(&outer_builder, outer_key_fields); + using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; + + stdlib::recursion::aggregation_state agg_obj = + stdlib::recursion::init_default_aggregation_state( + outer_builder); - // We just pad all the public inputs with the right number of zeroes std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); @@ -138,206 +144,40 @@ TEST_F(AvmRecursiveTests, recursion) std::vector calldata{ {} }; std::vector returndata{ {} }; - std::vector> public_inputs{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs - }; std::vector> public_inputs_vec{ kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata }; - bool verified = verifier.verify_proof(proof, public_inputs_vec); - ASSERT_TRUE(verified) << "native proof verification failed"; - - // Create the outer verifier, to verify the proof - const std::shared_ptr verification_key = verifier.key; - - // WTF is happening here? - - // 1. we make a goblin prover - // 2. we construct the "outer" circuit as a MegaCircuit - // previously the outer circuit was bb::avm::AvmRecursiveVerifier_::CircuitBuilder - // NOTE: why different? - // RecursiveFlavor = AvmRecursiveFlavor_ so...RecursiveFlavor::CircuitBuilder is Mega? - // 3. We create a bb::avm::AvmRecursiveVerifier_ to verify the outer circuit - // 4. `agg_output` is the result of `recurisve_verifier.verify_proof` - // 5. We apply `goblin.merge` on the outer_circuit - // 6. We generate a goblin proof of the merge circuit - // 7. We construct a GoblinRecursiveVerifier out of an UltraCircuitBuilder and a GoblinVerifier::VerifierInput - // GoblinVerifier::VerifierInput is generated from the eccvm proving key and the translator proving key - // OK so something weird going on is that when we recursively verify the goblin proof, we don't use the output? - // output needs to be spat up to next recursive layer (tube?) - // 8. The UltraCircuitBuilder that we run the goblin verifier on...we generate a proving/verification key for it - // and generate + verify a proof. Note: this is a native verifier - // 9. We create a MegaDeciderProvingKey out of the `outer_circuit` (the original circuit that verifies the AVM) - // 10. We generate a proof of outer_circuit - - // OK What are we expecting out of this? - - // Tier 1: Original AVM proof - // Tier 2: A Goblin-ified Mega circuit that verifies the AVM Proof - we get a proof of this (PI0) - // Tier 3: An ECCVM + Translator proof of the transcript in PI0 (PI1, PI2) - // Tier 4: An Ultra circuit that verifies PI0, PI1, PI2 - - // What do we currently do? - - // `outer_circuit` runs folding verifier on AVM proof `recursive_verifier.verify_proof` - // This spits out a transcript - // We then call `goblin.merge` on `outer_circuit`. Can we do this? If merge is no longer represented as commitments - // thne we can - - // OuterCircuit: [verifies AVM Pi0], [produces transcript via goblin.merge] - // Builder verifies goblin plonk proofs generated from OuterCircuit - // Builder needs to also verify OuterCircuit - // `outer_circuit` runs - GoblinProver goblin; - MegaCircuitBuilder outer_circuit(goblin.op_queue); - RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; - - auto agg_object = - stdlib::recursion::init_default_aggregation_state( - outer_circuit); - - auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - - bool agg_output_valid = - verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); - - ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; - ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; - - bool outer_circuit_checked = CircuitChecker::check(outer_circuit); - ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; - - std::cout << "A" << std::endl; - auto manifest = verifier.transcript->get_manifest(); - auto recursive_manifest = recursive_verifier.transcript->get_manifest(); - std::cout << "B" << std::endl; - - EXPECT_EQ(manifest.size(), recursive_manifest.size()); - for (size_t i = 0; i < recursive_manifest.size(); ++i) { - EXPECT_EQ(recursive_manifest[i], manifest[i]); - } - - for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { - EXPECT_EQ(key_el, rec_key_el.get_value()); - } - - EXPECT_EQ(verifier.key->circuit_size, recursive_verifier.key->circuit_size); - EXPECT_EQ(verifier.key->num_public_inputs, recursive_verifier.key->num_public_inputs); - - // in goblin test... - // there are several mega circuits being generated - // goblin proof is then constructed from the mega circuits - - // what is a goblin proof? - // eccvm proof, translator proof and a "merge" proof - // what is the merge proof? + StdlibProof stdlib_proof = bb::convert_native_proof_to_stdlib(&outer_builder, proof); - // `goblin.verify_merge` is called using a client builder input - // input is a mega circuit - // eww - // The next step is... - // An UltraBuilder is created - // The UltraBuilder is used to create a circuit that verifies the goblin proof - std::cout << "C" << std::endl; - goblin.merge(outer_circuit); + std::vector> public_inputs_ct; + public_inputs_ct.reserve(public_inputs_vec.size()); - GoblinProof g_proof = goblin.prove(); - std::cout << "D" << std::endl; - - // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their - // respective proving keys) - auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - std::cout << "E" << std::endl; - - UltraCircuitBuilder builder; - GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), - std::make_shared(goblin.get_translator_proving_key()) }; - bb::stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ &builder, goblin_vinput }; - std::cout << "F" << std::endl; - - // next step fails likely because of a lack of a merge proof - - auto goblin_verifier_output = gverifier.verify(g_proof); - - // Validate natively that `goblin_verifier_output` produces valid IPA claim - { - auto crs_factory = std::make_shared>("../srs_db/grumpkin", - 1 << CONST_ECCVM_LOG_N); - auto grumpkin_verifier_commitment_key = - std::make_shared>(1 << CONST_ECCVM_LOG_N, crs_factory); - OpeningClaim native_claim = goblin_verifier_output.opening_claim.get_native_opening_claim(); - auto native_ipa_transcript = std::make_shared( - convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data)); - - EXPECT_TRUE( - IPA::reduce_verify(grumpkin_verifier_commitment_key, native_claim, native_ipa_transcript)); + for (const auto& vec : public_inputs_vec) { + std::vector vec_ct; + vec_ct.reserve(vec.size()); + for (const auto& el : vec) { + vec_ct.push_back(bb::stdlib::witness_t(&outer_builder, el)); + } + public_inputs_ct.push_back(vec_ct); } - std::cout << "G" << std::endl; - - EXPECT_EQ(builder.failed(), false) << builder.err(); - EXPECT_TRUE(CircuitChecker::check(builder)); - // Construct and verify a proof for the Goblin Recursive Verifier circuit - { - auto proving_key = std::make_shared(builder); - std::cout << "H" << std::endl; - OuterProver prover(proving_key); - std::cout << "I" << std::endl; - auto verification_key = std::make_shared(proving_key->proving_key); - std::cout << "J" << std::endl; - OuterVerifier verifier(verification_key); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - - ASSERT(verified); - } - - // const size_t srs_size = 1 << 23; - auto ultra_instance = std::make_shared(outer_circuit); - MegaProver ultra_prover(ultra_instance); - auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - MegaVerifier ultra_verifier(ultra_verification_key); - - vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); - - auto recursion_proof = ultra_prover.construct_proof(); - bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); - EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; - - // Make a proof of the verification of an AVM proof - // const size_t srs_size = 1 << 23; - // auto ultra_instance = std::make_shared( - // outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - // auto ultra_instance = std::make_shared(outer_circuit); - - // OuterProver ultra_prover(ultra_instance); - // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - // OuterVerifier ultra_verifier(ultra_verification_key); - - // // // auto proving_key = std::make_shared(builder); - // // OuterProver outer_prover(ultra_instance); + auto proof_outputs = verifier.verify_proof(stdlib_proof, public_inputs_ct, agg_obj); - // // goblin.merge(outer_circit); - // // auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - // // GoblinVerifier ultra_verifier(ultra_verification_key); + auto outer_proving_key = std::make_shared>(outer_builder); + using UltraRollupProver = UltraProver_; - // vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + UltraRollupProver outer_prover(outer_proving_key); - // auto recursion_proof = ultra_prover.construct_proof(); - // bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); - // EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + auto outer_proof = outer_prover.construct_proof(); + auto outer_verification_key = + std::make_shared(outer_proving_key->proving_key); + auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); + UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); - // GoblinProof g_proof = goblin.prove(); + EXPECT_TRUE(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof)); - // // Verify the goblin proof (eccvm, translator, merge); (Construct ECCVM/Translator verification keys from their - // // respective proving keys) - // auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - // auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - // GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; - // bool g_verified = goblin_verifier.verify(g_proof); - // EXPECT_TRUE(g_verified); + // AvmGoblinRecursiveVerifier::test_recursive_avm(proof, verifier); } } // namespace tests_avm \ No newline at end of file From 3951df88840d0e9b94a14489479a01cdc6074820 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 20 Mar 2025 00:05:26 +0000 Subject: [PATCH 19/43] formatting --- .../acir_format/avm_recursion_constraint.cpp | 1 - .../protogalaxy/protogalaxy_prover_impl.hpp | 1 + .../ultra_recursive_verifier.cpp | 9 --- .../vm/avm/tests/recursive_verifier.test.cpp | 79 +++++++++++++++++++ 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 103433dde081..07725d6620d3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -9,7 +9,6 @@ #include "barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp" #include "barretenberg/vm/avm/recursion/recursive_flavor.hpp" #include "barretenberg/vm/avm/recursion/recursive_verifier.hpp" - #include "barretenberg/vm/avm/trace/common.hpp" #include "barretenberg/vm/avm/trace/helper.hpp" #include "barretenberg/vm/aztec_constants.hpp" diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index 06462e181f25..8416f6fa2c4d 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -144,6 +144,7 @@ FoldingResult ProtogalaxyProver_proving_key.log_circuit_size, keys[1]->proving_key.log_circuit_size); } + // NOTE: this is an optimization that is not needed for the goblin plonk work. cuts out a lot of field muls though! ASSERT(DeciderProvingKeys::NUM == 2); // this mechanism is not supported for the folding of multiple keys auto accumulator_polys = result.accumulator->proving_key.polynomials.get_unshifted(); auto key_polys = keys[1]->proving_key.polynomials.get_unshifted(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index dbba8d11a4e3..c8170f4373c3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -67,14 +67,8 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ for (size_t j = 0; j < 2; j++) { std::array bigfield_limbs; for (size_t k = 0; k < 4; k++) { - std::cout << "k = " << k << std::endl; - std::cout << "ultra recursive verifier public input indices [" << idx - << "] = " << key->pairing_point_accumulator_public_input_indices[idx] << std::endl; - std::cout << "x" << std::endl; - std::cout << "vkey pub inputs size = " << verification_key->public_inputs.size() << std::endl; bigfield_limbs[k] = verification_key->public_inputs[key->pairing_point_accumulator_public_input_indices[idx]]; - std::cout << "y" << std::endl; idx++; } base_field_vals[j] = Curve::BaseField::construct_from_limbs( @@ -112,7 +106,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ if constexpr (Flavor::HasZK) { libra_evaluations = std::move(sumcheck_output.claimed_libra_evaluations); } - std::cout << "XC" << std::endl; // Execute Shplemini to produce a batch opening claim subsequently verified by a univariate PCS const BatchOpeningClaim opening_claim = @@ -136,7 +129,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ agg_obj.aggregate(pairing_points, recursion_separator); Output output; output.agg_obj = std::move(agg_obj); - std::cout << "XD" << std::endl; // Extract the IPA claim from the public inputs // Parse out the nested IPA claim using key->ipa_claim_public_input_indices and runs the native IPA verifier. @@ -173,7 +165,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ output.ipa_opening_claim = std::move(ipa_claim); } } - std::cout << "XE" << std::endl; return output; } diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index c9726461f207..f4c633c8590d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -180,4 +180,83 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) // AvmGoblinRecursiveVerifier::test_recursive_avm(proof, verifier); } +TEST_F(AvmRecursiveTests, recursion) +{ + if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + GTEST_SKIP(); + } + + InnerBuilder circuit_builder = generate_avm_circuit(); + InnerComposer composer = InnerComposer(); + InnerProver prover = composer.create_prover(circuit_builder); + InnerVerifier verifier = composer.create_verifier(circuit_builder); + + HonkProof proof = prover.construct_proof(); + + // We just pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + + std::vector> public_inputs{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs + }; + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata + }; + + bool verified = verifier.verify_proof(proof, public_inputs_vec); + ASSERT_TRUE(verified) << "native proof verification failed"; + + // Create the outer verifier, to verify the proof + const std::shared_ptr verification_key = verifier.key; + OuterBuilder outer_circuit; + AvmRecursiveFlavor_::RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + + auto agg_object = + stdlib::recursion::init_default_aggregation_state(outer_circuit); + + auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + + bool agg_output_valid = + verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); + + ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; + ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; + + bool outer_circuit_checked = CircuitChecker::check(outer_circuit); + ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; + + auto manifest = verifier.transcript->get_manifest(); + auto recursive_manifest = recursive_verifier.transcript->get_manifest(); + + EXPECT_EQ(manifest.size(), recursive_manifest.size()); + for (size_t i = 0; i < recursive_manifest.size(); ++i) { + EXPECT_EQ(recursive_manifest[i], manifest[i]); + } + + for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { + EXPECT_EQ(key_el, rec_key_el.get_value()); + } + + EXPECT_EQ(verifier.key->circuit_size, recursive_verifier.key->circuit_size); + EXPECT_EQ(verifier.key->num_public_inputs, recursive_verifier.key->num_public_inputs); + + // Make a proof of the verification of an AVM proof + const size_t srs_size = 1 << 23; + auto ultra_instance = std::make_shared( + outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + OuterProver ultra_prover(ultra_instance); + auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + OuterVerifier ultra_verifier(ultra_verification_key); + + vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + + auto recursion_proof = ultra_prover.construct_proof(); + bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); + EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; +} } // namespace tests_avm \ No newline at end of file From eecc6332379d7248cd107e1eb5fbaa4c9cc10423 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 20 Mar 2025 18:16:34 +0000 Subject: [PATCH 20/43] separate out PG opt to unique PR --- .../protogalaxy/protogalaxy_prover_impl.hpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index 8416f6fa2c4d..6fbd2e47dc0a 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -144,17 +144,14 @@ FoldingResult ProtogalaxyProver_proving_key.log_circuit_size, keys[1]->proving_key.log_circuit_size); } - // NOTE: this is an optimization that is not needed for the goblin plonk work. cuts out a lot of field muls though! - ASSERT(DeciderProvingKeys::NUM == 2); // this mechanism is not supported for the folding of multiple keys - auto accumulator_polys = result.accumulator->proving_key.polynomials.get_unshifted(); - auto key_polys = keys[1]->proving_key.polynomials.get_unshifted(); - for (size_t i = 0; i < accumulator_polys.size(); ++i) { - PolynomialSpan acc = static_cast>(accumulator_polys[i]); - PolynomialSpan key = static_cast>(key_polys[i]); - const size_t size = std::min(acc.size() - acc.start_index, key.size() - key.start_index); - for (size_t j = 0; j < size; ++j) { - acc[j + acc.start_index] *= lagranges[0]; - acc[j + acc.start_index] += key[j + key.start_index] * lagranges[1]; + // Fold the proving key polynomials + for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) { + poly *= lagranges[0]; + } + for (size_t key_idx = 1; key_idx < DeciderProvingKeys::NUM; key_idx++) { + for (auto [acc_poly, key_poly] : zip_view(result.accumulator->proving_key.polynomials.get_unshifted(), + keys[key_idx]->proving_key.polynomials.get_unshifted())) { + acc_poly.add_scaled(key_poly, lagranges[key_idx]); } } From 928c8bce37192ea545f2594aa99e9a5c6af52f3a Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 20 Mar 2025 20:54:30 +0000 Subject: [PATCH 21/43] reinstate original avm recursion test --- .../vm/avm/tests/recursive_verifier.test.cpp | 175 +++++++++--------- 1 file changed, 92 insertions(+), 83 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index c738e34639cf..abadaffc0164 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -184,87 +184,96 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) // WORKTODO: commented out in merge, need to reinstate. Seems like just a problem of OuterBuilder now being set to Mega // and that not being properly reflected in this test, not sure this would ahve ever built.. -// TEST_F(AvmRecursiveTests, -// recursion) -// { -// if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { -// GTEST_SKIP(); -// } - -// InnerBuilder circuit_builder = generate_avm_circuit(); -// InnerComposer composer = InnerComposer(); -// InnerProver prover = composer.create_prover(circuit_builder); -// InnerVerifier verifier = composer.create_verifier(circuit_builder); - -// HonkProof proof = prover.construct_proof(); - -// // We just pad all the public inputs with the right number of zeroes -// std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); -// std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); -// std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); -// std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); -// std::vector calldata{ {} }; -// std::vector returndata{ {} }; - -// std::vector> public_inputs{ -// kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs -// }; -// std::vector> public_inputs_vec{ -// kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, -// returndata -// }; - -// bool verified = verifier.verify_proof(proof, public_inputs_vec); -// ASSERT_TRUE(verified) << "native proof verification failed"; - -// // Create the outer verifier, to verify the proof -// const std::shared_ptr verification_key = verifier.key; -// OuterBuilder outer_circuit; -// RecursiveVerifierUltra recursive_verifier{ &outer_circuit, verification_key }; - -// auto agg_object = -// stdlib::recursion::init_default_aggregation_state(outer_circuit); - -// auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); - -// bool agg_output_valid = -// verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); - -// ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; -// ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; - -// bool outer_circuit_checked = CircuitChecker::check(outer_circuit); -// ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; - -// auto manifest = verifier.transcript->get_manifest(); -// auto recursive_manifest = recursive_verifier.transcript->get_manifest(); - -// EXPECT_EQ(manifest.size(), recursive_manifest.size()); -// for (size_t i = 0; i < recursive_manifest.size(); ++i) { -// EXPECT_EQ(recursive_manifest[i], manifest[i]); -// } - -// for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { -// EXPECT_EQ(key_el, rec_key_el.get_value()); -// } - -// EXPECT_EQ(verifier.key->circuit_size, static_cast(recursive_verifier.key->circuit_size.get_value())); -// EXPECT_EQ(verifier.key->num_public_inputs, -// static_cast(recursive_verifier.key->num_public_inputs.get_value())); - -// // Make a proof of the verification of an AVM proof -// const size_t srs_size = 1 << 23; -// auto ultra_instance = std::make_shared( -// outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); -// OuterProver ultra_prover(ultra_instance); -// auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); -// OuterVerifier ultra_verifier(ultra_verification_key); - -// vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); - -// auto recursion_proof = ultra_prover.construct_proof(); -// bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); -// EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; -// } +TEST_F(AvmRecursiveTests, recursion) +{ + // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + // GTEST_SKIP(); + // } + + using RecursiveFlavor = AvmRecursiveFlavor_; + + using InnerFlavor = typename RecursiveFlavor::NativeFlavor; + using InnerFF = InnerFlavor::FF; + + using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + + using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; + using OuterProver = UltraProver; + using OuterVerifier = UltraVerifier; + using OuterDeciderProvingKey = DeciderProvingKey_; + + InnerBuilder circuit_builder = generate_avm_circuit(); + InnerComposer composer = InnerComposer(); + InnerProver prover = composer.create_prover(circuit_builder); + InnerVerifier verifier = composer.create_verifier(circuit_builder); + + HonkProof proof = prover.construct_proof(); + + // We just pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + + std::vector> public_inputs{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs + }; + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata + }; + + bool verified = verifier.verify_proof(proof, public_inputs_vec); + ASSERT_TRUE(verified) << "native proof verification failed"; + + // Create the outer verifier, to verify the proof + const std::shared_ptr verification_key = verifier.key; + OuterBuilder outer_circuit; + RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + + auto agg_object = + stdlib::recursion::init_default_aggregation_state(outer_circuit); + + auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + + bool agg_output_valid = + verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); + + ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; + ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; + + bool outer_circuit_checked = CircuitChecker::check(outer_circuit); + ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; + + auto manifest = verifier.transcript->get_manifest(); + auto recursive_manifest = recursive_verifier.transcript->get_manifest(); + + EXPECT_EQ(manifest.size(), recursive_manifest.size()); + for (size_t i = 0; i < recursive_manifest.size(); ++i) { + EXPECT_EQ(recursive_manifest[i], manifest[i]); + } + + for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { + EXPECT_EQ(key_el, rec_key_el.get_value()); + } + + EXPECT_EQ(verifier.key->circuit_size, static_cast(recursive_verifier.key->circuit_size.get_value())); + EXPECT_EQ(verifier.key->num_public_inputs, + static_cast(recursive_verifier.key->num_public_inputs.get_value())); + + // Make a proof of the verification of an AVM proof + const size_t srs_size = 1 << 23; + auto ultra_instance = std::make_shared( + outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); + OuterProver ultra_prover(ultra_instance); + auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + OuterVerifier ultra_verifier(ultra_verification_key); + + vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); + + auto recursion_proof = ultra_prover.construct_proof(); + bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); + EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; +} } // namespace tests_avm From d4386eb0706e947a5cf92493a909109c94c2c58e Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 20 Mar 2025 23:26:17 +0000 Subject: [PATCH 22/43] touch of cleanup in avm rec constraint --- .../acir_format/avm_recursion_constraint.cpp | 59 +++---------------- .../vm/avm/recursion/recursive_flavor.hpp | 4 +- 2 files changed, 11 insertions(+), 52 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 07725d6620d3..55f1e752856a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -162,24 +162,17 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints( ASSERT(input.proof_type == AVM); - // Construct an in-circuit representation of the verification key. - std::vector key_fields; - key_fields.reserve(input.key.size()); - for (const auto& idx : input.key) { - auto field = field_ct::from_witness_index(&builder, idx); - key_fields.emplace_back(field); - } - auto fields_from_witnesses = [&](std::vector const& input) { std::vector result; result.reserve(input.size()); for (const auto& idx : input) { - auto field = field_ct::from_witness_index(&builder, idx); - result.emplace_back(field); + result.emplace_back(field_ct::from_witness_index(&builder, idx)); } return result; }; + // Construct in-circuit representation of the verification key, proof and public inputs + const auto key_fields = fields_from_witnesses(input.key); const auto proof_fields = fields_from_witnesses(input.proof); const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); @@ -211,39 +204,32 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints( } // NEW VERSION THAT USES GOBLIN PLONK IN VERIFICATION ALGO -HonkRecursionConstraintOutput create_honk_recursion_constraints( +HonkRecursionConstraintOutput create_avm_recursion_constraints2( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object_indices, bool has_valid_witness_assignments) { - // using Flavor = AvmRecursiveFlavor_; - // using RecursiveVerificationKey = Flavor::VerificationKey; using RecursiveVerifier = bb::avm::AvmGoblinRecursiveVerifier; ASSERT(input.proof_type == AVM); - // Construct an in-circuit representation of the verification key. - std::vector key_fields; - key_fields.reserve(input.key.size()); - for (const auto& idx : input.key) { - auto field = field_ct::from_witness_index(&builder, idx); - key_fields.emplace_back(field); - } - auto fields_from_witnesses = [&](std::vector const& input) { std::vector result; result.reserve(input.size()); for (const auto& idx : input) { - auto field = field_ct::from_witness_index(&builder, idx); - result.emplace_back(field); + result.emplace_back(field_ct::from_witness_index(&builder, idx)); } return result; }; + // Construct in-circuit representation of the verification key, proof and public inputs + const auto key_fields = fields_from_witnesses(input.key); const auto proof_fields = fields_from_witnesses(input.proof); const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); + // WORKTODO: unacceptable level of 'auto' usage in this method + auto it = public_inputs_flattened.begin(); VmPublicInputs vm_public_inputs = avm_trace::convert_public_inputs(std::vector(it, it + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH)); @@ -260,7 +246,6 @@ HonkRecursionConstraintOutput create_honk_recursion_constraints( } // Recursively verify the proof - // auto vkey = std::make_shared(builder, key_fields); RecursiveVerifier verifier(&builder, key_fields); aggregation_state_ct input_agg_obj = bb::stdlib::recursion::convert_witness_indices_to_agg_obj( builder, input_aggregation_object_indices); @@ -268,32 +253,6 @@ HonkRecursionConstraintOutput create_honk_recursion_constraints( // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public // inputs is important, like what the plonk recursion constraint does. - /* - HonkRecursionConstraintOutput output; - if (is_rollup_honk_recursion_constraint) { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1168): Add formula to flavor - const size_t HONK_PROOF_LENGTH = 469; - // The extra calculation is for the IPA proof length. - ASSERT(input.proof.size() == HONK_PROOF_LENGTH + 1 + 4 * (CONST_ECCVM_LOG_N) + 2 + 2); - ASSERT(proof_fields.size() == HONK_PROOF_LENGTH + 65 + input.public_inputs.size()); - // split out the ipa proof - const std::ptrdiff_t honk_proof_with_pub_inputs_length = - static_cast(HONK_PROOF_LENGTH + input.public_inputs.size()); - output.ipa_proof = - StdlibProof(honk_proof.begin() + honk_proof_with_pub_inputs_length, honk_proof.end()); - honk_proof = StdlibProof(honk_proof.begin(), honk_proof.end() + honk_proof_with_pub_inputs_length); - } - UltraRecursiveVerifierOutput verifier_output = verifier.verify_proof(honk_proof, input_agg_obj); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs - // is important, like what the plonk recursion constraint does. - - output.agg_obj_indices = verifier_output.agg_obj.get_witness_indices(); - if (is_rollup_honk_recursion_constraint) { - ASSERT(HasIPAAccumulator); - output.ipa_claim = verifier_output.ipa_opening_claim; - } - - */ HonkRecursionConstraintOutput result{ .agg_obj_indices = output_agg_object.aggregation_object.get_witness_indices(), .ipa_claim = output_agg_object.ipa_claim, .ipa_proof = output_agg_object.ipa_proof }; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp index 5e641e0900fd..5f4728ad7250 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp @@ -75,7 +75,7 @@ template class AvmRecursiveFlavor_ { * @param builder * @param elements */ - VerificationKey(CircuitBuilder& builder, std::span elements) + VerificationKey(CircuitBuilder& builder, std::span elements) { size_t num_frs_read = 0; size_t num_frs_FF = bb::stdlib::field_conversion::calc_num_bn254_frs(); @@ -103,4 +103,4 @@ template class AvmRecursiveFlavor_ { using Transcript = bb::BaseTranscript>; }; -} // namespace bb \ No newline at end of file +} // namespace bb From 3d0f5041b79df87e3bbb689315d1528f68e45be6 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 20 Mar 2025 23:35:13 +0000 Subject: [PATCH 23/43] cmake cleanup --- barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt | 4 ++-- .../vm/avm/tests/recursive_verifier.test.cpp | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt index 9d311809be64..897e57e70ade 100644 --- a/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/vm/CMakeLists.txt @@ -1,3 +1,3 @@ if(NOT DISABLE_AZTEC_VM) - barretenberg_module(vm sumcheck stdlib_circuit_builders stdlib_honk_verifier stdlib_eccvm_verifier stdlib_translator_vm_verifier stdlib_honk_verifier goblin stdlib_goblin_verifier) -endif() \ No newline at end of file + barretenberg_module(vm sumcheck stdlib_honk_verifier stdlib_goblin_verifier) +endif() diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index abadaffc0164..2f9dffd9bf3c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -181,14 +181,11 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) // AvmGoblinRecursiveVerifier::test_recursive_avm(proof, verifier); } -// WORKTODO: commented out in merge, need to reinstate. Seems like just a problem of OuterBuilder now being set to Mega -// and that not being properly reflected in this test, not sure this would ahve ever built.. - TEST_F(AvmRecursiveTests, recursion) { - // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - // GTEST_SKIP(); - // } + if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + GTEST_SKIP(); + } using RecursiveFlavor = AvmRecursiveFlavor_; From ccb5630aae144a7db5104156a64271535f1eca3b Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 21 Mar 2025 21:11:13 +0000 Subject: [PATCH 24/43] update gen file and remove tmeplate on aggregate method --- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 4 ++-- .../aggregation_state/aggregation_state.hpp | 11 +++++++---- .../vm/avm/generated/recursive_verifier.cpp | 2 +- .../templates/recursive_verifier.cpp.hbs | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index ebe23d687c15..ad01d5093e95 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -100,7 +100,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.template aggregate(nested_agg_obj, recursion_separator); + agg_obj.aggregate(nested_agg_obj, recursion_separator); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u @@ -144,7 +144,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ pairing_points[0] = pairing_points[0].normalize(); pairing_points[1] = pairing_points[1].normalize(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge properly. - agg_obj.template aggregate(pairing_points, recursion_separator); + agg_obj.aggregate(pairing_points, recursion_separator); output.agg_obj = std::move(agg_obj); // Extract the IPA claim from the public inputs diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index af1b30725b7a..b4eea6854385 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -20,10 +20,12 @@ template struct aggregation_state { { return P0 == other.P0 && P1 == other.P1; }; - template + void aggregate(aggregation_state const& other, typename Curve::ScalarField recursion_separator) { - if constexpr (std::is_same_v) { + using Builder = typename Curve::Builder; + + if constexpr (std::is_same_v) { P0 += other.P0 * recursion_separator; P1 += other.P1 * recursion_separator; } else { @@ -36,10 +38,11 @@ template struct aggregation_state { } } - template void aggregate(std::array const& other, typename Curve::ScalarField recursion_separator) { - if constexpr (std::is_same_v) { + using Builder = typename Curve::Builder; + + if constexpr (std::is_same_v) { P0 += other[0] * recursion_separator; P1 += other[1] * recursion_separator; } else { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp index 6d4b9118d482..d464b1ca641c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp @@ -165,7 +165,7 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.template aggregate(pairing_points, recursion_separator); + agg_obj.aggregate(pairing_points, recursion_separator); return agg_obj; } diff --git a/bb-pilcom/bb-pil-backend/templates/recursive_verifier.cpp.hbs b/bb-pilcom/bb-pil-backend/templates/recursive_verifier.cpp.hbs index dd5f3f112f26..b00c0b8d9d21 100644 --- a/bb-pilcom/bb-pil-backend/templates/recursive_verifier.cpp.hbs +++ b/bb-pilcom/bb-pil-backend/templates/recursive_verifier.cpp.hbs @@ -153,5 +153,6 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: } template class AvmRecursiveVerifier_>; +template class AvmRecursiveVerifier_>; -} // namespace bb::{{snakeCase name}} \ No newline at end of file +} // namespace bb::{{snakeCase name}} From cbee9118a044e35a6b44e6d5af2f2528d27cae89 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 17:55:37 +0000 Subject: [PATCH 25/43] cleanup via lambda --- .../goblin_avm_recursive_verifier.hpp | 67 +++++++++---------- .../vm/avm/tests/recursive_verifier.test.cpp | 1 - 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index c1796dbdeee7..442241507c49 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -59,10 +59,9 @@ class AvmGoblinRecursiveVerifier { , builder(builder) {} - RecursiveAvmGoblinOutput verify_proof( - const StdlibProof& stdlib_proof, - const std::vector>& public_inputs, - AggregationObject agg_obj) const + RecursiveAvmGoblinOutput verify_proof(const StdlibProof& stdlib_proof, + const std::vector>& public_inputs, + AggregationObject agg_obj) const { using AvmRecursiveFlavor = AvmRecursiveFlavor_; @@ -116,48 +115,44 @@ class AvmGoblinRecursiveVerifier { */ // STEP 1: - // Take the UltraBuilder proof inputs and convert into MegaBuilder proof inputs + // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts // (later on we must validate that the AVM proof fed into the MegaCircuit matches the one fed into the upstream // UltraCircuit) - StdlibProof mega_stdlib_proof; - std::vector> mega_public_inputs; + GoblinProver goblin; MegaCircuitBuilder inner_builder(goblin.op_queue); - std::vector input_hash; - std::vector upstream_hash; - for (auto& element : stdlib_proof) { - FF val = FF::from_witness(&inner_builder, element.get_value()); - mega_stdlib_proof.emplace_back(val); - input_hash.emplace_back(val); - upstream_hash.emplace_back(element); - } - for (auto& input_vec : public_inputs) { - std::vector inner_vec; - for (auto& input : input_vec) { - FF val = FF::from_witness(&inner_builder, input.get_value()); - inner_vec.emplace_back(val); - input_hash.emplace_back(val); - upstream_hash.emplace_back(input); + // Buffers to be hashed containing the elements of the Mega and Ultra proof, public inputs, and VK + std::vector mega_hash_buffer; + std::vector ultra_hash_buffer; + + // lambda to convert from Ultra to Mega stdlib field buffer and add all elements to respective hash buffers + auto convert_stdlib_ultra_to_stdlib_mega = [&](const std::vector& ultra_object) { + std::vector mega_object; + for (const UltraFF& ultra_element : ultra_object) { + FF mega_element = FF::from_witness(&inner_builder, ultra_element.get_value()); + mega_object.emplace_back(mega_element); + mega_hash_buffer.emplace_back(mega_element); + ultra_hash_buffer.emplace_back(ultra_element); } - mega_public_inputs.emplace_back(inner_vec); + return mega_object; + }; + + // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts + StdlibProof mega_stdlib_proof = convert_stdlib_ultra_to_stdlib_mega(stdlib_proof); + std::vector> mega_public_inputs; + mega_public_inputs.reserve(public_inputs.size()); + for (const std::vector& input_vec : public_inputs) { + mega_public_inputs.emplace_back(convert_stdlib_ultra_to_stdlib_mega(input_vec)); } + std::vector key_fields = convert_stdlib_ultra_to_stdlib_mega(outer_key_fields); - // Step 1.5 Convert the UltraRollupCircuit representation of the AVM verification key into a MegaCircuit - // representation - std::vector key_fields; - for (const auto& f : outer_key_fields) { - FF val = FF::from_witness(&inner_builder, f.get_value()); - key_fields.emplace_back(val); - input_hash.emplace_back(val); - upstream_hash.emplace_back(f); - } - auto stdlib_key = std::make_shared::VerificationKey>( - inner_builder, std::span(key_fields)); + auto stdlib_key = + std::make_shared(inner_builder, std::span(key_fields)); // we use the hash of the proof + public inputs to validate that we're correctly transferring data between the // Mega and Ultra circuits - auto mega_input_hash = stdlib::poseidon2::hash(inner_builder, input_hash); + auto mega_input_hash = stdlib::poseidon2::hash(inner_builder, mega_hash_buffer); // NOTE: there doesn't seem to be an easy way to know *which* public input index will map to mega_input_hash // which is troublesome mega_input_hash.set_public(); @@ -228,7 +223,7 @@ class AvmGoblinRecursiveVerifier { mega_input_hash_public_input_index += 1; } auto ultra_output_hash = - stdlib::poseidon2::hash(*builder, upstream_hash); + stdlib::poseidon2::hash(*builder, ultra_hash_buffer); stdlib_recursion_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); // Step 10: gather up the ipa proof, ipa claim and output aggregation object produced from verifying the mega diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 2f9dffd9bf3c..803aec9e16c9 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -108,7 +108,6 @@ class AvmRecursiveTests : public ::testing::Test { TEST_F(AvmRecursiveTests, GoblinRecursion) { - InnerBuilder circuit_builder = generate_avm_circuit(); InnerComposer composer = InnerComposer(); InnerProver prover = composer.create_prover(circuit_builder); From 8b8285429b61e90d5a33d5b253999983db0c4bca Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 21:11:13 +0000 Subject: [PATCH 26/43] use new avm rec constraint method in test --- .../dsl/acir_format/acir_format.cpp | 5 ++++- .../acir_format/avm_recursion_constraint.cpp | 4 ++-- .../acir_format/avm_recursion_constraint.hpp | 20 ++++++++++--------- .../avm_recursion_constraint.test.cpp | 6 +++++- .../aggregation_state/aggregation_state.hpp | 2 ++ .../primitives/biggroup/biggroup_goblin.hpp | 5 +++++ .../goblin_avm_recursive_verifier.hpp | 20 +++++++++---------- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index d12cab07fbc9..26ac4c1e7772 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -520,9 +520,12 @@ PairingPointAccumulatorIndices process_avm_recursion_constraints( // Add recursion constraints size_t idx = 0; for (auto& constraint : constraint_system.avm_recursion_constraints) { - current_aggregation_object = create_avm_recursion_constraints( + HonkRecursionConstraintOutput output = create_avm_recursion_constraints_new( builder, constraint, current_aggregation_object, has_valid_witness_assignments); + // WORKTODO: Decide what to do here. Eventually we want to return the full output not just the pairing points. + current_aggregation_object = output.agg_obj_indices; + gate_counter.track_diff(constraint_system.gates_per_opcode, constraint_system.original_opcode_indices.avm_recursion_constraints.at(idx++)); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 55f1e752856a..885edfe938fc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -150,7 +150,7 @@ void create_dummy_vkey_and_proof(Builder& builder, * @param input_aggregation_object_indices. The aggregation object coming from previous Honk/Avm recursion constraints. * @param has_valid_witness_assignment. Do we have witnesses or are we just generating keys? */ -PairingPointAccumulatorIndices create_avm_recursion_constraints( +PairingPointAccumulatorIndices create_avm_recursion_constraints_legacy( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object_indices, @@ -204,7 +204,7 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints( } // NEW VERSION THAT USES GOBLIN PLONK IN VERIFICATION ALGO -HonkRecursionConstraintOutput create_avm_recursion_constraints2( +HonkRecursionConstraintOutput create_avm_recursion_constraints_new( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object_indices, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp index 5bae5709fd7d..5dbd66123627 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp @@ -8,15 +8,17 @@ using Builder = bb::UltraCircuitBuilder; using namespace bb; -PairingPointAccumulatorIndices create_avm_recursion_constraints(Builder& builder, - const RecursionConstraint& input, - PairingPointAccumulatorIndices input_aggregation_object, - bool has_valid_witness_assignments); +PairingPointAccumulatorIndices create_avm_recursion_constraints_legacy( + Builder& builder, + const RecursionConstraint& input, + PairingPointAccumulatorIndices input_aggregation_object, + bool has_valid_witness_assignments); -HonkRecursionConstraintOutput create_avm_recursion_constraints2(Builder& builder, - const RecursionConstraint& input, - PairingPointAccumulatorIndices input_aggregation_object, - bool has_valid_witness_assignments); +HonkRecursionConstraintOutput create_avm_recursion_constraints_new( + Builder& builder, + const RecursionConstraint& input, + PairingPointAccumulatorIndices input_aggregation_object, + bool has_valid_witness_assignments); } // namespace acir_format -#endif // DISABLE_AZTEC_VM \ No newline at end of file +#endif // DISABLE_AZTEC_VM diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp index 9718c72d3bce..379b7897f13f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp @@ -42,7 +42,11 @@ class AcirAvmRecursionConstraint : public ::testing::Test { using OuterVerificationKey = UltraFlavor::VerificationKey; using OuterBuilder = UltraCircuitBuilder; - static void SetUpTestSuite() { bb::srs::init_crs_factory(bb::srs::get_ignition_crs_path()); } + static void SetUpTestSuite() + { + bb::srs::init_crs_factory(bb::srs::get_ignition_crs_path()); + bb::srs::init_grumpkin_crs_factory(bb::srs::get_grumpkin_crs_path()); + } // mutate the input kernel_public_inputs_vec to add end gas values static InnerBuilder create_inner_circuit([[maybe_unused]] std::vector& kernel_public_inputs_vec) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index b4eea6854385..b26468591662 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -56,6 +56,7 @@ template struct aggregation_state { } PairingPointAccumulatorIndices get_witness_indices() + requires(!std::same_as) { PairingPointAccumulatorIndices witness_indices = { P0.x.binary_basis_limbs[0].element.normalize().witness_index, @@ -77,6 +78,7 @@ template struct aggregation_state { }; return witness_indices; } + void assign_object_to_proof_outputs() { P0 = P0.reduce(); 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 da4f10bcf933..48e298506f1d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp @@ -349,6 +349,11 @@ template class goblin_el bool_ct _is_infinity; }; +using BiggroupGoblin = goblin_element, + stdlib::field_t, + bb::g1>; + template inline std::ostream& operator<<(std::ostream& os, goblin_element const& v) { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 442241507c49..f209603d48a8 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -61,7 +61,7 @@ class AvmGoblinRecursiveVerifier { RecursiveAvmGoblinOutput verify_proof(const StdlibProof& stdlib_proof, const std::vector>& public_inputs, - AggregationObject agg_obj) const + AggregationObject input_agg_obj) const { using AvmRecursiveFlavor = AvmRecursiveFlavor_; @@ -160,11 +160,11 @@ class AvmGoblinRecursiveVerifier { // Step 2: Verify the AVM proof // NOTICE!!!! We don't currently propagate the aggregation object which we need to for this to be sound! RecursiveVerifier recursive_verifier{ &inner_builder, stdlib_key }; - auto mega_agg_object = + auto default_agg_object = stdlib::recursion::init_default_aggregation_state( inner_builder); - [[maybe_unused]] auto agg_output = - recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, mega_agg_object); + [[maybe_unused]] auto mega_agg_output = + recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, default_agg_object); // Step 3: run the goblin merge protocol // WORKTODO: this used to use goblin.merge() which I think added a merge rec verifier. Dont think this is needed @@ -191,11 +191,11 @@ class AvmGoblinRecursiveVerifier { stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ builder, goblin_vinput }; stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); - // NOTE: I think this part is wrong. What do we initialize the builder's agg object to be? - PairingPointAccumulatorIndices current_aggregation_object = - stdlib::recursion::init_default_agg_obj_indices(*builder); - // This is currently just setting the aggregation object to the default one. - builder->add_pairing_point_accumulator(current_aggregation_object); + // // NOTE: I think this part is wrong. What do we initialize the builder's agg object to be? + // PairingPointAccumulatorIndices current_aggregation_object = + // stdlib::recursion::init_default_agg_obj_indices(*builder); + // // This is currently just setting the aggregation object to the default one. + // builder->add_pairing_point_accumulator(current_aggregation_object); // We only calls the IPA recursive verifier once, so we can just add this IPA claim and proof builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); @@ -210,7 +210,7 @@ class AvmGoblinRecursiveVerifier { // Step 8: In our UltraCirfcuit, recursively verify the mega proof UltraRecursiveVerifier outer_verifier(builder, outer_vk); StdlibProof stdlib_recursion_proof = bb::convert_native_proof_to_stdlib(builder, recursion_proof); - auto outer_verifier_output = outer_verifier.verify_proof(stdlib_recursion_proof, agg_obj); + auto outer_verifier_output = outer_verifier.verify_proof(stdlib_recursion_proof, input_agg_obj); // Step 9: Validate that both `builder` and `inner_builder` use the same AVM proof data and AVM public inputs // Note: we don't seem to have a nice way of finding out where within a public input space a given value is that From af268a0283f3e93fd3429fcb0b5025bf7fe8a6f5 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 21:45:00 +0000 Subject: [PATCH 27/43] remove concept --- .../plonk_recursion/aggregation_state/aggregation_state.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index b26468591662..90d5b41ef2e4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -56,7 +56,6 @@ template struct aggregation_state { } PairingPointAccumulatorIndices get_witness_indices() - requires(!std::same_as) { PairingPointAccumulatorIndices witness_indices = { P0.x.binary_basis_limbs[0].element.normalize().witness_index, From 5eaabf02687a6fd785310d48f4d0e18dab179352 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 22:12:35 +0000 Subject: [PATCH 28/43] explicit buildr naming --- .../goblin_avm_recursive_verifier.hpp | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index f209603d48a8..0cc54cef6c3d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -43,7 +43,7 @@ class AvmGoblinRecursiveVerifier { using OuterAvmKey = AvmRecursiveFlavor_::VerificationKey; std::vector outer_key_fields; - Builder* builder; + Builder* ultra_builder; std::shared_ptr transcript; // Note: instead of passing in a native verification key we pass in a vector of field elements whose type equals @@ -56,7 +56,7 @@ class AvmGoblinRecursiveVerifier { // construct a MegaCircuit vkey context. However `to_fields` seems bugged when used in a recursive vkey setting. explicit AvmGoblinRecursiveVerifier(Builder* builder, const std::vector& outer_key_fields) : outer_key_fields(outer_key_fields) - , builder(builder) + , ultra_builder(builder) {} RecursiveAvmGoblinOutput verify_proof(const StdlibProof& stdlib_proof, @@ -120,7 +120,7 @@ class AvmGoblinRecursiveVerifier { // UltraCircuit) GoblinProver goblin; - MegaCircuitBuilder inner_builder(goblin.op_queue); + MegaCircuitBuilder mega_builder(goblin.op_queue); // Buffers to be hashed containing the elements of the Mega and Ultra proof, public inputs, and VK std::vector mega_hash_buffer; @@ -130,7 +130,7 @@ class AvmGoblinRecursiveVerifier { auto convert_stdlib_ultra_to_stdlib_mega = [&](const std::vector& ultra_object) { std::vector mega_object; for (const UltraFF& ultra_element : ultra_object) { - FF mega_element = FF::from_witness(&inner_builder, ultra_element.get_value()); + FF mega_element = FF::from_witness(&mega_builder, ultra_element.get_value()); mega_object.emplace_back(mega_element); mega_hash_buffer.emplace_back(mega_element); ultra_hash_buffer.emplace_back(ultra_element); @@ -148,33 +148,31 @@ class AvmGoblinRecursiveVerifier { std::vector key_fields = convert_stdlib_ultra_to_stdlib_mega(outer_key_fields); auto stdlib_key = - std::make_shared(inner_builder, std::span(key_fields)); + std::make_shared(mega_builder, std::span(key_fields)); // we use the hash of the proof + public inputs to validate that we're correctly transferring data between the // Mega and Ultra circuits - auto mega_input_hash = stdlib::poseidon2::hash(inner_builder, mega_hash_buffer); + auto mega_input_hash = stdlib::poseidon2::hash(mega_builder, mega_hash_buffer); // NOTE: there doesn't seem to be an easy way to know *which* public input index will map to mega_input_hash // which is troublesome mega_input_hash.set_public(); // Step 2: Verify the AVM proof // NOTICE!!!! We don't currently propagate the aggregation object which we need to for this to be sound! - RecursiveVerifier recursive_verifier{ &inner_builder, stdlib_key }; + RecursiveVerifier recursive_verifier{ &mega_builder, stdlib_key }; auto default_agg_object = stdlib::recursion::init_default_aggregation_state( - inner_builder); + mega_builder); [[maybe_unused]] auto mega_agg_output = recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, default_agg_object); // Step 3: run the goblin merge protocol - // WORKTODO: this used to use goblin.merge() which I think added a merge rec verifier. Dont think this is needed - // but need to take a look and confirm - goblin.prove_merge(inner_builder); - inner_builder.add_pairing_point_accumulator( - stdlib::recursion::init_default_agg_obj_indices(inner_builder)); + goblin.prove_merge(mega_builder); + mega_builder.add_pairing_point_accumulator( + stdlib::recursion::init_default_agg_obj_indices(mega_builder)); // Step 4: generate a proof of the above MegaCircuit - std::shared_ptr ultra_instance = std::make_shared(inner_builder); + std::shared_ptr ultra_instance = std::make_shared(mega_builder); MegaProver ultra_prover(ultra_instance); auto recursion_proof = ultra_prover.construct_proof(); @@ -188,7 +186,7 @@ class AvmGoblinRecursiveVerifier { goblin.get_translator_proving_key()) }; // Step 6: In our UltraCircuit, recursively verify the goblin proof - stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ builder, goblin_vinput }; + stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ ultra_builder, goblin_vinput }; stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); // // NOTE: I think this part is wrong. What do we initialize the builder's agg object to be? @@ -198,18 +196,20 @@ class AvmGoblinRecursiveVerifier { // builder->add_pairing_point_accumulator(current_aggregation_object); // We only calls the IPA recursive verifier once, so we can just add this IPA claim and proof - builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); - builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); - ASSERT(builder->ipa_proof.size() && "IPA proof should not be empty"); + ultra_builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); + ultra_builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); + ASSERT(ultra_builder->ipa_proof.size() && "IPA proof should not be empty"); // Step 7: Compute the verification key to recursively verify the MegaProof // NOTE: this part could be precomputed to save time auto native_outer_vk = std::make_shared(ultra_instance->proving_key); - auto outer_vk = std::make_shared(builder, native_outer_vk); + auto outer_vk = + std::make_shared(ultra_builder, native_outer_vk); // Step 8: In our UltraCirfcuit, recursively verify the mega proof - UltraRecursiveVerifier outer_verifier(builder, outer_vk); - StdlibProof stdlib_recursion_proof = bb::convert_native_proof_to_stdlib(builder, recursion_proof); + UltraRecursiveVerifier outer_verifier(ultra_builder, outer_vk); + StdlibProof stdlib_recursion_proof = + bb::convert_native_proof_to_stdlib(ultra_builder, recursion_proof); auto outer_verifier_output = outer_verifier.verify_proof(stdlib_recursion_proof, input_agg_obj); // Step 9: Validate that both `builder` and `inner_builder` use the same AVM proof data and AVM public inputs @@ -223,7 +223,7 @@ class AvmGoblinRecursiveVerifier { mega_input_hash_public_input_index += 1; } auto ultra_output_hash = - stdlib::poseidon2::hash(*builder, ultra_hash_buffer); + stdlib::poseidon2::hash(*ultra_builder, ultra_hash_buffer); stdlib_recursion_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); // Step 10: gather up the ipa proof, ipa claim and output aggregation object produced from verifying the mega From 24479347e15d5096f03455d3b3c938822cd83d92 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 22:36:13 +0000 Subject: [PATCH 29/43] undo agg method template change for testing --- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 6 ++++-- .../aggregation_state/aggregation_state.hpp | 10 ++++++---- .../vm/avm/generated/recursive_verifier.cpp | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index ad01d5093e95..20f564c1c45d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -100,7 +100,8 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.aggregate(nested_agg_obj, recursion_separator); + agg_obj.template aggregate(nested_agg_obj, recursion_separator); + // agg_obj.aggregate(nested_agg_obj, recursion_separator); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u @@ -144,7 +145,8 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ pairing_points[0] = pairing_points[0].normalize(); pairing_points[1] = pairing_points[1].normalize(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge properly. - agg_obj.aggregate(pairing_points, recursion_separator); + agg_obj.template aggregate(pairing_points, recursion_separator); + // agg_obj.aggregate(pairing_points, recursion_separator); output.agg_obj = std::move(agg_obj); // Extract the IPA claim from the public inputs diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index 90d5b41ef2e4..b96d5510ce4b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -21,11 +21,12 @@ template struct aggregation_state { return P0 == other.P0 && P1 == other.P1; }; + template void aggregate(aggregation_state const& other, typename Curve::ScalarField recursion_separator) { - using Builder = typename Curve::Builder; + // using Builder = typename Curve::Builder; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { P0 += other.P0 * recursion_separator; P1 += other.P1 * recursion_separator; } else { @@ -38,11 +39,12 @@ template struct aggregation_state { } } + template void aggregate(std::array const& other, typename Curve::ScalarField recursion_separator) { - using Builder = typename Curve::Builder; + // using Builder = typename Curve::Builder; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { P0 += other[0] * recursion_separator; P1 += other[1] * recursion_separator; } else { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp index d464b1ca641c..6d4b9118d482 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp @@ -165,7 +165,7 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.aggregate(pairing_points, recursion_separator); + agg_obj.template aggregate(pairing_points, recursion_separator); return agg_obj; } From 0f66a4047dadd806e136fe2becc415c0394aba5c Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 23:01:44 +0000 Subject: [PATCH 30/43] comment cleanup --- .../avm/recursion/goblin_avm_recursive_verifier.hpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 0cc54cef6c3d..847782a19775 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -111,13 +111,12 @@ class AvmGoblinRecursiveVerifier { | v UltraProof + IPA Proof - */ // STEP 1: - // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts - // (later on we must validate that the AVM proof fed into the MegaCircuit matches the one fed into the upstream - // UltraCircuit) + // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts to be used as input to the + // mega-arithmetized AVM recursive verifier circuit. Construct two hash buffers containing all of this data, one + // for the ultra representaiton and one for mega. GoblinProver goblin; MegaCircuitBuilder mega_builder(goblin.op_queue); @@ -189,12 +188,6 @@ class AvmGoblinRecursiveVerifier { stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ ultra_builder, goblin_vinput }; stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); - // // NOTE: I think this part is wrong. What do we initialize the builder's agg object to be? - // PairingPointAccumulatorIndices current_aggregation_object = - // stdlib::recursion::init_default_agg_obj_indices(*builder); - // // This is currently just setting the aggregation object to the default one. - // builder->add_pairing_point_accumulator(current_aggregation_object); - // We only calls the IPA recursive verifier once, so we can just add this IPA claim and proof ultra_builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); ultra_builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); From dd40b1c41faf41e598d3631e645dec60889327c4 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 23:07:09 +0000 Subject: [PATCH 31/43] use legacy method in avm constraints for now --- .../barretenberg/dsl/acir_format/acir_format.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 26ac4c1e7772..c27d10b02737 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -520,11 +520,16 @@ PairingPointAccumulatorIndices process_avm_recursion_constraints( // Add recursion constraints size_t idx = 0; for (auto& constraint : constraint_system.avm_recursion_constraints) { - HonkRecursionConstraintOutput output = create_avm_recursion_constraints_new( - builder, constraint, current_aggregation_object, has_valid_witness_assignments); + // HonkRecursionConstraintOutput output = create_avm_recursion_constraints_new( + // builder, constraint, current_aggregation_object, has_valid_witness_assignments); - // WORKTODO: Decide what to do here. Eventually we want to return the full output not just the pairing points. - current_aggregation_object = output.agg_obj_indices; + // // WORKTODO: Decide what to do here. Eventually we want to return the full output not just the pairing + // points. current_aggregation_object = output.agg_obj_indices; + + // WORKTODO: disable this in favor of the above. Need to figure out the right way to init the grump CRS in + // ultra honk write_vk + current_aggregation_object = create_avm_recursion_constraints_legacy( + builder, constraint, current_aggregation_object, has_valid_witness_assignments); gate_counter.track_diff(constraint_system.gates_per_opcode, constraint_system.original_opcode_indices.avm_recursion_constraints.at(idx++)); From e05005625ae3ec928d45add02892cd4b19d440de Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 24 Mar 2025 23:58:04 +0000 Subject: [PATCH 32/43] clarify naming --- .../vm/avm/recursion/goblin_avm_recursive_verifier.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 847782a19775..178f1b7cf2bd 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -171,9 +171,9 @@ class AvmGoblinRecursiveVerifier { stdlib::recursion::init_default_agg_obj_indices(mega_builder)); // Step 4: generate a proof of the above MegaCircuit - std::shared_ptr ultra_instance = std::make_shared(mega_builder); - MegaProver ultra_prover(ultra_instance); - auto recursion_proof = ultra_prover.construct_proof(); + std::shared_ptr mega_instance = std::make_shared(mega_builder); + MegaProver mega_prover(mega_instance); + auto recursion_proof = mega_prover.construct_proof(); // Step 5: make a goblin proof and construct a GoblinRecursiveVerifier GoblinProof g_proof = goblin.prove(); @@ -195,7 +195,7 @@ class AvmGoblinRecursiveVerifier { // Step 7: Compute the verification key to recursively verify the MegaProof // NOTE: this part could be precomputed to save time - auto native_outer_vk = std::make_shared(ultra_instance->proving_key); + auto native_outer_vk = std::make_shared(mega_instance->proving_key); auto outer_vk = std::make_shared(ultra_builder, native_outer_vk); From 1e7d9915c51fd0017ca249941037c49a805d184f Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 25 Mar 2025 21:12:00 +0000 Subject: [PATCH 33/43] cleanup and simplify gob avm rec verifier plus add issues --- .../goblin_avm_recursive_verifier.hpp | 104 +++++++----------- 1 file changed, 42 insertions(+), 62 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 178f1b7cf2bd..09df23a4f667 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -46,14 +46,8 @@ class AvmGoblinRecursiveVerifier { Builder* ultra_builder; std::shared_ptr transcript; - // Note: instead of passing in a native verification key we pass in a vector of field elements whose type equals - // Builder::FF - // Reason is a bit of a workaround. In avm_recursion_constraint.cpp we construct an AVM verification key in the - // current UltraRollupCircuit context. - // However, the UltraRollupCircuit doesn't verify the AVM - it verifies a MegaCircuit that verifies the AVM. - // We therefore need the vkey both present in the top-level UltraRollupCircuit *and* the downstream MegaCircuit. - // Initial plan was to take a vkey in the UltraRollupCircuit context as an input, and then use `key.to_fields()` to - // construct a MegaCircuit vkey context. However `to_fields` seems bugged when used in a recursive vkey setting. + // WORKTODO: Its a bit arbitrary which representation the inputs (proof, pub, VK) should be received in. We need + // both for the hash buffers but only mega for actual verification. Is there a clean choice here? explicit AvmGoblinRecursiveVerifier(Builder* builder, const std::vector& outer_key_fields) : outer_key_fields(outer_key_fields) , ultra_builder(builder) @@ -68,12 +62,9 @@ class AvmGoblinRecursiveVerifier { using FF = AvmRecursiveFlavor::FF; using UltraFF = UltraRollupRecursiveFlavor::FF; using RecursiveVerifier = avm::AvmRecursiveVerifier_; - using ECCVMVerificationKey = ECCVMFlavor::VerificationKey; - using TranslatorVerificationKey = TranslatorFlavor::VerificationKey; using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; using MegaProver = UltraProver_; - using MegaDeciderProvingKey = DeciderProvingKey_; using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; using UltraRecursiveVerifier = stdlib::recursion::honk::UltraRecursiveVerifier_; @@ -113,11 +104,13 @@ class AvmGoblinRecursiveVerifier { UltraProof + IPA Proof */ - // STEP 1: - // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts to be used as input to the - // mega-arithmetized AVM recursive verifier circuit. Construct two hash buffers containing all of this data, one - // for the ultra representaiton and one for mega. + // STEP 1: To establish consistency of the the proof and public inputs between the inner (Mega) circuit and the + // outer (Ultra) circuit, each circuit computes a hash of these components and consistency is checked on the + // result. + // WORKTODO: think through whether this mechanism is needed/makes sense. We dont do anything with these + // components in the ultra circuit aside from hashing them so what exactly is the hash check buying us? + // Instantiate Mega builder for the inner circuit (AVM proof recursive verifier) GoblinProver goblin; MegaCircuitBuilder mega_builder(goblin.op_queue); @@ -138,7 +131,7 @@ class AvmGoblinRecursiveVerifier { }; // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts - StdlibProof mega_stdlib_proof = convert_stdlib_ultra_to_stdlib_mega(stdlib_proof); + std::vector mega_stdlib_proof = convert_stdlib_ultra_to_stdlib_mega(stdlib_proof); std::vector> mega_public_inputs; mega_public_inputs.reserve(public_inputs.size()); for (const std::vector& input_vec : public_inputs) { @@ -146,70 +139,61 @@ class AvmGoblinRecursiveVerifier { } std::vector key_fields = convert_stdlib_ultra_to_stdlib_mega(outer_key_fields); - auto stdlib_key = - std::make_shared(mega_builder, std::span(key_fields)); - - // we use the hash of the proof + public inputs to validate that we're correctly transferring data between the - // Mega and Ultra circuits + // Compute the hash of the buffer in the Mega circuit auto mega_input_hash = stdlib::poseidon2::hash(mega_builder, mega_hash_buffer); - // NOTE: there doesn't seem to be an easy way to know *which* public input index will map to mega_input_hash - // which is troublesome - mega_input_hash.set_public(); + // WORKTODO: address this or make an issue: NOTE: there doesn't seem to be an easy way to know *which* public + // input index will map to mega_input_hash which is troublesome + mega_input_hash.set_public(); // Add the hash result to the public inputs to propagate it to the outer circuit - // Step 2: Verify the AVM proof - // NOTICE!!!! We don't currently propagate the aggregation object which we need to for this to be sound! + // Step 2: Construct a Mega-arithmetized AVM recursive verifier circuit + auto stdlib_key = + std::make_shared(mega_builder, std::span(key_fields)); RecursiveVerifier recursive_verifier{ &mega_builder, stdlib_key }; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1304): Do proper pairing point aggregation. auto default_agg_object = stdlib::recursion::init_default_aggregation_state( mega_builder); [[maybe_unused]] auto mega_agg_output = recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, default_agg_object); - - // Step 3: run the goblin merge protocol - goblin.prove_merge(mega_builder); mega_builder.add_pairing_point_accumulator( stdlib::recursion::init_default_agg_obj_indices(mega_builder)); - // Step 4: generate a proof of the above MegaCircuit - std::shared_ptr mega_instance = std::make_shared(mega_builder); - MegaProver mega_prover(mega_instance); - auto recursion_proof = mega_prover.construct_proof(); + // Step 3: Generate a Mega proof of the AVM recursive verifier circuit + MegaProver mega_prover(mega_builder); + HonkProof mega_proof = mega_prover.construct_proof(); - // Step 5: make a goblin proof and construct a GoblinRecursiveVerifier - GoblinProof g_proof = goblin.prove(); + // Step 4: Construct a corresponding Goblin proof (includes Merge, ECCVM, and Translator proofs) + goblin.prove_merge(mega_builder); + GoblinProof goblin_proof = goblin.prove(); - auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); + // Step 5: Recursively verify the Mega proof in the outer (Ultra) circuit + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1305): Mega + Goblin VKs must be circuit constants. + auto native_outer_vk = std::make_shared(mega_prover.proving_key->proving_key); + auto outer_vk = + std::make_shared(ultra_builder, native_outer_vk); + UltraRecursiveVerifier outer_verifier(ultra_builder, outer_vk); + StdlibProof ultra_proof = bb::convert_native_proof_to_stdlib(ultra_builder, mega_proof); + auto outer_verifier_output = outer_verifier.verify_proof(ultra_proof, input_agg_obj); + + // Step 6: Recursively verify the goblin proof in the Ultra circuit GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), std::make_shared( goblin.get_translator_proving_key()) }; - - // Step 6: In our UltraCircuit, recursively verify the goblin proof stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ ultra_builder, goblin_vinput }; - stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(g_proof); + stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(goblin_proof); - // We only calls the IPA recursive verifier once, so we can just add this IPA claim and proof + // We only call the IPA recursive verifier once, so we can just add this IPA claim and proof + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1306): Determine the right location/entity to + // handle this IPA data propagation. ultra_builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); ultra_builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); ASSERT(ultra_builder->ipa_proof.size() && "IPA proof should not be empty"); - // Step 7: Compute the verification key to recursively verify the MegaProof - // NOTE: this part could be precomputed to save time - auto native_outer_vk = std::make_shared(mega_instance->proving_key); - auto outer_vk = - std::make_shared(ultra_builder, native_outer_vk); - - // Step 8: In our UltraCirfcuit, recursively verify the mega proof - UltraRecursiveVerifier outer_verifier(ultra_builder, outer_vk); - StdlibProof stdlib_recursion_proof = - bb::convert_native_proof_to_stdlib(ultra_builder, recursion_proof); - auto outer_verifier_output = outer_verifier.verify_proof(stdlib_recursion_proof, input_agg_obj); - // Step 9: Validate that both `builder` and `inner_builder` use the same AVM proof data and AVM public inputs // Note: we don't seem to have a nice way of finding out where within a public input space a given value is that // we call `set_public` on. So we scan manually here to find the index :/ size_t mega_input_hash_public_input_index = 0; - for (const auto& proof_ele : stdlib_recursion_proof) { + for (const auto& proof_ele : ultra_proof) { if (proof_ele.get_value() == mega_input_hash.get_value()) { break; } @@ -217,17 +201,13 @@ class AvmGoblinRecursiveVerifier { } auto ultra_output_hash = stdlib::poseidon2::hash(*ultra_builder, ultra_hash_buffer); - stdlib_recursion_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); + ultra_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); // Step 10: gather up the ipa proof, ipa claim and output aggregation object produced from verifying the mega // proof + goblin proof, and return them - auto ipa_proof_output = goblin_verifier_output.ipa_transcript->proof_data; - auto ipa_claim_output = outer_verifier_output.ipa_opening_claim; - auto pairing_accumulator_output = outer_verifier_output.agg_obj; - - RecursiveAvmGoblinOutput result{ .ipa_proof = ipa_proof_output, - .ipa_claim = ipa_claim_output, - .aggregation_object = pairing_accumulator_output }; + RecursiveAvmGoblinOutput result{ .ipa_proof = goblin_verifier_output.ipa_transcript->proof_data, + .ipa_claim = goblin_verifier_output.opening_claim, + .aggregation_object = outer_verifier_output.agg_obj }; return result; } }; From f9a4c107a12cdf33a85031be5886c1bda38dbb0f Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 25 Mar 2025 21:24:06 +0000 Subject: [PATCH 34/43] WiP test cleanup --- .../vm/avm/tests/recursive_verifier.test.cpp | 153 ++++++++---------- 1 file changed, 65 insertions(+), 88 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 803aec9e16c9..db316358e07f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -32,6 +32,7 @@ using namespace bb::avm_trace; class AvmRecursiveTests : public ::testing::Test { public: using RecursiveFlavor = AvmRecursiveFlavor_; + using AVMVerificationKey = avm::AvmFlavor::VerificationKey; using InnerFlavor = typename RecursiveFlavor::NativeFlavor; using InnerBuilder = bb::avm::AvmCircuitBuilder; @@ -46,19 +47,7 @@ class AvmRecursiveTests : public ::testing::Test { using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; using RecursiveVerifierUltra = bb::avm::AvmRecursiveVerifier_>; - // In the goblin example, we have a GoblinProver object that takes in an inner circuit. we call `merge` method on - // GoblinProver taking the inner circuit as argument. And then we make a goblin proof. The `merge` method invokes a - // MergeRecursiveVerifier_ object and calls `verify_proof` - - // In the AVM context, that MergeRecursiveVerifier_ object needs to be an AvmRecursiveFlavor_ object - // There are two ways we can do this. - - // First way is the template param the goblin prover to take in an inner recursive verifier type - // 2nd way is add explicit new methods into goblin - // fastest might be to get option 2 working and then refactor into option 1 using OuterBuilder = UltraCircuitBuilder; - using OuterProver = UltraProver; - using OuterVerifier = UltraVerifier; using OuterDeciderProvingKey = DeciderProvingKey_; using OuterRecursiveFlavor = MegaRecursiveFlavor_; @@ -104,16 +93,56 @@ class AvmRecursiveTests : public ::testing::Test { return builder; } + + struct AVMVerifierInput { + HonkProof proof; + std::vector> public_inputs_vec; + std::shared_ptr vkey; + }; + + AVMVerifierInput create_avm_verifier_input() + { + InnerBuilder circuit_builder = generate_avm_circuit(); + InnerComposer composer = InnerComposer(); + InnerProver prover = composer.create_prover(circuit_builder); + + HonkProof proof = prover.construct_proof(); + std::vector> public_inputs_vec = construct_public_inputs_vec(); + + InnerVerifier verifier = composer.create_verifier(circuit_builder); + + // Verify the AVM proof natively for good measure + bool verified = verifier.verify_proof(proof, public_inputs_vec); + EXPECT_TRUE(verified) << "native proof verification failed"; + + return { proof, public_inputs_vec, verifier.key }; + } + + static std::vector> construct_public_inputs_vec() + { + // We just pad all the public inputs with the right number of zeroes + std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); + std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); + std::vector calldata{ {} }; + std::vector returndata{ {} }; + + std::vector> public_inputs_vec{ + kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, + returndata + }; + + return public_inputs_vec; + } }; TEST_F(AvmRecursiveTests, GoblinRecursion) { - InnerBuilder circuit_builder = generate_avm_circuit(); - InnerComposer composer = InnerComposer(); - InnerProver prover = composer.create_prover(circuit_builder); - InnerVerifier inner_verifier = composer.create_verifier(circuit_builder); + using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; - HonkProof proof = prover.construct_proof(); + // Generate the inputs to an AVM verifier + auto [proof, public_inputs_vec, verification_key] = create_avm_verifier_input(); UltraCircuitBuilder outer_builder; using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; @@ -121,33 +150,21 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) std::shared_ptr::VerificationKey> avm_key = std::make_shared::VerificationKey>( - &outer_builder, inner_verifier.key); + &outer_builder, verification_key); - auto key_fields_native = inner_verifier.key->to_field_elements(); + auto key_fields_native = verification_key->to_field_elements(); std::vector outer_key_fields; for (const auto& f : key_fields_native) { UltraFF val = UltraFF::from_witness(&outer_builder, f); outer_key_fields.push_back(val); } avm::AvmGoblinRecursiveVerifier verifier(&outer_builder, outer_key_fields); - using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; stdlib::recursion::aggregation_state agg_obj = stdlib::recursion::init_default_aggregation_state( outer_builder); - std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); - std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector calldata{ {} }; - std::vector returndata{ {} }; - - std::vector> public_inputs_vec{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata - }; - StdlibProof stdlib_proof = bb::convert_native_proof_to_stdlib(&outer_builder, proof); std::vector> public_inputs_ct; @@ -176,60 +193,28 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); EXPECT_TRUE(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof)); - - // AvmGoblinRecursiveVerifier::test_recursive_avm(proof, verifier); } TEST_F(AvmRecursiveTests, recursion) { - if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - GTEST_SKIP(); - } - - using RecursiveFlavor = AvmRecursiveFlavor_; - - using InnerFlavor = typename RecursiveFlavor::NativeFlavor; - using InnerFF = InnerFlavor::FF; - - using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; - - using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; - using OuterProver = UltraProver; - using OuterVerifier = UltraVerifier; - using OuterDeciderProvingKey = DeciderProvingKey_; - - InnerBuilder circuit_builder = generate_avm_circuit(); - InnerComposer composer = InnerComposer(); - InnerProver prover = composer.create_prover(circuit_builder); - InnerVerifier verifier = composer.create_verifier(circuit_builder); - - HonkProof proof = prover.construct_proof(); + // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + // GTEST_SKIP(); + // } - // We just pad all the public inputs with the right number of zeroes - std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); - std::vector kernel_value_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_side_effect_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector kernel_metadata_outputs(KERNEL_OUTPUTS_LENGTH); - std::vector calldata{ {} }; - std::vector returndata{ {} }; + using AvmRecursiveFlavor = AvmRecursiveFlavor_; + using AvmRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + using DeciderProvingKey = DeciderProvingKey_; - std::vector> public_inputs{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs - }; - std::vector> public_inputs_vec{ - kernel_inputs, kernel_value_outputs, kernel_side_effect_outputs, kernel_metadata_outputs, calldata, returndata - }; - - bool verified = verifier.verify_proof(proof, public_inputs_vec); - ASSERT_TRUE(verified) << "native proof verification failed"; + // Generate the inputs to an AVM verifier + auto [proof, public_inputs_vec, verification_key] = create_avm_verifier_input(); // Create the outer verifier, to verify the proof - const std::shared_ptr verification_key = verifier.key; OuterBuilder outer_circuit; - RecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; + AvmRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; auto agg_object = - stdlib::recursion::init_default_aggregation_state(outer_circuit); + stdlib::recursion::init_default_aggregation_state( + outer_circuit); auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); @@ -242,29 +227,21 @@ TEST_F(AvmRecursiveTests, recursion) bool outer_circuit_checked = CircuitChecker::check(outer_circuit); ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; - auto manifest = verifier.transcript->get_manifest(); - auto recursive_manifest = recursive_verifier.transcript->get_manifest(); - - EXPECT_EQ(manifest.size(), recursive_manifest.size()); - for (size_t i = 0; i < recursive_manifest.size(); ++i) { - EXPECT_EQ(recursive_manifest[i], manifest[i]); - } - - for (auto const [key_el, rec_key_el] : zip_view(verifier.key->get_all(), recursive_verifier.key->get_all())) { + for (auto const [key_el, rec_key_el] : zip_view(verification_key->get_all(), recursive_verifier.key->get_all())) { EXPECT_EQ(key_el, rec_key_el.get_value()); } - EXPECT_EQ(verifier.key->circuit_size, static_cast(recursive_verifier.key->circuit_size.get_value())); - EXPECT_EQ(verifier.key->num_public_inputs, + EXPECT_EQ(verification_key->circuit_size, static_cast(recursive_verifier.key->circuit_size.get_value())); + EXPECT_EQ(verification_key->num_public_inputs, static_cast(recursive_verifier.key->num_public_inputs.get_value())); // Make a proof of the verification of an AVM proof const size_t srs_size = 1 << 23; - auto ultra_instance = std::make_shared( + auto ultra_instance = std::make_shared( outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); - OuterProver ultra_prover(ultra_instance); + UltraProver ultra_prover(ultra_instance); auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - OuterVerifier ultra_verifier(ultra_verification_key); + UltraVerifier ultra_verifier(ultra_verification_key); vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); From 042d8eb3caec92bbba7710fcca870b6594e4479e Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 00:33:18 +0000 Subject: [PATCH 35/43] lots of test cleanup --- .../vm/avm/tests/recursive_verifier.test.cpp | 188 ++++++++---------- 1 file changed, 78 insertions(+), 110 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index db316358e07f..17b8ce6961c6 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -1,11 +1,7 @@ #include "barretenberg/vm/avm/recursion/recursive_verifier.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" -#include "barretenberg/goblin/goblin.hpp" #include "barretenberg/numeric/random/engine.hpp" -#include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" #include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp" -#include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.hpp" -#include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" @@ -27,41 +23,13 @@ namespace bb::stdlib::recursion::honk {} namespace tests_avm { using namespace bb; +using namespace bb::avm; using namespace bb::avm_trace; class AvmRecursiveTests : public ::testing::Test { public: - using RecursiveFlavor = AvmRecursiveFlavor_; - using AVMVerificationKey = avm::AvmFlavor::VerificationKey; - - using InnerFlavor = typename RecursiveFlavor::NativeFlavor; - using InnerBuilder = bb::avm::AvmCircuitBuilder; - using InnerProver = bb::avm::AvmProver; - using InnerVerifier = bb::avm::AvmVerifier; - using InnerComposer = bb::avm::AvmComposer; - using InnerG1 = InnerFlavor::Commitment; - using InnerFF = InnerFlavor::FF; - - using Transcript = InnerFlavor::Transcript; - - using RecursiveVerifier = bb::avm::AvmRecursiveVerifier_; - using RecursiveVerifierUltra = bb::avm::AvmRecursiveVerifier_>; - - using OuterBuilder = UltraCircuitBuilder; - using OuterDeciderProvingKey = DeciderProvingKey_; - - using OuterRecursiveFlavor = MegaRecursiveFlavor_; - using OuterFF = OuterRecursiveFlavor::FF; - using OuterRecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_; - - using ECCVMVerificationKey = bb::ECCVMFlavor::VerificationKey; - using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; - using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; - using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; - - using MegaProver = UltraProver_; - using MegaVerifier = UltraVerifier_; - using MegaDeciderProvingKey = DeciderProvingKey_; + using AvmBuilder = bb::avm::AvmCircuitBuilder; + using OuterBuilder = bb::UltraCircuitBuilder; static void SetUpTestSuite() { @@ -69,14 +37,39 @@ class AvmRecursiveTests : public ::testing::Test { bb::srs::init_grumpkin_crs_factory(bb::srs::get_grumpkin_crs_path()); } - AvmPublicInputs public_inputs; + struct AVMVerifierInput { + using AvmVerificationKey = avm::AvmFlavor::VerificationKey; + HonkProof proof; + std::vector> public_inputs_vec; + std::shared_ptr vkey; + }; + + // Generate native inputs to an AVM verifier based on the proof of a mock AVM circuit + static AVMVerifierInput create_avm_verifier_input() + { + AvmBuilder circuit_builder = generate_avm_circuit(); + AvmComposer composer; + + // Construct the AVM proof + AvmProver prover = composer.create_prover(circuit_builder); + HonkProof proof = prover.construct_proof(); + std::vector> public_inputs_vec = construct_public_inputs_vec(); + + // Verify the AVM proof natively for good measure + AvmVerifier verifier = composer.create_verifier(circuit_builder); + bool verified = verifier.verify_proof(proof, public_inputs_vec); + EXPECT_TRUE(verified) << "native proof verification failed"; + + return { proof, public_inputs_vec, verifier.key }; + } + private: // Generate an extremely simple avm trace - InnerBuilder generate_avm_circuit() + static AvmBuilder generate_avm_circuit() { - public_inputs = generate_base_public_inputs(); + AvmPublicInputs public_inputs = generate_base_public_inputs(); AvmTraceBuilder trace_builder(public_inputs); - InnerBuilder builder; + AvmBuilder builder; trace_builder.op_set(0, 1, 1, AvmMemoryTag::U8); trace_builder.op_set(0, 1, 2, AvmMemoryTag::U8); @@ -94,31 +87,7 @@ class AvmRecursiveTests : public ::testing::Test { return builder; } - struct AVMVerifierInput { - HonkProof proof; - std::vector> public_inputs_vec; - std::shared_ptr vkey; - }; - - AVMVerifierInput create_avm_verifier_input() - { - InnerBuilder circuit_builder = generate_avm_circuit(); - InnerComposer composer = InnerComposer(); - InnerProver prover = composer.create_prover(circuit_builder); - - HonkProof proof = prover.construct_proof(); - std::vector> public_inputs_vec = construct_public_inputs_vec(); - - InnerVerifier verifier = composer.create_verifier(circuit_builder); - - // Verify the AVM proof natively for good measure - bool verified = verifier.verify_proof(proof, public_inputs_vec); - EXPECT_TRUE(verified) << "native proof verification failed"; - - return { proof, public_inputs_vec, verifier.key }; - } - - static std::vector> construct_public_inputs_vec() + static std::vector> construct_public_inputs_vec() { // We just pad all the public inputs with the right number of zeroes std::vector kernel_inputs(KERNEL_INPUTS_LENGTH); @@ -139,54 +108,56 @@ class AvmRecursiveTests : public ::testing::Test { TEST_F(AvmRecursiveTests, GoblinRecursion) { - using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; - - // Generate the inputs to an AVM verifier - auto [proof, public_inputs_vec, verification_key] = create_avm_verifier_input(); - - UltraCircuitBuilder outer_builder; + using AvmRecursiveVerifier = avm::AvmGoblinRecursiveVerifier; using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; + using Curve = UltraRollupRecursiveFlavor::Curve; using UltraFF = UltraRollupRecursiveFlavor::FF; + using UltraRollupProver = UltraProver_; - std::shared_ptr::VerificationKey> avm_key = - std::make_shared::VerificationKey>( - &outer_builder, verification_key); - - auto key_fields_native = verification_key->to_field_elements(); - std::vector outer_key_fields; - for (const auto& f : key_fields_native) { - UltraFF val = UltraFF::from_witness(&outer_builder, f); - outer_key_fields.push_back(val); - } - avm::AvmGoblinRecursiveVerifier verifier(&outer_builder, outer_key_fields); + // Generate the inputs to an AVM verifier + auto [proof, public_inputs_vec, verification_key] = create_avm_verifier_input(); - stdlib::recursion::aggregation_state agg_obj = - stdlib::recursion::init_default_aggregation_state( - outer_builder); + OuterBuilder outer_circuit; - StdlibProof stdlib_proof = bb::convert_native_proof_to_stdlib(&outer_builder, proof); + // Construct stdlib representations of the proof, public inputs and verification key + StdlibProof stdlib_proof = bb::convert_native_proof_to_stdlib(&outer_circuit, proof); std::vector> public_inputs_ct; public_inputs_ct.reserve(public_inputs_vec.size()); - for (const auto& vec : public_inputs_vec) { std::vector vec_ct; vec_ct.reserve(vec.size()); - for (const auto& el : vec) { - vec_ct.push_back(bb::stdlib::witness_t(&outer_builder, el)); + for (const auto& val : vec) { + vec_ct.push_back(bb::stdlib::witness_t(&outer_circuit, val)); } public_inputs_ct.push_back(vec_ct); } - auto proof_outputs = verifier.verify_proof(stdlib_proof, public_inputs_ct, agg_obj); + auto key_fields_native = verification_key->to_field_elements(); + std::vector outer_key_fields; + for (const auto& f : key_fields_native) { + UltraFF val = UltraFF::from_witness(&outer_circuit, f); + outer_key_fields.push_back(val); + } + + // Construct the AVM recursive verifier + AvmRecursiveVerifier verifier(&outer_circuit, outer_key_fields); + stdlib::recursion::aggregation_state agg_obj = + stdlib::recursion::init_default_aggregation_state(outer_circuit); + auto verifier_output = verifier.verify_proof(stdlib_proof, public_inputs_ct, agg_obj); - auto outer_proving_key = std::make_shared>(outer_builder); - using UltraRollupProver = UltraProver_; + // Ensure that the pairing check is satisfed on the outputs of the recursive verifier + bool agg_output_valid = verification_key->pcs_verification_key->pairing_check( + verifier_output.aggregation_object.P0.get_value(), verifier_output.aggregation_object.P1.get_value()); + ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; + ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; + // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit + auto outer_proving_key = std::make_shared>(outer_circuit); UltraRollupProver outer_prover(outer_proving_key); - auto outer_proof = outer_prover.construct_proof(); + + // WORKTODO: are we meaningfully verifying the correct IPA claim here? auto outer_verification_key = std::make_shared(outer_proving_key->proving_key); auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); @@ -197,56 +168,53 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) TEST_F(AvmRecursiveTests, recursion) { - // if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { - // GTEST_SKIP(); - // } + if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { + GTEST_SKIP(); + } using AvmRecursiveFlavor = AvmRecursiveFlavor_; using AvmRecursiveVerifier = bb::avm::AvmRecursiveVerifier_; + using Curve = AvmRecursiveFlavor::Curve; using DeciderProvingKey = DeciderProvingKey_; // Generate the inputs to an AVM verifier auto [proof, public_inputs_vec, verification_key] = create_avm_verifier_input(); - // Create the outer verifier, to verify the proof + // Construct an AVM recursive verifier circuit OuterBuilder outer_circuit; AvmRecursiveVerifier recursive_verifier{ &outer_circuit, verification_key }; - - auto agg_object = - stdlib::recursion::init_default_aggregation_state( - outer_circuit); - + auto agg_object = stdlib::recursion::init_default_aggregation_state(outer_circuit); auto agg_output = recursive_verifier.verify_proof(proof, public_inputs_vec, agg_object); + // Ensure that the pairing check is satisfed on the outputs of the recursive verifier bool agg_output_valid = verification_key->pcs_verification_key->pairing_check(agg_output.P0.get_value(), agg_output.P1.get_value()); - ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; + // Run check circuit on the recursive verifier circuit bool outer_circuit_checked = CircuitChecker::check(outer_circuit); ASSERT_TRUE(outer_circuit_checked) << "outer circuit check failed"; + // Check that the native and recursive verification keys have equivalent values for (auto const [key_el, rec_key_el] : zip_view(verification_key->get_all(), recursive_verifier.key->get_all())) { EXPECT_EQ(key_el, rec_key_el.get_value()); } - EXPECT_EQ(verification_key->circuit_size, static_cast(recursive_verifier.key->circuit_size.get_value())); EXPECT_EQ(verification_key->num_public_inputs, static_cast(recursive_verifier.key->num_public_inputs.get_value())); - // Make a proof of the verification of an AVM proof + // Construct and verify an Ultra proof of the AVM recursive verifier circuit const size_t srs_size = 1 << 23; auto ultra_instance = std::make_shared( outer_circuit, TraceSettings{}, std::make_shared>(srs_size)); UltraProver ultra_prover(ultra_instance); - auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); - UltraVerifier ultra_verifier(ultra_verification_key); + auto ultra_proof = ultra_prover.construct_proof(); vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates); - auto recursion_proof = ultra_prover.construct_proof(); - bool recursion_verified = ultra_verifier.verify_proof(recursion_proof); - EXPECT_TRUE(recursion_verified) << "recursion proof verification failed"; + auto ultra_verification_key = std::make_shared(ultra_instance->proving_key); + UltraVerifier ultra_verifier(ultra_verification_key); + EXPECT_TRUE(ultra_verifier.verify_proof(ultra_proof)) << "recursion proof verification failed"; } } // namespace tests_avm From cb0eb9236b9e024899b56341bb87533639d4dfc9 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 03:32:05 +0000 Subject: [PATCH 36/43] use legacy method in acir format and add issue --- .../src/barretenberg/dsl/acir_format/acir_format.cpp | 12 +++--------- .../dsl/acir_format/avm_recursion_constraint.cpp | 4 ++-- .../dsl/acir_format/avm_recursion_constraint.hpp | 11 +++++------ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index c27d10b02737..355f3ceca684 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -520,15 +520,9 @@ PairingPointAccumulatorIndices process_avm_recursion_constraints( // Add recursion constraints size_t idx = 0; for (auto& constraint : constraint_system.avm_recursion_constraints) { - // HonkRecursionConstraintOutput output = create_avm_recursion_constraints_new( - // builder, constraint, current_aggregation_object, has_valid_witness_assignments); - - // // WORKTODO: Decide what to do here. Eventually we want to return the full output not just the pairing - // points. current_aggregation_object = output.agg_obj_indices; - - // WORKTODO: disable this in favor of the above. Need to figure out the right way to init the grump CRS in - // ultra honk write_vk - current_aggregation_object = create_avm_recursion_constraints_legacy( + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1303): Utilize method that employs Goblin in the + // AVM recursive verifier. + current_aggregation_object = create_avm_recursion_constraints( builder, constraint, current_aggregation_object, has_valid_witness_assignments); gate_counter.track_diff(constraint_system.gates_per_opcode, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 885edfe938fc..e8a56f5e1af3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -150,7 +150,7 @@ void create_dummy_vkey_and_proof(Builder& builder, * @param input_aggregation_object_indices. The aggregation object coming from previous Honk/Avm recursion constraints. * @param has_valid_witness_assignment. Do we have witnesses or are we just generating keys? */ -PairingPointAccumulatorIndices create_avm_recursion_constraints_legacy( +PairingPointAccumulatorIndices create_avm_recursion_constraints( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object_indices, @@ -204,7 +204,7 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints_legacy( } // NEW VERSION THAT USES GOBLIN PLONK IN VERIFICATION ALGO -HonkRecursionConstraintOutput create_avm_recursion_constraints_new( +HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object_indices, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp index 5dbd66123627..fffdaea8dffe 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.hpp @@ -8,13 +8,12 @@ using Builder = bb::UltraCircuitBuilder; using namespace bb; -PairingPointAccumulatorIndices create_avm_recursion_constraints_legacy( - Builder& builder, - const RecursionConstraint& input, - PairingPointAccumulatorIndices input_aggregation_object, - bool has_valid_witness_assignments); +PairingPointAccumulatorIndices create_avm_recursion_constraints(Builder& builder, + const RecursionConstraint& input, + PairingPointAccumulatorIndices input_aggregation_object, + bool has_valid_witness_assignments); -HonkRecursionConstraintOutput create_avm_recursion_constraints_new( +HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( Builder& builder, const RecursionConstraint& input, PairingPointAccumulatorIndices input_aggregation_object, From 98c4759ffde6198c48d650e6d74f3f48e0a6c6d6 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 03:36:31 +0000 Subject: [PATCH 37/43] simplify aggregate method --- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 4 ++-- .../aggregation_state/aggregation_state.hpp | 10 ++++------ .../vm/avm/generated/recursive_verifier.cpp | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index 20f564c1c45d..21471bd75542 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -100,7 +100,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.template aggregate(nested_agg_obj, recursion_separator); + agg_obj.aggregate(nested_agg_obj, recursion_separator); // agg_obj.aggregate(nested_agg_obj, recursion_separator); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported @@ -145,7 +145,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ pairing_points[0] = pairing_points[0].normalize(); pairing_points[1] = pairing_points[1].normalize(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge properly. - agg_obj.template aggregate(pairing_points, recursion_separator); + agg_obj.aggregate(pairing_points, recursion_separator); // agg_obj.aggregate(pairing_points, recursion_separator); output.agg_obj = std::move(agg_obj); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index b96d5510ce4b..90d5b41ef2e4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -21,12 +21,11 @@ template struct aggregation_state { return P0 == other.P0 && P1 == other.P1; }; - template void aggregate(aggregation_state const& other, typename Curve::ScalarField recursion_separator) { - // using Builder = typename Curve::Builder; + using Builder = typename Curve::Builder; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { P0 += other.P0 * recursion_separator; P1 += other.P1 * recursion_separator; } else { @@ -39,12 +38,11 @@ template struct aggregation_state { } } - template void aggregate(std::array const& other, typename Curve::ScalarField recursion_separator) { - // using Builder = typename Curve::Builder; + using Builder = typename Curve::Builder; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { P0 += other[0] * recursion_separator; P1 += other[1] * recursion_separator; } else { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp index 6d4b9118d482..d464b1ca641c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/recursive_verifier.cpp @@ -165,7 +165,7 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); - agg_obj.template aggregate(pairing_points, recursion_separator); + agg_obj.aggregate(pairing_points, recursion_separator); return agg_obj; } From 9c5bb9569c9e323440b987d4ffc5d9c4fafa200b Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 20:36:48 +0000 Subject: [PATCH 38/43] more comments and cleanup --- .../acir_format/avm_recursion_constraint.cpp | 2 - .../goblin_avm_recursive_verifier.hpp | 174 ++++++++---------- 2 files changed, 80 insertions(+), 96 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index e8a56f5e1af3..977db44d5c36 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -228,8 +228,6 @@ HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( const auto proof_fields = fields_from_witnesses(input.proof); const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); - // WORKTODO: unacceptable level of 'auto' usage in this method - auto it = public_inputs_flattened.begin(); VmPublicInputs vm_public_inputs = avm_trace::convert_public_inputs(std::vector(it, it + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH)); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 09df23a4f667..a990ebddbd3a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -22,97 +22,88 @@ #include "barretenberg/vm/avm/trace/trace.hpp" namespace bb::avm { +/** + * @brief Recursive verifier of AVM proofs that utilizes the Goblin mechanism for efficient EC operations. + * @details Recursive verification for AVM proofs proceeds in two phases: (1) recursive verification of the AVM proof in + * a Mega-arithmetized circuit C_M, and (2) recursive verification of the proof of C_M in an Ultra-arithmetized circuit + * C_U. This results in a protocol that overall is more efficient than direct recursive verification of the AVM proof in + * an Ultra circuit. + * + * The proof of the Mega-arithmetized AVM recursive verifier circuit C_M is of the form {\pi_M, \pi_G}, where \pi_M is a + * MegaHonk proof and \pi_G is a Goblin proof consisting of an ECCVM proof, a Translator proof, and a Merge proof. \pi_M + * establishes proper verification of the AVM proof up to the deferred EC operations, whose correctness is in turn + * established by \pi_G. Note: the ECCCVM proof (part of \pi_G) contains an IPA proof. Recursive verification of this + * component will stop short of full verification, resulting in an IPA claim that must be accumulated with other such + * claims before final verification later on (e.g. at the root). This is analogous to the aggregation of pairing point + * inputs for proving systems that use KZG, such as Ultra/MegaHonk. + * + * The Ultra-arithmetized circuit C_U is responsible for recursive verification of {\pi_M, \pi_G}, i.e. it contains both + * a Mega and a Goblin recursive verifier. The output of this recursive verification is a pairing check accumulator and + * an IPA claim accumulator. To ensure proper transfer the AVM verifier inputs {\pi, pub_inputs, VK}_{AVM} between the + * Mega and Ultra circuits, we utilize a hash consistency check. The representation of these inputs in C_M is hashed and + * the result h_M is propagated via the public inputs (i.e. it will be contained in \pi_M.pub_inputs). Then, C_U + * computes the hash h_U of its own representation of the same data and performs the check h_U = \pi_M.pub_inputs.h_M. + * + * @note The Mega circuit must be constrained to be a genuine AVM verifier circuit. This is done by fixing the VK(s) + * corresponding to proofs {\pi_M, \pi_G} to be circuit constants in C_U. + * + */ class AvmGoblinRecursiveVerifier { public: - using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; + using UltraBuilder = UltraCircuitBuilder; + using MegaBuilder = MegaCircuitBuilder; + using UltraRollupRecursiveFlavor = UltraRollupRecursiveFlavor_; + + using AggregationObject = bb::stdlib::recursion::aggregation_state>; + + using Transcript = bb::BaseTranscript>; + using UltraFF = UltraRollupRecursiveFlavor::Curve::ScalarField; + + using AvmRecursiveFlavor = AvmRecursiveFlavor_; + using AvmRecursiveVerificationKey = AvmRecursiveFlavor::VerificationKey; + struct RecursiveAvmGoblinOutput { - using UltraBuilder = UltraRollupRecursiveFlavor::CircuitBuilder; - using UltraFF = UltraRollupRecursiveFlavor::Curve::ScalarField; std::vector ipa_proof; OpeningClaim> ipa_claim; stdlib::recursion::aggregation_state> aggregation_object; }; - using Builder = typename UltraRollupRecursiveFlavor::CircuitBuilder; - using AggregationObject = bb::stdlib::recursion::aggregation_state>; - - using RecursiveFlavor = AvmRecursiveFlavor_; - - using Transcript = bb::BaseTranscript>; - using UltraFF = UltraRollupRecursiveFlavor::Curve::ScalarField; - using OuterAvmKey = AvmRecursiveFlavor_::VerificationKey; std::vector outer_key_fields; - Builder* ultra_builder; + UltraBuilder* ultra_builder; std::shared_ptr transcript; - // WORKTODO: Its a bit arbitrary which representation the inputs (proof, pub, VK) should be received in. We need - // both for the hash buffers but only mega for actual verification. Is there a clean choice here? - explicit AvmGoblinRecursiveVerifier(Builder* builder, const std::vector& outer_key_fields) + explicit AvmGoblinRecursiveVerifier(UltraBuilder* builder, const std::vector& outer_key_fields) : outer_key_fields(outer_key_fields) , ultra_builder(builder) {} - RecursiveAvmGoblinOutput verify_proof(const StdlibProof& stdlib_proof, + RecursiveAvmGoblinOutput verify_proof(const StdlibProof& stdlib_proof, const std::vector>& public_inputs, AggregationObject input_agg_obj) const { - using AvmRecursiveFlavor = AvmRecursiveFlavor_; using FF = AvmRecursiveFlavor::FF; - using UltraFF = UltraRollupRecursiveFlavor::FF; - using RecursiveVerifier = avm::AvmRecursiveVerifier_; + using AvmRecursiveVerifier = avm::AvmRecursiveVerifier_; using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; using MegaProver = UltraProver_; using MegaRecursiveFlavorForUltraCircuit = MegaRecursiveFlavor_; - using UltraRecursiveVerifier = + using MegaVerificationKey = MegaFlavor::VerificationKey; + using MegaRecursiveVerificationKey = MegaRecursiveFlavorForUltraCircuit::VerificationKey; + // A MegaHonk recursive verifier arithmetized with Ultra + using MegaRecursiveVerifier = stdlib::recursion::honk::UltraRecursiveVerifier_; - - /* - Here's an attempt at a flow chat that describes what this function is doing. - We start with an AVMProof and end up with an UltraProof and an IPA proof. - The goal is to verify the AVMProof using a goblin-enhanced MegaCircuit, - and then "blackbox" the goblin plonk component by recursively verifying both - the MegaCircuit proof AND the goblin plonk proofs - in an UltraRecursiveRollupCircuit - - AVMProof (this is `stdlib_proof`) - | - | - v - MegaCircuit (verifies AVMProof) - | - | - v - MergeProof + MegaProof - | | - | ----------------- - v | - ECCVMCircuit + TranslatorCircuit | - | | | - | | | - v v | - ECCVMProof TranslatorProof | - | | | - | | | - v v v - ####UltraRecursiveRollupCircuit##### (this is `builder`) - | - | - v - UltraProof + IPA Proof - */ + using GoblinRecursiveVerifier = stdlib::recursion::honk::GoblinRecursiveVerifier; + using GoblinRecursiveVerifierOutput = stdlib::recursion::honk::GoblinRecursiveVerifierOutput; // STEP 1: To establish consistency of the the proof and public inputs between the inner (Mega) circuit and the // outer (Ultra) circuit, each circuit computes a hash of these components and consistency is checked on the - // result. - // WORKTODO: think through whether this mechanism is needed/makes sense. We dont do anything with these - // components in the ultra circuit aside from hashing them so what exactly is the hash check buying us? + // result. The corresponding hash buffers are constructed here. // Instantiate Mega builder for the inner circuit (AVM proof recursive verifier) GoblinProver goblin; - MegaCircuitBuilder mega_builder(goblin.op_queue); + MegaBuilder mega_builder(goblin.op_queue); // Buffers to be hashed containing the elements of the Mega and Ultra proof, public inputs, and VK std::vector mega_hash_buffer; @@ -130,7 +121,8 @@ class AvmGoblinRecursiveVerifier { return mega_object; }; - // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts + // Convert the stdlib Ultra proof, public inputs, and VK to stdlib Mega counterparts and add them to the + // respective hash buffers. std::vector mega_stdlib_proof = convert_stdlib_ultra_to_stdlib_mega(stdlib_proof); std::vector> mega_public_inputs; mega_public_inputs.reserve(public_inputs.size()); @@ -140,71 +132,65 @@ class AvmGoblinRecursiveVerifier { std::vector key_fields = convert_stdlib_ultra_to_stdlib_mega(outer_key_fields); // Compute the hash of the buffer in the Mega circuit - auto mega_input_hash = stdlib::poseidon2::hash(mega_builder, mega_hash_buffer); + auto mega_input_hash = stdlib::poseidon2::hash(mega_builder, mega_hash_buffer); // WORKTODO: address this or make an issue: NOTE: there doesn't seem to be an easy way to know *which* public // input index will map to mega_input_hash which is troublesome + const size_t mega_hash_public_input_index = mega_builder.public_inputs.size(); mega_input_hash.set_public(); // Add the hash result to the public inputs to propagate it to the outer circuit // Step 2: Construct a Mega-arithmetized AVM recursive verifier circuit - auto stdlib_key = - std::make_shared(mega_builder, std::span(key_fields)); - RecursiveVerifier recursive_verifier{ &mega_builder, stdlib_key }; + auto stdlib_key = std::make_shared(mega_builder, std::span(key_fields)); + AvmRecursiveVerifier recursive_verifier{ &mega_builder, stdlib_key }; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1304): Do proper pairing point aggregation. auto default_agg_object = - stdlib::recursion::init_default_aggregation_state( - mega_builder); + stdlib::recursion::init_default_aggregation_state(mega_builder); [[maybe_unused]] auto mega_agg_output = recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs, default_agg_object); mega_builder.add_pairing_point_accumulator( - stdlib::recursion::init_default_agg_obj_indices(mega_builder)); + stdlib::recursion::init_default_agg_obj_indices(mega_builder)); - // Step 3: Generate a Mega proof of the AVM recursive verifier circuit + // STEP 3: Generate a Mega and Goblin proof {\pi_M, \pi_G} of the AVM recursive verifier circuit + + // Construct Mega proof MegaProver mega_prover(mega_builder); HonkProof mega_proof = mega_prover.construct_proof(); - // Step 4: Construct a corresponding Goblin proof (includes Merge, ECCVM, and Translator proofs) + // Construct corresponding Goblin proof (includes Merge, ECCVM, and Translator proofs) goblin.prove_merge(mega_builder); GoblinProof goblin_proof = goblin.prove(); - // Step 5: Recursively verify the Mega proof in the outer (Ultra) circuit + // STEP 4: Recursively verify the Mega and Goblin proofs {\pi_M, \pi_G} in the outer (Ultra) circuit + + // Recursively verify the Mega proof in the Ultra circuit // TODO(https://github.com/AztecProtocol/barretenberg/issues/1305): Mega + Goblin VKs must be circuit constants. - auto native_outer_vk = std::make_shared(mega_prover.proving_key->proving_key); - auto outer_vk = - std::make_shared(ultra_builder, native_outer_vk); - UltraRecursiveVerifier outer_verifier(ultra_builder, outer_vk); - StdlibProof ultra_proof = bb::convert_native_proof_to_stdlib(ultra_builder, mega_proof); + auto native_outer_vk = std::make_shared(mega_prover.proving_key->proving_key); + auto outer_vk = std::make_shared(ultra_builder, native_outer_vk); + MegaRecursiveVerifier outer_verifier(ultra_builder, outer_vk); + StdlibProof ultra_proof = bb::convert_native_proof_to_stdlib(ultra_builder, mega_proof); auto outer_verifier_output = outer_verifier.verify_proof(ultra_proof, input_agg_obj); - // Step 6: Recursively verify the goblin proof in the Ultra circuit + // Recursively verify the goblin proof in the Ultra circuit GoblinVerifier::VerifierInput goblin_vinput{ std::make_shared(goblin.get_eccvm_proving_key()), std::make_shared( goblin.get_translator_proving_key()) }; - stdlib::recursion::honk::GoblinRecursiveVerifier gverifier{ ultra_builder, goblin_vinput }; - stdlib::recursion::honk::GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(goblin_proof); + GoblinRecursiveVerifier gverifier{ ultra_builder, goblin_vinput }; + GoblinRecursiveVerifierOutput goblin_verifier_output = gverifier.verify(goblin_proof); - // We only call the IPA recursive verifier once, so we can just add this IPA claim and proof + // Propagate the IPA claim via the public inputs of the outer circuit // TODO(https://github.com/AztecProtocol/barretenberg/issues/1306): Determine the right location/entity to // handle this IPA data propagation. ultra_builder->add_ipa_claim(goblin_verifier_output.opening_claim.get_witness_indices()); ultra_builder->ipa_proof = convert_stdlib_proof_to_native(goblin_verifier_output.ipa_transcript->proof_data); ASSERT(ultra_builder->ipa_proof.size() && "IPA proof should not be empty"); - // Step 9: Validate that both `builder` and `inner_builder` use the same AVM proof data and AVM public inputs - // Note: we don't seem to have a nice way of finding out where within a public input space a given value is that - // we call `set_public` on. So we scan manually here to find the index :/ - size_t mega_input_hash_public_input_index = 0; - for (const auto& proof_ele : ultra_proof) { - if (proof_ele.get_value() == mega_input_hash.get_value()) { - break; - } - mega_input_hash_public_input_index += 1; - } - auto ultra_output_hash = - stdlib::poseidon2::hash(*ultra_builder, ultra_hash_buffer); - ultra_proof[mega_input_hash_public_input_index].assert_equal(ultra_output_hash); + // STEP 5: Validate the consistency of the AVM verifier inputs {\pi, pub_inputs, VK}_{AVM} between the inner + // (Mega) circuit and the outer (Ultra) by asserting equality on the hash of this data computed independently by + // each circuit. + + auto ultra_hash = stdlib::poseidon2::hash(*ultra_builder, ultra_hash_buffer); + ultra_proof[mega_hash_public_input_index].assert_equal(ultra_hash); - // Step 10: gather up the ipa proof, ipa claim and output aggregation object produced from verifying the mega - // proof + goblin proof, and return them + // Return the ipa proof, ipa claim and output aggregation object produced from verifying the Mega proof + Goblin RecursiveAvmGoblinOutput result{ .ipa_proof = goblin_verifier_output.ipa_transcript->proof_data, .ipa_claim = goblin_verifier_output.opening_claim, .aggregation_object = outer_verifier_output.agg_obj }; From dc84f4ea62092f1af0e8ba17d2e5f867986902fb Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 20:38:47 +0000 Subject: [PATCH 39/43] remove missed WORKTODO --- .../vm/avm/recursion/goblin_avm_recursive_verifier.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index a990ebddbd3a..d378258d49ee 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -131,14 +131,13 @@ class AvmGoblinRecursiveVerifier { } std::vector key_fields = convert_stdlib_ultra_to_stdlib_mega(outer_key_fields); - // Compute the hash of the buffer in the Mega circuit + // Compute the hash of the buffer in the Mega circuit and save its index within the public inputs auto mega_input_hash = stdlib::poseidon2::hash(mega_builder, mega_hash_buffer); - // WORKTODO: address this or make an issue: NOTE: there doesn't seem to be an easy way to know *which* public - // input index will map to mega_input_hash which is troublesome const size_t mega_hash_public_input_index = mega_builder.public_inputs.size(); mega_input_hash.set_public(); // Add the hash result to the public inputs to propagate it to the outer circuit - // Step 2: Construct a Mega-arithmetized AVM recursive verifier circuit + // STEP 2: Construct a Mega-arithmetized AVM recursive verifier circuit + auto stdlib_key = std::make_shared(mega_builder, std::span(key_fields)); AvmRecursiveVerifier recursive_verifier{ &mega_builder, stdlib_key }; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1304): Do proper pairing point aggregation. From df8517e202e64b9949d3c7339c3370e9b431991e Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 23:14:17 +0000 Subject: [PATCH 40/43] more cleaup and adding an issue --- .../stdlib/goblin_verifier/goblin_recursive_verifier.cpp | 8 +++++--- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 2 -- .../vm/avm/recursion/goblin_avm_recursive_verifier.hpp | 6 +++--- .../barretenberg/vm/avm/tests/recursive_verifier.test.cpp | 5 +++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.cpp index d260c8e6ce8c..5836ab618bbc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.cpp @@ -4,6 +4,9 @@ namespace bb::stdlib::recursion::honk { /** * @brief Runs the Goblin recursive verifier consisting of ECCVM, Translator and Merge verifiers. + * // TODO(https://github.com/AztecProtocol/barretenberg/issues/1309): Implement correct pairing point aggregation. + * Method needs to accept an input agg object, aggregate the pairing points from both translator and merge, then return + * the updated agg object. */ GoblinRecursiveVerifierOutput GoblinRecursiveVerifier::verify(const GoblinProof& proof) { @@ -15,8 +18,7 @@ GoblinRecursiveVerifierOutput GoblinRecursiveVerifier::verify(const GoblinProof& TranslatorVerifier translator_verifier{ builder, verification_keys.translator_verification_key, eccvm_verifier.transcript }; - - translator_verifier.verify_proof( + [[maybe_unused]] auto translator_pairing_points = translator_verifier.verify_proof( proof.translator_proof, eccvm_verifier.evaluation_challenge_x, eccvm_verifier.batching_challenge_v); // Verify the consistency between the ECCVM and Translator transcript polynomial evaluations @@ -35,7 +37,7 @@ GoblinRecursiveVerifierOutput GoblinRecursiveVerifier::verify(const GoblinProof& MergeVerifier merge_verifier{ builder }; StdlibProof stdlib_merge_proof = bb::convert_native_proof_to_stdlib(builder, proof.merge_proof); - merge_verifier.verify_proof(stdlib_merge_proof); + [[maybe_unused]] auto merge_pairing_points = merge_verifier.verify_proof(stdlib_merge_proof); return { opening_claim, ipa_transcript }; } } // namespace bb::stdlib::recursion::honk diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index 21471bd75542..ad01d5093e95 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -101,7 +101,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); agg_obj.aggregate(nested_agg_obj, recursion_separator); - // agg_obj.aggregate(nested_agg_obj, recursion_separator); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u @@ -146,7 +145,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ pairing_points[1] = pairing_points[1].normalize(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge properly. agg_obj.aggregate(pairing_points, recursion_separator); - // agg_obj.aggregate(pairing_points, recursion_separator); output.agg_obj = std::move(agg_obj); // Extract the IPA claim from the public inputs diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index d378258d49ee..41d14088e9c7 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -97,9 +97,9 @@ class AvmGoblinRecursiveVerifier { using GoblinRecursiveVerifier = stdlib::recursion::honk::GoblinRecursiveVerifier; using GoblinRecursiveVerifierOutput = stdlib::recursion::honk::GoblinRecursiveVerifierOutput; - // STEP 1: To establish consistency of the the proof and public inputs between the inner (Mega) circuit and the - // outer (Ultra) circuit, each circuit computes a hash of these components and consistency is checked on the - // result. The corresponding hash buffers are constructed here. + // STEP 1: To establish consistency of the the proof, public inputs and VK for the AVM between the inner (Mega) + // circuit and the outer (Ultra) circuit, each circuit computes a hash of these components and consistency is + // checked on the result. The corresponding hash buffers are constructed here. // Instantiate Mega builder for the inner circuit (AVM proof recursive verifier) GoblinProver goblin; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 17b8ce6961c6..c5eb7a6e4090 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -152,12 +152,13 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid."; ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed."; - // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit + // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim + // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier. auto outer_proving_key = std::make_shared>(outer_circuit); UltraRollupProver outer_prover(outer_proving_key); auto outer_proof = outer_prover.construct_proof(); - // WORKTODO: are we meaningfully verifying the correct IPA claim here? + // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit auto outer_verification_key = std::make_shared(outer_proving_key->proving_key); auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); From 33ce5f09874695c56982253de27af24b96b0e0fe Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 26 Mar 2025 23:35:11 +0000 Subject: [PATCH 41/43] more comment updates --- .../barretenberg/dsl/acir_format/acir_format.cpp | 4 ++-- .../dsl/acir_format/avm_recursion_constraint.cpp | 14 +++++++++++--- .../recursion/goblin_avm_recursive_verifier.hpp | 4 +++- .../vm/avm/tests/recursive_verifier.test.cpp | 11 +++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 355f3ceca684..b44595f6b3c3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -520,8 +520,8 @@ PairingPointAccumulatorIndices process_avm_recursion_constraints( // Add recursion constraints size_t idx = 0; for (auto& constraint : constraint_system.avm_recursion_constraints) { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1303): Utilize method that employs Goblin in the - // AVM recursive verifier. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1303): Utilize the version of this method that + // employs the Goblinized AVM recursive verifier. current_aggregation_object = create_avm_recursion_constraints( builder, constraint, current_aggregation_object, has_valid_witness_assignments); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 977db44d5c36..0a490e823eae 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -203,7 +203,15 @@ PairingPointAccumulatorIndices create_avm_recursion_constraints( return output_agg_object.get_witness_indices(); } -// NEW VERSION THAT USES GOBLIN PLONK IN VERIFICATION ALGO +/** + * @brief Add constraints associated with recursive verification an AVM proof using Goblin + * + * @param builder + * @param input + * @param input_aggregation_object_indices + * @param has_valid_witness_assignments + * @return HonkRecursionConstraintOutput {pairing agg object, ipa claim, ipa proof} + */ HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( Builder& builder, const RecursionConstraint& input, @@ -223,7 +231,7 @@ HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( return result; }; - // Construct in-circuit representation of the verification key, proof and public inputs + // Construct in-circuit representations of the verification key, proof and public inputs const auto key_fields = fields_from_witnesses(input.key); const auto proof_fields = fields_from_witnesses(input.proof); const auto public_inputs_flattened = fields_from_witnesses(input.public_inputs); @@ -243,7 +251,7 @@ HonkRecursionConstraintOutput create_avm_recursion_constraints_goblin( create_dummy_vkey_and_proof(builder, input.proof.size(), input.public_inputs.size(), key_fields, proof_fields); } - // Recursively verify the proof + // Execute the Goblin AVM recursive verifier RecursiveVerifier verifier(&builder, key_fields); aggregation_state_ct input_agg_obj = bb::stdlib::recursion::convert_witness_indices_to_agg_obj( builder, input_aggregation_object_indices); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 41d14088e9c7..42feecb558a4 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -62,6 +62,8 @@ class AvmGoblinRecursiveVerifier { using AvmRecursiveFlavor = AvmRecursiveFlavor_; using AvmRecursiveVerificationKey = AvmRecursiveFlavor::VerificationKey; + // The structure of the final output of the goblinized AVM recursive verifier. The IPA data comes from recursive + // verification of the ECCVM proof as part of Goblin recursive verification. struct RecursiveAvmGoblinOutput { std::vector ipa_proof; OpeningClaim> ipa_claim; @@ -189,7 +191,7 @@ class AvmGoblinRecursiveVerifier { auto ultra_hash = stdlib::poseidon2::hash(*ultra_builder, ultra_hash_buffer); ultra_proof[mega_hash_public_input_index].assert_equal(ultra_hash); - // Return the ipa proof, ipa claim and output aggregation object produced from verifying the Mega proof + Goblin + // Return ipa proof, ipa claim and output aggregation object produced from verifying the Mega + Goblin proofs RecursiveAvmGoblinOutput result{ .ipa_proof = goblin_verifier_output.ipa_transcript->proof_data, .ipa_claim = goblin_verifier_output.opening_claim, .aggregation_object = outer_verifier_output.agg_obj }; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index c5eb7a6e4090..5a0e8e33f88b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -106,6 +106,13 @@ class AvmRecursiveTests : public ::testing::Test { } }; +/** + * @brief A test of the Goblinized AVM recursive verifier. + * @details Constructs a simple AVM circuit for which a proof is verified using the Goblinized AVM recursive verifier. A + * proof is constructed and verified for the outer (Ultra) circuit produced by this algorithm. See the documentation in + * AvmGoblinRecursiveVerifier for details of the recursive verification algorithm. + * + */ TEST_F(AvmRecursiveTests, GoblinRecursion) { using AvmRecursiveVerifier = avm::AvmGoblinRecursiveVerifier; @@ -167,6 +174,10 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) EXPECT_TRUE(final_verifier.verify_proof(outer_proof, outer_proving_key->proving_key.ipa_proof)); } +/** + * @brief A test of the "vanilla" Ultra-arithmetized AVM recursive verifier. + * + */ TEST_F(AvmRecursiveTests, recursion) { if (std::getenv("AVM_ENABLE_FULL_PROVING") == nullptr) { From 7155b0f2f165c62f37355c05aa6d3d7397683b95 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 27 Mar 2025 00:01:42 +0000 Subject: [PATCH 42/43] tinyfix --- .../src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp index 5a0e8e33f88b..d6e607b0882d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/recursive_verifier.test.cpp @@ -166,8 +166,7 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) auto outer_proof = outer_prover.construct_proof(); // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit - auto outer_verification_key = - std::make_shared(outer_proving_key->proving_key); + auto outer_verification_key = std::make_shared(outer_proving_key->proving_key); auto ipa_verification_key = std::make_shared>(1 << CONST_ECCVM_LOG_N); UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key); From 46c7525af7f59a2e3368c525ce5dddf762fa823d Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Mon, 31 Mar 2025 21:00:12 +0000 Subject: [PATCH 43/43] comment fixes --- .../vm/avm/recursion/goblin_avm_recursive_verifier.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp index 42feecb558a4..7c4f943af944 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/goblin_avm_recursive_verifier.hpp @@ -39,9 +39,9 @@ namespace bb::avm { * * The Ultra-arithmetized circuit C_U is responsible for recursive verification of {\pi_M, \pi_G}, i.e. it contains both * a Mega and a Goblin recursive verifier. The output of this recursive verification is a pairing check accumulator and - * an IPA claim accumulator. To ensure proper transfer the AVM verifier inputs {\pi, pub_inputs, VK}_{AVM} between the - * Mega and Ultra circuits, we utilize a hash consistency check. The representation of these inputs in C_M is hashed and - * the result h_M is propagated via the public inputs (i.e. it will be contained in \pi_M.pub_inputs). Then, C_U + * an IPA claim accumulator. To ensure proper transfer of the AVM verifier inputs {\pi, pub_inputs, VK}_{AVM} between + * the Mega and Ultra circuits, we utilize a hash consistency check. The representation of these inputs in C_M is hashed + * and the result h_M is propagated via the public inputs (i.e. it will be contained in \pi_M.pub_inputs). Then, C_U * computes the hash h_U of its own representation of the same data and performs the check h_U = \pi_M.pub_inputs.h_M. * * @note The Mega circuit must be constrained to be a genuine AVM verifier circuit. This is done by fixing the VK(s) @@ -99,7 +99,7 @@ class AvmGoblinRecursiveVerifier { using GoblinRecursiveVerifier = stdlib::recursion::honk::GoblinRecursiveVerifier; using GoblinRecursiveVerifierOutput = stdlib::recursion::honk::GoblinRecursiveVerifierOutput; - // STEP 1: To establish consistency of the the proof, public inputs and VK for the AVM between the inner (Mega) + // STEP 1: To establish consistency of the proof, public inputs and VK for the AVM between the inner (Mega) // circuit and the outer (Ultra) circuit, each circuit computes a hash of these components and consistency is // checked on the result. The corresponding hash buffers are constructed here.