Skip to main content

sp1_sdk/light/
mod.rs

1//! # Light Prover
2//!
3//! A lightweight prover that only executes and verifies but does not generate proofs.
4//!
5//! Unlike [`MockProver`](crate::MockProver), the light prover uses the default
6//! [`Prover::verify`](crate::Prover::verify) implementation which does full cryptographic
7//! proof verification. This makes it useful as a lightweight verifier node that can
8//! validate proofs produced by other provers (CPU, CUDA, network).
9
10pub mod builder;
11
12use std::pin::Pin;
13
14use sp1_core_executor::SP1CoreOpts;
15use sp1_core_machine::{io::SP1Stdin, riscv::RiscvAir};
16use sp1_hypercube::Machine;
17use sp1_primitives::SP1Field;
18use sp1_prover::worker::{SP1LightNode, SP1NodeCore};
19
20use crate::{
21    prover::{BaseProveRequest, ProveRequest},
22    Prover, SP1ProofWithPublicValues, SP1ProvingKey,
23};
24use std::future::{Future, IntoFuture};
25
26/// A lightweight prover that only executes and verifies but does not generate proofs.
27#[derive(Clone)]
28pub struct LightProver {
29    inner: SP1LightNode,
30}
31
32impl LightProver {
33    /// Create a new light prover.
34    #[must_use]
35    pub async fn new() -> Self {
36        Self::new_with_machine(RiscvAir::machine()).await
37    }
38
39    /// Create a new light prover with a given machine.
40    #[must_use]
41    pub async fn new_with_machine(machine: Machine<SP1Field, RiscvAir<SP1Field>>) -> Self {
42        tracing::info!("initializing light prover");
43        Self { inner: SP1LightNode::new_with_machine(machine).await }
44    }
45
46    /// Create a new light prover with custom options.
47    #[must_use]
48    pub async fn new_with_opts(opts: SP1CoreOpts) -> Self {
49        Self::new_with_opts_and_machine(RiscvAir::machine(), opts).await
50    }
51
52    /// Create a new light prover with custom options and a given machine.
53    #[must_use]
54    pub async fn new_with_opts_and_machine(
55        machine: Machine<SP1Field, RiscvAir<SP1Field>>,
56        opts: SP1CoreOpts,
57    ) -> Self {
58        Self { inner: SP1LightNode::with_opts_and_machine(machine, opts).await }
59    }
60}
61
62impl Prover for LightProver {
63    type ProvingKey = SP1ProvingKey;
64
65    type Error = anyhow::Error;
66
67    type ProveRequest<'a> = LightProveRequest<'a>;
68
69    fn inner(&self) -> &SP1NodeCore {
70        self.inner.inner()
71    }
72
73    fn prove<'a>(&'a self, pk: &'a Self::ProvingKey, stdin: SP1Stdin) -> Self::ProveRequest<'a> {
74        LightProveRequest { base: BaseProveRequest::new(self, pk, stdin) }
75    }
76
77    fn setup(
78        &self,
79        elf: sp1_build::Elf,
80    ) -> impl crate::prover::SendFutureResult<Self::ProvingKey, Self::Error> {
81        async move {
82            let vk = self.inner.setup(&elf).await?;
83            let pk = SP1ProvingKey { vk, elf };
84            Ok(pk)
85        }
86    }
87
88    // verify() is intentionally NOT overridden here.
89    // The default Prover::verify performs real cryptographic verification,
90    // unlike MockProver which only checks public value hashes.
91}
92
93/// A light prove request.
94pub struct LightProveRequest<'a> {
95    pub(crate) base: BaseProveRequest<'a, LightProver>,
96}
97
98impl<'a> ProveRequest<'a, LightProver> for LightProveRequest<'a> {
99    fn base(&mut self) -> &mut BaseProveRequest<'a, LightProver> {
100        &mut self.base
101    }
102}
103
104impl<'a> IntoFuture for LightProveRequest<'a> {
105    type Output = Result<SP1ProofWithPublicValues, anyhow::Error>;
106    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
107
108    fn into_future(self) -> Self::IntoFuture {
109        Box::pin(async move {
110            Err(anyhow::anyhow!(
111                "Use LightProver for executing and verifying only. For proving, use CpuProver"
112            ))
113        })
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use crate::{prover::ProveRequest, utils::setup_logger, LightProver, Prover, SP1Stdin};
120
121    /// Test that execute works and prove errors.
122    #[tokio::test]
123    async fn test_light_execute_and_prove() {
124        setup_logger();
125        let prover = LightProver::new().await;
126        let pk =
127            prover.setup(test_artifacts::FIBONACCI_ELF).await.expect("failed to setup proving key");
128
129        // Execute should succeed.
130        let mut stdin = SP1Stdin::new();
131        stdin.write(&10usize);
132        let (pv, _) =
133            prover.execute(pk.elf.clone(), stdin).await.expect("failed to execute program");
134        assert!(!pv.as_slice().is_empty());
135
136        // Prove should error.
137        let mut stdin = SP1Stdin::new();
138        stdin.write(&10usize);
139        let result = prover.prove(&pk, stdin).core().await;
140        assert!(result.is_err());
141        assert!(result.unwrap_err().to_string().contains("executing and verifying only"));
142    }
143}