Skip to content

Commit b2aad63

Browse files
committed
recursion program: merge whir Merkle verification and root-of-unity exponanciation
1 parent cdf7f9f commit b2aad63

3 files changed

Lines changed: 90 additions & 77 deletions

File tree

crates/rec_aggregation/hashing.py

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -259,67 +259,6 @@ def whir_do_1_merkle_level(b, state_in, path_chunk, state_out):
259259
return
260260

261261

262-
@inline
263-
def hash_and_verify_merkle_hint(leaf_position_nibbles, root, height, num_chunks):
264-
# Hint and hash leaf
265-
leaf_data = Array(num_chunks * DIGEST_LEN)
266-
hint_witness("merkle_leaf", leaf_data)
267-
leaf_hash = slice_hash_rtl(leaf_data, num_chunks)
268-
269-
# Hint and verify merkle path (processing 4 levels per nibble)
270-
merkle_path = Array(height * DIGEST_LEN)
271-
hint_witness("merkle_path", merkle_path)
272-
273-
states = Array((div_ceil(height, 4) - 1) * DIGEST_LEN)
274-
275-
# First full nibble: leaf_hash -> states[0]
276-
match_range(leaf_position_nibbles[0], range(0, 16), lambda b: whir_do_4_merkle_levels(b, leaf_hash, merkle_path, states))
277-
278-
# Middle nibble chunks: states[k-1] -> states[k]
279-
for k in unroll(1, div_ceil(height, 4) - 1):
280-
match_range(
281-
leaf_position_nibbles[k],
282-
range(0, 16),
283-
lambda b: whir_do_4_merkle_levels(b, states + (k - 1) * DIGEST_LEN, merkle_path + 4 * k * DIGEST_LEN, states + k * DIGEST_LEN),
284-
)
285-
286-
# Last chunk -> root
287-
if height % 4 == 0:
288-
match_range(
289-
leaf_position_nibbles[div_ceil(height, 4) - 1],
290-
range(0, 16),
291-
lambda b: whir_do_4_merkle_levels(
292-
b, states + (div_ceil(height, 4) - 2) * DIGEST_LEN, merkle_path + 4 * (div_ceil(height, 4) - 1) * DIGEST_LEN, root
293-
),
294-
)
295-
elif height % 4 == 1:
296-
match_range(
297-
leaf_position_nibbles[(height - height % 4) / 4],
298-
range(0, 16),
299-
lambda b: whir_do_1_merkle_level(
300-
b, states + (div_ceil(height, 4) - 2) * DIGEST_LEN, merkle_path + 4 * ((height - height % 4) / 4) * DIGEST_LEN, root
301-
),
302-
)
303-
elif height % 4 == 2:
304-
match_range(
305-
leaf_position_nibbles[(height - height % 4) / 4],
306-
range(0, 16),
307-
lambda b: whir_do_2_merkle_levels(
308-
b, states + (div_ceil(height, 4) - 2) * DIGEST_LEN, merkle_path + 4 * ((height - height % 4) / 4) * DIGEST_LEN, root
309-
),
310-
)
311-
elif height % 4 == 3:
312-
match_range(
313-
leaf_position_nibbles[(height - height % 4) / 4],
314-
range(0, 16),
315-
lambda b: whir_do_3_merkle_levels(
316-
b, states + (div_ceil(height, 4) - 2) * DIGEST_LEN, merkle_path + 4 * ((height - height % 4) / 4) * DIGEST_LEN, root
317-
),
318-
)
319-
320-
return leaf_data
321-
322-
323262
def merkle_verif_batch(merkle_paths, leaves_digests, leave_positions, root, height, num_queries):
324263
match_range(
325264
height,

crates/rec_aggregation/utils.py

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -473,41 +473,116 @@ def checked_decompose_bits(a):
473473

474474

475475
@inline
476-
def checked_decompose_bits_and_compute_root_pow_const(a, domain_size):
477-
# Hint 6 nibbles (4 bits each) + 1 top-7-bit value = 7 hints
476+
def whir_4_merkle_step_and_pow(v, state_in, path_chunk, state_out, power_shift):
477+
whir_do_4_merkle_levels(v, state_in, path_chunk, state_out)
478+
return ROOT ** (power_shift * v)
479+
480+
481+
@inline
482+
def whir_3_merkle_step_and_pow(v, state_in, path_chunk, state_out, power_shift):
483+
whir_do_3_merkle_levels(v, state_in, path_chunk, state_out)
484+
return ROOT ** (power_shift * (v % 8))
485+
486+
487+
@inline
488+
def whir_2_merkle_step_and_pow(v, state_in, path_chunk, state_out, power_shift):
489+
whir_do_2_merkle_levels(v, state_in, path_chunk, state_out)
490+
return ROOT ** (power_shift * (v % 4))
491+
492+
493+
@inline
494+
def whir_1_merkle_step_and_pow(v, state_in, path_chunk, state_out, power_shift):
495+
whir_do_1_merkle_level(v, state_in, path_chunk, state_out)
496+
return ROOT ** (power_shift * (v % 2))
497+
498+
499+
@inline
500+
def decompose_and_verify_merkle_query(a, domain_size, prev_root, num_chunks):
478501
nibbles = Array(6)
479502
top7: Imu
480503
hint_decompose_bits_merkle_whir(nibbles, top7, a, 4)
481504

482505
for i in unroll(0, 6):
483506
assert nibbles[i] < 16
484-
485507
assert top7 < 2**7
486508

487509
partial_sum: Mut = nibbles[0]
488510
for i in unroll(1, 6):
489511
partial_sum += nibbles[i] * 16**i
490-
491512
if top7 == 2**7 - 1:
492513
assert partial_sum == 0
493-
494514
assert partial_sum + top7 * 2**24 == a
495515

496-
# Compute domain_generator^index
516+
leaf_data = Array(num_chunks * DIGEST_LEN)
517+
hint_witness("merkle_leaf", leaf_data)
518+
leaf_hash = slice_hash_rtl(leaf_data, num_chunks)
519+
520+
merkle_path = Array(domain_size * DIGEST_LEN)
521+
hint_witness("merkle_path", merkle_path)
522+
523+
n_nibbles = div_ceil(domain_size, 4)
524+
states = Array((n_nibbles - 1) * DIGEST_LEN)
525+
497526
prod: Mut = 1
498-
for k in unroll(0, (domain_size - domain_size % 4) / 4):
499-
nib_pow = match_range(nibbles[k], range(0, 16), lambda v: ROOT ** (2 ** (TWO_ADICITY - domain_size + 4 * k) * v))
527+
528+
# First nibble: leaf_hash -> states[0]
529+
nib_pow = match_range(
530+
nibbles[0],
531+
range(0, 16),
532+
lambda v: whir_4_merkle_step_and_pow(v, leaf_hash, merkle_path, states, 2 ** (TWO_ADICITY - domain_size)),
533+
)
534+
prod *= nib_pow
535+
536+
# Middle nibbles: states[k-1] -> states[k]
537+
for k in unroll(1, n_nibbles - 1):
538+
nib_pow = match_range(
539+
nibbles[k],
540+
range(0, 16),
541+
lambda v: whir_4_merkle_step_and_pow(
542+
v,
543+
states + (k - 1) * DIGEST_LEN,
544+
merkle_path + 4 * k * DIGEST_LEN,
545+
states + k * DIGEST_LEN,
546+
2 ** (TWO_ADICITY - domain_size + 4 * k),
547+
),
548+
)
500549
prod *= nib_pow
501550

502-
if domain_size % 4 != 0:
503-
edge_pow = match_range(
504-
nibbles[(domain_size - domain_size % 4) / 4],
551+
# Last nibble: states[-1] -> prev_root
552+
last_k = n_nibbles - 1
553+
last_state_in = states + (last_k - 1) * DIGEST_LEN
554+
last_path = merkle_path + 4 * last_k * DIGEST_LEN
555+
last_power_shift = 2 ** (TWO_ADICITY - domain_size + 4 * last_k)
556+
if domain_size % 4 == 0:
557+
nib_pow = match_range(
558+
nibbles[last_k],
559+
range(0, 16),
560+
lambda v: whir_4_merkle_step_and_pow(v, last_state_in, last_path, prev_root, last_power_shift),
561+
)
562+
prod *= nib_pow
563+
elif domain_size % 4 == 1:
564+
nib_pow = match_range(
565+
nibbles[last_k],
566+
range(0, 16),
567+
lambda v: whir_1_merkle_step_and_pow(v, last_state_in, last_path, prev_root, last_power_shift),
568+
)
569+
prod *= nib_pow
570+
elif domain_size % 4 == 2:
571+
nib_pow = match_range(
572+
nibbles[last_k],
505573
range(0, 16),
506-
lambda v: ROOT ** (2 ** (TWO_ADICITY - domain_size + 4 * ((domain_size - domain_size % 4) / 4)) * (v % 2 ** (domain_size % 4))),
574+
lambda v: whir_2_merkle_step_and_pow(v, last_state_in, last_path, prev_root, last_power_shift),
507575
)
508-
prod *= edge_pow
576+
prod *= nib_pow
577+
elif domain_size % 4 == 3:
578+
nib_pow = match_range(
579+
nibbles[last_k],
580+
range(0, 16),
581+
lambda v: whir_3_merkle_step_and_pow(v, last_state_in, last_path, prev_root, last_power_shift),
582+
)
583+
prod *= nib_pow
509584

510-
return nibbles, prod
585+
return leaf_data, prod
511586

512587

513588
def checked_decompose_bits_small_value_const(to_decompose, n_bits: Const):

crates/rec_aggregation/whir.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,7 @@ def decompose_and_verify_merkle_batch_with_height(num_queries, sampled, root, he
235235

236236
def decompose_and_verify_merkle_batch_const(num_queries, sampled, root, height: Const, num_chunks: Const, circle_values, merkle_leaves):
237237
for i in range(0, num_queries):
238-
nibbles, circle_values[i] = checked_decompose_bits_and_compute_root_pow_const(sampled[i], height)
239-
merkle_leaves[i] = hash_and_verify_merkle_hint(nibbles, root, height, num_chunks)
238+
merkle_leaves[i], circle_values[i] = decompose_and_verify_merkle_query(sampled[i], height, root, num_chunks)
240239
return
241240

242241

0 commit comments

Comments
 (0)