@@ -20,7 +20,11 @@ use executor::vm::logs::Log;
2020use executor:: vm:: memory:: U64HashMap ;
2121use math:: field:: element:: FieldElement ;
2222use stark:: constraints:: transition:: { TransitionConstraint , TransitionConstraintEvaluator } ;
23- use stark:: lookup:: { AirWithBuses , AuxiliaryTraceBuildData , NullBoundaryConstraintBuilder } ;
23+ use stark:: debug:: validate_trace;
24+ use stark:: domain:: Domain ;
25+ use stark:: lookup:: {
26+ AirWithBuses , AuxiliaryTraceBuildData , BusInteraction , BusValue , NullBoundaryConstraintBuilder ,
27+ } ;
2428use stark:: proof:: options:: ProofOptions ;
2529use stark:: proof:: stark:: MultiProof ;
2630use stark:: prover:: { IsStarkProver , Prover , ProvingError } ;
@@ -59,7 +63,9 @@ use crate::tables::keccak_rnd::{
5963use crate :: tables:: load:: {
6064 bus_interactions as load_bus_interactions, cols as load_cols, constraints as load_constraints,
6165} ;
62- use crate :: tables:: lt:: { LtOperation , bus_interactions as lt_bus_interactions, cols as lt_cols} ;
66+ use crate :: tables:: lt:: {
67+ LtOperation , bus_interactions as lt_bus_interactions, cols as lt_cols, lt_constraints,
68+ } ;
6369use crate :: tables:: memw:: {
6470 bus_interactions as memw_bus_interactions, cols as memw_cols, constraints as memw_constraints,
6571} ;
@@ -71,15 +77,17 @@ use crate::tables::memw_register::{
7177 bus_interactions as memw_register_bus_interactions, cols as memw_register_cols,
7278 constraints as memw_register_constraints,
7379} ;
74- use crate :: tables:: mul:: { bus_interactions as mul_bus_interactions, cols as mul_cols} ;
80+ use crate :: tables:: mul:: {
81+ bus_interactions as mul_bus_interactions, cols as mul_cols, mul_constraints,
82+ } ;
7583use crate :: tables:: page:: { bus_interactions as page_bus_interactions, cols as page_cols} ;
7684use crate :: tables:: register:: {
7785 bus_interactions as register_bus_interactions, cols as register_cols,
7886} ;
7987use crate :: tables:: shift:: {
8088 bus_interactions as shift_bus_interactions, cols as shift_cols, shift_constraints,
8189} ;
82- use crate :: tables:: types:: { GoldilocksExtension , GoldilocksField } ;
90+ use crate :: tables:: types:: { BusId , GoldilocksExtension , GoldilocksField } ;
8391
8492pub type F = GoldilocksField ;
8593pub type E = GoldilocksExtension ;
@@ -108,6 +116,79 @@ where
108116 )
109117}
110118
119+ // =============================================================================
120+ // Soundness regression helpers (negative AIR tests)
121+ // =============================================================================
122+
123+ /// Build a bus-less AIR carrying only the given in-chip transition constraints.
124+ /// With zero bus interactions, `AirWithBuses::new` appends no LogUp constraints
125+ /// and allocates no aux columns, so `validate_trace` evaluates exactly the chip's
126+ /// transition constraints over a main-only trace.
127+ pub fn busless_air < C : TransitionConstraint < F , E > + ' static > (
128+ num_columns : usize ,
129+ constraints : Vec < C > ,
130+ ) -> VmAir {
131+ let transition_constraints = constraints. into_iter ( ) . map ( |c| c. boxed ( ) ) . collect ( ) ;
132+ AirWithBuses :: new (
133+ num_columns,
134+ AuxiliaryTraceBuildData {
135+ interactions : vec ! [ ] ,
136+ } ,
137+ & ProofOptions :: default_test_options ( ) ,
138+ 1 ,
139+ transition_constraints,
140+ )
141+ }
142+
143+ /// Run `validate_trace` for a bus-less chip AIR over a main-only trace.
144+ /// Returns `true` iff every transition constraint holds on every row.
145+ pub fn validate_busless ( air : & VmAir , trace : & TraceTable < F , E > ) -> bool {
146+ let domain = Domain :: new ( air, trace. num_rows ( ) ) ;
147+ validate_trace ( air, & ( ) , trace, & domain, & [ ] , None )
148+ }
149+
150+ /// Number of transition constraints a production builder registers on top of its
151+ /// bus constraints, as a delta against a bus-only AIR with the same interactions
152+ /// but no in-chip constraints. Isolates the in-chip count even though
153+ /// `AirWithBuses::new` also appends LogUp constraints, so a plain count cannot.
154+ pub fn in_chip_constraint_count (
155+ wired : usize ,
156+ num_columns : usize ,
157+ buses : Vec < BusInteraction > ,
158+ ) -> usize {
159+ let bus_only = AirWithBuses :: < F , E , NullBoundaryConstraintBuilder , ( ) > :: new (
160+ num_columns,
161+ AuxiliaryTraceBuildData {
162+ interactions : buses,
163+ } ,
164+ & ProofOptions :: default_test_options ( ) ,
165+ 1 ,
166+ vec ! [ ] ,
167+ )
168+ . num_transition_constraints ( ) ;
169+ wired
170+ . checked_sub ( bus_only)
171+ . expect ( "wired (in-chip + bus constraints) must be >= bus-only constraint count" )
172+ }
173+
174+ /// Collect the `start_column`s of every `IS_HALFWORD` sender in `interactions`.
175+ /// Used to assert input/operand half-limbs are range-checked. Scope: only
176+ /// single-column `Packed` senders (which is how every current IS_HALFWORD sender is
177+ /// declared); it does not inspect `Linear` senders or sender multiplicities.
178+ pub fn is_halfword_sender_columns ( interactions : & [ BusInteraction ] ) -> Vec < usize > {
179+ let id: u64 = BusId :: IsHalfword . into ( ) ;
180+ interactions
181+ . iter ( )
182+ . filter ( |i| i. is_sender && i. bus_id == id)
183+ . flat_map ( |i| {
184+ i. values . iter ( ) . filter_map ( |v| match v {
185+ BusValue :: Packed { start_column, .. } => Some ( * start_column) ,
186+ BusValue :: Linear ( _) => None ,
187+ } )
188+ } )
189+ . collect ( )
190+ }
191+
111192// =============================================================================
112193// ELF Execution Helpers
113194// =============================================================================
@@ -540,9 +621,11 @@ pub fn create_bitwise_air(proof_options: &ProofOptions) -> VmAir {
540621 . with_name ( "BITWISE" )
541622}
542623
543- /// Create LT AIR with bus interactions.
624+ /// Create LT AIR with constraints and bus interactions.
544625pub fn create_lt_air ( proof_options : & ProofOptions ) -> VmAir {
545- let transition_constraints: Vec < Box < dyn TransitionConstraintEvaluator < F , E > > > = vec ! [ ] ;
626+ let ( constraints, _) = lt_constraints ( 0 ) ;
627+ let transition_constraints: Vec < Box < dyn TransitionConstraintEvaluator < F , E > > > =
628+ constraints. into_iter ( ) . map ( |c| c. boxed ( ) ) . collect ( ) ;
546629
547630 let auxiliary_trace_build_data = AuxiliaryTraceBuildData {
548631 interactions : lt_bus_interactions ( ) ,
@@ -680,9 +763,11 @@ pub fn create_decode_air(proof_options: &ProofOptions) -> VmAir {
680763 . with_name ( "DECODE" )
681764}
682765
683- /// Create MUL AIR with bus interactions.
766+ /// Create MUL AIR with constraints and bus interactions.
684767pub fn create_mul_air ( proof_options : & ProofOptions ) -> VmAir {
685- let transition_constraints: Vec < Box < dyn TransitionConstraintEvaluator < F , E > > > = vec ! [ ] ;
768+ let ( constraints, _) = mul_constraints ( 0 ) ;
769+ let transition_constraints: Vec < Box < dyn TransitionConstraintEvaluator < F , E > > > =
770+ constraints. into_iter ( ) . map ( |c| c. boxed ( ) ) . collect ( ) ;
686771
687772 let auxiliary_trace_build_data = AuxiliaryTraceBuildData {
688773 interactions : mul_bus_interactions ( ) ,
0 commit comments