Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
graph_description_multilinear_batching.test.cpp
Go to the documentation of this file.
10
11#include <vector>
12
13using namespace bb;
14
15namespace {
16
17using Curve = curve::BN254;
19using Commitment = Curve::AffineElement;
20using ProverClaim = MultilinearBatchingProverClaim;
21using VerifierClaim = MultilinearBatchingVerifierClaim<Curve>;
22
23class MultilinearBatchingGraphDescriptionTests : public ::testing::Test {
24 protected:
25 static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); }
26
27 // Size of the slot polynomials; the protocol pads every polynomial up to 2^VIRTUAL_LOG_N virtual variables, so
28 // the actual size only needs to be small.
29 static constexpr size_t LOG_N = 5;
30 static constexpr size_t VIRTUAL_LOG_N = MultilinearBatchingFlavor::VIRTUAL_LOG_N;
31 static constexpr size_t NUM_CLAIMS = CHONK_MAX_CLAIMS_PER_KERNEL;
32
36 static FF mle_padded(const Polynomial<FF>& poly, const std::vector<FF>& r, bool shift = false)
37 {
38 std::vector<FF> head(r.begin(), r.begin() + LOG_N);
39 FF value = poly.evaluate_mle(head, shift);
40 for (size_t j = LOG_N; j < r.size(); ++j) {
41 value *= (FF(1) - r[j]);
42 }
43 return value;
44 }
45
46 struct ClaimSet {
47 std::vector<ProverClaim> prover_claims;
48 std::vector<VerifierClaim> verifier_claims;
49 };
50
54 static ClaimSet build_honest_claims()
55 {
56 const size_t dyadic_size = 1UL << LOG_N;
57 CommitmentKey<Curve> commitment_key(dyadic_size);
58
59 ClaimSet set;
60 for (size_t i = 0; i < NUM_CLAIMS; ++i) {
61 std::vector<FF> challenge(VIRTUAL_LOG_N);
62 for (auto& c : challenge) {
64 }
65
66 Polynomial<FF> non_shifted = Polynomial<FF>::random(dyadic_size);
67 Polynomial<FF> shifted = Polynomial<FF>::random(dyadic_size - 1, dyadic_size, /*start_index=*/1);
68
69 const FF non_shifted_eval = mle_padded(non_shifted, challenge);
70 const FF shifted_eval = mle_padded(shifted, challenge, /*shift=*/true);
71 const Commitment non_shifted_commitment = commitment_key.commit(non_shifted);
72 const Commitment shifted_commitment = commitment_key.commit(shifted);
73
74 set.verifier_claims.push_back(VerifierClaim{ .challenge = challenge,
75 .non_shifted_evaluation = non_shifted_eval,
76 .shifted_evaluation = shifted_eval,
77 .non_shifted_commitment = non_shifted_commitment,
78 .shifted_commitment = shifted_commitment });
79 set.prover_claims.push_back(ProverClaim{ .challenge = std::move(challenge),
80 .non_shifted_evaluation = non_shifted_eval,
81 .shifted_evaluation = shifted_eval,
82 .non_shifted_polynomial = std::move(non_shifted),
83 .shifted_polynomial = std::move(shifted),
84 .non_shifted_commitment = non_shifted_commitment,
85 .shifted_commitment = shifted_commitment,
86 .dyadic_size = dyadic_size });
87 }
88 return set;
89 }
90};
91
92TEST_F(MultilinearBatchingGraphDescriptionTests, RecursiveVerifierGraphDescription)
93{
94 ClaimSet set = build_honest_claims();
95
96 auto prover_transcript = std::make_shared<NativeTranscript>();
97 // Seed the transcript so the first batching challenge is not drawn from an empty hash buffer; in production the
98 // shared transcript already holds the group's instance sumchecks at this point.
99 prover_transcript->send_to_verifier("init", FF::random_element());
100 MultilinearBatchingProver prover(std::move(set.prover_claims), prover_transcript);
101 HonkProof proof = prover.construct_proof();
102
103 using RecursiveVerifier = MultilinearBatchingRecursiveVerifier;
104 using RecursiveCurve = typename RecursiveVerifier::Curve;
106 using RecursiveFF = typename RecursiveCurve::ScalarField;
107
110 typename RecursiveVerifier::Proof stdlib_proof(builder, proof);
111 transcript->load_proof(stdlib_proof);
112 RecursiveFF seed = transcript->template receive_from_prover<RecursiveFF>("init");
113 // The seed stands in for the prior transcript content (the group's instance sumchecks), which in production is
114 // constrained elsewhere in the kernel; here it is only hashed, so fix it to keep the analyzer from flagging it.
115 seed.fix_witness();
116
117 std::vector<RecursiveClaim> recursive_claims;
118 recursive_claims.reserve(set.verifier_claims.size());
119 for (const auto& claim : set.verifier_claims) {
120 RecursiveClaim recursive_claim = RecursiveClaim::template stdlib_from_native<RecursiveCurve>(&builder, claim);
121 // The claims stand in for values the kernel would receive already constrained; clear the free-witness tags so
122 // the verifier's origin-tag mechanism does not flag them when they mix with transcript values.
123 for (auto& challenge_element : recursive_claim.challenge) {
124 challenge_element.unset_free_witness_tag();
125 }
126 recursive_claim.non_shifted_evaluation.unset_free_witness_tag();
127 recursive_claim.shifted_evaluation.unset_free_witness_tag();
128 // Each input evaluation is consumed exactly once (in the target-sum computation); in production it is
129 // produced by the instance sumcheck verifier in the same circuit. Fix the witnesses so the StaticAnalyzer
130 // does not flag them as under-constrained.
131 recursive_claim.non_shifted_evaluation.fix_witness();
132 recursive_claim.shifted_evaluation.fix_witness();
133 recursive_claim.non_shifted_commitment.unset_free_witness_tag();
134 recursive_claim.shifted_commitment.unset_free_witness_tag();
135 recursive_claims.push_back(std::move(recursive_claim));
136 }
137
138 RecursiveVerifier verifier(transcript);
139 auto [verified, new_claim] = verifier.verify_proof(recursive_claims);
140
141 EXPECT_TRUE(verified);
142 EXPECT_FALSE(builder.failed()) << builder.err();
143 EXPECT_TRUE(CircuitChecker::check(builder));
144
145 // The output claim is consumed downstream in production (the kernel propagates it to the next accumulation step);
146 // here it is unused, so fix its witnesses to keep the StaticAnalyzer from flagging them as under-constrained.
147 for (auto& challenge_element : new_claim.challenge) {
148 challenge_element.fix_witness();
149 }
150 new_claim.non_shifted_evaluation.fix_witness();
151 new_claim.shifted_evaluation.fix_witness();
152 new_claim.non_shifted_commitment.fix_witness();
153 new_claim.shifted_commitment.fix_witness();
154
155 info("Multilinear batching recursive verifier: finalized num gates = ",
156 builder.get_num_finalized_gates_inefficient());
157
158 auto analyzer = cdg::MegaStaticAnalyzer(builder);
159 auto [connected_components, variables_in_one_gate] = analyzer.analyze_circuit();
160 EXPECT_EQ(connected_components.size(), 1);
161 for (const uint32_t var_idx : variables_in_one_gate) {
162 analyzer.print_variable_info(var_idx);
163 }
164 EXPECT_EQ(variables_in_one_gate.size(), 0);
165}
166
167} // namespace
CommitmentKey object over a pairing group 𝔾₁.
Public entrypoint for multilinear batching.
static Polynomial random(size_t size, size_t start_index=0)
Fr evaluate_mle(std::span< const Fr > evaluation_points, bool shift=false) const
evaluate multi-linear extension p(X_0,…,X_{n-1}) = \sum_i a_i*L_i(X_0,…,X_{n-1}) at u = (u_0,...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
typename Group::affine_element AffineElement
Definition grumpkin.hpp:64
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:160
MultilinearBatchingVerifier< true > MultilinearBatchingRecursiveVerifier
StaticAnalyzer_< bb::fr, bb::MegaCircuitBuilder > MegaStaticAnalyzer
Definition graph.hpp:189
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Prover's claim for multilinear batching - contains polynomials and their evaluation claims.
Verifier's claim for multilinear batching - contains commitments and evaluation claims.
static field random_element(numeric::RNG *engine=nullptr) noexcept