@@ -42,7 +42,7 @@ use crate::test_utils::{
4242 E , F , VmAir , create_bitwise_air, create_branch_air, create_commit_air, create_cpu_air,
4343 create_decode_air, create_dvrm_air, create_halt_air, create_load_air, create_lt_air,
4444 create_memw_air, create_memw_aligned_air, create_memw_register_air, create_mul_air,
45- create_page_air, create_register_air , create_shift_air,
45+ create_page_air, create_shift_air,
4646} ;
4747
4848use stark:: proof:: options:: { GoldilocksCubicProofOptions , ProofOptions } ;
@@ -196,7 +196,6 @@ pub(crate) struct VmAirs {
196196 pub branches : Vec < VmAir > ,
197197 pub halt : VmAir ,
198198 pub commit : VmAir ,
199- pub register : VmAir ,
200199 pub pages : Vec < VmAir > ,
201200 pub memw_registers : Vec < VmAir > ,
202201}
@@ -209,7 +208,6 @@ impl VmAirs {
209208 ( & self . decode, & mut traces. decode, & ( ) ) ,
210209 ( & self . halt, & mut traces. halt, & ( ) ) ,
211210 ( & self . commit, & mut traces. commit, & ( ) ) ,
212- ( & self . register, & mut traces. register, & ( ) ) ,
213211 ] ;
214212
215213 for ( air, trace) in self . cpus . iter ( ) . zip ( traces. cpus . iter_mut ( ) ) {
@@ -264,7 +262,6 @@ impl VmAirs {
264262 & self . decode,
265263 & self . halt,
266264 & self . commit,
267- & self . register,
268265 ] ;
269266
270267 for air in & self . cpus {
@@ -359,10 +356,6 @@ impl VmAirs {
359356 . collect ( ) ;
360357 let halt = create_halt_air ( proof_options) ;
361358 let commit = create_commit_air ( proof_options) ;
362- let register = create_register_air ( proof_options) . with_preprocessed (
363- register:: preprocessed_commitment ( proof_options, elf. entry_point ) ,
364- register:: NUM_PREPROCESSED_COLS ,
365- ) ;
366359 let pages: Vec < _ > = page_configs
367360 . iter ( )
368361 . map ( |config| {
@@ -393,7 +386,6 @@ impl VmAirs {
393386 branches,
394387 halt,
395388 commit,
396- register,
397389 pages,
398390 memw_registers,
399391 }
@@ -459,18 +451,109 @@ pub(crate) fn compute_commit_bus_offset(
459451 Some ( total)
460452}
461453
462- /// Compute the expected COMMIT bus balance for a `MultiProof`.
454+ /// Compute the bus balance offset for the REGISTER table on the Memory bus.
455+ ///
456+ /// For each of the 67 register word addresses, computes the LogUp contribution
457+ /// of both REG-C1 (receiver, init token) and REG-C2 (sender, fini token).
458+ ///
459+ /// After HALT finalization, all registers have deterministic fini state:
460+ /// - x0-x31 (addr 0-63): fini=0, ts=MAX
461+ /// - x254 (addr 508): fini=0, ts=MAX
462+ /// - x255 (addr 510-511): fini_lo=1, fini_hi=0, ts=MAX
463+ ///
464+ /// Init values are also deterministic from the ELF entry point:
465+ /// - SP (x2): init = STACK_TOP
466+ /// - PC (x255): init = entry_point
467+ /// - All others: init = 0
468+ ///
469+ /// The fingerprint formula per bus interaction is:
470+ /// `lc = BusId::Memory * α^0 + is_register * α^1 + addr * α^2 + addr_hi * α^3
471+ /// + ts_lo * α^4 + ts_hi * α^5 + value * α^6`
472+ /// `fingerprint = z - lc`
473+ ///
474+ /// REG-C1 (receiver): contribution = -1/fingerprint
475+ /// REG-C2 (sender): contribution = +1/fingerprint
476+ ///
477+ /// Returns `Some(Σ contributions)`, or `None` on a fingerprint collision.
478+ pub ( crate ) fn compute_register_bus_offset (
479+ entry_point : u64 ,
480+ z : & FieldElement < E > ,
481+ alpha : & FieldElement < E > ,
482+ ) -> Option < FieldElement < E > > {
483+ let bus_id = FieldElement :: < E > :: from ( BusId :: Memory as u64 ) ;
484+ let alpha2 = alpha * alpha;
485+ let alpha3 = & alpha2 * alpha;
486+ let alpha4 = & alpha3 * alpha;
487+ let alpha5 = & alpha4 * alpha;
488+ let alpha6 = & alpha5 * alpha;
489+
490+ // Precompute constants that are the same for all addresses
491+ let is_register = FieldElement :: < E > :: one ( ) ; // is_register = 1
492+ // addr_hi = 0, contributes nothing
493+ let base_lc = & bus_id + & ( & is_register * alpha) ;
494+
495+ // Fini timestamp: ts = u64::MAX = 0xFFFFFFFF_FFFFFFFF
496+ let ts_lo = FieldElement :: < E > :: from ( 0xFFFF_FFFFu64 ) ;
497+ let ts_hi = FieldElement :: < E > :: from ( 0xFFFF_FFFFu64 ) ;
498+ let fini_ts_contrib = & ( & ts_lo * & alpha4) + & ( & ts_hi * & alpha5) ;
499+
500+ let mut total = FieldElement :: < E > :: zero ( ) ;
501+
502+ let addr_list = register:: register_word_address_list ( ) ;
503+
504+ for & addr in & addr_list {
505+ let addr_fe = FieldElement :: < E > :: from ( addr) ;
506+ let addr_contrib = & addr_fe * & alpha2;
507+
508+ // Init value: deterministic from entry_point
509+ let init_value = register:: init_value_for_address ( addr, entry_point) ;
510+
511+ // Fini value: deterministic after HALT finalization
512+ // HALT writes 0 to all registers except x255 which gets value=1.
513+ // x255 lo word (addr 510) → fini=1, x255 hi word (addr 511) → fini=0.
514+ let fini_value: u32 = if addr == 510 { 1 } else { 0 } ;
515+
516+ // REG-C1 (receiver, sign=-1): init token with ts=0
517+ // lc = base_lc + addr * α^2 + 0*α^3 + 0*α^4 + 0*α^5 + init_value * α^6
518+ let lc_init = & base_lc
519+ + & addr_contrib
520+ + & ( FieldElement :: < E > :: from ( init_value as u64 ) * & alpha6) ;
521+ let fp_init = z - & lc_init;
522+ let inv_init = fp_init. inv ( ) . ok ( ) ?;
523+ total = total - inv_init; // receiver: -1/fp
524+
525+ // REG-C2 (sender, sign=+1): fini token with ts=MAX
526+ // lc = base_lc + addr * α^2 + 0*α^3 + MAX_LO*α^4 + MAX_HI*α^5 + fini_value * α^6
527+ let lc_fini = & base_lc
528+ + & addr_contrib
529+ + & fini_ts_contrib
530+ + & ( FieldElement :: < E > :: from ( fini_value as u64 ) * & alpha6) ;
531+ let fp_fini = z - & lc_fini;
532+ let inv_fini = fp_fini. inv ( ) . ok ( ) ?;
533+ total = total + inv_fini; // sender: +1/fp
534+ }
535+
536+ Some ( total)
537+ }
538+
539+ /// Compute the expected bus balance for a `MultiProof`.
463540///
464541/// Replays Phase A of the transcript to recover (z, alpha), then computes
465- /// the offset from the given public output bytes. Call this after `multi_prove`
466- /// and before `multi_verify`.
467- pub ( crate ) fn compute_expected_commit_bus_balance (
542+ /// the combined offset from:
543+ /// 1. COMMIT output bus (public output bytes)
544+ /// 2. REGISTER Memory bus (verifier-computed init/fini tokens)
545+ ///
546+ /// Call this after `multi_prove` and before `multi_verify`.
547+ pub ( crate ) fn compute_expected_bus_balance (
468548 airs : & [ & dyn AIR < Field = F , FieldExtension = E , PublicInputs = ( ) > ] ,
469549 proof : & MultiProof < F , E , ( ) > ,
470550 public_output_bytes : & [ u8 ] ,
551+ entry_point : u64 ,
471552) -> Option < FieldElement < E > > {
472553 let ( z, alpha) = replay_transcript_phase_a ( airs, proof) ;
473- compute_commit_bus_offset ( public_output_bytes, & z, & alpha)
554+ let commit_offset = compute_commit_bus_offset ( public_output_bytes, & z, & alpha) ?;
555+ let register_offset = compute_register_bus_offset ( entry_point, & z, & alpha) ?;
556+ Some ( commit_offset + register_offset)
474557}
475558
476559// =============================================================================
@@ -612,11 +695,12 @@ pub fn verify_with_options(
612695 Traces :: page_configs_from_elf_and_runtime ( & program, & vm_proof. runtime_page_ranges ) ;
613696
614697 // Cross-check: table_counts must match the number of sub-proofs.
615- // Fixed tables (bitwise, decode, halt, commit, register) = 5, plus page tables.
616- let expected_proof_count = vm_proof. table_counts . total ( ) + 5 + page_configs. len ( ) ;
698+ // Fixed tables (bitwise, decode, halt, commit) = 4, plus page tables.
699+ // REGISTER table is eliminated: its bus contribution is verifier-computed.
700+ let expected_proof_count = vm_proof. table_counts . total ( ) + 4 + page_configs. len ( ) ;
617701 if expected_proof_count != vm_proof. proof . proofs . len ( ) {
618702 return Err ( Error :: InvalidTableCounts ( format ! (
619- "table_counts total ({}) + 5 fixed + {} pages = {}, but proof contains {} sub-proofs" ,
703+ "table_counts total ({}) + 4 fixed + {} pages = {}, but proof contains {} sub-proofs" ,
620704 vm_proof. table_counts. total( ) ,
621705 page_configs. len( ) ,
622706 expected_proof_count,
@@ -632,14 +716,16 @@ pub fn verify_with_options(
632716 & vm_proof. table_counts ,
633717 ) ;
634718
635- // Recompute the COMMIT output bus offset from VmProof.public_output.
636- // If public_output was tampered, the recomputed offset won't match the
637- // actual bus total in the proof, and multi_verify will reject.
719+ // Recompute bus offsets from verifier-known data:
720+ // 1. COMMIT output bus: from VmProof.public_output
721+ // 2. REGISTER Memory bus: from ELF entry_point (init/fini are deterministic)
722+ // Tampering with either will cause bus imbalance and multi_verify rejection.
638723 let air_refs = airs. air_refs ( ) ;
639- let expected_bus_balance = match compute_expected_commit_bus_balance (
724+ let expected_bus_balance = match compute_expected_bus_balance (
640725 & air_refs,
641726 & vm_proof. proof ,
642727 & vm_proof. public_output ,
728+ program. entry_point ,
643729 ) {
644730 Some ( balance) => balance,
645731 None => return Ok ( false ) ,
0 commit comments