openvm_continuations/verifier/leaf/
mod.rs1use openvm_circuit::{
2 arch::{instructions::program::Program, SystemConfig, ADDR_SPACE_OFFSET},
3 system::memory::merkle::public_values::PUBLIC_VALUES_ADDRESS_SPACE_OFFSET,
4};
5use openvm_native_compiler::{conversion::CompilerOptions, prelude::*};
6use openvm_native_recursion::{
7 challenger::duplex::DuplexChallengerVariable, fri::TwoAdicFriPcsVariable, hints::Hintable,
8 stark::StarkVerifier, types::new_from_inner_multi_vk, utils::const_fri_config,
9 vars::StarkProofVariable,
10};
11use openvm_stark_sdk::{
12 config::{baby_bear_poseidon2::BabyBearPoseidon2Config, FriParameters},
13 openvm_stark_backend::{
14 keygen::types::MultiStarkVerifyingKey, p3_field::PrimeCharacteristicRing,
15 p3_util::log2_strict_usize, proof::Proof,
16 },
17};
18
19use crate::{
20 verifier::{
21 common::{
22 assert_or_assign_connector_pvs, assert_or_assign_memory_pvs,
23 assert_required_air_for_app_vm_present, get_connector_pvs, get_memory_pvs,
24 get_program_commit, types::VmVerifierPvs,
25 },
26 leaf::types::UserPublicValuesRootProof,
27 utils::VariableP2Compressor,
28 },
29 C, F,
30};
31
32pub mod types;
33mod vars;
34
35pub struct LeafVmVerifierConfig {
37 pub app_fri_params: FriParameters,
38 pub app_system_config: SystemConfig,
39 pub compiler_options: CompilerOptions,
40}
41
42impl LeafVmVerifierConfig {
43 pub fn build_program(
44 &self,
45 app_vm_vk: &MultiStarkVerifyingKey<BabyBearPoseidon2Config>,
46 ) -> Program<F> {
47 let m_advice = new_from_inner_multi_vk(app_vm_vk);
48 let mut builder = Builder::<C>::default();
49
50 {
51 builder.cycle_tracker_start("InitializePcsConst");
52 let pcs = TwoAdicFriPcsVariable {
53 config: const_fri_config(&mut builder, &self.app_fri_params),
54 };
55 builder.cycle_tracker_end("InitializePcsConst");
56 builder.cycle_tracker_start("ReadProofsFromInput");
57 let proofs: Array<C, StarkProofVariable<_>> =
58 <Vec<Proof<BabyBearPoseidon2Config>> as Hintable<C>>::read(&mut builder);
59 builder.assert_nonzero(&proofs.len());
61 builder.cycle_tracker_end("ReadProofsFromInput");
62
63 builder.cycle_tracker_start("VerifyProofs");
64 let pvs = VmVerifierPvs::<Felt<F>>::uninit(&mut builder);
65 builder.range(0, proofs.len()).for_each(|i_vec, builder| {
66 let i = i_vec[0];
67 let proof = builder.get(&proofs, i);
68 assert_required_air_for_app_vm_present(builder, &proof);
69 StarkVerifier::verify::<DuplexChallengerVariable<C>>(
70 builder, &pcs, &m_advice, &proof,
71 );
72 {
73 let commit = get_program_commit(builder, &proof);
74 builder.if_eq(i, RVar::zero()).then_or_else(
75 |builder| {
76 builder.assign(&pvs.app_commit, commit);
77 },
78 |builder| builder.assert_eq::<[_; DIGEST_SIZE]>(pvs.app_commit, commit),
79 );
80 }
81
82 let proof_connector_pvs = get_connector_pvs(builder, &proof);
85 assert_or_assign_connector_pvs(builder, &pvs.connector, i, &proof_connector_pvs);
86
87 let proof_memory_pvs = get_memory_pvs(builder, &proof);
90 assert_or_assign_memory_pvs(builder, &pvs.memory, i, &proof_memory_pvs);
91 });
92 builder.cycle_tracker_end("VerifyProofs");
93 builder.cycle_tracker_start("ExtractPublicValuesCommit");
94 let is_terminate = builder.cast_felt_to_var(pvs.connector.is_terminate);
95 builder.if_eq(is_terminate, F::ONE).then(|builder| {
96 let (pv_commit, expected_memory_root) =
97 self.verify_user_public_values_root(builder);
98 builder.assert_eq::<[_; DIGEST_SIZE]>(pvs.memory.final_root, expected_memory_root);
99 builder.assign(&pvs.public_values_commit, pv_commit);
100 });
101 for pv in pvs.flatten() {
102 builder.commit_public_value(pv);
103 }
104 builder.cycle_tracker_end("ExtractPublicValuesCommit");
105
106 builder.halt();
107 }
108
109 builder.compile_isa_with_options(self.compiler_options)
110 }
111
112 fn verify_user_public_values_root(
116 &self,
117 builder: &mut Builder<C>,
118 ) -> ([Felt<F>; DIGEST_SIZE], [Felt<F>; DIGEST_SIZE]) {
119 let memory_dimensions = self.app_system_config.memory_config.memory_dimensions();
120 let pv_as = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + ADDR_SPACE_OFFSET;
121 let pv_start_idx = memory_dimensions.label_to_index((pv_as, 0));
122 let pv_height = log2_strict_usize(self.app_system_config.num_public_values / DIGEST_SIZE);
123 let proof_len = memory_dimensions.overall_height() - pv_height;
124 let idx_prefix = pv_start_idx >> pv_height;
125
126 let root_proof = UserPublicValuesRootProof::<F>::read(builder);
128 builder.assert_eq::<Usize<_>>(root_proof.sibling_hashes.len(), Usize::from(proof_len));
129 let mut curr_commit = root_proof.public_values_commit;
130 let compressor = VariableP2Compressor::new(builder);
132 for i in 0..proof_len {
133 let sibling_hash = builder.get(&root_proof.sibling_hashes, i);
134 let (l_hash, r_hash) = if idx_prefix & (1 << i) != 0 {
135 (sibling_hash, curr_commit)
136 } else {
137 (curr_commit, sibling_hash)
138 };
139 curr_commit = compressor.compress(builder, &l_hash, &r_hash);
140 }
141 (root_proof.public_values_commit, curr_commit)
142 }
143}