20 transcript->load_proof(proof);
24 const FF binding_hash = hash;
30 std::vector<FF> calculated_hashes;
31 for (
size_t idx = 0; idx < MAX_MERGE_SIZE; ++idx) {
32 for (
size_t col = 0; col < NUM_WIRES; ++col) {
33 subtable_cols[idx][col] = transcript->template receive_from_prover<Commitment>(
36 calculated_hashes.push_back(transcript->template get_challenge<FF>(
"HASH_" +
std::to_string(idx)));
42 std::array<Commitment, NUM_WIRES> zk_columns;
43 for (
size_t col = 0; col < NUM_WIRES; ++col) {
44 zk_columns[col] = transcript->template receive_from_prover<Commitment>(
"ZK_COLUMN_" +
std::to_string(col));
50 std::vector<Commitment> flattened_cols;
51 flattened_cols.reserve(NUM_EVALS_FROM_COLUMNS);
52 for (
size_t col = 0; col < NUM_WIRES; ++col) {
53 flattened_cols.push_back(
std::move(zk_columns[col]));
55 for (
auto& subtable_col : subtable_cols) {
56 for (
size_t col = 0; col < NUM_WIRES; col++) {
57 flattened_cols.push_back(
std::move(subtable_col[col]));
64 const FF N = transcript->template receive_from_prover<FF>(
"NUM_SUBTABLES");
69 FF running_product =
FF(1);
70 for (
size_t idx = 0; idx < MAX_MERGE_SIZE; idx++) {
71 running_product *= (
N -
FF(idx + 1));
74 bool is_valid_num_subtables =
true;
75 if constexpr (IsRecursive) {
76 is_valid_num_subtables = running_product.get_value().is_zero();
77 running_product.assert_equal(
FF(0));
79 is_valid_num_subtables = running_product.is_zero();
82 std::vector<FF> shift_sizes;
83 shift_sizes.reserve(NUM_COLUMN_TABLES);
86 std::vector<FF> indicator_array = compute_indicator_array(
N);
88 for (
size_t i = 0; i < MAX_MERGE_SIZE; ++i) {
90 shift_sizes.push_back(transcript->template receive_from_prover<FF>(
"SHIFT_SIZE_" +
std::to_string(i)));
91 shift_sizes[idx] = shift_sizes[idx] * indicator_array[i];
98 for (
size_t col = 0; col < NUM_WIRES; ++col) {
99 merged_commitments[col] =
100 transcript->template receive_from_prover<Commitment>(
"MERGED_COLUMN_" +
std::to_string(col));
106 std::vector<FF> degree_check_challenges;
107 degree_check_challenges.reserve(NUM_EVALS_FROM_COLUMNS);
108 const FF degree_check_challenge = transcript->template get_challenge<FF>(
"DEGREE_CHECK_CHALLENGE");
109 degree_check_challenges = {
FF(1), degree_check_challenge };
110 for (
size_t idx = 2; idx < NUM_EVALS_FROM_COLUMNS; idx++) {
111 degree_check_challenges.push_back(degree_check_challenges.back() * degree_check_challenge);
117 Commitment degree_check_commitment = transcript->template receive_from_prover<Commitment>(
"DEGREE_CHECK_POLY");
122 const FF kappa = transcript->template get_challenge<FF>(
"KAPPA");
123 const FF kappa_inv = kappa.invert();
125 std::vector<FF> powers_of_kappa;
126 powers_of_kappa.reserve(shift_sizes.size());
127 for (
const FF& shift_size : shift_sizes) {
128 if constexpr (IsRecursive) {
131 powers_of_kappa.push_back(kappa.template pow<CONST_OP_QUEUE_LOG_SIZE + 1>(shift_size));
134 static_cast<uint32_t
>(shift_size), 1UL << (CONST_OP_QUEUE_LOG_SIZE + 1),
"Shift size is too large");
135 powers_of_kappa.push_back(kappa.pow(shift_size));
139 std::vector<FF> powers_of_kappa_inv;
140 powers_of_kappa_inv.reserve(powers_of_kappa.size());
141 if constexpr (IsRecursive) {
142 for (
const FF& kappa_pow : powers_of_kappa) {
143 powers_of_kappa_inv.push_back(kappa_pow.invert());
146 powers_of_kappa_inv = powers_of_kappa;
154 std::vector<FF> evals;
155 evals.reserve(NUM_EVALS);
156 for (
size_t i = 0; i < NUM_EVALS_FROM_COLUMNS; ++i) {
157 const FF received_eval = transcript->template receive_from_prover<FF>(
"C_EVAL_" +
std::to_string(i));
158 evals.push_back(received_eval);
162 for (
size_t col = 0; col < NUM_WIRES; ++col) {
163 evals.push_back(transcript->template receive_from_prover<FF>(
"MERGED_EVAL_" +
std::to_string(col)));
167 evals.push_back(transcript->template receive_from_prover<FF>(
"DEGREE_CHECK_EVAL"));
174 if constexpr (IsRecursive) {
177 for (
FF& kappa_pow : powers_of_kappa) {
178 origin_tags.push_back(kappa_pow.get_origin_tag());
179 kappa_pow.set_origin_tag(evals[0].get_origin_tag());
181 for (
FF& kappa_pow : powers_of_kappa_inv) {
182 kappa_pow.set_origin_tag(evals[0].get_origin_tag());
186 const bool concatenation_verified = check_concatenation_identity(evals, powers_of_kappa);
187 const bool degree_check_verified =
188 check_degree_identity(evals, powers_of_kappa_inv, kappa, degree_check_challenges);
189 const bool hash_verified = check_hash_consistency(binding_hash, calculated_hashes, indicator_array);
192 if constexpr (IsRecursive) {
193 for (
auto [kappa_pow, origin_tag] :
zip_view(powers_of_kappa, origin_tags)) {
194 kappa_pow.set_origin_tag(origin_tag);
196 for (
auto [kappa_pow, origin_tag] :
zip_view(powers_of_kappa_inv, origin_tags)) {
197 kappa_pow.set_origin_tag(origin_tag);
205 opening_claims.reserve(NUM_OPENING_CLAIMS);
206 for (
size_t idx = 0; idx < NUM_EVALS_FROM_COLUMNS; ++idx) {
207 opening_claims.push_back(
OpeningClaim<Curve>{ { kappa, evals[idx] }, flattened_cols[idx] });
209 for (
size_t idx = 0; idx < NUM_WIRES; ++idx) {
210 opening_claims.push_back(
211 OpeningClaim<Curve>{ { kappa, evals[NUM_EVALS_FROM_COLUMNS + idx] }, merged_commitments[idx] });
213 opening_claims.push_back(
OpeningClaim<Curve>{ { kappa_inv, evals.back() }, degree_check_commitment });
215 ShplonkVerifier shplonk_verifier = ShplonkVerifier::reduce_verification_no_finalize(opening_claims, transcript);
218 if constexpr (IsRecursive) {
219 g1_identity = Commitment::one(kappa.get_context());
221 g1_identity = Commitment::one();
228 PairingPoints pairing_points = PCS::reduce_verify_batch_opening_claim(
std::move(batch_claim), transcript);
230 vinfo(
"BatchMergeVerifier: concatenation check passed: ", concatenation_verified ?
"true" :
"false");
231 vinfo(
"BatchMergeVerifier: degree check passed: ", degree_check_verified ?
"true" :
"false");
232 vinfo(
"BatchMergeVerifier: hash check passed: ", hash_verified ?
"true" :
"false");
233 vinfo(
"BatchMergeVerifier: is N in [1, MAX_MERGE_SIZE]: ", is_valid_num_subtables ?
"true" :
"false");
235 return { pairing_points,
237 degree_check_verified && concatenation_verified && hash_verified && is_valid_num_subtables };