sp1_sdk/network/prove.rs
1//! # Network Prove
2//!
3//! This module provides a builder for creating a proof request to the network.
4
5use std::time::Duration;
6
7use alloy_primitives::{Address, B256};
8use anyhow::Result;
9
10use crate::{
11 prover::BaseProveRequest, utils::sp1_dump, NetworkProver, ProveRequest,
12 SP1ProofWithPublicValues,
13};
14
15use super::{proto::types::FulfillmentStrategy, validation};
16
17use std::{
18 future::{Future, IntoFuture},
19 pin::Pin,
20};
21
22/// A builder for creating a proof request to the network.
23pub struct NetworkProveBuilder<'a> {
24 pub(crate) base: BaseProveRequest<'a, NetworkProver>,
25 pub(crate) timeout: Option<Duration>,
26 pub(crate) strategy: FulfillmentStrategy,
27 pub(crate) skip_simulation: bool,
28 pub(crate) cycle_limit: Option<u64>,
29 pub(crate) gas_limit: Option<u64>,
30 pub(crate) tee_2fa: bool,
31 pub(crate) min_auction_period: u64,
32 pub(crate) whitelist: Option<Vec<Address>>,
33 pub(crate) auctioneer: Option<Address>,
34 pub(crate) executor: Option<Address>,
35 pub(crate) verifier: Option<Address>,
36 pub(crate) treasury: Option<Address>,
37 pub(crate) max_price_per_pgu: Option<u64>,
38 pub(crate) auction_timeout: Option<Duration>,
39 pub(crate) private_stdin: bool,
40}
41
42impl NetworkProveBuilder<'_> {
43 /// Set the timeout for the proof's generation.
44 ///
45 /// # Details
46 /// This method sets the timeout for the proof's generation. If the proof is not generated
47 /// within the timeout, the [`NetworkProveBuilder::run`] will return an error.
48 ///
49 /// # Example
50 /// ```rust,no_run
51 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
52 /// use std::time::Duration;
53 ///
54 /// tokio_test::block_on(async {
55 /// let elf = Elf::Static(&[1, 2, 3]);
56 /// let stdin = SP1Stdin::new();
57 ///
58 /// let client = ProverClient::builder().network().build().await;
59 /// let pk = client.setup(elf).await.unwrap();
60 /// let proof = client.prove(&pk, stdin).timeout(Duration::from_secs(60)).await.unwrap();
61 /// });
62 /// ```
63 #[must_use]
64 pub fn timeout(mut self, timeout: Duration) -> Self {
65 self.timeout = Some(timeout);
66 self
67 }
68
69 /// Set whether to skip the local execution simulation step.
70 ///
71 /// # Details
72 /// This method sets whether to skip the local execution simulation step. If the simulation
73 /// step is skipped, the request will sent to the network without verifying that the execution
74 /// succeeds locally (without generating a proof). This feature is recommended for users who
75 /// want to optimize the latency of the proof generation on the network.
76 ///
77 /// # Example
78 /// ```rust,no_run
79 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
80 ///
81 /// tokio_test::block_on(async {
82 /// let elf = Elf::Static(&[1, 2, 3]);
83 /// let stdin = SP1Stdin::new();
84 ///
85 /// let client = ProverClient::builder().network().build().await;
86 /// let pk = client.setup(elf).await.unwrap();
87 /// let proof = client.prove(&pk, stdin).skip_simulation(true).await.unwrap();
88 /// });
89 /// ```
90 #[must_use]
91 pub fn skip_simulation(mut self, skip_simulation: bool) -> Self {
92 self.skip_simulation = skip_simulation;
93 self
94 }
95
96 /// Sets the fulfillment strategy for the client.
97 ///
98 /// # Details
99 /// The strategy determines how the client will fulfill requests.
100 ///
101 /// # Example
102 /// ```rust,no_run
103 /// use sp1_sdk::{network::FulfillmentStrategy, Elf, Prover, ProverClient, SP1Stdin};
104 ///
105 /// tokio_test::block_on(async {
106 /// let elf = Elf::Static(&[1, 2, 3]);
107 /// let stdin = SP1Stdin::new();
108 ///
109 /// let client = ProverClient::builder().network().build().await;
110 /// let pk = client.setup(elf).await.unwrap();
111 /// let proof = client.prove(&pk, stdin).strategy(FulfillmentStrategy::Hosted).await.unwrap();
112 /// });
113 /// ```
114 #[must_use]
115 pub fn strategy(mut self, strategy: FulfillmentStrategy) -> Self {
116 self.strategy = strategy;
117 self
118 }
119
120 /// Sets the cycle limit for the proof request.
121 ///
122 /// # Details
123 /// The cycle limit determines the maximum number of cycles that the program should take to
124 /// execute. By default, the cycle limit is determined by simulating the program locally.
125 /// However, you can manually set it if you know the exact cycle count needed and want to skip
126 /// the simulation step locally.
127 ///
128 /// The cycle limit ensures that a prover on the network will stop generating a proof once the
129 /// cycle limit is reached, which prevents denial of service attacks.
130 ///
131 /// # Example
132 /// ```rust,no_run
133 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
134 ///
135 /// tokio_test::block_on(async {
136 /// let elf = Elf::Static(&[1, 2, 3]);
137 /// let stdin = SP1Stdin::new();
138 ///
139 /// let client = ProverClient::builder().network().build().await;
140 /// let pk = client.setup(elf).await.unwrap();
141 /// let proof = client
142 /// .prove(&pk, stdin)
143 /// .cycle_limit(1_000_000) // Set 1M cycle limit.
144 /// .skip_simulation(true) // Skip simulation since the limit is set manually.
145 /// .await
146 /// .unwrap();
147 /// });
148 /// ```
149 #[must_use]
150 pub fn cycle_limit(mut self, cycle_limit: u64) -> Self {
151 self.cycle_limit = Some(cycle_limit);
152 self
153 }
154
155 /// Sets the gas limit for the proof request.
156 ///
157 /// # Details
158 /// The gas limit determines the maximum amount of gas that the program should consume. By
159 /// default, the gas limit is determined by simulating the program locally. However, you can
160 /// manually set it if you know the exact gas count needed and want to skip the simulation
161 /// step locally.
162 ///
163 /// The gas limit ensures that a prover on the network will stop generating a proof once the
164 /// gas limit is reached, which prevents denial of service attacks.
165 ///
166 /// # Example
167 /// ```rust,no_run
168 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
169 ///
170 /// tokio_test::block_on(async {
171 /// let elf = Elf::Static(&[1, 2, 3]);
172 /// let stdin = SP1Stdin::new();
173 ///
174 /// let client = ProverClient::builder().network().build().await;
175 /// let pk = client.setup(elf).await.unwrap();
176 /// let proof = client
177 /// .prove(&pk, stdin)
178 /// .gas_limit(1_000_000) // Set 1M gas limit.
179 /// .skip_simulation(true) // Skip simulation since the limit is set manually.
180 /// .await
181 /// .unwrap();
182 /// });
183 /// ```
184 #[must_use]
185 pub fn gas_limit(mut self, gas_limit: u64) -> Self {
186 self.gas_limit = Some(gas_limit);
187 self
188 }
189
190 /// Set the TEE proof type to use.
191 ///
192 /// # Details
193 /// This method sets the TEE proof type to use.
194 ///
195 /// # Example
196 /// ```rust,no_run
197 /// async fn create_proof() {
198 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
199 ///
200 /// let elf = Elf::Static(&[1, 2, 3]);
201 /// let stdin = SP1Stdin::new();
202 ///
203 /// let client = ProverClient::builder().network().build().await;
204 /// let pk = client.setup(elf).await.unwrap();
205 /// let proof = client.prove(&pk, stdin).tee_2fa().await.unwrap();
206 /// }
207 /// ```
208 #[must_use]
209 #[cfg(feature = "tee-2fa")]
210 pub fn tee_2fa(mut self) -> Self {
211 self.tee_2fa = true;
212 self
213 }
214
215 /// Set the minimum auction period for the proof request in seconds.
216 ///
217 /// # Details
218 /// The minimum auction period determines how long to wait before settling the auction for the
219 /// proof request. The auction only settles after both the minimum time has passed and at least
220 /// one bid is received. If a value is not specified, the default is 1 second.
221 ///
222 /// Only relevant if the strategy is set to [`FulfillmentStrategy::Auction`].
223 ///
224 /// # Example
225 /// ```rust,no_run
226 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
227 /// use std::time::Duration;
228 ///
229 /// tokio_test::block_on(async {
230 /// let elf = Elf::Static(&[1, 2, 3]);
231 /// let stdin = SP1Stdin::new();
232 ///
233 /// let client = ProverClient::builder().network().build().await;
234 /// let pk = client.setup(elf).await.unwrap();
235 /// let builder = client.prove(&pk, stdin).min_auction_period(60).await;
236 /// });
237 /// ```
238 #[must_use]
239 pub fn min_auction_period(mut self, min_auction_period: u64) -> Self {
240 self.min_auction_period = min_auction_period;
241 self
242 }
243
244 /// Set the whitelist for the proof request.
245 ///
246 /// # Details
247 /// The whitelist determines which provers are allowed to bid on the proof request.
248 ///
249 /// Only relevant if the strategy is set to [`FulfillmentStrategy::Auction`].
250 ///
251 /// If whitelist is `None` when requesting a proof, a set of recently reliable provers will be
252 /// used.
253 ///
254 /// # Example
255 /// ```rust,no_run
256 /// use alloy_primitives::Address;
257 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
258 /// use std::str::FromStr;
259 ///
260 /// tokio_test::block_on(async {
261 /// let elf = Elf::Static(&[1, 2, 3]);
262 /// let stdin = SP1Stdin::new();
263 ///
264 /// let client = ProverClient::builder().network().build().await;
265 /// let pk = client.setup(elf).await.unwrap();
266 /// let whitelist =
267 /// vec![Address::from_str("0x123").unwrap(), Address::from_str("0x456").unwrap()];
268 /// let proof = client.prove(&pk, stdin).whitelist(Some(whitelist)).await.unwrap();
269 /// });
270 /// ```
271 #[must_use]
272 pub fn whitelist(mut self, whitelist: Option<Vec<Address>>) -> Self {
273 self.whitelist = whitelist;
274 self
275 }
276
277 /// Enable private stdin for this proof request.
278 ///
279 /// # Details
280 /// When enabled, the stdin artifact is uploaded to a private S3 prefix and
281 /// the public URI is redacted from `ProofRequest` broadcasts. Only the
282 /// requester, the network's execution oracle, and (post-settlement) the
283 /// assigned fulfiller can download it via the authenticated `GetStdinUri`
284 /// RPC.
285 ///
286 /// This can be used together with [`NetworkProveBuilder::whitelist`] to limit
287 /// which fulfillers that will be able to win the auction and download stdin.
288 ///
289 /// # Example
290 /// ```rust,no_run
291 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
292 ///
293 /// tokio_test::block_on(async {
294 /// let elf = Elf::Static(&[1, 2, 3]);
295 /// let stdin = SP1Stdin::new();
296 ///
297 /// let client = ProverClient::builder().network().build().await;
298 /// let pk = client.setup(elf).await.unwrap();
299 /// let proof = client.prove(&pk, stdin).private_stdin(true).await.unwrap();
300 /// });
301 /// ```
302 #[must_use]
303 pub fn private_stdin(mut self, enabled: bool) -> Self {
304 self.private_stdin = enabled;
305 self
306 }
307
308 /// Set the auctioneer for the proof request.
309 ///
310 /// # Details
311 /// Only the specified auctioneer will be able to manage the auction for this request.
312 ///
313 /// Only relevant if the strategy is set to [`FulfillmentStrategy::Auction`].
314 ///
315 /// # Example
316 /// ```rust,no_run
317 /// use alloy_primitives::Address;
318 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
319 /// use std::str::FromStr;
320 ///
321 /// tokio_test::block_on(async {
322 /// let elf = Elf::Static(&[1, 2, 3]);
323 /// let stdin = SP1Stdin::new();
324 ///
325 /// let client = ProverClient::builder().network().build().await;
326 /// let pk = client.setup(elf).await.unwrap();
327 /// let auctioneer = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
328 /// let proof = client.prove(&pk, stdin).auctioneer(auctioneer).await.unwrap();
329 /// });
330 /// ```
331 #[must_use]
332 pub fn auctioneer(mut self, auctioneer: Address) -> Self {
333 self.auctioneer = Some(auctioneer);
334 self
335 }
336
337 /// Set the executor for the proof request.
338 ///
339 /// # Details
340 /// Only the specified executor will be able to fulfill this request. This is useful for
341 /// whitelisting specific provers for private or prioritized jobs.
342 ///
343 /// # Example
344 /// ```rust,no_run
345 /// use alloy_primitives::Address;
346 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
347 /// use std::str::FromStr;
348 ///
349 /// tokio_test::block_on(async {
350 /// let elf = Elf::Static(&[1, 2, 3]);
351 /// let stdin = SP1Stdin::new();
352 ///
353 /// let client = ProverClient::builder().network().build().await;
354 /// let pk = client.setup(elf).await.unwrap();
355 /// let executor = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
356 /// let proof = client.prove(&pk, stdin).executor(executor).await.unwrap();
357 /// });
358 /// ```
359 #[must_use]
360 pub fn executor(mut self, executor: Address) -> Self {
361 self.executor = Some(executor);
362 self
363 }
364
365 /// Set the verifier for the proof request.
366 ///
367 /// # Details
368 /// Only the specified verifier will be able to verify the proof. Only relevant if the mode is
369 /// not [`SP1ProofMode::Compressed`], as this mode will be verified within the `VApp`.
370 ///
371 /// # Example
372 /// ```rust,no_run
373 /// use alloy_primitives::Address;
374 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
375 /// use std::str::FromStr;
376 ///
377 /// tokio_test::block_on(async {
378 /// let elf = Elf::Static(&[1, 2, 3]);
379 /// let stdin = SP1Stdin::new();
380 ///
381 /// let client = ProverClient::builder().network().build().await;
382 /// let pk = client.setup(elf).await.unwrap();
383 /// let verifier = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
384 /// let proof = client.prove(&pk, stdin).verifier(verifier).await.unwrap();
385 /// });
386 /// ```
387 #[must_use]
388 pub fn verifier(mut self, verifier: Address) -> Self {
389 self.verifier = Some(verifier);
390 self
391 }
392
393 /// Set the treasury for the proof request.
394 ///
395 /// # Details
396 /// The treasury is the address that will receive the protocol fee portion of the proof request
397 /// reward when it is fulfilled.
398 ///
399 /// # Example
400 /// ```rust,no_run
401 /// use alloy_primitives::Address;
402 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
403 /// use std::str::FromStr;
404 ///
405 /// tokio_test::block_on(async {
406 /// let elf = Elf::Static(&[1, 2, 3]);
407 /// let stdin = SP1Stdin::new();
408 ///
409 /// let client = ProverClient::builder().network().build().await;
410 /// let pk = client.setup(elf).await.unwrap();
411 /// let treasury = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
412 /// let proof = client.prove(&pk, stdin).treasury(treasury).await.unwrap();
413 /// });
414 /// ```
415 #[must_use]
416 pub fn treasury(mut self, treasury: Address) -> Self {
417 self.treasury = Some(treasury);
418 self
419 }
420
421 /// Sets the max price per PGU for the proof request.
422 ///
423 /// # Details
424 /// The max price per PGU (prover gas unit) lets you specify the maximum amount of PROVE
425 /// you are willing to pay per PGU, protecting you from unexpected price escalation. If a value
426 /// is not specified, a default value will be used.
427 ///
428 /// Only relevant if the strategy is set to [`FulfillmentStrategy::Auction`].
429 ///
430 /// # Example
431 /// ```rust,no_run
432 /// use alloy_primitives::Address;
433 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
434 /// use std::str::FromStr;
435 ///
436 /// tokio_test::block_on(async {
437 /// let elf = Elf::Static(&[1, 2, 3]);
438 /// let stdin = SP1Stdin::new();
439 ///
440 /// let client = ProverClient::builder().network().build().await;
441 /// let pk = client.setup(elf).await.unwrap();
442 /// let treasury = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
443 /// let proof = client
444 /// .prove(&pk, stdin)
445 /// .max_price_per_pgu(1_000_000_000_000_000_000u64) // Set 1 PROVE (18 decimals).
446 /// .await
447 /// .unwrap();
448 /// });
449 /// ```
450 #[must_use]
451 pub fn max_price_per_pgu(mut self, max_price_per_pgu: u64) -> Self {
452 self.max_price_per_pgu = Some(max_price_per_pgu);
453 self
454 }
455
456 /// Sets the auction timeout for the proof request.
457 ///
458 /// # Details
459 /// The auction timeout determines how long to wait for a prover to bid on the proof request.
460 /// If no provers bid on the request within this timeout, the proof request will be canceled. If
461 /// a value is not specified, the default is 30 seconds.
462 ///
463 /// Only relevant if the strategy is set to [`FulfillmentStrategy::Auction`].
464 ///
465 /// # Example
466 /// ```rust,no_run
467 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
468 /// use std::time::Duration;
469 ///
470 /// tokio_test::block_on(async {
471 /// let elf = Elf::Static(&[1, 2, 3]);
472 /// let stdin = SP1Stdin::new();
473 ///
474 /// let client = ProverClient::builder().network().build().await;
475 /// let pk = client.setup(elf).await.unwrap();
476 /// let proof = client
477 /// .prove(&pk, stdin)
478 /// .auction_timeout(Duration::from_secs(60)) // Wait 60 seconds for a prover to pick up the request.
479 /// .await
480 /// .unwrap();
481 /// });
482 /// ```
483 #[must_use]
484 pub fn auction_timeout(mut self, auction_timeout: Duration) -> Self {
485 self.auction_timeout = Some(auction_timeout);
486 self
487 }
488
489 /// Request a proof from the prover network asynchronously.
490 ///
491 /// # Details
492 /// This method will request a proof from the prover network asynchronously. If the prover fails
493 /// to request a proof, the method will return an error. It will not wait for the proof to be
494 /// generated.
495 ///
496 /// # Example
497 /// ```rust,no_run
498 /// use sp1_sdk::{Elf, Prover, ProverClient, SP1Stdin};
499 ///
500 /// tokio_test::block_on(async {
501 /// let elf = Elf::Static(&[1, 2, 3]);
502 /// let stdin = SP1Stdin::new();
503 ///
504 /// let client = ProverClient::builder().network().build().await;
505 /// let pk = client.setup(elf).await.unwrap();
506 /// let request_id = client.prove(&pk, stdin).request().await.unwrap();
507 /// })
508 /// ```
509 pub async fn request(self) -> Result<B256> {
510 self.base
511 .prover
512 .request_proof_impl(
513 self.base.pk,
514 &self.base.stdin,
515 self.base.mode,
516 self.strategy,
517 self.timeout,
518 self.skip_simulation,
519 self.cycle_limit,
520 self.gas_limit,
521 self.min_auction_period,
522 self.whitelist,
523 self.auctioneer,
524 self.executor,
525 self.verifier,
526 self.treasury,
527 self.max_price_per_pgu,
528 self.private_stdin,
529 )
530 .await
531 }
532}
533
534impl<'a> ProveRequest<'a, NetworkProver> for NetworkProveBuilder<'a> {
535 fn base(&mut self) -> &mut BaseProveRequest<'a, NetworkProver> {
536 &mut self.base
537 }
538}
539
540impl<'a> IntoFuture for NetworkProveBuilder<'a> {
541 type Output = Result<SP1ProofWithPublicValues>;
542
543 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
544
545 fn into_future(mut self) -> Self::IntoFuture {
546 Box::pin(async move {
547 // Validate strategy compatibility with network mode before proceeding.
548 validation::validate_strategy_compatibility(
549 self.base.prover.network_mode(),
550 self.strategy,
551 )
552 .map_err(|e| anyhow::anyhow!("{e}"))?;
553
554 // Check for deprecated environment variable.
555 if let Ok(val) = std::env::var("SKIP_SIMULATION") {
556 tracing::warn!(
557 "SKIP_SIMULATION environment variable is deprecated. Please use .skip_simulation() instead."
558 );
559 self.skip_simulation = matches!(val.to_lowercase().as_str(), "true" | "1");
560 }
561
562 sp1_dump(&self.base.pk.elf, &self.base.stdin);
563
564 tracing::info!(mode = ?self.base.mode, "requesting proof from network");
565 self.base
566 .prover
567 .prove_impl(
568 self.base.pk,
569 &self.base.stdin,
570 self.base.mode,
571 self.strategy,
572 self.timeout,
573 self.skip_simulation,
574 self.cycle_limit,
575 self.gas_limit,
576 self.tee_2fa,
577 self.min_auction_period,
578 self.whitelist,
579 self.auctioneer,
580 self.executor,
581 self.verifier,
582 self.treasury,
583 self.max_price_per_pgu,
584 self.auction_timeout,
585 self.private_stdin,
586 )
587 .await
588 })
589 }
590}