Skip to main content

airbender_host/prover/
mod.rs

1use crate::error::{HostError, Result};
2use crate::proof::Proof;
3use crate::receipt::Receipt;
4use execution_utils::unrolled::UnrolledProgramProof;
5use std::path::{Path, PathBuf};
6
7mod cpu_prover;
8mod dev_prover;
9#[cfg(feature = "gpu-prover")]
10mod gpu_prover;
11
12pub use self::cpu_prover::{CpuProver, CpuProverBuilder};
13pub use self::dev_prover::{DevProver, DevProverBuilder};
14#[cfg(feature = "gpu-prover")]
15pub use self::gpu_prover::{GpuProver, GpuProverBuilder};
16
17pub(super) const DEFAULT_RAM_BOUND_BYTES: usize = 1 << 30;
18pub(super) const DEFAULT_CPU_CYCLE_BOUND: usize = u32::MAX as usize;
19
20/// Host prover interface.
21pub trait Prover {
22    fn prove(&self, input_words: &[u32]) -> Result<ProveResult>;
23}
24
25/// Maximum proof layer generated by the prover.
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
27pub enum ProverLevel {
28    Base,
29    RecursionUnrolled,
30    #[default]
31    RecursionUnified,
32}
33
34impl ProverLevel {
35    #[cfg(feature = "gpu-prover")]
36    pub fn as_unrolled_level(self) -> execution_utils::unrolled_gpu::UnrolledProverLevel {
37        match self {
38            ProverLevel::Base => execution_utils::unrolled_gpu::UnrolledProverLevel::Base,
39            ProverLevel::RecursionUnrolled => {
40                execution_utils::unrolled_gpu::UnrolledProverLevel::RecursionUnrolled
41            }
42            ProverLevel::RecursionUnified => {
43                execution_utils::unrolled_gpu::UnrolledProverLevel::RecursionUnified
44            }
45        }
46    }
47}
48
49/// Proof and receipt produced by `prove`.
50#[derive(Clone, Debug)]
51pub struct ProveResult {
52    pub proof: Proof,
53    pub cycles: u64,
54    pub receipt: Receipt,
55}
56
57pub(super) fn resolve_app_bin_path(path: &Path) -> Result<PathBuf> {
58    let base_path = base_path(path)?;
59    let app_bin_path = PathBuf::from(format!("{base_path}.bin"));
60
61    if !app_bin_path.exists() {
62        return Err(HostError::Prover(format!(
63            "binary not found: {}",
64            app_bin_path.display()
65        )));
66    }
67
68    app_bin_path.canonicalize().map_err(|err| {
69        HostError::Prover(format!(
70            "failed to canonicalize binary path {}: {err}",
71            app_bin_path.display()
72        ))
73    })
74}
75
76pub(super) fn resolve_text_path(app_bin_path: &Path) -> Result<PathBuf> {
77    let mut app_text_path = app_bin_path.to_path_buf();
78    app_text_path.set_extension("text");
79
80    if !app_text_path.exists() {
81        return Err(HostError::Prover(format!(
82            "text file not found: {}",
83            app_text_path.display()
84        )));
85    }
86
87    app_text_path.canonicalize().map_err(|err| {
88        HostError::Prover(format!(
89            "failed to canonicalize text path {}: {err}",
90            app_text_path.display()
91        ))
92    })
93}
94
95pub(super) fn base_path(app_bin_path: &Path) -> Result<String> {
96    let path_str = app_bin_path
97        .to_str()
98        .ok_or_else(|| HostError::Prover("app path is not valid UTF-8".to_string()))?;
99    if let Some(stripped) = path_str.strip_suffix(".bin") {
100        Ok(stripped.to_string())
101    } else {
102        Ok(path_str.to_string())
103    }
104}
105
106pub(super) fn resolve_worker_threads(worker_threads: Option<usize>) -> usize {
107    worker_threads
108        .or_else(|| {
109            std::thread::available_parallelism()
110                .ok()
111                .map(|count| count.get())
112        })
113        .unwrap_or(1)
114}
115
116pub(super) fn receipt_from_real_proof(proof: &UnrolledProgramProof) -> Receipt {
117    let mut registers = [0u32; 32];
118    for (idx, reg) in proof
119        .register_final_values
120        .iter()
121        .take(registers.len())
122        .enumerate()
123    {
124        registers[idx] = reg.value;
125    }
126    Receipt::from_registers(registers)
127}