17template <
typename Curve,
typename =
void>
struct BuilderTypeHelper {
18 struct DummyBuilder {};
23 using type =
typename Curve::Builder;
31template <
typename Curve>
class MergeTests :
public testing::Test {
56 using InnerBuilder =
typename InnerFlavor::CircuitBuilder;
59 for (
size_t idx = 0; idx < num_subtables_up_to_tail; ++idx) {
60 InnerBuilder circuit{ op_queue };
65 op_queue->construct_zk_columns();
67 InnerBuilder hiding_circuit{ op_queue };
76 template <
typename T>
static auto to_native(
const T& val)
79 return val.get_value();
92 auto commitment = Commitment::from_witness(&
builder, native_commitment);
93 commitment.unset_free_witness_tag();
97 return native_commitment;
137 const size_t shift_idx = 0;
138 const size_t m_commitment_idx = 1;
139 const size_t l_eval_idx = 22;
141 switch (tampering_mode) {
144 merge_proof[shift_idx] +=
bb::fr(1);
149 FrCodec::deserialize_from_fields<curve::BN254::AffineElement>(std::span{ merge_proof }.subspan(
150 m_commitment_idx, FrCodec::calc_num_fields<curve::BN254::AffineElement>()));
151 m_commitment = m_commitment + curve::BN254::AffineElement::one();
152 auto m_commitment_frs = FrCodec::serialize_to_fields<curve::BN254::AffineElement>(m_commitment);
153 for (
size_t idx = 0; idx < 4; ++idx) {
154 merge_proof[m_commitment_idx + idx] = m_commitment_frs[idx];
160 merge_proof[l_eval_idx] -=
bb::fr(1);
174 const bool expected =
true)
178 MergeProver merge_prover{ op_queue, prover_transcript };
183 auto t_current = op_queue->construct_current_ultra_ops_subtable_columns();
184 auto T_prev = op_queue->construct_table_columns_up_to_tail();
188 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
189 native_t_commitments[idx] = merge_prover.pcs_commitment_key.commit(t_current[idx]);
190 native_T_prev_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_prev[idx]);
193 auto T_merged = op_queue->construct_ultra_ops_table_columns();
195 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
196 expected_merged_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_merged[idx]);
204 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
217 bool verified = pairing_verified && result.reduction_succeeded;
218 EXPECT_EQ(verified, expected);
222 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
223 EXPECT_EQ(
to_native(result.merged_commitments[idx]), expected_merged_commitments[idx])
224 <<
"Merged table commitment mismatch at index " << idx;
231 EXPECT_EQ(circuit_valid, expected);
248 EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE);
309 GTEST_SKIP() <<
"Native-only test";
315 using InnerBuilder =
typename InnerFlavor::CircuitBuilder;
321 InnerBuilder circuit{ op_queue };
327 op_queue->construct_ultra_ops_table_columns(
false);
330 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
332 merged_table[idx] =
Polynomial(right_table[idx]);
339 prover_transcript->send_to_verifier(
"shift_size",
static_cast<uint32_t
>(0));
341 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
342 prover_transcript->send_to_verifier(
"MERGED_TABLE_" +
std::to_string(idx),
343 ck.commit(merged_table[idx]));
347 "LEFT_TABLE_DEGREE_CHECK_1",
348 "LEFT_TABLE_DEGREE_CHECK_2",
349 "LEFT_TABLE_DEGREE_CHECK_3" };
351 prover_transcript->template get_challenges<bb::fr>(degree_labels);
353 prover_transcript->send_to_verifier(
"REVERSED_BATCHED_LEFT_TABLES",
354 ck.commit(reversed_batched_left_tables));
357 "SHPLONK_MERGE_BATCHING_CHALLENGE_0",
"SHPLONK_MERGE_BATCHING_CHALLENGE_1",
358 "SHPLONK_MERGE_BATCHING_CHALLENGE_2",
"SHPLONK_MERGE_BATCHING_CHALLENGE_3",
359 "SHPLONK_MERGE_BATCHING_CHALLENGE_4",
"SHPLONK_MERGE_BATCHING_CHALLENGE_5",
360 "SHPLONK_MERGE_BATCHING_CHALLENGE_6",
"SHPLONK_MERGE_BATCHING_CHALLENGE_7",
361 "SHPLONK_MERGE_BATCHING_CHALLENGE_8",
"SHPLONK_MERGE_BATCHING_CHALLENGE_9",
362 "SHPLONK_MERGE_BATCHING_CHALLENGE_10",
"SHPLONK_MERGE_BATCHING_CHALLENGE_11",
363 "SHPLONK_MERGE_BATCHING_CHALLENGE_12"
365 auto shplonk_batching_challenges = prover_transcript->template get_challenges<bb::fr>(shplonk_labels);
367 bb::fr kappa = prover_transcript->template get_challenge<bb::fr>(
"kappa");
371 std::vector<bb::fr> evals;
372 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
373 evals.emplace_back(left_table[idx].evaluate(kappa));
374 prover_transcript->send_to_verifier(
"LEFT_TABLE_EVAL_" +
std::to_string(idx), evals.back());
376 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
377 evals.emplace_back(right_table[idx].evaluate(kappa));
378 prover_transcript->send_to_verifier(
"RIGHT_TABLE_EVAL_" +
std::to_string(idx), evals.back());
380 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
381 evals.emplace_back(merged_table[idx].evaluate(kappa));
382 prover_transcript->send_to_verifier(
"MERGED_TABLE_EVAL_" +
std::to_string(idx), evals.back());
384 evals.emplace_back(reversed_batched_left_tables.
evaluate(kappa_inv));
385 prover_transcript->send_to_verifier(
"REVERSED_BATCHED_LEFT_TABLES_EVAL", evals.back());
389 size_t poly_size = merged_table[0].size();
390 Polynomial shplonk_batched_quotient(poly_size);
392 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
396 shplonk_batched_quotient.
add_scaled(right_table[idx], beta_r);
397 shplonk_batched_quotient.
at(0) -= beta_r * evals[
NUM_WIRES + idx];
400 shplonk_batched_quotient.
add_scaled(merged_table[idx], beta_m);
401 shplonk_batched_quotient.
at(0) -= beta_m * evals[2 *
NUM_WIRES + idx];
406 prover_transcript->send_to_verifier(
"SHPLONK_BATCHED_QUOTIENT",
ck.commit(shplonk_batched_quotient));
409 bb::fr z = prover_transcript->template get_challenge<bb::fr>(
"shplonk_opening_challenge");
411 Q_prime *= -(z - kappa);
413 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
418 Q_prime.
add_scaled(merged_table[idx], beta_m);
419 Q_prime.
at(0) -= beta_m * evals[2 *
NUM_WIRES + idx];
424 .opening_pair = { z,
bb::fr(0) } };
427 auto native_proof = prover_transcript->export_proof();
431 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
432 input_commitments.t_commitments[idx] =
ck.commit(right_table[idx]);
433 input_commitments.T_prev_commitments[idx] =
ck.commit(left_table[idx]);
441 bool verified = pairing_verified && result.reduction_succeeded;
442 EXPECT_TRUE(verified);
448using CurveTypes = ::testing::Types<curve::BN254,
449 stdlib::bn254<MegaCircuitBuilder>,
450 stdlib::bn254<UltraCircuitBuilder>>;
456 TestFixture::test_merge_proof_size();
461 TestFixture::test_single_merge();
466 TestFixture::test_multiple_merges();
471 TestFixture::test_degree_check_failure();
476 TestFixture::test_merge_failure();
481 TestFixture::test_eval_failure();
486 TestFixture::test_honest_empty_left_table();
504 if constexpr (!TestFixture::IsRecursive) {
505 GTEST_SKIP() <<
"OriginTag tests only apply to recursive context";
508 using BuilderType =
typename TestFixture::BuilderType;
509 using MergeVerifierType =
typename TestFixture::MergeVerifierType;
510 using Transcript =
typename TestFixture::Transcript;
511 constexpr size_t NUM_WIRES = TestFixture::NUM_WIRES;
517 auto op_queue_1 = TestFixture::construct_final_merge_op_queue();
519 MergeProver prover_1{ op_queue_1, prover_transcript_1 };
522 auto op_queue_2 = TestFixture::construct_final_merge_op_queue();
524 MergeProver prover_2{ op_queue_2, prover_transcript_2 };
528 auto t_1 = op_queue_1->construct_current_ultra_ops_subtable_columns();
529 auto T_prev_1 = op_queue_1->construct_table_columns_up_to_tail();
532 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
533 native_t_commitments_1[idx] = prover_1.pcs_commitment_key.commit(t_1[idx]);
534 native_T_prev_commitments_1[idx] = prover_1.pcs_commitment_key.commit(T_prev_1[idx]);
539 [[maybe_unused]] MergeVerifierType verifier_1{ transcript_1 };
541 [[maybe_unused]]
auto proof_1_recursive = TestFixture::create_proof(
builder, proof_1);
545 typename MergeVerifierType::InputCommitments input_commitments_1;
546 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
547 input_commitments_1.t_commitments[idx] = TestFixture::create_commitment(
builder, native_t_commitments_1[idx]);
548 input_commitments_1.T_prev_commitments[idx] =
549 TestFixture::create_commitment(
builder, native_T_prev_commitments_1[idx]);
555 MergeVerifierType verifier_2{ transcript_2 };
557 auto proof_2_recursive = TestFixture::create_proof(
builder, proof_2);
573 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
575 if constexpr (TestFixture::IsRecursive) {
576 input_commitments_1.t_commitments[idx].set_origin_tag(transcript_1_tag);
577 input_commitments_1.T_prev_commitments[idx].set_origin_tag(transcript_1_tag);
584 info(
"Attempting to mix transcript_1 commitments with transcript_2 proof verification...");
589 verifier_2.reduce_to_pairing_check(proof_2_recursive, input_commitments_1),
590 "Tags from different transcripts were involved in the same computation");
611 constexpr size_t NUM_WIRES = 4;
614 size_t frs_per_Fr = 1;
615 size_t frs_per_G = FrCodec::calc_num_fields<curve::BN254::AffineElement>();
616 size_t frs_per_uint32 = 1;
621 manifest_expected.
add_entry(round,
"shift_size", frs_per_uint32);
622 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
625 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_0");
626 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_1");
627 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_2");
628 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_3");
632 for (
size_t idx = 0; idx < 13; ++idx) {
636 manifest_expected.
add_entry(round,
"REVERSED_BATCHED_LEFT_TABLES", frs_per_G);
640 manifest_expected.
add_challenge(round,
"shplonk_opening_challenge");
641 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
644 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
647 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
650 manifest_expected.
add_entry(round,
"REVERSED_BATCHED_LEFT_TABLES_EVAL", frs_per_Fr);
651 manifest_expected.
add_entry(round,
"SHPLONK_BATCHED_QUOTIENT", frs_per_G);
655 manifest_expected.
add_entry(round,
"KZG:W", frs_per_G);
657 return manifest_expected;
670 transcript->enable_manifest();
675 auto manifest_expected = construct_merge_manifest();
676 auto prover_manifest = transcript->get_manifest();
678 ASSERT_GT(manifest_expected.size(), 0);
679 ASSERT_EQ(prover_manifest.size(), manifest_expected.size())
680 <<
"Prover manifest has " << prover_manifest.size() <<
" rounds, expected " << manifest_expected.size();
682 for (
size_t round = 0; round < manifest_expected.size(); ++round) {
683 ASSERT_EQ(prover_manifest[round], manifest_expected[round]) <<
"Prover manifest discrepancy in round " << round;
696 prover_transcript->enable_manifest();
697 MergeProver merge_prover{ op_queue, prover_transcript };
702 auto t_current = op_queue->construct_current_ultra_ops_subtable_columns();
703 auto T_prev = op_queue->construct_table_columns_up_to_tail();
705 merge_commitments.
t_commitments[idx] = merge_prover.pcs_commitment_key.commit(t_current[idx]);
706 merge_commitments.
T_prev_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_prev[idx]);
711 verifier_transcript->enable_manifest();
716 ASSERT_TRUE(result.pairing_points.check() && result.reduction_succeeded);
719 auto prover_manifest = prover_transcript->get_manifest();
720 auto verifier_manifest = verifier_transcript->get_manifest();
722 ASSERT_GT(prover_manifest.size(), 0);
723 ASSERT_EQ(prover_manifest.size(), verifier_manifest.size())
724 <<
"Prover has " << prover_manifest.size() <<
" rounds, verifier has " << verifier_manifest.size();
726 for (
size_t round = 0; round < prover_manifest.size(); ++round) {
727 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
728 <<
"Prover/Verifier manifest discrepancy in round " << round;
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
CommitmentKey object over a pairing group 𝔾₁.
static void construct_simple_circuit(MegaBuilder &builder)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
static void compute_opening_proof(const CK &ck, const ProverOpeningClaim< Curve > &opening_claim, const std::shared_ptr< Transcript > &prover_trancript)
Computes the KZG commitment to an opening proof polynomial at a single evaluation point.
static constexpr size_t NUM_WIRES
static constexpr size_t NUM_WIRES
Prover for the single-step Goblin ECC op queue merge protocol.
BB_PROFILE MergeProof construct_proof()
Prove proper construction of the aggregate Goblin ECC op queue polynomials T_j.
Unified test fixture for native and recursive merge verification.
static bool check_circuit(BuilderType &builder)
Check circuit validity (only relevant in recursive context)
typename Curve::ScalarField FF
typename Curve::Element GroupElement
static void prove_and_verify_merge(const std::shared_ptr< ECCOpQueue > &op_queue, const TamperProofMode tampering_mode=TamperProofMode::None, const bool expected=true)
Prove and verify a merge proof in both native and recursive contexts.
static Commitment create_commitment(BuilderType &builder, const curve::BN254::AffineElement &native_commitment)
Create a commitment from a native commitment value.
static std::shared_ptr< ECCOpQueue > construct_final_merge_op_queue(const size_t num_subtables_up_to_tail=1)
static void test_honest_empty_left_table()
Test that an honestly constructed merge proof with shift_size = 0 (empty left table) verifies.
typename Curve::AffineElement Commitment
typename MergeVerifierType::Proof Proof
static constexpr bool IsRecursive
static void test_eval_failure()
Test failure when g_j(kappa) ≠ kappa^{k-1} * l_j(1/kappa)
static void test_merge_proof_size()
Test that merge proof size matches the expected constant.
static auto to_native(const T &val)
Convert a stdlib type to its native value.
static void tamper_with_proof(std::vector< bb::fr > &merge_proof, const TamperProofMode tampering_mode)
Tamper with the merge proof for failure testing.
static Proof create_proof(BuilderType &builder, const std::vector< bb::fr > &native_proof)
Create a proof object from a vector of field elements.
static void test_multiple_merges()
Test a final merge proof with multiple historical subtables up to the tail.
typename MergeVerifierType::InputCommitments InputCommitments
typename MergeVerifierType::PairingPoints PairingPoints
static void test_degree_check_failure()
Test failure when degree(l) > shift_size (as read from the proof)
static constexpr size_t NUM_WIRES
typename MergeVerifierType::Transcript Transcript
typename MergeVerifierType::TableCommitments TableCommitments
typename BuilderTypeHelper< Curve >::type BuilderType
static void SetUpTestSuite()
static void test_single_merge()
Test basic merge proof construction and verification.
static void test_merge_failure()
Test failure when m ≠ l + X^k r.
Test class for merge protocol transcript pinning tests.
static void SetUpTestSuite()
static TranscriptManifest construct_merge_manifest()
Construct the expected manifest for a Merge protocol proof.
Verifier for the single-step Goblin ECC op queue merge protocol.
TranscriptFor_t< Curve > Transcript
std::conditional_t< Curve::is_stdlib_type, stdlib::recursion::PairingPoints< Curve >, bb::PairingPoints< Curve > > PairingPoints
ReductionResult reduce_to_pairing_check(const Proof &proof, const InputCommitments &input_commitments)
Reduce the merge proof to a pairing check.
std::array< Commitment, NUM_WIRES > TableCommitments
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
void add_scaled(PolynomialSpan< const Fr > other, const Fr &scaling_factor)
adds the polynomial q(X) 'other', multiplied by a scaling factor.
Fr evaluate(const Fr &z) const
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
void factor_roots(const Fr &root)
Divides p(X) by (X-r) in-place. Assumes that p(rⱼ)=0 for all j.
Polynomial p and an opening pair (r,v) such that p(r) = v.
void add_entry(size_t round, const std::string &element_label, size_t element_size)
void add_challenge(size_t round, const std::string &label)
Add a single challenge label to the manifest for the given round.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
typename Group::affine_element AffineElement
typename Group::element Element
static constexpr bool is_stdlib_type
typename Group::affine_element AffineElement
A simple wrapper around a vector of stdlib field elements representing a proof.
testing::Types< stdlib::secp256k1< UltraCircuitBuilder >, stdlib::secp256r1< UltraCircuitBuilder >, stdlib::secp256k1< MegaCircuitBuilder >, stdlib::secp256r1< MegaCircuitBuilder > > CurveTypes
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
TEST_F(IPATest, ChallengesAreZero)
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
OriginTag extract_transcript_tag(const TranscriptType &transcript)
Extract origin tag context from a transcript.
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
CommitmentKey< Curve > ck
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::string to_string(bb::avm2::ValueTag tag)
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
typename Curve::Builder type
PairingPoints pairing_points
constexpr field invert() const noexcept