@@ -47,6 +47,16 @@ use openvm_circuit::{
4747 } ,
4848 system:: { memory:: CHUNK , program:: trace:: compute_exe_commit} ,
4949} ;
50+ use openvm_continuations:: {
51+ verifier:: {
52+ common:: {
53+ assert_or_assign_connector_pvs, assert_or_assign_memory_pvs,
54+ assert_required_air_for_app_vm_present, get_connector_pvs, get_memory_pvs,
55+ get_program_commit,
56+ } ,
57+ leaf:: types:: UserPublicValuesRootProof ,
58+ } ,
59+ } ;
5060
5161const LEAF_LOG_BLOWUP : usize = 1 ;
5262const INTERNAL_LOG_BLOWUP : usize = 2 ;
@@ -63,18 +73,76 @@ impl CenoLeafVmVerifierConfig {
6373 pub fn build_program ( & self ) -> Program < F > {
6474 let mut builder = Builder :: < C > :: default ( ) ;
6575
76+ let pvs = VmVerifierPvs :: < Felt < F > > :: uninit ( & mut builder) ;
77+
6678 {
6779 builder. cycle_tracker_start ( "VerifyCenoProof" ) ;
6880
6981 let zkvm_proof_input_variables = ZKVMProofInput :: read ( & mut builder) ;
7082 verify_zkvm_proof ( & mut builder, zkvm_proof_input_variables, & self . vk ) ;
7183
84+ {
85+ let commit = get_program_commit ( builder, & proof) ;
86+ builder. if_eq ( i, RVar :: zero ( ) ) . then_or_else (
87+ |builder| {
88+ builder. assign ( & pvs. app_commit , commit) ;
89+ } ,
90+ |builder| builder. assert_eq :: < [ _ ; DIGEST_SIZE ] > ( pvs. app_commit , commit) ,
91+ ) ;
92+ }
93+
94+ let proof_connector_pvs = get_connector_pvs ( builder, & proof) ;
95+ assert_or_assign_connector_pvs ( builder, & pvs. connector , i, & proof_connector_pvs) ;
96+
97+ let proof_memory_pvs = get_memory_pvs ( builder, & proof) ;
98+ assert_or_assign_memory_pvs ( builder, & pvs. memory , i, & proof_memory_pvs) ;
99+
100+ let is_terminate = builder. cast_felt_to_var ( pvs. connector . is_terminate ) ;
101+ builder. if_eq ( is_terminate, F :: ONE ) . then ( |builder| {
102+ let ( pv_commit, expected_memory_root) =
103+ self . verify_user_public_values_root ( builder) ;
104+ builder. assert_eq :: < [ _ ; DIGEST_SIZE ] > ( pvs. memory . final_root , expected_memory_root) ;
105+ builder. assign ( & pvs. public_values_commit , pv_commit) ;
106+ } ) ;
107+ for pv in pvs. flatten ( ) {
108+ builder. commit_public_value ( pv) ;
109+ }
110+
72111 builder. cycle_tracker_end ( "VerifyCenoProof" ) ;
73112 builder. halt ( ) ;
74113 }
75114
76115 builder. compile_isa_with_options ( self . compiler_options )
77116 }
117+
118+ fn verify_user_public_values_root (
119+ & self ,
120+ builder : & mut Builder < C > ,
121+ ) -> ( [ Felt < F > ; DIGEST_SIZE ] , [ Felt < F > ; DIGEST_SIZE ] ) {
122+ let memory_dimensions = self . app_system_config . memory_config . memory_dimensions ( ) ;
123+ let pv_as = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions. as_offset ;
124+ let pv_start_idx = memory_dimensions. label_to_index ( ( pv_as, 0 ) ) ;
125+ let pv_height = log2_strict_usize ( self . app_system_config . num_public_values / DIGEST_SIZE ) ;
126+ let proof_len = memory_dimensions. overall_height ( ) - pv_height;
127+ let idx_prefix = pv_start_idx >> pv_height;
128+
129+ // Read the public values root proof from the input stream.
130+ let root_proof = UserPublicValuesRootProof :: < F > :: read ( builder) ;
131+ builder. assert_eq :: < Usize < _ > > ( root_proof. sibling_hashes . len ( ) , Usize :: from ( proof_len) ) ;
132+ let mut curr_commit = root_proof. public_values_commit ;
133+ // Share the same state array to avoid unnecessary allocations.
134+ let compressor = VariableP2Compressor :: new ( builder) ;
135+ for i in 0 ..proof_len {
136+ let sibling_hash = builder. get ( & root_proof. sibling_hashes , i) ;
137+ let ( l_hash, r_hash) = if idx_prefix & ( 1 << i) != 0 {
138+ ( sibling_hash, curr_commit)
139+ } else {
140+ ( curr_commit, sibling_hash)
141+ } ;
142+ curr_commit = compressor. compress ( builder, & l_hash, & r_hash) ;
143+ }
144+ ( root_proof. public_values_commit , curr_commit)
145+ }
78146}
79147
80148#[ derive( Clone , Serialize , Deserialize ) ]
@@ -96,6 +164,10 @@ pub fn compress_to_root_proof(
96164 . enumerate ( )
97165 . map ( |( shard_id, p) | ZKVMProofInput :: from ( ( shard_id, p) ) )
98166 . collect ( ) ;
167+ let user_public_values = zkvm_proof_inputs
168+ . iter ( )
169+ . flat_map ( |p| p. raw_pi . iter ( ) . flat_map ( |v| v. clone ( ) ) . collect :: < Vec < F > > ( ) )
170+ . collect ( ) ;
99171
100172 let aggregation_start_timestamp = Instant :: now ( ) ;
101173
@@ -273,10 +345,7 @@ pub fn compress_to_root_proof(
273345 // Export e2e stark proof (used in verify_e2e_stark_proof)
274346 let root_stark_proof = VmStarkProof {
275347 proof : proofs. pop ( ) . unwrap ( ) ,
276- user_public_values : zkvm_proof_inputs
277- . iter ( )
278- . flat_map ( |p| p. raw_pi . iter ( ) . flat_map ( |v| v. clone ( ) ) . collect :: < Vec < F > > ( ) )
279- . collect ( ) ,
348+ user_public_values,
280349 } ;
281350 let file = File :: create ( "root_stark_proof.bin" ) . expect ( "Create export proof file" ) ;
282351 bincode:: serialize_into ( file, & root_stark_proof) . expect ( "failed to serialize internal proof" ) ;
0 commit comments