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 >
138132where
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