Skip to content

Commit

Permalink
feat: send G_0 in proof to reduce tube size (#9766)
Browse files Browse the repository at this point in the history
Removes the G^0 MSM computation from the recursive verifier and instead
includes it in the proof.

Adds test to ensure that IPA recursive verifier is a fixed circuit no
matter the ECCVM size.

For the command: `FLOW=prove_then_verify_tube ./run_acir_tests.sh
fold_basic`, which has 6 circuits:
Tube gates before constification and before MSM removal: 7104756
Tube gates after: 4172057

For the ClientTubeBase test with 8 circuits, we see:
Tube before: 10047313
Tube gates after: 4172057
  • Loading branch information
lucasxia01 authored Nov 8, 2024
1 parent 2e13938 commit 9bc5a2f
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ProxyCaller {
const OpeningClaim<Curve>& opening_claim,
const std::shared_ptr<Transcript>& transcript)
{
return IPA<Curve>::reduce_verify_internal(vk, opening_claim, transcript);
return IPA<Curve>::reduce_verify_internal_native(vk, opening_claim, transcript);
}
};
} // namespace bb
Expand Down
72 changes: 21 additions & 51 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@ template <typename Curve_> class IPA {
}

// Step 7
// Send G_0 to the verifier
transcript->send_to_verifier("IPA:G_0", G_vec_local[0]);

// Step 8
// Send a_0 to the verifier
transcript->send_to_verifier("IPA:a_0", a_vec[0]);
}
Expand Down Expand Up @@ -312,7 +316,7 @@ template <typename Curve_> class IPA {
*10. Compute \f$C_{right}=a_{0}G_{s}+a_{0}b_{0}U\f$
*11. Check that \f$C_{right} = C_0\f$. If they match, return true. Otherwise return false.
*/
static bool reduce_verify_internal(const std::shared_ptr<VK>& vk,
static bool reduce_verify_internal_native(const std::shared_ptr<VK>& vk,
const OpeningClaim<Curve>& opening_claim,
auto& transcript)
requires(!Curve::is_stdlib_type)
Expand Down Expand Up @@ -409,6 +413,8 @@ template <typename Curve_> class IPA {
// Compute G₀
Commitment G_zero = bb::scalar_multiplication::pippenger_without_endomorphism_basis_points<Curve>(
s_poly, {&G_vec_local[0], /*size*/ poly_length}, vk->pippenger_runtime_state);
Commitment G_zero_sent = transcript->template receive_from_prover<Commitment>("IPA:G_0");
ASSERT(G_zero == G_zero_sent && "G_0 should be equal to G_0 sent in transcript.");

// Step 9.
// Receive a₀ from the prover
Expand Down Expand Up @@ -436,8 +442,7 @@ template <typename Curve_> class IPA {
* @todo (https://github.com/AztecProtocol/barretenberg/issues/1018): simulator should use the native verify
* function with parallelisation
*/
static VerifierAccumulator reduce_verify_internal(const std::shared_ptr<VK>& vk,
const OpeningClaim<Curve>& opening_claim,
static VerifierAccumulator reduce_verify_internal_recursive(const OpeningClaim<Curve>& opening_claim,
auto& transcript)
requires Curve::is_stdlib_type
{
Expand All @@ -448,9 +453,7 @@ template <typename Curve_> class IPA {
// to a bb::fr, not a grumpkin::fr, which is a BaseField element for
// Grumpkin

// Ensure polynomial length cannot be changed from its default specified valued
poly_length_var.fix_witness();

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1144): need checks here on poly_length.
const auto poly_length = static_cast<uint32_t>(poly_length_var.get_value());

// Step 2.
Expand Down Expand Up @@ -505,47 +508,15 @@ template <typename Curve_> class IPA {
}
}


// Step 5.
// Construct vector s
// We implement a linear-time algorithm to optimally compute this vector
// Note: currently requires an extra vector of size `poly_length / 2` to cache temporaries
// this might able to be optimized if we care enough, but the size of this poly shouldn't be large relative to the builder polynomial sizes
std::vector<Fr> s_vec_temporaries(poly_length / 2);
std::vector<Fr> s_vec(poly_length);

Fr* previous_round_s = &s_vec_temporaries[0];
Fr* current_round_s = &s_vec[0];
// if number of rounds is even we need to swap these so that s_vec always contains the result
if ((log_poly_length & 1) == 0)
{
std::swap(previous_round_s, current_round_s);
}
previous_round_s[0] = Fr(1);
for (size_t i = 0; i < log_poly_length; ++i)
{
const size_t round_size = 1 << (i + 1);
const Fr round_challenge = round_challenges_inv[i];
for (size_t j = 0; j < round_size / 2; ++j)
{
current_round_s[j * 2] = previous_round_s[j];
current_round_s[j * 2 + 1] = previous_round_s[j] * round_challenge;
}
std::swap(current_round_s, previous_round_s);
}
// Receive G₀ from the prover
Commitment G_zero = transcript->template receive_from_prover<Commitment>("IPA:G_0");

// Step 6.
// Receive a₀ from the prover
const auto a_zero = transcript->template receive_from_prover<Fr>("IPA:a_0");

// Step 7.
// Compute G₀
// Unlike the native verification function, the verifier commitment key only containts the SRS so we can apply
// batch_mul directly on it.
const std::vector<Commitment> srs_elements = vk->get_monomial_points();
Commitment G_zero = Commitment::batch_mul(srs_elements, s_vec);

// Step 8.
// Compute R = C' + ∑_{j ∈ [k]} u_j^{-1}L_j + ∑_{j ∈ [k]} u_jR_j - G₀ * a₀ - (f(\beta) + a₀ * b₀) ⋅ U
// This is a combination of several IPA relations into a large batch mul
// which should be equal to -C
Expand All @@ -556,7 +527,7 @@ template <typename Curve_> class IPA {
GroupElement ipa_relation = GroupElement::batch_mul(msm_elements, msm_scalars);
ipa_relation.assert_equal(-opening_claim.commitment);

ASSERT(ipa_relation.get_value() == -opening_claim.commitment.get_value());
ASSERT(ipa_relation.get_value() == -opening_claim.commitment.get_value() && "IPA relation failed.");
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1144): Add proper constraints for taking the log of a field_t.
Fr stdlib_log_poly_length(static_cast<uint256_t>(log_poly_length));
return {stdlib_log_poly_length, round_challenges_inv, G_zero};
Expand Down Expand Up @@ -597,7 +568,7 @@ template <typename Curve_> class IPA {
const auto& transcript)
requires(!Curve::is_stdlib_type)
{
return reduce_verify_internal(vk, opening_claim, transcript);
return reduce_verify_internal_native(vk, opening_claim, transcript);
}

/**
Expand All @@ -613,12 +584,11 @@ template <typename Curve_> class IPA {
*/
// TODO(https://github.com/AztecProtocol/barretenberg/issues/912): Return the proper VerifierAccumulator once
// implemented
static VerifierAccumulator reduce_verify(const std::shared_ptr<VK>& vk,
const OpeningClaim<Curve>& opening_claim,
static VerifierAccumulator reduce_verify(const OpeningClaim<Curve>& opening_claim,
const auto& transcript)
requires(Curve::is_stdlib_type)
{
return reduce_verify_internal(vk, opening_claim, transcript);
return reduce_verify_internal_recursive(opening_claim, transcript);
}
/**
* @brief A method that produces an IPA opening claim from Shplemini accumulator containing vectors of commitments
Expand Down Expand Up @@ -662,7 +632,7 @@ template <typename Curve_> class IPA {
requires(!Curve::is_stdlib_type)
{
const auto opening_claim = reduce_batch_opening_claim(batch_opening_claim);
return reduce_verify_internal(vk, opening_claim, transcript);
return reduce_verify_internal_native(vk, opening_claim, transcript);
}

/**
Expand All @@ -674,12 +644,12 @@ template <typename Curve_> class IPA {
* @return VerifierAccumulator
*/
static VerifierAccumulator reduce_verify_batch_opening_claim(const BatchOpeningClaim<Curve>& batch_opening_claim,
const std::shared_ptr<VK>& vk,
[[maybe_unused]] const std::shared_ptr<VK>& vk,
auto& transcript)
requires(Curve::is_stdlib_type)
{
const auto opening_claim = reduce_batch_opening_claim(batch_opening_claim);
return reduce_verify_internal(vk, opening_claim, transcript);
return reduce_verify_internal_recursive(opening_claim, transcript);
}

/**
Expand Down Expand Up @@ -789,13 +759,13 @@ template <typename Curve_> class IPA {
* @param claim_2
* @return std::pair<OpeningClaim<Curve>, Polynomial<bb::fq>>
*/
static std::pair<OpeningClaim<Curve>, Polynomial<bb::fq>> accumulate(const std::shared_ptr<VK>& verifier_ck, auto& transcript_1, OpeningClaim<Curve> claim_1, auto& transcript_2, OpeningClaim<Curve> claim_2)
static std::pair<OpeningClaim<Curve>, Polynomial<bb::fq>> accumulate(auto& transcript_1, OpeningClaim<Curve> claim_1, auto& transcript_2, OpeningClaim<Curve> claim_2)
requires Curve::is_stdlib_type
{
using Builder = typename Curve::Builder;
// Step 1: Run the verifier for each IPA instance
VerifierAccumulator pair_1 = reduce_verify(verifier_ck, claim_1, transcript_1);
VerifierAccumulator pair_2 = reduce_verify(verifier_ck, claim_2, transcript_2);
VerifierAccumulator pair_1 = reduce_verify(claim_1, transcript_1);
VerifierAccumulator pair_2 = reduce_verify(claim_2, transcript_2);

// Step 2: Generate the challenges by hashing the pairs
using StdlibTranscript = BaseTranscript<stdlib::recursion::honk::StdlibTranscriptParams<Builder>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ TEST_F(IPATest, ChallengesAreZero)
auto new_random_vector = random_vector;
new_random_vector[i] = Fr::zero();
transcript->initialize(new_random_vector, lrs, { uint256_t(n) });
EXPECT_ANY_THROW(IPA::reduce_verify_internal(this->vk(), opening_claim, transcript));
EXPECT_ANY_THROW(IPA::reduce_verify_internal_native(this->vk(), opening_claim, transcript));
}
}

Expand Down Expand Up @@ -179,7 +179,7 @@ TEST_F(IPATest, AIsZeroAfterOneRound)
transcript->reset_indices();

// Verify
EXPECT_TRUE(IPA::reduce_verify_internal(this->vk(), opening_claim, transcript));
EXPECT_TRUE(IPA::reduce_verify_internal_native(this->vk(), opening_claim, transcript));
}
#endif
} // namespace bb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "barretenberg/stdlib/primitives/curves/grumpkin.hpp"
#include "barretenberg/stdlib/transcript/transcript.hpp"
#include "barretenberg/transcript/transcript.hpp"
#include "barretenberg/ultra_honk/decider_proving_key.hpp"

using namespace bb;

Expand Down Expand Up @@ -60,23 +61,85 @@ class IPARecursiveTests : public CommitmentTest<NativeCurve> {
return { recursive_verifier_transcript, stdlib_opening_claim };
}

Builder build_ipa_recursive_verifier_circuit(const size_t POLY_LENGTH)
{
Builder builder;
auto [stdlib_transcript, stdlib_claim] = create_ipa_claim(builder, POLY_LENGTH);

RecursiveIPA::reduce_verify(stdlib_claim, stdlib_transcript);
builder.finalize_circuit(/*ensure_nonzero=*/true);
return builder;
}

/**
* @brief Tests IPA recursion
* @details Creates an IPA claim and then runs the recursive IPA verification and checks that the circuit is valid.
* @param POLY_LENGTH
*/
void test_recursive_ipa(const size_t POLY_LENGTH)
{
Builder builder;
auto recursive_verifier_ck = std::make_shared<VerifierCommitmentKey<Curve>>(&builder, POLY_LENGTH, this->vk());
auto [stdlib_transcript, stdlib_claim] = create_ipa_claim(builder, POLY_LENGTH);

RecursiveIPA::reduce_verify(recursive_verifier_ck, stdlib_claim, stdlib_transcript);
builder.finalize_circuit(/*ensure_nonzero=*/false);
Builder builder(build_ipa_recursive_verifier_circuit(POLY_LENGTH));
info("IPA Recursive Verifier num finalized gates = ", builder.get_num_finalized_gates());
EXPECT_TRUE(CircuitChecker::check(builder));
}

/**
* @brief Checks the the IPA Recursive Verifier circuit is fixed no matter the ECCVM trace size.
* @details Compares the builder blocks and locates which index in which block is different. Also compares the vks
* to find which commitment is different.
*/
void test_fixed_ipa_recursive_verifier()
{

srs::init_crs_factory("../srs_db/ignition");

Builder builder_1(build_ipa_recursive_verifier_circuit(1 << 10));
Builder builder_2(build_ipa_recursive_verifier_circuit(1 << 11));

UltraFlavor::ProvingKey pk_1 = (DeciderProvingKey_<UltraFlavor>(builder_1)).proving_key;
UltraFlavor::ProvingKey pk_2 = (DeciderProvingKey_<UltraFlavor>(builder_2)).proving_key;
UltraFlavor::VerificationKey verification_key_1(pk_1);
UltraFlavor::VerificationKey verification_key_2(pk_2);
bool broke(false);
auto check_eq = [&broke](auto& p1, auto& p2) {
EXPECT_TRUE(p1.size() == p2.size());
for (size_t idx = 0; idx < p1.size(); idx++) {
if (p1[idx] != p2[idx]) {
broke = true;
break;
}
}
};

// Compares block by block to find which gate is different.
size_t block_idx = 0;
for (auto [b_10, b_11] : zip_view(builder_1.blocks.get(), builder_2.blocks.get())) {
info("block index: ", block_idx);
EXPECT_TRUE(b_10.q_1().size() == b_11.q_1().size());
EXPECT_TRUE(b_10.selectors.size() == 13);
EXPECT_TRUE(b_11.selectors.size() == 13);
for (auto [p_10, p_11] : zip_view(b_10.selectors, b_11.selectors)) {
check_eq(p_10, p_11);
}
block_idx++;
}

EXPECT_TRUE(verification_key_1.circuit_size == verification_key_2.circuit_size);
EXPECT_TRUE(verification_key_1.num_public_inputs == verification_key_2.num_public_inputs);

// Compares the VKs to find which commitment is different.
UltraFlavor::CommitmentLabels labels;
for (auto [vk_10, vk_11, label] :
zip_view(verification_key_1.get_all(), verification_key_2.get_all(), labels.get_precomputed())) {
if (vk_10 != vk_11) {
broke = true;
info("Mismatch verification key label: ", label, " left: ", vk_10, " right: ", vk_11);
}
}

EXPECT_FALSE(broke);
}

/**
* @brief Tests IPA accumulation by accumulating two IPA claims and proving the accumulated claim
* @details Creates two IPA claims, and then two IPA accumulators through recursive verification. Proves the
Expand All @@ -89,15 +152,13 @@ class IPARecursiveTests : public CommitmentTest<NativeCurve> {
// accumulate the claims into one claim. This accumulation is done in circuit. Create two accumulators, which
// contain the commitment and an opening claim.
Builder builder;
auto recursive_verifier_ck = std::make_shared<VerifierCommitmentKey<Curve>>(&builder, POLY_LENGTH, this->vk());

auto [transcript_1, claim_1] = create_ipa_claim(builder, POLY_LENGTH);
auto [transcript_2, claim_2] = create_ipa_claim(builder, POLY_LENGTH);

// Creates two IPA accumulators and accumulators from the two claims. Also constructs the accumulated h
// polynomial.
auto [output_claim, challenge_poly] =
RecursiveIPA::accumulate(recursive_verifier_ck, transcript_1, claim_1, transcript_2, claim_2);
auto [output_claim, challenge_poly] = RecursiveIPA::accumulate(transcript_1, claim_1, transcript_2, claim_2);
builder.finalize_circuit(/*ensure_nonzero=*/false);
info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
builder.get_num_finalized_gates());
Expand Down Expand Up @@ -168,4 +229,9 @@ TEST_F(IPARecursiveTests, AccumulateSmall)
TEST_F(IPARecursiveTests, AccumulateMedium)
{
test_accumulation(/*POLY_LENGTH=*/1024);
}

TEST_F(IPARecursiveTests, ConstantVerifier)
{
test_fixed_ipa_recursive_verifier();
}
5 changes: 4 additions & 1 deletion barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ class ECCVMFlavor {
uint32_t ipa_poly_degree;
std::vector<Commitment> ipa_l_comms;
std::vector<Commitment> ipa_r_comms;
Commitment ipa_G_0_eval;
FF ipa_a_0_eval;

Transcript() = default;
Expand Down Expand Up @@ -1191,7 +1192,8 @@ class ECCVMFlavor {
ipa_r_comms.emplace_back(NativeTranscript::template deserialize_from_buffer<Commitment>(
NativeTranscript::proof_data, num_frs_read));
}

ipa_G_0_eval = NativeTranscript::template deserialize_from_buffer<Commitment>(NativeTranscript::proof_data,
num_frs_read);
ipa_a_0_eval =
NativeTranscript::template deserialize_from_buffer<FF>(NativeTranscript::proof_data, num_frs_read);
}
Expand Down Expand Up @@ -1340,6 +1342,7 @@ class ECCVMFlavor {
NativeTranscript::template serialize_to_buffer(ipa_r_comms[i], NativeTranscript::proof_data);
}

serialize_to_buffer(ipa_G_0_eval, proof_data);
serialize_to_buffer(ipa_a_0_eval, proof_data);

ASSERT(NativeTranscript::proof_data.size() == old_proof_length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class ECCVMTranscriptTests : public ::testing::Test {
}

round++;
manifest_expected.add_entry(round, "IPA:G_0", frs_per_G);
manifest_expected.add_entry(round, "IPA:a_0", frs_per_Fr);
manifest_expected.add_challenge(round, "Translation:batching_challenge");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ template <typename Flavor> void ECCVMRecursiveVerifier_<Flavor>::verify_proof(co

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1142): Handle this return value correctly.
const typename PCS::VerifierAccumulator batched_opening_accumulator =
PCS::reduce_verify(key->pcs_verification_key, batch_opening_claim, transcript);

PCS::reduce_verify(batch_opening_claim, transcript);
ASSERT(sumcheck_verified);
}

Expand Down

0 comments on commit 9bc5a2f

Please sign in to comment.