87template <
typename Curve_,
size_t log_poly_length = CONST_ECCVM_LOG_N>
class IPA {
116 static_assert(log_poly_length >= 1,
"log_poly_length must be at least 1");
121 FRIEND_TEST(IPATest, ChallengesAreZero);
122 FRIEND_TEST(IPATest, AIsZeroAfterOneRound);
137 std::vector<Fr> squares(log_poly_length);
139 for (
size_t i = 0; i < log_poly_length; ++i) {
141 square = square.sqr();
182 template <
typename Transcript>
183 static void compute_opening_proof_internal(
const CK&
ck,
185 const std::shared_ptr<Transcript>& transcript)
191 template <
typename Transcript>
192 static void compute_inner_product_proof_internal(
const CK&
ck,
195 const std::shared_ptr<Transcript>& transcript)
203 const Fr generator_challenge = transcript->template get_challenge<Fr>(
"IPA:generator_challenge");
204 if (generator_challenge.
is_zero()) {
212 const auto aux_generator = Commitment::one() * generator_challenge;
217 "The polynomial degree plus 1 should be positive and a power of two");
221 auto a_vec = witness.
full();
227 "IPA witness length must equal the compile-time poly_length (2^log_poly_length)");
239 G_vec_local[idx] = srs_elements[idx];
265 const auto compute_inner_prods = [&](
size_t half) {
272 inner_prod_left_right.first += a_vec[idx] * b_vec[half + idx];
274 inner_prod_left_right.second += a_vec[half + idx] * b_vec[idx];
283 const auto finish_round = [&](
size_t round_idx,
size_t half) {
288 const Fr round_challenge = transcript->template get_short_challenge<Fr>(
"IPA:round_challenge_" +
index);
289 if (round_challenge.
is_zero()) {
292 const Fr round_challenge_inv = round_challenge.
invert();
293 challenge_product *= round_challenge;
301 a_vec.at(idx) = round_challenge_inv * a_vec[idx] + a_vec[half + idx];
302 b_vec.
at(idx) = round_challenge * b_vec[idx] + b_vec[half + idx];
305 return round_challenge;
308 size_t round_idx = 0;
309 while (round_idx < log_poly_length) {
310 const size_t half = round_size / 2;
313 auto [inner_prod_L, inner_prod_R] = compute_inner_prods(half);
317 L_i = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_vec.at(0), half } },
318 { &G_vec_local[half], half });
319 L_i += aux_generator * inner_prod_L;
323 R_i = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_vec.at(half), half } },
324 { &G_vec_local[0], half });
325 R_i += aux_generator * inner_prod_R;
327 const Fr u1 = finish_round(round_idx, half);
329 if (round_idx + 1 == log_poly_length) {
332 G_vec_local[0] =
Commitment(G_vec_local[0] * u1 + G_vec_local[1]);
342 const size_t quarter = half / 2;
343 auto [inner_prod_L2, inner_prod_R2] = compute_inner_prods(quarter);
346 std::vector<Fr> a_scaled(half);
351 L_i = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_scaled[0], quarter } },
352 { &G_vec_local[quarter], quarter });
353 L_i += scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_vec.at(0), quarter } },
354 { &G_vec_local[3 * quarter], quarter });
355 L_i += aux_generator * inner_prod_L2;
358 R_i = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_scaled[quarter], quarter } },
359 { &G_vec_local[0], quarter });
360 R_i += scalar_multiplication::pippenger_unsafe<Curve>({ 0, { &a_vec.at(quarter), quarter } },
361 { &G_vec_local[2 * quarter], quarter });
362 R_i += aux_generator * inner_prod_R2;
364 const Fr u2 = finish_round(round_idx + 1, quarter);
369 auto G_folded = GroupElement::batch_two_round_fold(
374 round_size = quarter;
381 const Fr challenge_product_inv = challenge_product.
invert();
383 transcript->send_to_verifier(
"IPA:G_0",
Commitment(G_vec_local[0] * challenge_product_inv));
386 transcript->send_to_verifier(
"IPA:a_0", a_vec[0] * challenge_product);
399 template <
typename Transcript>
400 static void add_claim_to_hash_buffer(
const CK&
ck,
401 const ProverOpeningClaim<Curve>& opening_claim,
402 const std::shared_ptr<Transcript>& transcript)
414 const auto commitment =
ck.commit(polynomial);
415 transcript->add_to_hash_buffer(
"IPA:commitment", commitment);
416 transcript->add_to_hash_buffer(
"IPA:challenge", opening_claim.opening_pair.challenge);
417 transcript->add_to_hash_buffer(
"IPA:evaluation", opening_claim.opening_pair.evaluation);
426 struct TranscriptData {
429 Polynomial<Fr> s_vec;
432 Commitment G_zero_from_prover;
434 std::vector<Fr> round_challenges_inv;
460 template <
typename Transcript,
typename BZeroFromRounds>
461 static TranscriptData read_inner_product_transcript_data(
const Commitment& commitment,
462 const Fr& evaluation,
463 BZeroFromRounds&& b_zero_from_round_challenges,
464 const std::shared_ptr<Transcript>& transcript)
469 const Fr generator_challenge = transcript->template get_challenge<Fr>(
"IPA:generator_challenge");
470 if (generator_challenge.
is_zero()) {
473 const Commitment aux_generator = Commitment::one() * generator_challenge;
477 const GroupElement C_prime = commitment + (aux_generator * evaluation);
479 const auto pippenger_size = 2 * log_poly_length;
480 std::vector<Fr> round_challenges(log_poly_length);
481 std::vector<Commitment> msm_elements(pippenger_size);
482 std::vector<Fr> msm_scalars(pippenger_size);
486 for (
size_t round_idx = 0; round_idx < log_poly_length; ++round_idx) {
488 const auto element_L = transcript->template receive_from_prover<Commitment>(
"IPA:L_" +
index);
489 const auto element_R = transcript->template receive_from_prover<Commitment>(
"IPA:R_" +
index);
490 round_challenges[round_idx] = transcript->template get_short_challenge<Fr>(
"IPA:round_challenge_" +
index);
491 if (round_challenges[round_idx].is_zero()) {
494 msm_elements[2 * round_idx] = element_L;
495 msm_elements[2 * round_idx + 1] = element_R;
498 std::vector<Fr> round_challenges_inv = round_challenges;
502 for (
size_t round_idx = 0; round_idx < log_poly_length; ++round_idx) {
503 msm_scalars[2 * round_idx] = round_challenges_inv[round_idx];
504 msm_scalars[2 * round_idx + 1] = round_challenges[round_idx];
509 GroupElement LR_sums = scalar_multiplication::pippenger<Curve>(
510 { 0, { &msm_scalars[0], pippenger_size } }, { &msm_elements[0], pippenger_size });
522 Commitment G_zero_from_prover = transcript->template receive_from_prover<Commitment>(
"IPA:G_0");
523 Fr a_zero = transcript->template receive_from_prover<Fr>(
"IPA:a_0");
534 template <
typename Transcript>
535 static TranscriptData read_transcript_data(
const OpeningClaim<Curve>& opening_claim,
536 const std::shared_ptr<Transcript>& transcript)
539 return read_inner_product_transcript_data(
540 opening_claim.commitment,
541 opening_claim.opening_pair.evaluation,
543 return evaluate_challenge_poly(
544 std::vector<Fr>(round_challenges_inv.begin(), round_challenges_inv.end()),
545 opening_claim.opening_pair.challenge);
575 static bool reduce_verify_internal_native(
const VK&
vk,
const OpeningClaim<Curve>& opening_claim,
auto& transcript)
581 auto data = read_transcript_data(opening_claim, transcript);
593 scalar_multiplication::pippenger_unsafe<Curve>(
data.s_vec, { &srs_elements[0], poly_length });
595 if (G_zero !=
data.G_zero_from_prover) {
596 info(
"IPA verification failed: G_0 mismatch");
602 Commitment aux_generator = Commitment::one() *
data.gen_challenge;
607 return (
data.C_zero.normalize() == right_hand_side.normalize());
620 template <
typename Transcript>
621 static void add_claim_to_hash_buffer(
const OpeningClaim<Curve>& opening_claim,
622 const std::shared_ptr<Transcript>& transcript)
628 transcript->add_to_hash_buffer(
"IPA:commitment", opening_claim.commitment);
629 transcript->add_to_hash_buffer(
"IPA:challenge", opening_claim.opening_pair.challenge);
630 transcript->add_to_hash_buffer(
"IPA:evaluation", opening_claim.opening_pair.evaluation);
649 template <
typename BZeroFromRounds>
651 const Fr& evaluation,
652 BZeroFromRounds&& b_zero_from_round_challenges,
658 const Fr generator_challenge = transcript->template get_challenge<Fr>(
"IPA:generator_challenge");
659 typename Curve::Builder*
builder = generator_challenge.get_context();
661 auto pippenger_size = 2 * log_poly_length + 2;
662 std::vector<Fr> round_challenges(log_poly_length);
663 std::vector<Fr> round_challenges_inv(log_poly_length);
668 std::vector<Commitment> msm_elements(pippenger_size);
670 std::vector<Fr> msm_scalars(pippenger_size);
672 for (
size_t round_idx = 0; round_idx < log_poly_length; ++round_idx) {
674 auto element_L = transcript->template receive_from_prover<Commitment>(
"IPA:L_" +
index);
675 auto element_R = transcript->template receive_from_prover<Commitment>(
"IPA:R_" +
index);
676 round_challenges[round_idx] = transcript->template get_short_challenge<Fr>(
"IPA:round_challenge_" +
index);
677 round_challenges_inv[round_idx] = round_challenges[round_idx].invert();
679 msm_elements[2 * round_idx] = element_L;
680 msm_elements[2 * round_idx + 1] = element_R;
681 msm_scalars[2 * round_idx] = round_challenges_inv[round_idx];
682 msm_scalars[2 * round_idx + 1] = round_challenges[round_idx];
690 Commitment G_zero = transcript->template receive_from_prover<Commitment>(
"IPA:G_0");
693 auto a_zero = transcript->template receive_from_prover<Fr>(
"IPA:a_0");
698 const auto last_round_tag = round_challenges.back().get_origin_tag();
699 G_zero.set_origin_tag(last_round_tag);
700 a_zero.set_origin_tag(last_round_tag);
705 msm_elements[2 * log_poly_length] = -G_zero;
706 msm_elements[(2 * log_poly_length) + 1] = -Commitment::one(
builder);
707 msm_scalars[2 * log_poly_length] = a_zero;
708 msm_scalars[(2 * log_poly_length) + 1] = generator_challenge * a_zero.madd(b_zero, { -evaluation });
709 GroupElement ipa_relation = GroupElement::batch_mul(msm_elements, msm_scalars);
710 auto neg_commitment = -commitment;
711 ipa_relation.assert_equal(neg_commitment);
713 return { round_challenges_inv, G_zero, ipa_relation.get_value() == -commitment.get_value() };
716 static VerifierAccumulator reduce_verify_internal_recursive(
const OpeningClaim<Curve>& opening_claim,
720 return reduce_verify_inner_product_recursive(
721 opening_claim.commitment,
722 opening_claim.opening_pair.evaluation,
724 return evaluate_challenge_poly(
725 std::vector<Fr>(round_challenges_inv.begin(), round_challenges_inv.end()),
726 opening_claim.opening_pair.challenge);
742 template <
typename Transcript = NativeTranscript>
743 static void compute_opening_proof(
const CK&
ck,
744 const ProverOpeningClaim<Curve>& opening_claim,
745 const std::shared_ptr<Transcript>& transcript)
747 add_claim_to_hash_buffer(
ck, opening_claim, transcript);
748 compute_opening_proof_internal(
ck, opening_claim, transcript);
762 template <
typename Transcript = NativeTranscript>
763 static bool reduce_verify(
const VK&
vk,
764 const OpeningClaim<Curve>& opening_claim,
765 const std::shared_ptr<Transcript>& transcript)
768 add_claim_to_hash_buffer(opening_claim, transcript);
769 return reduce_verify_internal_native(
vk, opening_claim, transcript);
795 template <
typename Transcript = NativeTranscript>
796 static bool batch_reduce_verify(
const VK&
vk,
797 const std::vector<OpeningClaim<Curve>>& opening_claims,
798 const std::vector<std::shared_ptr<Transcript>>& transcripts)
801 const size_t num_claims = opening_claims.size();
802 if (num_claims != transcripts.size()) {
803 info(
"IPA batch verification failed: claims/transcripts size mismatch");
806 if (num_claims == 0) {
807 info(
"IPA batch verification failed: no claims provided");
813 std::vector<Fr> a_zeros(num_claims);
814 std::vector<Fr> b_zeros(num_claims);
815 std::vector<Fr> gen_challenges(num_claims);
816 std::vector<Commitment> G_zeros_from_prover(num_claims);
819 for (
size_t i = 0; i < num_claims; i++) {
820 add_claim_to_hash_buffer(opening_claims[i], transcripts[i]);
821 auto data = read_transcript_data(opening_claims[i], transcripts[i]);
823 b_zeros[i] =
data.b_zero;
825 gen_challenges[i] =
data.gen_challenge;
826 G_zeros_from_prover[i] =
data.G_zero_from_prover;
827 a_zeros[i] =
data.a_zero;
834 std::vector<Fr> alpha_pows(num_claims);
836 for (
size_t i = 1; i < num_claims; i++) {
837 alpha_pows[i] = alpha_pows[i - 1] * alpha;
840 std::vector<Fr> beta_pows(num_claims);
842 for (
size_t i = 1; i < num_claims; i++) {
843 beta_pows[i] = beta_pows[i - 1] * beta;
848 if (poly_length > srs_elements.size()) {
861 Polynomial<Fr> combined_msm_scalars(poly_length);
862 for (
size_t i = 0; i < num_claims; i++) {
863 combined_msm_scalars.add_scaled(s_vecs[i], gamma * beta_pows[i] - alpha_pows[i] * a_zeros[i]);
865 Commitment batched_commitment = scalar_multiplication::pippenger_unsafe<Curve>(
866 combined_msm_scalars, { &srs_elements[0], poly_length });
869 GroupElement prover_g_zero_batch = G_zeros_from_prover[0] * beta_pows[0];
870 for (
size_t i = 1; i < num_claims; i++) {
871 prover_g_zero_batch += G_zeros_from_prover[i] * beta_pows[i];
875 GroupElement C_batch = C_zeros[0];
876 for (
size_t i = 1; i < num_claims; i++) {
877 C_batch = C_batch + C_zeros[i] * alpha_pows[i];
882 for (
size_t i = 0; i < num_claims; i++) {
883 bU_scalar += alpha_pows[i] * a_zeros[i] * b_zeros[i] * gen_challenges[i];
888 GroupElement left_hand_side = batched_commitment + C_batch;
889 GroupElement right_hand_side = prover_g_zero_batch * gamma + Commitment::one() * bU_scalar;
890 return (left_hand_side.normalize() == right_hand_side.normalize());
904 static VerifierAccumulator reduce_verify(
const OpeningClaim<Curve>& opening_claim,
const auto& transcript)
910 add_claim_to_hash_buffer(opening_claim, transcript);
911 return reduce_verify_internal_recursive(opening_claim, transcript);
931 static bool full_verify_recursive(
const VK&
vk,
const OpeningClaim<Curve>& opening_claim,
auto& transcript)
935 if (
vk.get_monomial_points().size() < poly_length) {
940 add_claim_to_hash_buffer(opening_claim, transcript);
941 VerifierAccumulator verifier_accumulator = reduce_verify_internal_recursive(opening_claim, transcript);
942 auto round_challenges_inv = verifier_accumulator.u_challenges_inv;
943 auto claimed_G_zero = verifier_accumulator.comm;
953 std::vector<Fr> s_vec_temporaries(poly_length / 2);
954 std::vector<Fr> s_vec(poly_length);
956 Fr* previous_round_s = &s_vec_temporaries[0];
957 Fr* current_round_s = &s_vec[0];
959 if constexpr ((log_poly_length & 1) == 0) {
960 std::swap(previous_round_s, current_round_s);
962 previous_round_s[0] =
Fr(1);
963 for (
size_t i = 0; i < log_poly_length; ++i) {
964 const size_t round_size = 1 << (i + 1);
965 const Fr round_challenge = round_challenges_inv[i];
966 for (
size_t j = 0; j < round_size / 2; ++j) {
967 current_round_s[j * 2] = previous_round_s[j];
968 current_round_s[j * 2 + 1] = previous_round_s[j] * round_challenge;
970 std::swap(current_round_s, previous_round_s);
980 std::vector<Commitment> srs_elements =
vk.get_monomial_points();
981 srs_elements.resize(poly_length);
982 std::vector<Commitment> remaining_srs(srs_elements.begin() + 1, srs_elements.end());
983 std::vector<Fr> remaining_s(s_vec.begin() + 1, s_vec.end());
984 Commitment first_term = srs_elements[0] * s_vec[0];
985 Commitment remaining_term = Commitment::fixed_batch_mul(remaining_srs, remaining_s, {}, 8);
986 Commitment computed_G_zero = first_term + remaining_term;
989 claimed_G_zero.assert_equal(computed_G_zero,
"G_zero doesn't match received G_zero.");
991 bool running_truth_value = verifier_accumulator.running_truth_value;
992 return running_truth_value;
1003 static Fr evaluate_challenge_poly(
const std::vector<Fr>& u_challenges_inv,
Fr r)
1007 Fr challenge_poly_eval = 1;
1011 for (
size_t i = 0; i < log_poly_length - 1; i++) {
1012 Fr monomial = u_challenges_inv[log_poly_length - 1 - i] * r_pow;
1013 challenge_poly_eval *= (
Fr(1) + monomial);
1014 r_pow = r_pow.
sqr();
1017 Fr monomial = u_challenges_inv[0] * r_pow;
1018 challenge_poly_eval *= (
Fr(1) + monomial);
1019 return challenge_poly_eval;
1033 static Fr evaluate_and_accumulate_challenge_polys(std::vector<Fr> u_challenges_inv_1,
1034 std::vector<Fr> u_challenges_inv_2,
1039 evaluate_challenge_poly(u_challenges_inv_1, r) + alpha * evaluate_challenge_poly(u_challenges_inv_2, r);
1050 static Polynomial<bb::fq> construct_poly_from_u_challenges_inv(
const std::span<const bb::fq>& u_challenges_inv)
1053 BB_ASSERT_EQ(u_challenges_inv.size(), log_poly_length);
1059 bb::fq* previous_round_s = &s_vec_temporaries[0];
1060 bb::fq* current_round_s = &s_vec[0];
1062 if ((log_poly_length & 1) == 0) {
1063 std::swap(previous_round_s, current_round_s);
1065 previous_round_s[0] =
bb::fq(1);
1066 for (
size_t i = 0; i < log_poly_length; ++i) {
1067 const size_t round_size = 1 << (i + 1);
1068 const bb::fq round_challenge = u_challenges_inv[i];
1072 current_round_s[j * 2] = previous_round_s[j];
1073 current_round_s[j * 2 + 1] = previous_round_s[j] * round_challenge;
1076 std::swap(current_round_s, previous_round_s);
1078 return { s_vec, poly_length };
1091 static Polynomial<bb::fq> create_challenge_poly(
const std::vector<bb::fq>& u_challenges_inv_1,
1096 Polynomial<bb::fq> challenge_poly(1 << log_poly_length);
1097 Polynomial challenge_poly_1 = construct_poly_from_u_challenges_inv(u_challenges_inv_1);
1098 Polynomial challenge_poly_2 = construct_poly_from_u_challenges_inv(u_challenges_inv_2);
1099 challenge_poly += challenge_poly_1;
1100 challenge_poly.add_scaled(challenge_poly_2, alpha);
1101 return challenge_poly;
1120 OpeningClaim<Curve> claim_1,
1122 OpeningClaim<Curve> claim_2)
1127 VerifierAccumulator verifier_accumulator_1 = reduce_verify(claim_1, transcript_1);
1128 VerifierAccumulator verifier_accumulator_2 = reduce_verify(claim_2, transcript_2);
1138 const CommitmentKey<curve::Grumpkin>&
ck,
1139 OpeningClaim<Curve> output_claim,
1140 const Polynomial<bb::fq>& challenge_poly)
1145 const OpeningPair<NativeCurve> opening_pair{
bb::fq(output_claim.opening_pair.challenge.get_value()),
1146 bb::fq(output_claim.opening_pair.evaluation.get_value()) };
1147 BB_ASSERT_EQ(challenge_poly.evaluate(opening_pair.challenge),
1148 opening_pair.evaluation,
1149 "Opening claim does not hold for challenge polynomial.");
1150 IPA<NativeCurve, log_poly_length>::compute_opening_proof(
1151 ck, { challenge_poly, opening_pair }, prover_transcript);
1153 output_claim.opening_pair.evaluation.self_reduce();
1154 return { output_claim, prover_transcript->export_proof() };
1167 VerifierAccumulator verifier_accumulator_1,
1168 VerifierAccumulator verifier_accumulator_2)
1171 static_assert(IsAnyOf<typename Curve::Builder, UltraCircuitBuilder>);
1176 transcript.add_to_hash_buffer(
"u_challenges_inv_1", verifier_accumulator_1.u_challenges_inv);
1177 transcript.add_to_hash_buffer(
"U_1", verifier_accumulator_1.comm);
1178 transcript.add_to_hash_buffer(
"u_challenges_inv_2", verifier_accumulator_2.u_challenges_inv);
1179 transcript.add_to_hash_buffer(
"U_2", verifier_accumulator_2.comm);
1183 OpeningClaim<Curve> output_claim;
1184 output_claim.commitment = verifier_accumulator_1.comm + verifier_accumulator_2.comm * alpha;
1185 output_claim.opening_pair.challenge = r;
1186 output_claim.opening_pair.evaluation = evaluate_and_accumulate_challenge_polys(
1187 verifier_accumulator_1.u_challenges_inv, verifier_accumulator_2.u_challenges_inv, r, alpha);
1192 for (
Fr u_inv_i : verifier_accumulator_1.u_challenges_inv) {
1193 native_u_challenges_inv_1.push_back(
bb::fq(u_inv_i.get_value()));
1195 for (
Fr u_inv_i : verifier_accumulator_2.u_challenges_inv) {
1196 native_u_challenges_inv_2.push_back(
bb::fq(u_inv_i.get_value()));
1198 Polynomial<bb::fq> challenge_poly =
1199 create_challenge_poly(native_u_challenges_inv_1, native_u_challenges_inv_2,
bb::fq(alpha.get_value()));
1201 return prove_challenge_poly_opening(
ck,
std::move(output_claim), challenge_poly);
1212 VerifierAccumulator verifier_accumulator)
1215 static_assert(IsAnyOf<typename Curve::Builder, UltraCircuitBuilder>);
1218 transcript.add_to_hash_buffer(
"u_challenges_inv", verifier_accumulator.u_challenges_inv);
1219 transcript.add_to_hash_buffer(
"U", verifier_accumulator.comm);
1220 auto r = transcript.template get_challenge<Fr>(
"IPA:r");
1222 OpeningClaim<Curve> output_claim;
1223 output_claim.commitment = verifier_accumulator.comm;
1224 output_claim.opening_pair.challenge = r;
1225 output_claim.opening_pair.evaluation = evaluate_challenge_poly(verifier_accumulator.u_challenges_inv, r);
1228 for (
Fr u_inv_i : verifier_accumulator.u_challenges_inv) {
1229 native_u_challenges_inv.push_back(
bb::fq(u_inv_i.get_value()));
1231 Polynomial<bb::fq> challenge_poly =
1234 return prove_challenge_poly_opening(
ck,
std::move(output_claim), challenge_poly);
1240 static bool verify_accumulator(
const VK&
vk,
const NativeAccumulator& accumulator)
1259 if (accumulators.empty()) {
1263 if (poly_length > srs_elements.size()) {
1264 throw_or_abort(
"potential bug: Not enough SRS points for IPA::batch_verify_accumulators!");
1269 for (
size_t idx = 0; idx < accumulators.size(); ++idx) {
1270 transcript.add_to_hash_buffer(
"IPA:batch_u_" +
std::to_string(idx), accumulators[idx].u_challenges_inv);
1271 transcript.add_to_hash_buffer(
"IPA:batch_U_" +
std::to_string(idx), accumulators[idx].claimed_commitment);
1273 const Fr rho = transcript.template get_challenge<Fr>(
"IPA:batch_rho");
1275 Polynomial<Fr> combined_challenge_poly(poly_length);
1276 GroupElement combined_commitment = GroupElement::infinity();
1278 bool all_relations_succeeded =
true;
1279 for (
const auto& accumulator : accumulators) {
1280 Polynomial<Fr> challenge_poly(
1282 combined_challenge_poly.add_scaled(challenge_poly, rho_power);
1283 combined_commitment += GroupElement(
accumulator.claimed_commitment) * rho_power;
1284 all_relations_succeeded = all_relations_succeeded &&
accumulator.relation_succeeded;
1288 GroupElement combined_G_zero;
1291 combined_G_zero = scalar_multiplication::pippenger_unsafe<Curve>(combined_challenge_poly,
1292 { &srs_elements[0], poly_length });
1294 if (combined_G_zero.normalize() != combined_commitment.normalize()) {
1295 info(
"IPA batch verification failed: combined G_0 mismatch");
1298 return all_relations_succeeded;
1306 using Builder =
typename Curve::Builder;
1307 using Curve = stdlib::grumpkin<Builder>;
1309 CommitmentKey<NativeCurve> ipa_commitment_key(poly_length);
1310 size_t n = poly_length;
1311 auto poly = Polynomial<bb::fq>(n);
1312 for (
size_t i = 0; i < n; i++) {
1316 bb::fq eval = poly.evaluate(x);
1317 auto commitment = ipa_commitment_key.commit(poly);
1318 const OpeningPair<NativeCurve> opening_pair = { x, eval };
1319 IPA<NativeCurve>::compute_opening_proof(ipa_commitment_key, { poly, opening_pair }, ipa_transcript);
1321 auto stdlib_comm = Curve::Group::from_witness(&
builder, commitment);
1322 auto stdlib_x = Curve::ScalarField::from_witness(&
builder, x);
1323 auto stdlib_eval = Curve::ScalarField::from_witness(&
builder, eval);
1324 OpeningClaim<Curve> stdlib_opening_claim{ { stdlib_x, stdlib_eval }, stdlib_comm };
1326 return { stdlib_opening_claim, ipa_transcript->export_proof() };