openvm_verify_stark_circuit/verifier/
trace.rs

1use std::borrow::{Borrow, BorrowMut};
2
3use openvm_circuit::arch::POSEIDON2_WIDTH;
4use openvm_continuations::{
5    circuit::{
6        deferral::DeferralCircuitPvs, root::NUM_DIGESTS_IN_VM_COMMIT, subair::hash_slice_trace,
7        utils::vk_commit_components,
8    },
9    utils::{digests_to_poseidon2_input, pad_slice_to_poseidon2_input, poseidon2_input_to_digests},
10};
11use openvm_cpu_backend::CpuBackend;
12use openvm_poseidon2_air::Permutation;
13use openvm_stark_backend::{proof::Proof, prover::AirProvingContext};
14use openvm_stark_sdk::config::baby_bear_poseidon2::{
15    poseidon2_compress_with_capacity, poseidon2_perm, BabyBearPoseidon2Config, DIGEST_SIZE, F,
16};
17use openvm_verify_stark_host::pvs::{
18    DeferralPvs, VerifierBasePvs, VerifierDefPvs, VmPvs, DEF_PVS_AIR_ID, VERIFIER_PVS_AIR_ID,
19    VM_PVS_AIR_ID,
20};
21use p3_field::{Field, PrimeCharacteristicRing, PrimeField32};
22use p3_matrix::dense::RowMajorMatrix;
23
24use crate::verifier::{DeferredVerifyPvsCols, RecursiveDeferredVerifyCols};
25
26#[repr(C)]
27#[derive(Clone, Copy)]
28pub struct DeferredVerifyPvsRecord<F> {
29    pub program_commit_hash: [F; DIGEST_SIZE],
30    pub initial_root_hash: [F; DIGEST_SIZE],
31    pub initial_pc_hash: [F; DIGEST_SIZE],
32    pub intermediate_exe_commit: [F; DIGEST_SIZE],
33    pub intermediate_vk_states: [[F; POSEIDON2_WIDTH]; NUM_DIGESTS_IN_VM_COMMIT - 1],
34    pub app_exe_commit: [F; DIGEST_SIZE],
35    pub app_vm_commit: [F; DIGEST_SIZE],
36}
37
38#[allow(clippy::type_complexity)]
39pub fn generate_record(
40    proof: &Proof<BabyBearPoseidon2Config>,
41) -> (
42    DeferredVerifyPvsRecord<F>,
43    Vec<[F; POSEIDON2_WIDTH]>,
44    Vec<[F; POSEIDON2_WIDTH]>,
45    Vec<usize>,
46) {
47    let (base_pvs_slice, _) = proof.public_values[VERIFIER_PVS_AIR_ID]
48        .as_slice()
49        .split_at(VerifierBasePvs::<u8>::width());
50    let child_verifier_pvs: &VerifierBasePvs<F> = base_pvs_slice.borrow();
51    let child_vm_pvs: &VmPvs<F> = proof.public_values[VM_PVS_AIR_ID].as_slice().borrow();
52    let depth_minus_one = child_verifier_pvs.recursion_depth - F::ONE;
53    let range_check_inputs = vec![depth_minus_one.as_canonical_u32() as usize];
54
55    let padded_program_commit = pad_slice_to_poseidon2_input(&child_vm_pvs.program_commit, F::ZERO);
56    let padded_initial_root = pad_slice_to_poseidon2_input(&child_vm_pvs.initial_root, F::ZERO);
57    let padded_initial_pc = pad_slice_to_poseidon2_input(&[child_vm_pvs.initial_pc], F::ZERO);
58
59    let perm = poseidon2_perm();
60    let program_commit_hash = perm.permute(padded_program_commit)[..DIGEST_SIZE]
61        .try_into()
62        .unwrap();
63    let initial_root_hash = perm.permute(padded_initial_root)[..DIGEST_SIZE]
64        .try_into()
65        .unwrap();
66    let initial_pc_hash = perm.permute(padded_initial_pc)[..DIGEST_SIZE]
67        .try_into()
68        .unwrap();
69
70    let mut poseidon2_compress_inputs = Vec::with_capacity(5);
71    let mut poseidon2_permute_inputs = Vec::new();
72    poseidon2_compress_inputs.extend_from_slice(&[
73        padded_program_commit,
74        padded_initial_root,
75        padded_initial_pc,
76    ]);
77
78    let intermediate_exe_commit =
79        poseidon2_compress_with_capacity(program_commit_hash, initial_root_hash).0;
80    poseidon2_compress_inputs.push(digests_to_poseidon2_input(
81        program_commit_hash,
82        initial_root_hash,
83    ));
84
85    let (intermediate_vk_states_vec, app_vm_commit) = hash_slice_trace(
86        &vk_commit_components(child_verifier_pvs),
87        Some(&mut poseidon2_permute_inputs),
88        Some(&mut poseidon2_compress_inputs),
89    );
90    let intermediate_vk_states = intermediate_vk_states_vec.try_into().unwrap();
91
92    let app_exe_commit =
93        poseidon2_compress_with_capacity(intermediate_exe_commit, initial_pc_hash).0;
94    poseidon2_compress_inputs.push(digests_to_poseidon2_input(
95        intermediate_exe_commit,
96        initial_pc_hash,
97    ));
98
99    (
100        DeferredVerifyPvsRecord {
101            program_commit_hash,
102            initial_root_hash,
103            initial_pc_hash,
104            intermediate_exe_commit,
105            intermediate_vk_states,
106            app_exe_commit,
107            app_vm_commit,
108        },
109        poseidon2_compress_inputs,
110        poseidon2_permute_inputs,
111        range_check_inputs,
112    )
113}
114
115pub fn generate_proving_ctx(
116    proof: &Proof<BabyBearPoseidon2Config>,
117    record: DeferredVerifyPvsRecord<F>,
118    final_transcript_state: [F; POSEIDON2_WIDTH],
119    output_commit: [F; DIGEST_SIZE],
120    def_idx: usize,
121    deferral_enabled: bool,
122) -> AirProvingContext<CpuBackend<BabyBearPoseidon2Config>> {
123    let base_width = DeferredVerifyPvsCols::<u8>::width();
124    let rec_width = RecursiveDeferredVerifyCols::<u8>::width();
125    let width = base_width + if deferral_enabled { rec_width } else { 0 };
126
127    let mut trace = vec![F::ZERO; width];
128    let (base_cols_slice, def_cols_slice) = trace.as_mut_slice().split_at_mut(base_width);
129    let cols: &mut DeferredVerifyPvsCols<F> = base_cols_slice.borrow_mut();
130
131    let (base_pvs_slice, def_pvs_slice) = proof.public_values[VERIFIER_PVS_AIR_ID]
132        .as_slice()
133        .split_at(VerifierBasePvs::<u8>::width());
134    let child_verifier_pvs: &VerifierBasePvs<F> = base_pvs_slice.borrow();
135    let child_vm_pvs: &VmPvs<F> = proof.public_values[VM_PVS_AIR_ID].as_slice().borrow();
136
137    cols.child_verifier_pvs = *child_verifier_pvs;
138    cols.child_vm_pvs = *child_vm_pvs;
139    let depth_minus_one = child_verifier_pvs.recursion_depth - F::ONE;
140    cols.recursion_depth_minus_one_inv = if depth_minus_one == F::ZERO {
141        F::ZERO
142    } else {
143        depth_minus_one.inverse()
144    };
145    cols.program_commit_hash = record.program_commit_hash;
146    cols.initial_root_hash = record.initial_root_hash;
147    cols.initial_pc_hash = record.initial_pc_hash;
148    cols.intermediate_exe_commit = record.intermediate_exe_commit;
149    cols.intermediate_vk_states = record.intermediate_vk_states;
150    cols.app_exe_commit = record.app_exe_commit;
151    cols.app_vm_commit = record.app_vm_commit;
152    cols.final_transcript_state = final_transcript_state;
153
154    if deferral_enabled {
155        let rec_cols: &mut RecursiveDeferredVerifyCols<_> = def_cols_slice.borrow_mut();
156        let def_verifier_pvs: &VerifierDefPvs<_> = def_pvs_slice.borrow();
157        let def_pvs: &DeferralPvs<_> = proof.public_values[DEF_PVS_AIR_ID].as_slice().borrow();
158        rec_cols.child_def_verifier_pvs = *def_verifier_pvs;
159        rec_cols.child_def_pvs = *def_pvs;
160    }
161
162    let mut public_values = vec![F::ZERO; DeferralCircuitPvs::<u8>::width()];
163    let deferral_pvs: &mut DeferralCircuitPvs<F> = public_values.as_mut_slice().borrow_mut();
164
165    // Note final_transcript_state is computed by the verifier sub-circuit,
166    // and is thus added to the list of Poseidon2 compress inputs there
167    let (left, right) = poseidon2_input_to_digests(final_transcript_state);
168    deferral_pvs.input_commit = poseidon2_compress_with_capacity(left, right).0;
169    deferral_pvs.output_commit = output_commit;
170    deferral_pvs.def_idx = F::from_usize(def_idx);
171
172    AirProvingContext {
173        cached_mains: vec![],
174        common_main: RowMajorMatrix::new(trace, width),
175        public_values,
176    }
177}