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}