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 };
73 BB_ASSERT_LTE(op_queue->get_current_subtable_size(), bb::HIDING_KERNEL_ULTRA_OPS);
74 while (op_queue->get_current_subtable_size() < bb::HIDING_KERNEL_ULTRA_OPS) {
75 op_queue->no_op_ultra_only();
84 template <
typename T>
static auto to_native(
const T& val)
87 return val.get_value();
100 auto commitment = Commitment::from_witness(&
builder, native_commitment);
101 commitment.unset_free_witness_tag();
105 return native_commitment;
145 const size_t m_commitment_idx = 0;
146 const size_t l_eval_idx = 21;
148 switch (tampering_mode) {
152 FrCodec::deserialize_from_fields<curve::BN254::AffineElement>(std::span{ merge_proof }.subspan(
153 m_commitment_idx, FrCodec::calc_num_fields<curve::BN254::AffineElement>()));
154 m_commitment = m_commitment + curve::BN254::AffineElement::one();
155 auto m_commitment_frs = FrCodec::serialize_to_fields<curve::BN254::AffineElement>(m_commitment);
156 for (
size_t idx = 0; idx < 4; ++idx) {
157 merge_proof[m_commitment_idx + idx] = m_commitment_frs[idx];
163 merge_proof[l_eval_idx] -=
bb::fr(1);
177 const bool expected =
true)
181 MergeProver merge_prover{ op_queue, prover_transcript };
186 auto t_current = op_queue->construct_current_ultra_ops_subtable_columns();
187 auto T_prev = op_queue->construct_table_columns_up_to_tail();
191 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
192 native_t_commitments[idx] = merge_prover.pcs_commitment_key.commit(t_current[idx]);
193 native_T_prev_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_prev[idx]);
196 auto T_merged = op_queue->construct_ultra_ops_table_columns();
198 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
199 expected_merged_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_merged[idx]);
207 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
220 bool verified = pairing_verified &&
result.reduction_succeeded;
221 EXPECT_EQ(verified, expected);
225 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
226 EXPECT_EQ(
to_native(
result.merged_commitments[idx]), expected_merged_commitments[idx])
227 <<
"Merged table commitment mismatch at index " << idx;
234 EXPECT_EQ(circuit_valid, expected);
251 EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE);
305 GTEST_SKIP() <<
"Native-only test";
313 const size_t shift_size =
317 const size_t left_size = shift_size + 2;
318 const size_t right_size = 4;
319 const size_t merged_size = shift_size + right_size;
324 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
329 for (
size_t i = 0; i < left_size; i++) {
330 merged_table[idx].at(i) += left_table[idx].at(i);
332 for (
size_t i = 0; i < right_size; i++) {
333 merged_table[idx].at(shift_size + i) += right_table[idx].at(i);
340 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
341 prover_transcript->send_to_verifier(
"MERGED_TABLE_" +
std::to_string(idx),
342 ck.commit(merged_table[idx]));
346 "LEFT_TABLE_DEGREE_CHECK_1",
347 "LEFT_TABLE_DEGREE_CHECK_2",
348 "LEFT_TABLE_DEGREE_CHECK_3" };
349 auto degree_check_challenges = prover_transcript->template get_challenges<bb::fr>(degree_labels);
355 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
356 batched_left_tables.
add_scaled(left_table[idx], degree_check_challenges[idx]);
358 Polynomial reversed_batched_left_tables(shift_size);
359 for (
size_t j = 0; j < shift_size; j++) {
360 reversed_batched_left_tables.
at(j) = batched_left_tables.
at(shift_size - 1 - j);
362 prover_transcript->send_to_verifier(
"REVERSED_BATCHED_LEFT_TABLES",
363 ck.commit(reversed_batched_left_tables));
365 bb::fr kappa = prover_transcript->template get_challenge<bb::fr>(
"kappa");
368 std::vector<bb::fr> evals;
369 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
370 evals.emplace_back(left_table[idx].evaluate(kappa));
371 prover_transcript->send_to_verifier(
"LEFT_TABLE_EVAL_" +
std::to_string(idx), evals.back());
373 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
374 evals.emplace_back(right_table[idx].evaluate(kappa));
375 prover_transcript->send_to_verifier(
"RIGHT_TABLE_EVAL_" +
std::to_string(idx), evals.back());
377 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
378 evals.emplace_back(merged_table[idx].evaluate(kappa));
379 prover_transcript->send_to_verifier(
"MERGED_TABLE_EVAL_" +
std::to_string(idx), evals.back());
381 evals.emplace_back(reversed_batched_left_tables.
evaluate(kappa_inv));
382 prover_transcript->send_to_verifier(
"REVERSED_BATCHED_LEFT_TABLES_EVAL", evals.back());
385 "SHPLONK_MERGE_BATCHING_CHALLENGE_0",
"SHPLONK_MERGE_BATCHING_CHALLENGE_1",
386 "SHPLONK_MERGE_BATCHING_CHALLENGE_2",
"SHPLONK_MERGE_BATCHING_CHALLENGE_3",
387 "SHPLONK_MERGE_BATCHING_CHALLENGE_4",
"SHPLONK_MERGE_BATCHING_CHALLENGE_5",
388 "SHPLONK_MERGE_BATCHING_CHALLENGE_6",
"SHPLONK_MERGE_BATCHING_CHALLENGE_7",
389 "SHPLONK_MERGE_BATCHING_CHALLENGE_8",
"SHPLONK_MERGE_BATCHING_CHALLENGE_9",
390 "SHPLONK_MERGE_BATCHING_CHALLENGE_10",
"SHPLONK_MERGE_BATCHING_CHALLENGE_11",
391 "SHPLONK_MERGE_BATCHING_CHALLENGE_12"
393 auto shplonk_batching_challenges = prover_transcript->template get_short_challenges<bb::fr>(shplonk_labels);
397 Polynomial shplonk_batched_quotient(merged_size);
398 for (
size_t table_idx = 0; table_idx < 3; table_idx++) {
399 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
400 bb::fr challenge = shplonk_batching_challenges[(table_idx *
NUM_WIRES) + idx];
401 shplonk_batched_quotient.
add_scaled((*tables[table_idx])[idx], challenge);
402 shplonk_batched_quotient.
at(0) -= challenge * evals[(table_idx *
NUM_WIRES) + idx];
407 Polynomial reversed_copy(reversed_batched_left_tables);
408 reversed_copy.
at(0) -= evals.back();
410 shplonk_batched_quotient.
add_scaled(reversed_copy, shplonk_batching_challenges.back());
412 prover_transcript->send_to_verifier(
"SHPLONK_BATCHED_QUOTIENT",
ck.commit(shplonk_batched_quotient));
414 bb::fr z = prover_transcript->template get_challenge<bb::fr>(
"shplonk_opening_challenge");
416 Q_prime *= -(z - kappa);
417 for (
size_t table_idx = 0; table_idx < 3; table_idx++) {
418 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
419 bb::fr challenge = shplonk_batching_challenges[(table_idx *
NUM_WIRES) + idx];
420 Q_prime.
add_scaled((*tables[table_idx])[idx], challenge);
421 Q_prime.
at(0) -= challenge * evals[(table_idx *
NUM_WIRES) + idx];
425 Polynomial reversed_copy(reversed_batched_left_tables);
426 reversed_copy.
at(0) -= evals.back();
428 shplonk_batching_challenges.back() * (z - kappa) * (z - kappa_inv).invert());
432 .opening_pair = { z,
bb::fr(0) } };
435 auto native_proof = prover_transcript->export_proof();
438 for (
size_t idx = 0; idx <
NUM_WIRES; idx++) {
439 input_commitments.t_commitments[idx] =
ck.commit(right_table[idx]);
440 input_commitments.T_prev_commitments[idx] =
ck.commit(left_table[idx]);
448 EXPECT_TRUE(
result.pairing_points.check());
449 EXPECT_FALSE(
result.reduction_succeeded);
455using CurveTypes = ::testing::Types<curve::BN254,
456 stdlib::bn254<MegaCircuitBuilder>,
457 stdlib::bn254<UltraCircuitBuilder>>;
463 TestFixture::test_merge_proof_size();
468 TestFixture::test_single_merge();
473 TestFixture::test_multiple_merges();
478 TestFixture::test_merge_failure();
483 TestFixture::test_eval_failure();
488 TestFixture::test_degree_check_failure();
506 if constexpr (!TestFixture::IsRecursive) {
507 GTEST_SKIP() <<
"OriginTag tests only apply to recursive context";
510 using BuilderType =
typename TestFixture::BuilderType;
511 using MergeVerifierType =
typename TestFixture::MergeVerifierType;
512 using Transcript =
typename TestFixture::Transcript;
513 constexpr size_t NUM_WIRES = TestFixture::NUM_WIRES;
519 auto op_queue_1 = TestFixture::construct_final_merge_op_queue();
521 MergeProver prover_1{ op_queue_1, prover_transcript_1 };
524 auto op_queue_2 = TestFixture::construct_final_merge_op_queue();
526 MergeProver prover_2{ op_queue_2, prover_transcript_2 };
530 auto t_1 = op_queue_1->construct_current_ultra_ops_subtable_columns();
531 auto T_prev_1 = op_queue_1->construct_table_columns_up_to_tail();
534 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
535 native_t_commitments_1[idx] = prover_1.pcs_commitment_key.commit(t_1[idx]);
536 native_T_prev_commitments_1[idx] = prover_1.pcs_commitment_key.commit(T_prev_1[idx]);
541 [[maybe_unused]] MergeVerifierType verifier_1{ transcript_1 };
543 [[maybe_unused]]
auto proof_1_recursive = TestFixture::create_proof(
builder, proof_1);
547 typename MergeVerifierType::InputCommitments input_commitments_1;
548 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
549 input_commitments_1.t_commitments[idx] = TestFixture::create_commitment(
builder, native_t_commitments_1[idx]);
550 input_commitments_1.T_prev_commitments[idx] =
551 TestFixture::create_commitment(
builder, native_T_prev_commitments_1[idx]);
557 MergeVerifierType verifier_2{ transcript_2 };
559 auto proof_2_recursive = TestFixture::create_proof(
builder, proof_2);
575 for (
size_t idx = 0; idx < NUM_WIRES; idx++) {
577 if constexpr (TestFixture::IsRecursive) {
578 input_commitments_1.t_commitments[idx].set_origin_tag(transcript_1_tag);
579 input_commitments_1.T_prev_commitments[idx].set_origin_tag(transcript_1_tag);
586 info(
"Attempting to mix transcript_1 commitments with transcript_2 proof verification...");
591 verifier_2.reduce_to_pairing_check(proof_2_recursive, input_commitments_1),
592 "Tags from different transcripts were involved in the same computation");
615 size_t frs_per_Fr = 1;
616 size_t frs_per_G = FrCodec::calc_num_fields<curve::BN254::AffineElement>();
621 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
624 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_0");
625 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_1");
626 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_2");
627 manifest_expected.
add_challenge(round,
"LEFT_TABLE_DEGREE_CHECK_3");
631 manifest_expected.
add_entry(round,
"REVERSED_BATCHED_LEFT_TABLES", frs_per_G);
636 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
639 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
642 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
645 manifest_expected.
add_entry(round,
"REVERSED_BATCHED_LEFT_TABLES_EVAL", frs_per_Fr);
647 for (
size_t idx = 0; idx < (3 * NUM_WIRES) + 1; ++idx) {
653 manifest_expected.
add_entry(round,
"SHPLONK_BATCHED_QUOTIENT", frs_per_G);
654 manifest_expected.
add_challenge(round,
"shplonk_opening_challenge");
658 manifest_expected.
add_entry(round,
"KZG:W", frs_per_G);
660 return manifest_expected;
673 transcript->enable_manifest();
678 auto manifest_expected = construct_merge_manifest();
679 auto prover_manifest = transcript->get_manifest();
681 ASSERT_GT(manifest_expected.size(), 0);
682 ASSERT_EQ(prover_manifest.size(), manifest_expected.size())
683 <<
"Prover manifest has " << prover_manifest.size() <<
" rounds, expected " << manifest_expected.size();
685 for (
size_t round = 0; round < manifest_expected.size(); ++round) {
686 ASSERT_EQ(prover_manifest[round], manifest_expected[round]) <<
"Prover manifest discrepancy in round " << round;
699 prover_transcript->enable_manifest();
700 MergeProver merge_prover{ op_queue, prover_transcript };
705 auto t_current = op_queue->construct_current_ultra_ops_subtable_columns();
706 auto T_prev = op_queue->construct_table_columns_up_to_tail();
708 merge_commitments.
t_commitments[idx] = merge_prover.pcs_commitment_key.commit(t_current[idx]);
709 merge_commitments.
T_prev_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_prev[idx]);
714 verifier_transcript->enable_manifest();
716 auto result = merge_verifier.reduce_to_pairing_check(merge_proof, merge_commitments);
719 ASSERT_TRUE(
result.pairing_points.check() &&
result.reduction_succeeded);
722 auto prover_manifest = prover_transcript->get_manifest();
723 auto verifier_manifest = verifier_transcript->get_manifest();
725 ASSERT_GT(prover_manifest.size(), 0);
726 ASSERT_EQ(prover_manifest.size(), verifier_manifest.size())
727 <<
"Prover has " << prover_manifest.size() <<
" rounds, verifier has " << verifier_manifest.size();
729 for (
size_t round = 0; round < prover_manifest.size(); ++round) {
730 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
731 <<
"Prover/Verifier manifest discrepancy in round " << round;
#define BB_ASSERT_LTE(left, right,...)
#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 size_t get_append_offset_for_verifier()
static constexpr size_t compute_fixed_append_offset(size_t append_offset, bool include_zk_prefix=true)
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)
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 deg(l) ≥ shift_size.
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
static Polynomial random(size_t size, size_t start_index=0)
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