Skip to main content

sp1_sdk/prover/
execute.rs

1use crate::StatusCode;
2
3use super::Prover;
4use sp1_core_executor::{ExecutionError, ExecutionReport, HookEnv, SP1ContextBuilder};
5use sp1_core_machine::io::SP1Stdin;
6use sp1_primitives::{io::SP1PublicValues, Elf};
7use std::{
8    future::{Future, IntoFuture},
9    pin::Pin,
10};
11
12/// A request for executing a program.
13pub struct ExecuteRequest<'a, P: Prover> {
14    pub(crate) prover: &'a P,
15    pub(crate) elf: Elf,
16    pub(crate) stdin: SP1Stdin,
17    pub(crate) context_builder: SP1ContextBuilder<'static>,
18}
19
20impl<'a, P: Prover> ExecuteRequest<'a, P> {
21    pub(crate) fn new(prover: &'a P, elf: Elf, stdin: SP1Stdin) -> Self {
22        Self { prover, elf, stdin, context_builder: SP1ContextBuilder::new() }
23    }
24
25    /// Add a executor [`sp1_core_executor::Hook`] into the context.
26    ///
27    /// # Arguments
28    /// * `fd` - The file descriptor that triggers this execution hook.
29    /// * `f` - The function to invoke when the hook is triggered.
30    ///
31    /// # Details
32    /// Hooks may be invoked from within SP1 by writing to the specified file descriptor `fd`
33    /// with [`sp1_zkvm::io::write`], returning a list of arbitrary data that may be read
34    /// with successive calls to [`sp1_zkvm::io::read`].
35    ///
36    /// # Example
37    /// ```rust,no_run
38    /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
39    ///
40    /// tokio_test::block_on(async {
41    ///     let elf = Elf::Static(&[1, 2, 3]);
42    ///     let stdin = SP1Stdin::new();
43    ///
44    ///     let client = ProverClient::builder().cpu().build().await;
45    ///     let result = client
46    ///         .execute(elf, stdin)
47    ///         .with_hook(1, |env, data| {
48    ///             println!("Hook triggered with data: {:?}", data);
49    ///             vec![vec![1, 2, 3]]
50    ///         })
51    ///         .await
52    ///         .unwrap();
53    /// });
54    /// ```
55    #[must_use]
56    pub fn with_hook(
57        mut self,
58        fd: u32,
59        f: impl FnMut(HookEnv, &[u8]) -> Vec<Vec<u8>> + Send + Sync + 'static,
60    ) -> Self {
61        self.context_builder.hook(fd, f);
62        self
63    }
64
65    /// Set the maximum number of cpu cycles to use for execution.
66    ///
67    /// # Arguments
68    /// * `max_cycles` - The maximum number of cycles to use for execution.
69    ///
70    /// # Details
71    /// If the cycle limit is exceeded, execution will fail with the
72    /// [`sp1_core_executor::ExecutionError::ExceededCycleLimit`]. This is useful for preventing
73    /// infinite loops in the and limiting the execution time of the program.
74    ///
75    /// # Example
76    /// ```rust,no_run
77    /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
78    ///
79    /// tokio_test::block_on(async {
80    ///     let elf = Elf::Static(&[1, 2, 3]);
81    ///     let stdin = SP1Stdin::new();
82    ///
83    ///     let client = ProverClient::builder().cpu().build().await;
84    ///     let result = client.execute(elf, stdin).cycle_limit(1000000).await.unwrap();
85    /// });
86    /// ```
87    #[must_use]
88    pub fn cycle_limit(mut self, max_cycles: u64) -> Self {
89        self.context_builder.max_cycles(max_cycles);
90        self
91    }
92
93    /// Whether to enable deferred proof verification in the executor.
94    ///
95    /// # Arguments
96    /// * `value` - Whether to enable deferred proof verification in the executor.
97    ///
98    /// # Details
99    /// Default: `true`. If set to `false`, the executor will skip deferred proof verification.
100    /// This is useful for reducing the execution time of the program and optimistically assuming
101    /// that the deferred proofs are correct. Can also be used for mock proof setups that require
102    /// verifying mock compressed proofs.
103    ///
104    /// # Example
105    /// ```rust,no_run
106    /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
107    ///
108    /// tokio_test::block_on(async {
109    ///     let elf = Elf::Static(&[1, 2, 3]);
110    ///     let stdin = SP1Stdin::new();
111    ///
112    ///     let client = ProverClient::builder().cpu().build().await;
113    ///     let result = client.execute(elf, stdin).deferred_proof_verification(false).await.unwrap();
114    /// });
115    /// ```
116    #[must_use]
117    pub fn deferred_proof_verification(mut self, value: bool) -> Self {
118        self.context_builder.set_deferred_proof_verification(value);
119        self
120    }
121
122    /// Whether to enable gas calculation in the executor.
123    ///
124    /// # Arguments
125    /// * `value` - Whether to enable gas calculation in the executor.
126    ///
127    /// # Details
128    /// Default: `true`. If set to `false`, the executor will not calculate gas.
129    /// This is useful for reducing the execution time of the program, since gas calculation
130    /// must perform extra work to simulate parts of the proving process.
131    ///
132    /// Gas may be retrieved through the [`ExecutionReport`] available through [`Self::run`].
133    /// It will be `None` if and only if this option is disabled.
134    ///
135    /// # Example
136    /// ```rust,no_run
137    /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
138    ///
139    /// tokio_test::block_on(async {
140    ///     let elf = Elf::Static(&[1, 2, 3]);
141    ///     let stdin = SP1Stdin::new();
142    ///
143    ///     let client = ProverClient::builder().cpu().build().await;
144    ///     let result = client.execute(elf, stdin).calculate_gas(false).await.unwrap();
145    /// });
146    /// ```
147    #[must_use]
148    pub fn calculate_gas(mut self, value: bool) -> Self {
149        self.context_builder.calculate_gas(value);
150        self
151    }
152
153    /// Set the expected exit code of the program.
154    ///
155    /// # Arguments
156    /// * `code` - The expected exit code of the program.
157    #[must_use]
158    pub fn expected_exit_code(mut self, code: StatusCode) -> Self {
159        self.context_builder.expected_exit_code(code);
160        self
161    }
162
163    // todo!(n): workaround this
164    // /// Override the default stdout of the guest program.
165    // ///
166    // /// # Example
167    // /// ```rust,no_run
168    // /// use sp1_sdk::{include_elf, Prover, ProverClient, SP1Stdin};
169    // ///
170    // /// let mut stdout = Vec::new();
171    // ///
172    // /// let elf = &[1, 2, 3];
173    // /// let stdin = SP1Stdin::new();
174    // ///
175    // /// let client = ProverClient::builder().cpu().build();
176    // /// client.execute(elf, &stdin).stdout(&mut stdout).run();
177    // /// ```
178    // #[must_use]
179    // pub fn stdout<W: IoWriter>(mut self, writer: &'a mut W) -> Self {
180    //     self.context_builder.stdout(writer);
181    //     self
182    // }
183
184    // /// Override the default stdout of the guest program.
185    // ///
186    // /// # Example
187    // /// ```rust,no_run
188    // /// use sp1_sdk::{include_elf, Prover, ProverClient, SP1Stdin};
189    // ///
190    // /// let mut stderr = Vec::new();
191    // ///
192    // /// let elf = &[1, 2, 3];
193    // /// let stdin = SP1Stdin::new();
194    // ///
195    // /// let client = ProverClient::builder().cpu().build();
196    // /// client.execute(elf, &stdin).stderr(&mut stderr).run();
197    // /// ```
198    // #[must_use]
199    // pub fn stderr<W: IoWriter>(mut self, writer: &'a mut W) -> Self {
200    //     self.context_builder.stderr(writer);
201    //     self
202    // }
203}
204
205impl<'a, P: Prover> IntoFuture for ExecuteRequest<'a, P> {
206    type Output = Result<(SP1PublicValues, ExecutionReport), ExecutionError>;
207
208    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
209
210    fn into_future(self) -> Self::IntoFuture {
211        let task = async move {
212            let Self { prover, elf, stdin, mut context_builder } = self;
213            let inner = prover.inner();
214            let context = context_builder.build();
215            let (pv, _digest, report) = inner
216                .execute(&elf, stdin, context)
217                .await
218                .map_err(|e| ExecutionError::Other(e.to_string()))?;
219
220            // todo!(n): if there exists stdout/stderr pipes can just forward them with an mpsc
221            // here, and then write to the actual stdout/stderr writers from this
222            // future.
223            Ok((pv, report))
224        };
225        Box::pin(task)
226    }
227}