Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: extract merge from UC and simplify #4343

Merged
merged 6 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions barretenberg/cpp/src/barretenberg/goblin/goblin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "barretenberg/proof_system/instance_inspector.hpp"
#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp"
#include "barretenberg/translator_vm/goblin_translator_composer.hpp"
#include "barretenberg/ultra_honk/merge_prover.hpp"
#include "barretenberg/ultra_honk/merge_verifier.hpp"
#include "barretenberg/ultra_honk/ultra_composer.hpp"

namespace bb {
Expand All @@ -33,7 +35,8 @@ class Goblin {
using TranslatorBuilder = bb::GoblinTranslatorCircuitBuilder;
using TranslatorComposer = bb::GoblinTranslatorComposer;
using RecursiveMergeVerifier = bb::stdlib::recursion::goblin::MergeRecursiveVerifier_<GoblinUltraCircuitBuilder>;
using MergeVerifier = bb::MergeVerifier_<GoblinUltraFlavor>;
using MergeProver = bb::MergeProver;
using MergeVerifier = bb::MergeVerifier;
/**
* @brief Output of goblin::accumulate; an Ultra proof and the corresponding verification key
*
Expand Down Expand Up @@ -107,7 +110,7 @@ class Goblin {
auto ultra_proof = prover.construct_proof();

// Construct and store the merge proof to be recursively verified on the next call to accumulate
auto merge_prover = composer.create_merge_prover(op_queue);
MergeProver merge_prover{ op_queue };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan to eventually do this for all Provers/Verifiers or is there sth specific to the Merge ones that motivated this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Merge classes it didn't make any sense for the Composer to be involved, this was just leftover from when the merge protocol was baked into the main Ultra protocol. More generally, it makes sense for classes to be constructed directly via their constructors when possible. The only argument for having methods like create_prover/verifier on the Composer is that the composer is doing some behind the scenes work that's required for the construction of both of these classes. I suspect that we could do what I've done here for most of the other classes currently constructed by the composer and I think we probably should.

merge_proof = merge_prover.construct_proof();

if (!merge_proof_exists) {
Expand Down Expand Up @@ -211,7 +214,7 @@ class Goblin {
// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): no merge prover for now since we're not
// mocking the first set of ecc ops
// // Construct and store the merge proof to be recursively verified on the next call to accumulate
// auto merge_prover = composer.create_merge_prover(op_queue);
// MergeProver merge_prover{ op_queue };
// merge_proof = merge_prover.construct_proof();

// if (!merge_proof_exists) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class RecursiveMergeVerifierTest : public testing::Test {

// Generate a proof over the inner circuit
InnerComposer inner_composer;
auto merge_prover = inner_composer.create_merge_prover(op_queue);
MergeProver merge_prover{ op_queue };
auto merge_proof = merge_prover.construct_proof();

// Create a recursive merge verification circuit for the merge proof
Expand All @@ -61,7 +61,7 @@ class RecursiveMergeVerifierTest : public testing::Test {

// Check 1: Perform native merge verification then perform the pairing on the outputs of the recursive merge
// verifier and check that the result agrees.
auto native_verifier = inner_composer.create_merge_verifier();
MergeVerifier native_verifier;
bool verified_native = native_verifier.verify_proof(merge_proof);
VerifierCommitmentKey pcs_verification_key(0, srs::get_crs_factory());
auto verified_recursive =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "barretenberg/common/log.hpp"
#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp"
#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp"
#include "barretenberg/ultra_honk/merge_prover.hpp"
#include "barretenberg/ultra_honk/merge_verifier.hpp"
#include "barretenberg/ultra_honk/ultra_composer.hpp"
#include "barretenberg/ultra_honk/ultra_prover.hpp"

Expand Down Expand Up @@ -71,10 +73,10 @@ class GoblinUltraHonkComposerTests : public ::testing::Test {
* @brief Construct and verify a Goblin ECC op queue merge proof
*
*/
bool construct_and_verify_merge_proof(auto& composer, auto& op_queue)
bool construct_and_verify_merge_proof(auto& op_queue)
{
auto merge_prover = composer.create_merge_prover(op_queue);
auto merge_verifier = composer.create_merge_verifier();
MergeProver merge_prover{ op_queue };
MergeVerifier merge_verifier;
auto merge_proof = merge_prover.construct_proof();
bool verified = merge_verifier.verify_proof(merge_proof);

Expand Down Expand Up @@ -108,7 +110,7 @@ TEST_F(GoblinUltraHonkComposerTests, SingleCircuit)
EXPECT_TRUE(honk_verified);

// Construct and verify Goblin ECC op queue Merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
auto merge_verified = construct_and_verify_merge_proof(op_queue);
EXPECT_TRUE(merge_verified);
}

Expand All @@ -135,7 +137,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsMergeOnly)
auto composer = GoblinUltraComposer();

// Construct and verify Goblin ECC op queue Merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
auto merge_verified = construct_and_verify_merge_proof(op_queue);
EXPECT_TRUE(merge_verified);
}
}
Expand Down Expand Up @@ -195,7 +197,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkAndMerge)
EXPECT_TRUE(honk_verified);

// Construct and verify Goblin ECC op queue Merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
auto merge_verified = construct_and_verify_merge_proof(op_queue);
EXPECT_TRUE(merge_verified);
}

Expand Down
46 changes: 22 additions & 24 deletions barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
namespace bb {

/**
* Create MergeProver_
* @brief Create MergeProver
* @details We require an SRS at least as large as the current op queue size in order to commit to the shifted
* per-circuit contribution t_i^{shift}
*
*/
template <typename Flavor>
MergeProver_<Flavor>::MergeProver_(const std::shared_ptr<CommitmentKey>& commitment_key,
const std::shared_ptr<ECCOpQueue>& op_queue,
const std::shared_ptr<Transcript>& transcript)
: transcript(transcript)
, op_queue(op_queue)
, pcs_commitment_key(commitment_key)
{}
MergeProver::MergeProver(const std::shared_ptr<ECCOpQueue>& op_queue)
: op_queue(op_queue)
, pcs_commitment_key(std::make_shared<CommitmentKey>(op_queue->ultra_ops[0].size()))
{
// Update internal size data in the op queue that allows for extraction of e.g. previous aggregate transcript
op_queue->set_size_data();
}

/**
* @brief Prove proper construction of the aggregate Goblin ECC op queue polynomials T_i^(j), j = 1,2,3,4.
Expand All @@ -26,11 +27,12 @@ MergeProver_<Flavor>::MergeProver_(const std::shared_ptr<CommitmentKey>& commitm
* TODO(#746): Prove connection between t_i^{shift}, committed to herein, and t_i, used in the main protocol. See issue
* for details (https://github.com/AztecProtocol/barretenberg/issues/746).
*
* @tparam Flavor
* @return HonkProof&
* @return honk::proof
*/
template <typename Flavor> HonkProof& MergeProver_<Flavor>::construct_proof()
HonkProof MergeProver::construct_proof()
{
transcript = std::make_shared<Transcript>();

size_t N = op_queue->get_current_size();

// Extract T_i, T_{i-1}
Expand All @@ -40,14 +42,14 @@ template <typename Flavor> HonkProof& MergeProver_<Flavor>::construct_proof()
ASSERT(T_prev[0].size() > 0);

// Construct t_i^{shift} as T_i - T_{i-1}
std::array<Polynomial, Flavor::NUM_WIRES> t_shift;
for (size_t i = 0; i < Flavor::NUM_WIRES; ++i) {
std::array<Polynomial, NUM_WIRES> t_shift;
for (size_t i = 0; i < NUM_WIRES; ++i) {
t_shift[i] = Polynomial(T_current[i]);
t_shift[i] -= T_prev[i];
}

// Compute/get commitments [t_i^{shift}], [T_{i-1}], and [T_i] and add to transcript
std::array<Commitment, Flavor::NUM_WIRES> C_T_current;
std::array<Commitment, NUM_WIRES> C_T_current;
for (size_t idx = 0; idx < t_shift.size(); ++idx) {
// Get previous transcript commitment [T_{i-1}] from op queue
auto C_T_prev = op_queue->ultra_ops_commitments[idx];
Expand All @@ -72,20 +74,20 @@ template <typename Flavor> HonkProof& MergeProver_<Flavor>::construct_proof()
// Add univariate opening claims for each polynomial.
std::vector<OpeningClaim> opening_claims;
// Compute evaluation T_{i-1}(\kappa)
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
auto polynomial = Polynomial(T_prev[idx]);
auto evaluation = polynomial.evaluate(kappa);
transcript->send_to_verifier("T_prev_eval_" + std::to_string(idx + 1), evaluation);
opening_claims.emplace_back(OpeningClaim{ polynomial, { kappa, evaluation } });
}
// Compute evaluation t_i^{shift}(\kappa)
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
auto evaluation = t_shift[idx].evaluate(kappa);
transcript->send_to_verifier("t_shift_eval_" + std::to_string(idx + 1), evaluation);
opening_claims.emplace_back(OpeningClaim{ t_shift[idx], { kappa, evaluation } });
}
// Compute evaluation T_i(\kappa)
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
auto polynomial = Polynomial(T_current[idx]);
auto evaluation = polynomial.evaluate(kappa);
transcript->send_to_verifier("T_current_eval_" + std::to_string(idx + 1), evaluation);
Expand All @@ -112,11 +114,7 @@ template <typename Flavor> HonkProof& MergeProver_<Flavor>::construct_proof()
auto quotient_commitment = pcs_commitment_key->commit(quotient);
transcript->send_to_verifier("KZG:W", quotient_commitment);

proof = transcript->proof_data;
return proof;
return transcript->proof_data;
}

template class MergeProver_<UltraFlavor>;
template class MergeProver_<GoblinUltraFlavor>;

} // namespace bb
} // namespace bb
31 changes: 14 additions & 17 deletions barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,28 @@ namespace bb {
/**
* @brief Prover class for the Goblin ECC op queue transcript merge protocol
*
* @tparam Flavor
*/
template <typename Flavor> class MergeProver_ {
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using CommitmentKey = typename Flavor::CommitmentKey;
using Commitment = typename Flavor::Commitment;
using PCS = typename Flavor::PCS;
using Curve = typename Flavor::Curve;
using OpeningClaim = ProverOpeningClaim<Curve>;
using OpeningPair = bb::OpeningPair<Curve>;
class MergeProver {
using Curve = curve::BN254;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't still be taken from the flavor, even though GoblinUltra flavor is going to be the fixed one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because essentially the only data being extracted from the Flavor here was curve data. We'll only ever run the merge protocol on bn254 so there's no need for templating (similar to how the Translator is not templated).

using FF = Curve::ScalarField;
using Polynomial = polynomial;
using CommitmentKey = bb::CommitmentKey<Curve>;
using Commitment = Curve::AffineElement;
using PCS = bb::KZG<Curve>;
using OpeningClaim = typename bb::ProverOpeningClaim<Curve>;
using Transcript = BaseTranscript;

public:
std::shared_ptr<Transcript> transcript;
std::shared_ptr<ECCOpQueue> op_queue;
std::shared_ptr<CommitmentKey> pcs_commitment_key;

explicit MergeProver_(const std::shared_ptr<CommitmentKey>&,
const std::shared_ptr<ECCOpQueue>&,
const std::shared_ptr<Transcript>& transcript = std::make_shared<Transcript>());
BBERG_PROFILE HonkProof& construct_proof();
explicit MergeProver(const std::shared_ptr<ECCOpQueue>&);

BBERG_PROFILE HonkProof construct_proof();

private:
HonkProof proof;
std::shared_ptr<ECCOpQueue> op_queue;
std::shared_ptr<CommitmentKey> pcs_commitment_key;
static constexpr size_t NUM_WIRES = GoblinUltraFlavor::NUM_WIRES;
};

} // namespace bb
36 changes: 15 additions & 21 deletions barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

namespace bb {

template <typename Flavor>
MergeVerifier_<Flavor>::MergeVerifier_()
: transcript(std::make_shared<Transcript>())
, pcs_verification_key(std::make_unique<VerifierCommitmentKey>(0, bb::srs::get_crs_factory())){};
MergeVerifier::MergeVerifier()
: pcs_verification_key(std::make_unique<VerifierCommitmentKey>(0, bb::srs::get_crs_factory())){};

/**
* @brief Verify proper construction of the aggregate Goblin ECC op queue polynomials T_i^(j), j = 1,2,3,4.
Expand All @@ -15,18 +13,17 @@ MergeVerifier_<Flavor>::MergeVerifier_()
* M_{i-1}), where the shift magnitude M_{i-1} is the length of T_{i-1}. This protocol verfies that the aggregate op
* queue has been constructed correctly via a simple Schwartz-Zippel check. Evaluations are checked via batched KZG.
*
* @tparam Flavor
* @return HonkProof&
*/
template <typename Flavor> bool MergeVerifier_<Flavor>::verify_proof(const HonkProof& proof)
bool MergeVerifier::verify_proof(const HonkProof& proof)
{
transcript = std::make_shared<Transcript>(proof);

// Receive commitments [t_i^{shift}], [T_{i-1}], and [T_i]
std::array<Commitment, Flavor::NUM_WIRES> C_T_prev;
std::array<Commitment, Flavor::NUM_WIRES> C_t_shift;
std::array<Commitment, Flavor::NUM_WIRES> C_T_current;
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
std::array<Commitment, NUM_WIRES> C_T_prev;
std::array<Commitment, NUM_WIRES> C_t_shift;
std::array<Commitment, NUM_WIRES> C_T_current;
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
C_T_prev[idx] = transcript->template receive_from_prover<Commitment>("T_PREV_" + std::to_string(idx + 1));
C_t_shift[idx] = transcript->template receive_from_prover<Commitment>("t_SHIFT_" + std::to_string(idx + 1));
C_T_current[idx] = transcript->template receive_from_prover<Commitment>("T_CURRENT_" + std::to_string(idx + 1));
Expand All @@ -35,27 +32,27 @@ template <typename Flavor> bool MergeVerifier_<Flavor>::verify_proof(const HonkP
FF kappa = transcript->get_challenge("kappa");

// Receive transcript poly evaluations and add corresponding univariate opening claims {(\kappa, p(\kappa), [p(X)]}
std::array<FF, Flavor::NUM_WIRES> T_prev_evals;
std::array<FF, Flavor::NUM_WIRES> t_shift_evals;
std::array<FF, Flavor::NUM_WIRES> T_current_evals;
std::array<FF, NUM_WIRES> T_prev_evals;
std::array<FF, NUM_WIRES> t_shift_evals;
std::array<FF, NUM_WIRES> T_current_evals;
std::vector<OpeningClaim> opening_claims;
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
T_prev_evals[idx] = transcript->template receive_from_prover<FF>("T_prev_eval_" + std::to_string(idx + 1));
opening_claims.emplace_back(OpeningClaim{ { kappa, T_prev_evals[idx] }, C_T_prev[idx] });
}
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
t_shift_evals[idx] = transcript->template receive_from_prover<FF>("t_shift_eval_" + std::to_string(idx + 1));
opening_claims.emplace_back(OpeningClaim{ { kappa, t_shift_evals[idx] }, C_t_shift[idx] });
}
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
T_current_evals[idx] =
transcript->template receive_from_prover<FF>("T_current_eval_" + std::to_string(idx + 1));
opening_claims.emplace_back(OpeningClaim{ { kappa, T_current_evals[idx] }, C_T_current[idx] });
}

// Check the identity T_i(\kappa) = T_{i-1}(\kappa) + t_i^{shift}(\kappa). If it fails, return false
bool identity_checked = true;
for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) {
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
identity_checked = identity_checked && (T_current_evals[idx] == T_prev_evals[idx] + t_shift_evals[idx]);
}

Expand All @@ -79,7 +76,4 @@ template <typename Flavor> bool MergeVerifier_<Flavor>::verify_proof(const HonkP
return identity_checked && verified;
}

template class MergeVerifier_<UltraFlavor>;
template class MergeVerifier_<GoblinUltraFlavor>;

} // namespace bb
} // namespace bb
28 changes: 13 additions & 15 deletions barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,25 @@ namespace bb {
/**
* @brief Verifier class for the Goblin ECC op queue transcript merge protocol
*
* @tparam Flavor
*/
template <typename Flavor> class MergeVerifier_ {
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using CommitmentKey = typename Flavor::CommitmentKey;
using Commitment = typename Flavor::Commitment;
using PCS = typename Flavor::PCS;
using Curve = typename Flavor::Curve;
using OpeningClaim = typename bb::OpeningClaim<Curve>;
using VerificationKey = typename Flavor::VerificationKey;
using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey;
using Transcript = typename Flavor::Transcript;
class MergeVerifier {
using Curve = curve::BN254;
using FF = typename Curve::ScalarField;
using Commitment = typename Curve::AffineElement;
using PCS = bb::KZG<Curve>;
using OpeningClaim = bb::OpeningClaim<Curve>;
using VerifierCommitmentKey = bb::VerifierCommitmentKey<Curve>;
using Transcript = BaseTranscript;

public:
std::shared_ptr<Transcript> transcript;
std::shared_ptr<ECCOpQueue> op_queue;
std::shared_ptr<VerifierCommitmentKey> pcs_verification_key;

explicit MergeVerifier_();
explicit MergeVerifier();
bool verify_proof(const HonkProof& proof);

private:
std::shared_ptr<VerifierCommitmentKey> pcs_verification_key;
static constexpr size_t NUM_WIRES = GoblinUltraFlavor::NUM_WIRES;
};

} // namespace bb
Loading
Loading