openvm_sdk/
lib.rs

1use std::{fs::read, marker::PhantomData, path::Path, sync::Arc};
2
3use commit::commit_app_exe;
4use config::AppConfig;
5use eyre::Result;
6use keygen::{AppProvingKey, AppVerifyingKey};
7use openvm_build::{
8    build_guest_package, find_unique_executable, get_package, GuestOptions, TargetFilter,
9};
10use openvm_circuit::{
11    arch::{
12        hasher::poseidon2::vm_poseidon2_hasher, instructions::exe::VmExe, verify_segments,
13        ContinuationVmProof, ExecutionError, VerifiedExecutionPayload, VmConfig, VmExecutor,
14        VmVerificationError,
15    },
16    system::{
17        memory::{tree::public_values::extract_public_values, CHUNK},
18        program::trace::VmCommittedExe,
19    },
20};
21use openvm_continuations::verifier::root::types::RootVmVerifierInput;
22pub use openvm_continuations::{
23    static_verifier::{DefaultStaticVerifierPvHandler, StaticVerifierPvHandler},
24    RootSC, C, F, SC,
25};
26use openvm_native_recursion::halo2::{
27    utils::Halo2ParamsReader,
28    wrapper::{EvmVerifier, Halo2WrapperProvingKey},
29    RawEvmProof,
30};
31use openvm_stark_backend::proof::Proof;
32use openvm_stark_sdk::{
33    config::{baby_bear_poseidon2::BabyBearPoseidon2Engine, FriParameters},
34    engine::StarkFriEngine,
35    openvm_stark_backend::{verifier::VerificationError, Chip},
36};
37use openvm_transpiler::{
38    elf::Elf,
39    openvm_platform::memory::MEM_SIZE,
40    transpiler::{Transpiler, TranspilerError},
41    FromElf,
42};
43
44use crate::{
45    config::AggConfig,
46    keygen::{AggProvingKey, AggStarkProvingKey},
47    prover::{AppProver, ContinuationProver, StarkProver},
48};
49
50pub mod codec;
51pub mod commit;
52pub mod config;
53pub mod keygen;
54pub mod prover;
55
56mod stdin;
57pub use stdin::*;
58
59use crate::types::EvmProof;
60
61pub mod fs;
62pub mod types;
63
64pub type NonRootCommittedExe = VmCommittedExe<SC>;
65
66/// The payload of a verified guest VM execution with user public values extracted and
67/// verified.
68pub struct VerifiedContinuationVmPayload {
69    /// The Merklelized hash of:
70    /// - Program code commitment (commitment of the cached trace)
71    /// - Merkle root of the initial memory
72    /// - Starting program counter (`pc_start`)
73    ///
74    /// The Merklelization uses Poseidon2 as a cryptographic hash function (for the leaves)
75    /// and a cryptographic compression function (for internal nodes).
76    pub exe_commit: [F; CHUNK],
77    pub user_public_values: Vec<F>,
78}
79
80pub struct GenericSdk<E: StarkFriEngine<SC>> {
81    _phantom: PhantomData<E>,
82}
83
84impl<E: StarkFriEngine<SC>> Default for GenericSdk<E> {
85    fn default() -> Self {
86        Self {
87            _phantom: PhantomData,
88        }
89    }
90}
91
92pub type Sdk = GenericSdk<BabyBearPoseidon2Engine>;
93
94impl<E: StarkFriEngine<SC>> GenericSdk<E> {
95    pub fn new() -> Self {
96        Self::default()
97    }
98
99    pub fn build<P: AsRef<Path>>(
100        &self,
101        guest_opts: GuestOptions,
102        pkg_dir: P,
103        target_filter: &Option<TargetFilter>,
104    ) -> Result<Elf> {
105        let pkg = get_package(pkg_dir.as_ref());
106        let target_dir = match build_guest_package(&pkg, &guest_opts, None, target_filter) {
107            Ok(target_dir) => target_dir,
108            Err(Some(code)) => {
109                return Err(eyre::eyre!("Failed to build guest: code = {}", code));
110            }
111            Err(None) => {
112                return Err(eyre::eyre!(
113                    "Failed to build guest (OPENVM_SKIP_BUILD is set)"
114                ));
115            }
116        };
117
118        let elf_path = find_unique_executable(pkg_dir, target_dir, target_filter)?;
119        let data = read(&elf_path)?;
120        Elf::decode(&data, MEM_SIZE as u32)
121    }
122
123    pub fn transpile(
124        &self,
125        elf: Elf,
126        transpiler: Transpiler<F>,
127    ) -> Result<VmExe<F>, TranspilerError> {
128        VmExe::from_elf(elf, transpiler)
129    }
130
131    pub fn execute<VC: VmConfig<F>>(
132        &self,
133        exe: VmExe<F>,
134        vm_config: VC,
135        inputs: StdIn,
136    ) -> Result<Vec<F>, ExecutionError>
137    where
138        VC::Executor: Chip<SC>,
139        VC::Periphery: Chip<SC>,
140    {
141        let vm = VmExecutor::new(vm_config);
142        let final_memory = vm.execute(exe, inputs)?;
143        let public_values = extract_public_values(
144            &vm.config.system().memory_config.memory_dimensions(),
145            vm.config.system().num_public_values,
146            final_memory.as_ref().unwrap(),
147        );
148        Ok(public_values)
149    }
150
151    pub fn commit_app_exe(
152        &self,
153        app_fri_params: FriParameters,
154        exe: VmExe<F>,
155    ) -> Result<Arc<NonRootCommittedExe>> {
156        let committed_exe = commit_app_exe(app_fri_params, exe);
157        Ok(committed_exe)
158    }
159
160    pub fn app_keygen<VC: VmConfig<F>>(&self, config: AppConfig<VC>) -> Result<AppProvingKey<VC>>
161    where
162        VC::Executor: Chip<SC>,
163        VC::Periphery: Chip<SC>,
164    {
165        let app_pk = AppProvingKey::keygen(config);
166        Ok(app_pk)
167    }
168
169    pub fn generate_app_proof<VC: VmConfig<F>>(
170        &self,
171        app_pk: Arc<AppProvingKey<VC>>,
172        app_committed_exe: Arc<NonRootCommittedExe>,
173        inputs: StdIn,
174    ) -> Result<ContinuationVmProof<SC>>
175    where
176        VC::Executor: Chip<SC>,
177        VC::Periphery: Chip<SC>,
178    {
179        let app_prover = AppProver::new(app_pk.app_vm_pk.clone(), app_committed_exe);
180        let proof = app_prover.generate_app_proof(inputs);
181        Ok(proof)
182    }
183
184    /// Verifies the [ContinuationVmProof], which is a collection of STARK proofs as well as
185    /// additional Merkle proof for user public values.
186    ///
187    /// This function verifies the STARK proofs and additional conditions to ensure that the
188    /// `proof` is a valid proof of guest VM execution that terminates successfully (exit code 0)
189    /// _with respect to_ a commitment to some VM executable.
190    /// It is the responsibility of the caller to check that the commitment matches the expected
191    /// VM executable.
192    pub fn verify_app_proof(
193        &self,
194        app_vk: &AppVerifyingKey,
195        proof: &ContinuationVmProof<SC>,
196    ) -> Result<VerifiedContinuationVmPayload, VmVerificationError> {
197        let engine = E::new(app_vk.fri_params);
198        let VerifiedExecutionPayload {
199            exe_commit,
200            final_memory_root,
201        } = verify_segments(&engine, &app_vk.app_vm_vk, &proof.per_segment)?;
202
203        let hasher = vm_poseidon2_hasher();
204        proof
205            .user_public_values
206            .verify(&hasher, app_vk.memory_dimensions, final_memory_root)?;
207
208        Ok(VerifiedContinuationVmPayload {
209            exe_commit,
210            user_public_values: proof.user_public_values.public_values.clone(),
211        })
212    }
213
214    pub fn verify_app_proof_without_continuations(
215        &self,
216        app_vk: &AppVerifyingKey,
217        proof: &Proof<SC>,
218    ) -> Result<(), VerificationError> {
219        let e = E::new(app_vk.fri_params);
220        e.verify(&app_vk.app_vm_vk, proof)
221    }
222
223    pub fn agg_keygen(
224        &self,
225        config: AggConfig,
226        reader: &impl Halo2ParamsReader,
227        pv_handler: &impl StaticVerifierPvHandler,
228    ) -> Result<AggProvingKey> {
229        let agg_pk = AggProvingKey::keygen(config, reader, pv_handler);
230        Ok(agg_pk)
231    }
232
233    pub fn generate_root_verifier_input<VC: VmConfig<F>>(
234        &self,
235        app_pk: Arc<AppProvingKey<VC>>,
236        app_exe: Arc<NonRootCommittedExe>,
237        agg_stark_pk: AggStarkProvingKey,
238        inputs: StdIn,
239    ) -> Result<RootVmVerifierInput<SC>>
240    where
241        VC::Executor: Chip<SC>,
242        VC::Periphery: Chip<SC>,
243    {
244        let stark_prover = StarkProver::new(app_pk, app_exe, agg_stark_pk);
245        let proof = stark_prover.generate_root_verifier_input(inputs);
246        Ok(proof)
247    }
248
249    pub fn generate_evm_proof<VC: VmConfig<F>>(
250        &self,
251        reader: &impl Halo2ParamsReader,
252        app_pk: Arc<AppProvingKey<VC>>,
253        app_exe: Arc<NonRootCommittedExe>,
254        agg_pk: AggProvingKey,
255        inputs: StdIn,
256    ) -> Result<EvmProof>
257    where
258        VC::Executor: Chip<SC>,
259        VC::Periphery: Chip<SC>,
260    {
261        let e2e_prover = ContinuationProver::new(reader, app_pk, app_exe, agg_pk);
262        let proof = e2e_prover.generate_proof_for_evm(inputs);
263        Ok(proof)
264    }
265
266    pub fn generate_snark_verifier_contract(
267        &self,
268        reader: &impl Halo2ParamsReader,
269        agg_pk: &AggProvingKey,
270    ) -> Result<EvmVerifier> {
271        let params = reader.read_params(agg_pk.halo2_pk.wrapper.pinning.metadata.config_params.k);
272        let evm_verifier = agg_pk.halo2_pk.wrapper.generate_evm_verifier(&params);
273        Ok(evm_verifier)
274    }
275
276    pub fn verify_evm_proof(
277        &self,
278        evm_verifier: &EvmVerifier,
279        evm_proof: &EvmProof,
280    ) -> Result<u64> {
281        let evm_proof: RawEvmProof = evm_proof.clone().try_into()?;
282        let gas_cost = Halo2WrapperProvingKey::evm_verify(evm_verifier, &evm_proof)
283            .map_err(|reason| eyre::eyre!("Sdk::verify_evm_proof: {reason:?}"))?;
284        Ok(gas_cost)
285    }
286}