Skip to main content

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}