Skip to content

Commit 97e0e91

Browse files
Simplification (#500)
* Simplification * Rename metadata -> cache * Rework * CacheLDE -> LDE * Drop has_aux_trace param from build_round1 * Route reconstruct_round1 through build_round1 * Revert "Drop has_aux_trace param from build_round1" This reverts commit 164c7c0. * Pass has_aux_trace to build_round1 from reconstruct_round1 --------- Co-authored-by: gabrielbosio <gabrielbosio95@gmail.com>
1 parent 6b8be5b commit 97e0e91

1 file changed

Lines changed: 120 additions & 127 deletions

File tree

crypto/stark/src/prover.rs

Lines changed: 120 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ where
8181
{
8282
/// The Merkle trees constructed to obtain the commitment of the entire trace table.
8383
/// For preprocessed tables, this contains only the multiplicity columns.
84-
/// Wrapped in Arc to share with Round1Metadata without deep-cloning (~64MB per table).
84+
/// Wrapped in Arc to share with Round1Commitments without deep-cloning (~64MB per table).
8585
pub(crate) lde_trace_merkle_tree: Arc<BatchedMerkleTree<F>>,
8686
/// The root of the Merkle tree in `lde_trace_merkle_tree`.
8787
pub(crate) lde_trace_merkle_root: Commitment,
@@ -124,17 +124,11 @@ where
124124
precomputed_tree: Option<Arc<BatchedMerkleTree<Field>>>,
125125
precomputed_root: Option<Commitment>,
126126
num_precomputed_cols: usize,
127-
/// Cached main LDE columns from Phase A, used in Phase D to skip recomputation.
128-
cached_main_lde: Vec<Vec<FieldElement<Field>>>,
129127
}
130128

131-
/// Metadata from Round 1 commitments — stores Merkle trees, roots, and cached LDE evaluations.
132-
/// LDE evaluations are cached from Phase A/C and consumed in Phase D (Rounds 2-4),
133-
/// eliminating the expensive recomputation (iFFT + coset shift + FFT per column).
134-
///
135-
/// Memory trade-off: all N tables' LDE columns are live simultaneously between Phase A/C
136-
/// and Phase D (O(N × cols × lde_size)).
137-
pub struct Round1Metadata<Field, FieldExtension>
129+
/// Round 1 commitment artifacts — Merkle trees, roots, challenges, and bus inputs.
130+
/// Borrowed (not consumed) when building `Round1` in Phase D.
131+
pub struct Round1Commitments<Field, FieldExtension>
138132
where
139133
Field: IsFFTField + IsSubFieldOf<FieldExtension>,
140134
FieldExtension: IsField,
@@ -160,10 +154,68 @@ where
160154
rap_challenges: Vec<FieldElement<FieldExtension>>,
161155
/// Bus interaction public inputs (initial and final aux column values).
162156
bus_public_inputs: Option<BusPublicInputs<FieldExtension>>,
163-
/// Cached main LDE columns from Phase A (consumed by Phase D).
164-
cached_main_lde: Vec<Vec<FieldElement<Field>>>,
165-
/// Cached aux LDE columns from Phase C (consumed by Phase D).
166-
cached_aux_lde: Vec<Vec<FieldElement<FieldExtension>>>,
157+
}
158+
159+
/// LDE columns for main (Phase A) and auxiliary (Phase C) traces, consumed by value in Phase D.
160+
///
161+
/// Memory trade-off: all N tables' LDE columns are live simultaneously between Phase A/C
162+
/// and Phase D (O(N × cols × lde_size)).
163+
struct Lde<Field: IsFFTField, FieldExtension: IsField> {
164+
main: Vec<Vec<FieldElement<Field>>>,
165+
aux: Vec<Vec<FieldElement<FieldExtension>>>,
166+
}
167+
168+
impl<Field, FieldExtension> Round1Commitments<Field, FieldExtension>
169+
where
170+
Field: IsFFTField + IsSubFieldOf<FieldExtension> + Send + Sync,
171+
FieldExtension: IsField + Send + Sync,
172+
FieldElement<Field>: AsBytes,
173+
FieldElement<FieldExtension>: AsBytes,
174+
{
175+
/// Build a `Round1` by consuming a `Lde` and borrowing commitment data.
176+
fn build_round1(
177+
&self,
178+
lde: Lde<Field, FieldExtension>,
179+
step_size: usize,
180+
blowup_factor: usize,
181+
has_aux_trace: bool,
182+
) -> Round1<Field, FieldExtension> {
183+
let lde_trace = LDETraceTable::from_columns(lde.main, lde.aux, step_size, blowup_factor);
184+
185+
let main = Round1CommitmentData::<Field> {
186+
lde_trace_merkle_tree: Arc::clone(&self.main_merkle_tree),
187+
lde_trace_merkle_root: self.main_merkle_root,
188+
precomputed_merkle_tree: self.precomputed_merkle_tree.as_ref().map(Arc::clone),
189+
precomputed_merkle_root: self.precomputed_merkle_root,
190+
num_precomputed_cols: self.num_precomputed_cols,
191+
};
192+
193+
let aux = if has_aux_trace {
194+
Some(Round1CommitmentData::<FieldExtension> {
195+
lde_trace_merkle_tree: Arc::clone(
196+
self.aux_merkle_tree
197+
.as_ref()
198+
.expect("aux tree must exist when has_aux_trace"),
199+
),
200+
lde_trace_merkle_root: self
201+
.aux_merkle_root
202+
.expect("aux root must exist when has_aux_trace"),
203+
precomputed_merkle_tree: None,
204+
precomputed_merkle_root: None,
205+
num_precomputed_cols: 0,
206+
})
207+
} else {
208+
None
209+
};
210+
211+
Round1 {
212+
lde_trace,
213+
main,
214+
aux,
215+
rap_challenges: self.rap_challenges.clone(),
216+
bus_public_inputs: self.bus_public_inputs.clone(),
217+
}
218+
}
167219
}
168220

169221
/// Pre-computed twiddle factors and coset weights for a given domain size.
@@ -513,7 +565,6 @@ pub trait IsStarkProver<
513565
FieldElement<Field>: AsBytes,
514566
FieldElement<FieldExtension>: AsBytes,
515567
{
516-
let num_cols = trace.num_main_columns;
517568
let lde_size = domain.interpolation_domain_size * domain.blowup_factor;
518569
let mut columns = trace.extract_columns_main(lde_size);
519570
#[cfg(feature = "instruments")]
@@ -529,7 +580,7 @@ pub trait IsStarkProver<
529580
.ok_or(ProvingError::EmptyCommitment)?;
530581

531582
let (mult_tree, mult_root) =
532-
Self::commit_columns_bit_reversed(&columns[num_precomputed_cols..num_cols])
583+
Self::commit_columns_bit_reversed(&columns[num_precomputed_cols..])
533584
.ok_or(ProvingError::EmptyCommitment)?;
534585
#[cfg(feature = "instruments")]
535586
crate::instruments::accum_r1_main(main_lde_dur, t_sub.elapsed());
@@ -549,78 +600,48 @@ pub trait IsStarkProver<
549600
))
550601
}
551602

552-
/// Recompute Round1 from the trace, reusing the Merkle trees stored in metadata.
603+
/// Recompute Round1 from the trace, reusing the Merkle trees stored in commitments.
553604
///
554-
/// Only used by `run_debug_checks` — Phase D consumes the cached LDE from
555-
/// metadata directly and does not go through this path.
605+
/// Only used by `run_debug_checks` — Phase D consumes the cached LDE
606+
/// directly and does not go through this path.
556607
#[cfg(feature = "debug-checks")]
557608
fn reconstruct_round1(
558609
air: &dyn AIR<Field = Field, FieldExtension = FieldExtension, PublicInputs = PI>,
559610
trace: &TraceTable<Field, FieldExtension>,
560611
domain: &Domain<Field>,
561-
metadata: &Round1Metadata<Field, FieldExtension>,
612+
commitment: &Round1Commitments<Field, FieldExtension>,
562613
twiddles: &LdeTwiddles<Field>,
563614
) -> Result<Round1<Field, FieldExtension>, ProvingError>
564615
where
565616
FieldElement<Field>: AsBytes,
566617
FieldElement<FieldExtension>: AsBytes,
567618
{
568619
let lde_size = domain.interpolation_domain_size * domain.blowup_factor;
569-
let mut main_columns = trace.extract_columns_main(lde_size);
570-
Self::expand_columns_to_lde::<Field>(&mut main_columns, domain, twiddles);
571-
572-
let main = Round1CommitmentData::<Field> {
573-
lde_trace_merkle_tree: Arc::clone(&metadata.main_merkle_tree),
574-
lde_trace_merkle_root: metadata.main_merkle_root,
575-
precomputed_merkle_tree: metadata.precomputed_merkle_tree.as_ref().map(Arc::clone),
576-
precomputed_merkle_root: metadata.precomputed_merkle_root,
577-
num_precomputed_cols: metadata.num_precomputed_cols,
578-
};
620+
let mut main = trace.extract_columns_main(lde_size);
621+
Self::expand_columns_to_lde::<Field>(&mut main, domain, twiddles);
579622

580-
let (aux, aux_columns) = if air.has_aux_trace() {
581-
let mut aux_columns = trace.extract_columns_aux(lde_size);
582-
Self::expand_columns_to_lde::<FieldExtension>(&mut aux_columns, domain, twiddles);
583-
let aux_commitment = Round1CommitmentData::<FieldExtension> {
584-
lde_trace_merkle_tree: Arc::clone(
585-
metadata
586-
.aux_merkle_tree
587-
.as_ref()
588-
.expect("aux tree must exist when has_aux_trace"),
589-
),
590-
lde_trace_merkle_root: metadata
591-
.aux_merkle_root
592-
.expect("aux root must exist when has_aux_trace"),
593-
precomputed_merkle_tree: None,
594-
precomputed_merkle_root: None,
595-
num_precomputed_cols: 0,
596-
};
597-
(Some(aux_commitment), aux_columns)
623+
let aux = if air.has_aux_trace() {
624+
let mut aux = trace.extract_columns_aux(lde_size);
625+
Self::expand_columns_to_lde::<FieldExtension>(&mut aux, domain, twiddles);
626+
aux
598627
} else {
599-
(None, Vec::new())
628+
Vec::new()
600629
};
601630

602-
let lde_trace = LDETraceTable::from_columns(
603-
main_columns,
604-
aux_columns,
631+
Ok(commitment.build_round1(
632+
Lde { main, aux },
605633
air.step_size(),
606634
domain.blowup_factor,
607-
);
608-
609-
Ok(Round1 {
610-
lde_trace,
611-
main,
612-
aux,
613-
rap_challenges: metadata.rap_challenges.clone(),
614-
bus_public_inputs: metadata.bus_public_inputs.clone(),
615-
})
635+
air.has_aux_trace(),
636+
))
616637
}
617638

618639
/// Reconstruct Round1 for every table, print the bus balance report, and
619640
/// validate each trace. Called once after Phase C commits.
620641
#[cfg(feature = "debug-checks")]
621642
fn run_debug_checks(
622643
air_trace_pairs: &[AirTracePair<'_, Field, FieldExtension, PI>],
623-
metadatas: &[Round1Metadata<Field, FieldExtension>],
644+
commitments: &[Round1Commitments<Field, FieldExtension>],
624645
domains: &[Domain<Field>],
625646
twiddle_caches: &[LdeTwiddles<Field>],
626647
) where
@@ -630,12 +651,12 @@ pub trait IsStarkProver<
630651
{
631652
let mut temp_results: Vec<Round1<Field, FieldExtension>> =
632653
Vec::with_capacity(air_trace_pairs.len());
633-
for (((air, trace, _), metadata), (domain, twiddles)) in air_trace_pairs
654+
for (((air, trace, _), commitment), (domain, twiddles)) in air_trace_pairs
634655
.iter()
635-
.zip(metadatas.iter())
656+
.zip(commitments.iter())
636657
.zip(domains.iter().zip(twiddle_caches.iter()))
637658
{
638-
let result = Self::reconstruct_round1(*air, *trace, domain, metadata, twiddles)
659+
let result = Self::reconstruct_round1(*air, *trace, domain, commitment, twiddles)
639660
.expect("reconstruct_round1 failed in debug-checks");
640661
temp_results.push(result);
641662
}
@@ -1521,6 +1542,7 @@ pub trait IsStarkProver<
15211542
let phase_start = Instant::now();
15221543

15231544
let mut main_commits: Vec<MainCommitData<Field>> = Vec::with_capacity(num_airs);
1545+
let mut main_ldes: Vec<Vec<Vec<FieldElement<Field>>>> = Vec::with_capacity(num_airs);
15241546

15251547
for chunk_start in (0..num_airs).step_by(k) {
15261548
let chunk_end = (chunk_start + k).min(num_airs);
@@ -1564,8 +1586,8 @@ pub trait IsStarkProver<
15641586
precomputed_tree: pre_tree.map(Arc::new),
15651587
precomputed_root: pre_root,
15661588
num_precomputed_cols: n_pre,
1567-
cached_main_lde: cached_main,
15681589
});
1590+
main_ldes.push(cached_main);
15691591
}
15701592
}
15711593

@@ -1693,15 +1715,19 @@ pub trait IsStarkProver<
16931715
}
16941716
}
16951717

1696-
// Build metadata sequentially from main_commits + aux_results + bus_inputs
1697-
let mut metadatas: Vec<Round1Metadata<Field, FieldExtension>> =
1718+
// Build commitments and cached LDEs as separate vecs:
1719+
// commitments are borrowed in Phase D, LDEs are consumed by value.
1720+
let mut commitments: Vec<Round1Commitments<Field, FieldExtension>> =
16981721
Vec::with_capacity(num_airs);
1699-
for ((main_commit, (aux_tree, aux_root, cached_aux)), bus_public_inputs) in main_commits
1700-
.into_iter()
1701-
.zip(aux_results)
1702-
.zip(bus_inputs_vec)
1722+
let mut cached_ldes: Vec<Lde<Field, FieldExtension>> = Vec::with_capacity(num_airs);
1723+
for (((main_commit, main_lde), (aux_tree, aux_root, cached_aux)), bus_public_inputs) in
1724+
main_commits
1725+
.into_iter()
1726+
.zip(main_ldes)
1727+
.zip(aux_results)
1728+
.zip(bus_inputs_vec)
17031729
{
1704-
metadatas.push(Round1Metadata {
1730+
commitments.push(Round1Commitments {
17051731
main_merkle_tree: main_commit.main_tree,
17061732
main_merkle_root: main_commit.main_root,
17071733
precomputed_merkle_tree: main_commit.precomputed_tree,
@@ -1711,16 +1737,18 @@ pub trait IsStarkProver<
17111737
aux_merkle_root: aux_root,
17121738
rap_challenges: lookup_challenges.clone(),
17131739
bus_public_inputs,
1714-
cached_main_lde: main_commit.cached_main_lde,
1715-
cached_aux_lde: cached_aux,
1740+
});
1741+
cached_ldes.push(Lde {
1742+
main: main_lde,
1743+
aux: cached_aux,
17161744
});
17171745
}
17181746

17191747
#[cfg(feature = "instruments")]
17201748
let aux_commit_elapsed = phase_start.elapsed();
17211749

17221750
#[cfg(feature = "debug-checks")]
1723-
Self::run_debug_checks(&air_trace_pairs, &metadatas, &domains, &twiddle_caches);
1751+
Self::run_debug_checks(&air_trace_pairs, &commitments, &domains, &twiddle_caches);
17241752

17251753
// =====================================================================
17261754
// Rounds 2-4: Parallel per-table proving in chunks of K
@@ -1740,25 +1768,31 @@ pub trait IsStarkProver<
17401768
)> = Vec::with_capacity(num_airs);
17411769

17421770
let mut proofs = Vec::with_capacity(num_airs);
1771+
let mut lde_drain = cached_ldes.into_iter();
17431772
for chunk_start in (0..num_airs).step_by(k) {
17441773
let chunk_end = (chunk_start + k).min(num_airs);
1774+
let chunk_size = chunk_end - chunk_start;
17451775

1746-
let chunk_metadatas = &mut metadatas[chunk_start..chunk_end];
1776+
let chunk_ldes: Vec<Lde<Field, FieldExtension>> =
1777+
lde_drain.by_ref().take(chunk_size).collect();
1778+
let chunk_commitments = &commitments[chunk_start..chunk_end];
17471779
let chunk_transcripts = &mut table_transcripts[chunk_start..chunk_end];
17481780

17491781
#[cfg(feature = "parallel")]
1750-
let iter = chunk_metadatas
1751-
.par_iter_mut()
1782+
let iter = chunk_ldes
1783+
.into_par_iter()
1784+
.zip(chunk_commitments.par_iter())
17521785
.zip(chunk_transcripts.par_iter_mut())
17531786
.enumerate();
17541787
#[cfg(not(feature = "parallel"))]
1755-
let iter = chunk_metadatas
1756-
.iter_mut()
1788+
let iter = chunk_ldes
1789+
.into_iter()
1790+
.zip(chunk_commitments.iter())
17571791
.zip(chunk_transcripts.iter_mut())
17581792
.enumerate();
17591793

17601794
let chunk_results: Vec<Result<_, ProvingError>> = iter
1761-
.map(|(j, (metadata, table_transcript))| {
1795+
.map(|(j, ((lde, commitment), table_transcript))| {
17621796
let idx = chunk_start + j;
17631797
let (air, trace, pub_inputs) = &air_trace_pairs[idx];
17641798
let _ = trace; // used by instruments
@@ -1767,55 +1801,14 @@ pub trait IsStarkProver<
17671801
#[cfg(feature = "instruments")]
17681802
let table_start = Instant::now();
17691803

1770-
// Build Round1 from cached LDE (zero-copy move, no recomputation).
1771-
let cached_main = std::mem::take(&mut metadata.cached_main_lde);
1772-
let cached_aux = std::mem::take(&mut metadata.cached_aux_lde);
1773-
1774-
let lde_trace = LDETraceTable::from_columns(
1775-
cached_main,
1776-
cached_aux,
1804+
// Build Round1 from cached LDE (consumed by value, no recomputation).
1805+
let round_1_result = commitment.build_round1(
1806+
lde,
17771807
air.step_size(),
17781808
domain.blowup_factor,
1809+
air.has_aux_trace(),
17791810
);
17801811

1781-
let main = Round1CommitmentData::<Field> {
1782-
lde_trace_merkle_tree: Arc::clone(&metadata.main_merkle_tree),
1783-
lde_trace_merkle_root: metadata.main_merkle_root,
1784-
precomputed_merkle_tree: metadata
1785-
.precomputed_merkle_tree
1786-
.as_ref()
1787-
.map(Arc::clone),
1788-
precomputed_merkle_root: metadata.precomputed_merkle_root,
1789-
num_precomputed_cols: metadata.num_precomputed_cols,
1790-
};
1791-
1792-
let aux = if air.has_aux_trace() {
1793-
Some(Round1CommitmentData::<FieldExtension> {
1794-
lde_trace_merkle_tree: Arc::clone(
1795-
metadata
1796-
.aux_merkle_tree
1797-
.as_ref()
1798-
.expect("aux tree must exist when has_aux_trace"),
1799-
),
1800-
lde_trace_merkle_root: metadata
1801-
.aux_merkle_root
1802-
.expect("aux root must exist when has_aux_trace"),
1803-
precomputed_merkle_tree: None,
1804-
precomputed_merkle_root: None,
1805-
num_precomputed_cols: 0,
1806-
})
1807-
} else {
1808-
None
1809-
};
1810-
1811-
let round_1_result = Round1 {
1812-
lde_trace,
1813-
main,
1814-
aux,
1815-
rap_challenges: metadata.rap_challenges.clone(),
1816-
bus_public_inputs: metadata.bus_public_inputs.clone(),
1817-
};
1818-
18191812
if let Some(ref bpi) = round_1_result.bus_public_inputs {
18201813
table_transcript.append_field_element(&bpi.table_contribution);
18211814
}

0 commit comments

Comments
 (0)