Skip to content

Commit 2253061

Browse files
authored
Merge branch 'main' into opt/20-parallel-fri-merkle
2 parents 608895e + f3e1936 commit 2253061

5 files changed

Lines changed: 152 additions & 113 deletions

File tree

crypto/stark/src/prover.rs

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -689,30 +689,50 @@ pub trait IsStarkProver<
689689
FieldElement<FieldExtension>: AsBytes + Sync + Send,
690690
{
691691
let num_parts = lde_composition_poly_parts_evaluations.len();
692+
if num_parts == 0 {
693+
return None;
694+
}
692695
let num_rows = lde_composition_poly_parts_evaluations[0].len();
693-
694-
// Transpose columns → rows with pre-allocated capacity.
695-
let mut rows: Vec<Vec<FieldElement<FieldExtension>>> = Vec::with_capacity(num_rows);
696-
for i in 0..num_rows {
697-
let mut row = Vec::with_capacity(num_parts);
698-
for part in lde_composition_poly_parts_evaluations.iter() {
699-
row.push(part[i].clone());
700-
}
701-
rows.push(row);
696+
if num_rows == 0 {
697+
return None;
702698
}
703699

704-
in_place_bit_reverse_permute(&mut rows);
700+
let num_leaves = num_rows / 2;
701+
debug_assert!(
702+
num_rows.is_power_of_two(),
703+
"num_rows must be a power of two for reverse_index to be correct"
704+
);
705+
debug_assert!(
706+
num_rows.is_power_of_two(),
707+
"num_rows must be a power of two for reverse_index"
708+
);
705709

706-
// Merge consecutive pairs: [row0, row1] → row0 ++ row1.
707-
// Drain pairs from the original vec to avoid cloning.
708-
let mut merged: Vec<Vec<FieldElement<FieldExtension>>> = Vec::with_capacity(num_rows / 2);
709-
let mut iter = rows.into_iter();
710-
while let (Some(mut first), Some(second)) = (iter.next(), iter.next()) {
711-
first.extend(second);
712-
merged.push(first);
713-
}
710+
// Skip the transpose + merge by computing leaf data inline.
711+
// Each leaf = row_pair[2*i] ++ row_pair[2*i+1] after bit-reverse.
712+
// Build the merged leaf data and hash in one pass.
713+
#[cfg(feature = "parallel")]
714+
let iter = (0..num_leaves).into_par_iter();
715+
#[cfg(not(feature = "parallel"))]
716+
let iter = 0..num_leaves;
714717

715-
Self::batch_commit_extension(&merged)
718+
let hashed_leaves: Vec<Commitment> = iter
719+
.map(|leaf_idx| {
720+
let br_0 = reverse_index(2 * leaf_idx, num_rows as u64);
721+
let br_1 = reverse_index(2 * leaf_idx + 1, num_rows as u64);
722+
let mut leaf = Vec::with_capacity(2 * num_parts);
723+
for part in lde_composition_poly_parts_evaluations.iter() {
724+
leaf.push(part[br_0].clone());
725+
}
726+
for part in lde_composition_poly_parts_evaluations.iter() {
727+
leaf.push(part[br_1].clone());
728+
}
729+
BatchedMerkleTreeBackend::<FieldExtension>::hash_data(&leaf)
730+
})
731+
.collect();
732+
733+
let tree = BatchedMerkleTree::<FieldExtension>::build_from_hashed_leaves(hashed_leaves)?;
734+
let root = tree.root;
735+
Some((tree, root))
716736
}
717737

718738
/// Algebraically decompose H(x) = H₀(x²) + x·H₁(x²) on the LDE coset, then

prover/src/constraints/cpu.rs

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ pub const BIT_FLAG_COLUMNS: &[usize] = &[
8686
cols::ECALL,
8787
cols::EBREAK,
8888
// Sign bits
89-
cols::RV1_SIGN_BIT,
90-
cols::ARG2_SIGN_BIT,
91-
cols::RES_SIGN_BIT,
89+
cols::RV1_EXT_BIT,
90+
cols::RV2_EXT_BIT,
91+
cols::RES_EXT_BIT,
9292
// Computed flags
9393
cols::IS_EQUAL,
9494
cols::BRANCH_COND,
@@ -379,7 +379,7 @@ impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for Arg1LowerCon
379379
}
380380
}
381381

382-
/// Constraint: arg1[4:8] = rv1[2] * (1 - word_instr) + (2^32 - 1) * rv1_sign_bit * signed
382+
/// Constraint: arg1[4:8] = rv1[2] * (1 - word_instr) + (2^32 - 1) * rv1_ext_bit * signed
383383
///
384384
/// Upper 32 bits of arg1 depends on word_instr and sign extension.
385385
pub struct Arg1UpperConstraint {
@@ -406,15 +406,15 @@ impl Arg1UpperConstraint {
406406
.get_main_evaluation_element(0, cols::WORD_INSTR)
407407
.clone();
408408
let signed = step.get_main_evaluation_element(0, cols::SIGNED).clone();
409-
let rv1_sign_bit = step
410-
.get_main_evaluation_element(0, cols::RV1_SIGN_BIT)
409+
let rv1_ext_bit = step
410+
.get_main_evaluation_element(0, cols::RV1_EXT_BIT)
411411
.clone();
412412

413413
let one = FieldElement::<F>::one();
414414
let mask_32: FieldElement<F> = FieldElement::from((1u64 << 32) - 1); // 2^32 - 1
415415

416-
// Expected: rv1_upper * (1 - word_instr) + mask_32 * rv1_sign_bit * signed
417-
let expected = rv1_upper * (one - &word_instr) + mask_32 * rv1_sign_bit * signed;
416+
// Expected: rv1_upper * (1 - word_instr) + mask_32 * rv1_ext_bit * signed
417+
let expected = rv1_upper * (one - &word_instr) + mask_32 * rv1_ext_bit * signed;
418418

419419
// Constraint: arg1_hi - expected = 0
420420
arg1_hi - expected
@@ -423,7 +423,7 @@ impl Arg1UpperConstraint {
423423

424424
impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for Arg1UpperConstraint {
425425
fn degree(&self) -> usize {
426-
// rv1_sign_bit * signed * word_instr has degree 3
426+
// rv1_ext_bit * signed * word_instr has degree 3
427427
3
428428
}
429429

@@ -549,47 +549,47 @@ pub fn create_slt_res_zero_constraints(
549549
}
550550

551551
// =========================================================================
552-
// Sign Bit Constraints
552+
// Extension Bit Constraints (SIGN template from spec)
553553
// =========================================================================
554554

555-
/// Constraint: sign bits are zero when word_instr = 0
555+
/// Constraint: ext_bit must be zero when word_instr = 0
556556
///
557-
/// (rv1_sign_bit + arg2_sign_bit + res_sign_bit) * (1 - word_instr) = 0
558-
pub struct SignBitZeroConstraint {
557+
/// (1 - word_instr) * ext_bit = 0
558+
///
559+
/// One instance per extension bit (rv1_ext_bit, rv2_ext_bit, res_ext_bit).
560+
pub struct ExtBitZeroConstraint {
559561
constraint_idx: usize,
562+
ext_bit_col: usize,
560563
}
561564

562-
impl SignBitZeroConstraint {
563-
pub fn new(constraint_idx: usize) -> Self {
564-
Self { constraint_idx }
565+
impl ExtBitZeroConstraint {
566+
pub fn new(constraint_idx: usize, ext_bit_col: usize) -> Self {
567+
Self {
568+
constraint_idx,
569+
ext_bit_col,
570+
}
565571
}
566572

567573
fn compute<F, E>(&self, step: &TableView<F, E>) -> FieldElement<F>
568574
where
569575
F: IsSubFieldOf<E>,
570576
E: IsField,
571577
{
572-
let rv1_sign_bit = step
573-
.get_main_evaluation_element(0, cols::RV1_SIGN_BIT)
574-
.clone();
575-
let arg2_sign_bit = step
576-
.get_main_evaluation_element(0, cols::ARG2_SIGN_BIT)
577-
.clone();
578-
let res_sign_bit = step
579-
.get_main_evaluation_element(0, cols::RES_SIGN_BIT)
578+
let ext_bit = step
579+
.get_main_evaluation_element(0, self.ext_bit_col)
580580
.clone();
581581
let word_instr = step
582582
.get_main_evaluation_element(0, cols::WORD_INSTR)
583583
.clone();
584584

585585
let one = FieldElement::<F>::one();
586586

587-
// (sum of sign bits) * (1 - word_instr) = 0
588-
(rv1_sign_bit + arg2_sign_bit + res_sign_bit) * (one - word_instr)
587+
// (1 - word_instr) * ext_bit = 0
588+
(one - word_instr) * ext_bit
589589
}
590590
}
591591

592-
impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for SignBitZeroConstraint {
592+
impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for ExtBitZeroConstraint {
593593
fn degree(&self) -> usize {
594594
2
595595
}
@@ -842,7 +842,7 @@ impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for Arg2LowerCon
842842
}
843843
}
844844

845-
/// Constraint: arg2[4:] = (1-LOAD)*((1-word_instr)*rv2[2] + signed*arg2_sign_bit*(2^32-1)) + (1-BEQ-BLT-STORE)*imm[1]
845+
/// Constraint: arg2[4:] = (1-LOAD)*((1-word_instr)*rv2[2] + signed*rv2_ext_bit*(2^32-1)) + (1-BEQ-BLT-STORE)*imm[1]
846846
///
847847
/// arg2 upper 32 bits with sign extension logic.
848848
pub struct Arg2UpperConstraint {
@@ -880,13 +880,13 @@ impl Arg2UpperConstraint {
880880
let blt = step.get_main_evaluation_element(0, cols::BLT);
881881
let word_instr = step.get_main_evaluation_element(0, cols::WORD_INSTR);
882882
let signed = step.get_main_evaluation_element(0, cols::SIGNED);
883-
let arg2_sign_bit = step.get_main_evaluation_element(0, cols::ARG2_SIGN_BIT);
883+
let rv2_ext_bit = step.get_main_evaluation_element(0, cols::RV2_EXT_BIT);
884884

885885
let one = FieldElement::<F>::one();
886886
let mask_32: FieldElement<F> = FieldElement::from((1u64 << 32) - 1);
887887

888-
// rv2_term = (1 - word_instr) * rv2[2] + signed * arg2_sign_bit * (2^32 - 1)
889-
let rv2_term = (&one - word_instr) * rv2_upper + signed * arg2_sign_bit * &mask_32;
888+
// rv2_term = (1 - word_instr) * rv2[2] + signed * rv2_ext_bit * (2^32 - 1)
889+
let rv2_term = (&one - word_instr) * rv2_upper + signed * rv2_ext_bit * &mask_32;
890890

891891
// expected = (1-LOAD) * rv2_term + (1-BEQ-BLT-STORE) * imm[1]
892892
// STORE now gets rv2_term (with sign extension), not imm
@@ -899,7 +899,7 @@ impl Arg2UpperConstraint {
899899

900900
impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for Arg2UpperConstraint {
901901
fn degree(&self) -> usize {
902-
// (1-LOAD) * signed * arg2_sign_bit has degree 3
902+
// (1-LOAD) * signed * rv2_ext_bit has degree 3
903903
3
904904
}
905905

@@ -989,7 +989,7 @@ impl TransitionConstraint<GoldilocksField, GoldilocksExtension> for RvdLowerCons
989989
}
990990
}
991991

992-
/// Constraint: (1-LOAD) * (rvd[1] - ((1-word_instr)*res[4:] + res_sign_bit*(2^32-1))) = 0
992+
/// Constraint: (1-LOAD) * (rvd[1] - ((1-word_instr)*res[4:] + res_ext_bit*(2^32-1))) = 0
993993
///
994994
/// When not LOAD, rvd upper 32 bits equals res upper with sign extension.
995995
/// For LOAD: rvd is the loaded value, not res (which is the address).
@@ -1016,13 +1016,13 @@ impl RvdUpperConstraint {
10161016

10171017
let load = step.get_main_evaluation_element(0, cols::LOAD);
10181018
let word_instr = step.get_main_evaluation_element(0, cols::WORD_INSTR);
1019-
let res_sign_bit = step.get_main_evaluation_element(0, cols::RES_SIGN_BIT);
1019+
let res_ext_bit = step.get_main_evaluation_element(0, cols::RES_EXT_BIT);
10201020

10211021
let one = FieldElement::<F>::one();
10221022
let mask_32: FieldElement<F> = FieldElement::from((1u64 << 32) - 1);
10231023

1024-
// expected = (1 - word_instr) * res_hi + res_sign_bit * (2^32 - 1)
1025-
let expected = (&one - word_instr) * res_hi + res_sign_bit * mask_32;
1024+
// expected = (1 - word_instr) * res_hi + res_ext_bit * (2^32 - 1)
1025+
let expected = (&one - word_instr) * res_hi + res_ext_bit * mask_32;
10261026

10271027
// (1 - LOAD) * (rvd[1] - expected) = 0
10281028
(one - load) * (rvd_1 - expected)
@@ -1217,14 +1217,14 @@ pub fn create_jalr_constraints(constraint_idx_start: usize) -> (Vec<AddConstrain
12171217
/// - Rvd lower: 1
12181218
/// - Rvd upper: 1
12191219
/// - SLT res zero: 7 (bytes 1-7)
1220-
/// - Sign bit zero: 1
1220+
/// - Ext bit zero (SIGN template): 3 (rv1_ext_bit, rv2_ext_bit, res_ext_bit)
12211221
/// - rv1 zero-forcing (CM48): 3 (rv1[0..2] when read_register1 = 0)
12221222
/// - rv2 zero-forcing (CM50): 3 (rv2[0..2] when read_register2 = 0)
12231223
/// - Next PC (non-branching): 2
12241224
///
1225-
/// Total: 64 constraints (32 IS_BIT + 8 ADD + 24 other)
1225+
/// Total: 66 constraints (32 IS_BIT + 8 ADD + 26 other)
12261226
pub const NUM_CPU_CONSTRAINTS: usize =
1227-
32 + 2 + 2 + 2 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 7 + 1 + 3 + 3 + 2;
1227+
32 + 2 + 2 + 2 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 7 + 3 + 3 + 3 + 2;
12281228

12291229
/// Creates all CPU constraints.
12301230
///
@@ -1313,8 +1313,21 @@ pub fn create_all_cpu_constraints() -> (
13131313
other.push(Box::new(c));
13141314
}
13151315

1316-
// Sign bit zero constraint
1317-
other.push(Box::new(SignBitZeroConstraint::new(next_idx)));
1316+
// Extension bit zero constraints (SIGN template: !word_instr => ext_bit = 0)
1317+
other.push(Box::new(ExtBitZeroConstraint::new(
1318+
next_idx,
1319+
cols::RV1_EXT_BIT,
1320+
)));
1321+
next_idx += 1;
1322+
other.push(Box::new(ExtBitZeroConstraint::new(
1323+
next_idx,
1324+
cols::RV2_EXT_BIT,
1325+
)));
1326+
next_idx += 1;
1327+
other.push(Box::new(ExtBitZeroConstraint::new(
1328+
next_idx,
1329+
cols::RES_EXT_BIT,
1330+
)));
13181331
next_idx += 1;
13191332

13201333
// Next PC (non-branching) constraints

0 commit comments

Comments
 (0)