,
) -> Result<(GetProofRequestStatusResponse, Option)> {
- let mut rpc = self.prover_network_client().await?;
- let res = rpc
- .get_proof_request_status(GetProofRequestStatusRequest {
- request_id: request_id.to_vec(),
- })
- .await?
- .into_inner();
+ // Get the status.
+ let res = self
+ .with_retry_timeout(
+ || async {
+ let mut rpc = self.prover_network_client().await?;
+ Ok(rpc
+ .get_proof_request_status(GetProofRequestStatusRequest {
+ request_id: request_id.to_vec(),
+ })
+ .await?
+ .into_inner())
+ },
+ timeout.unwrap_or(DEFAULT_RETRY_TIMEOUT),
+ "getting proof request status",
+ )
+ .await?;
let status = FulfillmentStatus::try_from(res.fulfillment_status)?;
let proof = match status {
@@ -175,7 +283,7 @@ impl NetworkClient {
#[allow(clippy::too_many_arguments)]
pub async fn request_proof(
&self,
- vk_hash: &[u8],
+ vk_hash: B256,
stdin: &SP1Stdin,
mode: ProofMode,
version: &str,
@@ -190,7 +298,8 @@ impl NetworkClient {
// Create the stdin artifact.
let mut store = self.artifact_store_client().await?;
- let stdin_uri = self.create_artifact_with_content(&mut store, &stdin).await?;
+ let stdin_uri =
+ self.create_artifact_with_content(&mut store, ArtifactType::Stdin, &stdin).await?;
// Send the request.
let mut rpc = self.prover_network_client().await?;
@@ -218,63 +327,75 @@ impl NetworkClient {
}
pub(crate) async fn prover_network_client(&self) -> Result> {
- let rpc_url = self.rpc_url.clone();
- let mut endpoint = Channel::from_shared(rpc_url.clone())?;
-
- // Check if the URL scheme is HTTPS and configure TLS.
- if rpc_url.starts_with("https://") {
- let tls_config = ClientTlsConfig::new().with_enabled_roots();
- endpoint = endpoint.tls_config(tls_config)?;
- }
-
- let channel = endpoint.connect().await?;
+ let channel = grpc::configure_endpoint(&self.rpc_url)?.connect().await?;
Ok(ProverNetworkClient::new(channel))
}
pub(crate) async fn artifact_store_client(&self) -> Result> {
- let rpc_url = self.rpc_url.clone();
- let mut endpoint = Channel::from_shared(rpc_url.clone())?;
-
- // Check if the URL scheme is HTTPS and configure TLS.
- if rpc_url.starts_with("https://") {
- let tls_config = ClientTlsConfig::new().with_enabled_roots();
- endpoint = endpoint.tls_config(tls_config)?;
- }
-
- let channel = endpoint.connect().await?;
- Ok(ArtifactStoreClient::new(channel.clone()))
+ let channel = grpc::configure_endpoint(&self.rpc_url)?.connect().await?;
+ Ok(ArtifactStoreClient::new(channel))
}
- pub(crate) async fn create_artifact_with_content(
+ pub(crate) async fn create_artifact_with_content(
&self,
store: &mut ArtifactStoreClient,
+ artifact_type: ArtifactType,
item: &T,
) -> Result {
let signature = self.signer.sign_message_sync("create_artifact".as_bytes())?;
- let request = CreateArtifactRequest { signature: signature.as_bytes().to_vec() };
+ let request = CreateArtifactRequest {
+ artifact_type: artifact_type.into(),
+ signature: signature.as_bytes().to_vec(),
+ };
+
+ // Create the artifact.
let response = store.create_artifact(request).await?.into_inner();
let presigned_url = response.artifact_presigned_url;
let uri = response.artifact_uri;
- let response =
- self.http.put(&presigned_url).body(bincode::serialize::(item)?).send().await?;
-
- if !response.status().is_success() {
- log::debug!("Artifact upload failed with status: {}", response.status());
- }
- assert!(response.status().is_success());
+ // Upload the content.
+ self.with_retry(
+ || async {
+ let response = self
+ .http
+ .put(&presigned_url)
+ .body(bincode::serialize::(item)?)
+ .send()
+ .await?;
+
+ if !response.status().is_success() {
+ return Err(anyhow::anyhow!(
+ "Failed to upload artifact: HTTP {}",
+ response.status()
+ ));
+ }
+ Ok(())
+ },
+ "uploading artifact content",
+ )
+ .await?;
Ok(uri)
}
pub(crate) async fn download_artifact(&self, uri: &str) -> Result> {
- let response = self.http.get(uri).send().await.context("Failed to download from URI")?;
-
- if !response.status().is_success() {
- return Err(anyhow::anyhow!("Failed to download artifact: HTTP {}", response.status()));
- }
-
- Ok(response.bytes().await.context("Failed to read response body")?.to_vec())
+ self.with_retry(
+ || async {
+ let response =
+ self.http.get(uri).send().await.context("Failed to download from URI")?;
+
+ if !response.status().is_success() {
+ return Err(anyhow::anyhow!(
+ "Failed to download artifact: HTTP {}",
+ response.status()
+ ));
+ }
+
+ Ok(response.bytes().await.context("Failed to read response body")?.to_vec())
+ },
+ "downloading artifact",
+ )
+ .await
}
}
diff --git a/crates/sdk/src/network/error.rs b/crates/sdk/src/network/error.rs
new file mode 100644
index 0000000000..3cde6d6047
--- /dev/null
+++ b/crates/sdk/src/network/error.rs
@@ -0,0 +1,39 @@
+use thiserror::Error;
+use tonic::Status;
+
+/// An error that can occur when interacting with the prover network.
+#[derive(Error, Debug)]
+pub enum Error {
+ /// The program execution failed.
+ #[error("Program simulation failed")]
+ SimulationFailed,
+
+ /// The proof request is unexecutable.
+ #[error("Proof request 0x{} is unexecutable", hex::encode(.request_id))]
+ RequestUnexecutable {
+ /// The ID of the request that cannot be executed.
+ request_id: Vec,
+ },
+
+ /// The proof request is unfulfillable.
+ #[error("Proof request 0x{} is unfulfillable", hex::encode(.request_id))]
+ RequestUnfulfillable {
+ /// The ID of the request that cannot be fulfilled.
+ request_id: Vec,
+ },
+
+ /// The proof request timed out.
+ #[error("Proof request 0x{} timed out", hex::encode(.request_id))]
+ RequestTimedOut {
+ /// The ID of the request that timed out.
+ request_id: Vec,
+ },
+
+ /// An error occurred while interacting with the RPC server.
+ #[error("RPC error")]
+ RpcError(#[from] Status),
+
+ /// An unknown error occurred.
+ #[error("Other error: {0}")]
+ Other(#[from] anyhow::Error),
+}
diff --git a/crates/sdk/src/network/grpc.rs b/crates/sdk/src/network/grpc.rs
new file mode 100644
index 0000000000..2d68f1cbd5
--- /dev/null
+++ b/crates/sdk/src/network/grpc.rs
@@ -0,0 +1,24 @@
+use std::time::Duration;
+use tonic::transport::{ClientTlsConfig, Endpoint, Error};
+
+/// Configures the endpoint for the gRPC client.
+///
+/// Sets reasonable settings to handle timeouts and keep-alive.
+pub fn configure_endpoint(addr: &str) -> Result {
+ let mut endpoint = Endpoint::new(addr.to_string())?
+ .timeout(Duration::from_secs(60))
+ .connect_timeout(Duration::from_secs(15))
+ .keep_alive_while_idle(true)
+ .http2_keep_alive_interval(Duration::from_secs(15))
+ .keep_alive_timeout(Duration::from_secs(15))
+ .tcp_keepalive(Some(Duration::from_secs(60)))
+ .tcp_nodelay(true);
+
+ // Configure TLS if using HTTPS.
+ if addr.starts_with("https://") {
+ let tls_config = ClientTlsConfig::new().with_enabled_roots();
+ endpoint = endpoint.tls_config(tls_config)?;
+ }
+
+ Ok(endpoint)
+}
diff --git a/crates/sdk/src/network/mod.rs b/crates/sdk/src/network/mod.rs
index 6cc201f327..48dfdeb0c6 100644
--- a/crates/sdk/src/network/mod.rs
+++ b/crates/sdk/src/network/mod.rs
@@ -4,19 +4,32 @@
pub mod client;
pub mod prover;
-mod sign_message;
#[rustfmt::skip]
#[allow(missing_docs)]
#[allow(clippy::default_trait_access)]
#[allow(clippy::too_many_lines)]
pub mod proto;
pub mod builder;
+mod error;
+mod grpc;
pub mod prove;
+mod retry;
pub mod utils;
+pub use error::*;
+
pub use crate::network::client::NetworkClient;
pub use crate::network::proto::network::FulfillmentStrategy;
+// Re-export for verification key hash + request ID.
+pub use alloy_primitives::B256;
+
+/// The default RPC URL for the prover network.
+pub(crate) const DEFAULT_NETWORK_RPC_URL: &str = "https://rpc.production.succinct.tools/";
-pub(crate) const DEFAULT_PROVER_NETWORK_RPC: &str = "https://rpc.production.succinct.tools/";
+/// The default timeout for the prover network (4 hours).
pub(crate) const DEFAULT_TIMEOUT_SECS: u64 = 14400;
+
+/// The default cycle limit for the prover network (100M cycles).
+///
+/// This will only be used if both simulation is skipped and the cycle limit is not explicitly set.
pub(crate) const DEFAULT_CYCLE_LIMIT: u64 = 100_000_000;
diff --git a/crates/sdk/src/network/proto/artifact.rs b/crates/sdk/src/network/proto/artifact.rs
index e1ff3ad034..bda90769e9 100644
--- a/crates/sdk/src/network/proto/artifact.rs
+++ b/crates/sdk/src/network/proto/artifact.rs
@@ -4,6 +4,9 @@ pub struct CreateArtifactRequest {
/// The signature of the user on a pre-defined message. Used for authentication.
#[prost(bytes = "vec", tag = "1")]
pub signature: ::prost::alloc::vec::Vec,
+ /// The type of artifact to create.
+ #[prost(enumeration = "ArtifactType", tag = "2")]
+ pub artifact_type: i32,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
pub struct CreateArtifactResponse {
@@ -14,11 +17,58 @@ pub struct CreateArtifactResponse {
#[prost(string, tag = "2")]
pub artifact_presigned_url: ::prost::alloc::string::String,
}
+#[derive(
+ serde::Serialize,
+ serde::Deserialize,
+ Clone,
+ Copy,
+ Debug,
+ PartialEq,
+ Eq,
+ Hash,
+ PartialOrd,
+ Ord,
+ ::prost::Enumeration,
+)]
+#[repr(i32)]
+pub enum ArtifactType {
+ UnspecifiedArtifactType = 0,
+ /// A program artifact.
+ Program = 1,
+ /// A stdin artifact.
+ Stdin = 2,
+ /// A proof artifact.
+ Proof = 3,
+}
+impl ArtifactType {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::UnspecifiedArtifactType => "UNSPECIFIED_ARTIFACT_TYPE",
+ Self::Program => "PROGRAM",
+ Self::Stdin => "STDIN",
+ Self::Proof => "PROOF",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "UNSPECIFIED_ARTIFACT_TYPE" => Some(Self::UnspecifiedArtifactType),
+ "PROGRAM" => Some(Self::Program),
+ "STDIN" => Some(Self::Stdin),
+ "PROOF" => Some(Self::Proof),
+ _ => None,
+ }
+ }
+}
/// Generated client implementations.
pub mod artifact_store_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::http::Uri;
- use tonic::codegen::{Body, Bytes, CompressionEncoding, GrpcMethod, InterceptedService, StdError, http};
+ use tonic::codegen::*;
#[derive(Debug, Clone)]
pub struct ArtifactStoreClient {
inner: tonic::client::Grpc,
@@ -57,11 +107,11 @@ pub mod artifact_store_client {
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
- http::Request,
- Response = http::Response<
- >::ResponseBody,
- >,
+ http::Request,
+ Response = http::Response<
+ >::ResponseBody,
>,
+ >,
>>::Error:
Into + std::marker::Send + std::marker::Sync,
{
@@ -123,7 +173,7 @@ pub mod artifact_store_client {
/// Generated server implementations.
pub mod artifact_store_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
- use tonic::codegen::{Arc, Body, BoxFuture, CompressionEncoding, Context, EnabledCompressionEncodings, InterceptedService, Poll, StdError, async_trait, empty_body, http};
+ use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with ArtifactStoreServer.
#[async_trait]
pub trait ArtifactStore: std::marker::Send + std::marker::Sync + 'static {
diff --git a/crates/sdk/src/network/proto/mod.rs b/crates/sdk/src/network/proto/mod.rs
index 48eb63675a..95ba33205e 100644
--- a/crates/sdk/src/network/proto/mod.rs
+++ b/crates/sdk/src/network/proto/mod.rs
@@ -1,2 +1,4 @@
+#![allow(clippy::doc_markdown, clippy::must_use_candidate, clippy::wildcard_imports)]
+
pub mod artifact;
pub mod network;
diff --git a/crates/sdk/src/network/proto/network.rs b/crates/sdk/src/network/proto/network.rs
index 52691b93ab..6c411f84ab 100644
--- a/crates/sdk/src/network/proto/network.rs
+++ b/crates/sdk/src/network/proto/network.rs
@@ -254,20 +254,28 @@ pub struct ProofRequest {
/// The unix timestamp of when the request was updated.
#[prost(uint64, tag = "19")]
pub updated_at: u64,
- /// The unix timestamp of when the request was fulfilled.
+ /// The unix timestamp of when the request was fulfilled. Only included if
+ /// the request has a fulfillment status of FULFILLED.
#[prost(uint64, optional, tag = "20")]
pub fulfilled_at: ::core::option::Option,
/// The transaction hash of the request.
#[prost(bytes = "vec", tag = "21")]
pub tx_hash: ::prost::alloc::vec::Vec,
- /// The cycle count for the request.
+ /// The cycle used during the execution of the request. Only included if the
+ /// request has an execution status of EXECUTED.
#[prost(uint64, optional, tag = "22")]
pub cycles: ::core::option::Option,
- /// The amount deducted from the fulfiller's balance.
- #[prost(string, optional, tag = "23")]
- pub deduction_amount: ::core::option::Option<::prost::alloc::string::String>,
- /// The amount refunded to the fulfiller's balance.
+ /// The public values hash from the execution of the request. Only included if
+ /// the request has an execution status of EXECUTED.
+ #[prost(bytes = "vec", optional, tag = "23")]
+ pub public_values_hash: ::core::option::Option<::prost::alloc::vec::Vec>,
+ /// The amount deducted from the fulfiller's balance. Only included if the
+ /// request has a fulfillment status of ASSIGNED.
#[prost(string, optional, tag = "24")]
+ pub deduction_amount: ::core::option::Option<::prost::alloc::string::String>,
+ /// The amount refunded to the fulfiller's balance. Only included if the
+ /// request has a fulfillment status of EXECUTED.
+ #[prost(string, optional, tag = "25")]
pub refund_amount: ::core::option::Option<::prost::alloc::string::String>,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
@@ -287,14 +295,22 @@ pub struct GetProofRequestStatusResponse {
/// The transaction hash of the request.
#[prost(bytes = "vec", tag = "3")]
pub request_tx_hash: ::prost::alloc::vec::Vec,
+ /// The deadline of the request. A request should be ignored if it is past
+ /// its deadline.
+ #[prost(uint64, tag = "4")]
+ pub deadline: u64,
/// The optional transaction hash of the proof fulfill. Only included if the
/// request has a fulfillment status of FULFILLED.
- #[prost(bytes = "vec", optional, tag = "4")]
+ #[prost(bytes = "vec", optional, tag = "5")]
pub fulfill_tx_hash: ::core::option::Option<::prost::alloc::vec::Vec>,
/// The optional proof URI, where you can download the result of the request.
/// Only included if the request has a fulfillment status of FULFILLED.
- #[prost(string, optional, tag = "5")]
+ #[prost(string, optional, tag = "6")]
pub proof_uri: ::core::option::Option<::prost::alloc::string::String>,
+ /// The optional public values hash from the execution of the request. Only
+ /// included if the request has an execution status of EXECUTED.
+ #[prost(bytes = "vec", optional, tag = "7")]
+ pub public_values_hash: ::core::option::Option<::prost::alloc::vec::Vec>,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
pub struct GetProofRequestDetailsRequest {
@@ -557,6 +573,38 @@ pub struct RemoveDelegationResponse {
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, ::prost::Message)]
pub struct RemoveDelegationResponseBody {}
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
+pub struct TerminateDelegationRequest {
+ /// The message format of the body.
+ #[prost(enumeration = "MessageFormat", tag = "1")]
+ pub format: i32,
+ /// The signature of the sender.
+ #[prost(bytes = "vec", tag = "2")]
+ pub signature: ::prost::alloc::vec::Vec,
+ /// The body of the request.
+ #[prost(message, optional, tag = "3")]
+ pub body: ::core::option::Option,
+}
+#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
+pub struct TerminateDelegationRequestBody {
+ /// The account nonce of the sender.
+ #[prost(uint64, tag = "1")]
+ pub nonce: u64,
+ /// The address of the owner whose delegation to terminate.
+ #[prost(bytes = "vec", tag = "2")]
+ pub owner: ::prost::alloc::vec::Vec,
+}
+#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
+pub struct TerminateDelegationResponse {
+ /// The transaction hash.
+ #[prost(bytes = "vec", tag = "1")]
+ pub tx_hash: ::prost::alloc::vec::Vec,
+ /// The body of the response.
+ #[prost(message, optional, tag = "2")]
+ pub body: ::core::option::Option,
+}
+#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, ::prost::Message)]
+pub struct TerminateDelegationResponseBody {}
+#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
pub struct AcceptDelegationRequest {
/// The message format of the body.
#[prost(enumeration = "MessageFormat", tag = "1")]
@@ -949,14 +997,17 @@ pub struct Reservation {
#[prost(uint64, tag = "5")]
pub created_at: u64,
}
-#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, ::prost::Message)]
+#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
pub struct GetFilteredReservationsRequest {
+ /// Requester address to filter for.
+ #[prost(bytes = "vec", optional, tag = "1")]
+ pub requester: ::core::option::Option<::prost::alloc::vec::Vec>,
/// The optional maximum number of reservations to return (default is 10,
/// maximum is 100).
- #[prost(uint32, optional, tag = "1")]
+ #[prost(uint32, optional, tag = "2")]
pub limit: ::core::option::Option,
/// The optional page number to return (default is 1).
- #[prost(uint32, optional, tag = "2")]
+ #[prost(uint32, optional, tag = "3")]
pub page: ::core::option::Option,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
@@ -1120,19 +1171,19 @@ pub enum MessageFormat {
Json = 2,
}
impl MessageFormat {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedMessageFormat => "UNSPECIFIED_MESSAGE_FORMAT",
Self::Binary => "BINARY",
Self::Json => "JSON",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_MESSAGE_FORMAT" => Some(Self::UnspecifiedMessageFormat),
"BINARY" => Some(Self::Binary),
@@ -1167,11 +1218,11 @@ pub enum ProofMode {
Groth16 = 4,
}
impl ProofMode {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedProofMode => "UNSPECIFIED_PROOF_MODE",
Self::Core => "CORE",
@@ -1180,8 +1231,8 @@ impl ProofMode {
Self::Groth16 => "GROTH16",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_PROOF_MODE" => Some(Self::UnspecifiedProofMode),
"CORE" => Some(Self::Core),
@@ -1219,11 +1270,11 @@ pub enum FulfillmentStrategy {
Auction = 3,
}
impl FulfillmentStrategy {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedFulfillmentStrategy => "UNSPECIFIED_FULFILLMENT_STRATEGY",
Self::Hosted => "HOSTED",
@@ -1231,8 +1282,8 @@ impl FulfillmentStrategy {
Self::Auction => "AUCTION",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_FULFILLMENT_STRATEGY" => Some(Self::UnspecifiedFulfillmentStrategy),
"HOSTED" => Some(Self::Hosted),
@@ -1269,11 +1320,11 @@ pub enum FulfillmentStatus {
Unfulfillable = 4,
}
impl FulfillmentStatus {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedFulfillmentStatus => "UNSPECIFIED_FULFILLMENT_STATUS",
Self::Requested => "REQUESTED",
@@ -1282,8 +1333,8 @@ impl FulfillmentStatus {
Self::Unfulfillable => "UNFULFILLABLE",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_FULFILLMENT_STATUS" => Some(Self::UnspecifiedFulfillmentStatus),
"REQUESTED" => Some(Self::Requested),
@@ -1319,11 +1370,11 @@ pub enum ExecutionStatus {
Unexecutable = 3,
}
impl ExecutionStatus {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedExecutionStatus => "UNSPECIFIED_EXECUTION_STATUS",
Self::Unexecuted => "UNEXECUTED",
@@ -1331,8 +1382,8 @@ impl ExecutionStatus {
Self::Unexecutable => "UNEXECUTABLE",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_EXECUTION_STATUS" => Some(Self::UnspecifiedExecutionStatus),
"UNEXECUTED" => Some(Self::Unexecuted),
@@ -1373,11 +1424,11 @@ pub enum BalanceOperation {
Bid = 6,
}
impl BalanceOperation {
- /// String value of the enum field names used in the `ProtoBuf` definition.
+ /// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
- /// (if the `ProtoBuf` definition does not change) and safe for programmatic use.
- #[must_use] pub fn as_str_name(&self) -> &'static str {
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
match self {
Self::UnspecifiedBalanceChangeOperation => "UNSPECIFIED_BALANCE_CHANGE_OPERATION",
Self::Deposit => "DEPOSIT",
@@ -1388,8 +1439,8 @@ impl BalanceOperation {
Self::Bid => "BID",
}
}
- /// Creates an enum from field names used in the `ProtoBuf` definition.
- #[must_use] pub fn from_str_name(value: &str) -> ::core::option::Option {
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
match value {
"UNSPECIFIED_BALANCE_CHANGE_OPERATION" => Some(Self::UnspecifiedBalanceChangeOperation),
"DEPOSIT" => Some(Self::Deposit),
@@ -1406,7 +1457,7 @@ impl BalanceOperation {
pub mod prover_network_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::http::Uri;
- use tonic::codegen::{Body, Bytes, CompressionEncoding, GrpcMethod, InterceptedService, StdError, http};
+ use tonic::codegen::*;
#[derive(Debug, Clone)]
pub struct ProverNetworkClient {
inner: tonic::client::Grpc,
@@ -1806,6 +1857,26 @@ pub mod prover_network_client {
.insert(GrpcMethod::new("network.ProverNetwork", "RemoveDelegation"));
self.inner.unary(req, path, codec).await
}
+ /// Terminate a delegation. Only callable by the delegate of a delegation.
+ pub async fn terminate_delegation(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status>
+ {
+ self.inner.ready().await.map_err(|e| {
+ tonic::Status::new(
+ tonic::Code::Unknown,
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic::codec::ProstCodec::default();
+ let path =
+ http::uri::PathAndQuery::from_static("/network.ProverNetwork/TerminateDelegation");
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("network.ProverNetwork", "TerminateDelegation"));
+ self.inner.unary(req, path, codec).await
+ }
/// Accept a delegation. Only callable by the delegate of a delegation.
pub async fn accept_delegation(
&mut self,
@@ -2181,7 +2252,7 @@ pub mod prover_network_client {
/// Generated server implementations.
pub mod prover_network_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
- use tonic::codegen::{Arc, Body, BoxFuture, CompressionEncoding, Context, EnabledCompressionEncodings, InterceptedService, Poll, StdError, async_trait, empty_body, http};
+ use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with ProverNetworkServer.
#[async_trait]
pub trait ProverNetwork: std::marker::Send + std::marker::Sync + 'static {
@@ -2277,6 +2348,11 @@ pub mod prover_network_server {
&self,
request: tonic::Request,
) -> std::result::Result, tonic::Status>;
+ /// Terminate a delegation. Only callable by the delegate of a delegation.
+ async fn terminate_delegation(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
/// Accept a delegation. Only callable by the delegate of a delegation.
async fn accept_delegation(
&self,
@@ -3123,6 +3199,48 @@ pub mod prover_network_server {
};
Box::pin(fut)
}
+ "/network.ProverNetwork/TerminateDelegation" => {
+ #[allow(non_camel_case_types)]
+ struct TerminateDelegationSvc(pub Arc);
+ impl
+ tonic::server::UnaryService
+ for TerminateDelegationSvc
+ {
+ type Response = super::TerminateDelegationResponse;
+ type Future = BoxFuture, tonic::Status>;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::terminate_delegation(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = TerminateDelegationSvc(inner);
+ let codec = tonic::codec::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
"/network.ProverNetwork/AcceptDelegation" => {
#[allow(non_camel_case_types)]
struct AcceptDelegationSvc(pub Arc);
diff --git a/crates/sdk/src/network/prove.rs b/crates/sdk/src/network/prove.rs
index 2d33ea628a..f7f9a0b676 100644
--- a/crates/sdk/src/network/prove.rs
+++ b/crates/sdk/src/network/prove.rs
@@ -4,6 +4,7 @@
use std::time::Duration;
+use alloy_primitives::B256;
use anyhow::Result;
use sp1_core_machine::io::SP1Stdin;
use sp1_prover::SP1ProvingKey;
@@ -23,6 +24,7 @@ pub struct NetworkProveBuilder<'a> {
pub(crate) timeout: Option,
pub(crate) strategy: FulfillmentStrategy,
pub(crate) skip_simulation: bool,
+ pub(crate) cycle_limit: Option,
}
impl<'a> NetworkProveBuilder<'a> {
@@ -231,6 +233,92 @@ impl<'a> NetworkProveBuilder<'a> {
self
}
+ /// Sets the cycle limit for the proof request.
+ ///
+ /// # Details
+ /// The cycle limit determines the maximum number of cycles that the program should take to
+ /// execute. By default, the cycle limit is determined by simulating the program locally.
+ /// However, you can manually set it if you know the exact cycle count needed and want to skip
+ /// the simulation step locally.
+ ///
+ /// The cycle limit ensures that a prover on the network will stop generating a proof once the
+ /// cycle limit is reached, which prevents denial of service attacks.
+ ///
+ /// # Example
+ /// ```rust,no_run
+ /// use sp1_sdk::{ProverClient, SP1Stdin, Prover};
+ ///
+ /// let elf = &[1, 2, 3];
+ /// let stdin = SP1Stdin::new();
+ ///
+ /// let client = ProverClient::builder().network().build();
+ /// let (pk, vk) = client.setup(elf);
+ /// let proof = client.prove(&pk, &stdin)
+ /// .cycle_limit(1_000_000) // Set 1M cycle limit.
+ /// .skip_simulation(true) // Skip simulation since the limit is set manually.
+ /// .run()
+ /// .unwrap();
+ /// ```
+ #[must_use]
+ pub fn cycle_limit(mut self, cycle_limit: u64) -> Self {
+ self.cycle_limit = Some(cycle_limit);
+ self
+ }
+
+ /// Request a proof from the prover network.
+ ///
+ /// # Details
+ /// This method will request a proof from the prover network. If the prover fails to request
+ /// a proof, the method will return an error. It will not wait for the proof to be generated.
+ ///
+ /// # Example
+ /// ```rust,no_run
+ /// use sp1_sdk::{ProverClient, SP1Stdin, Prover};
+ ///
+ /// let elf = &[1, 2, 3];
+ /// let stdin = SP1Stdin::new();
+ ///
+ /// let client = ProverClient::builder().network().build();
+ /// let (pk, vk) = client.setup(elf);
+ /// let request_id = client.prove(&pk, &stdin)
+ /// .request()
+ /// .unwrap();
+ /// ```
+ pub fn request(self) -> Result {
+ block_on(self.request_async())
+ }
+
+ /// Request a proof from the prover network asynchronously.
+ ///
+ /// # Details
+ /// This method will request a proof from the prover network asynchronously. If the prover fails
+ /// to request a proof, the method will return an error. It will not wait for the proof to be
+ /// generated.
+ ///
+ /// # Example
+ /// ```rust,no_run
+ /// use sp1_sdk::{ProverClient, SP1Stdin, Prover};
+ ///
+ /// tokio_test::block_on(async {
+ /// let elf = &[1, 2, 3];
+ /// let stdin = SP1Stdin::new();
+ ///
+ /// let client = ProverClient::builder().network().build();
+ /// let (pk, vk) = client.setup(elf);
+ /// let request_id = client.prove(&pk, &stdin)
+ /// .request_async()
+ /// .await
+ /// .unwrap();
+ /// })
+ /// ```
+ pub async fn request_async(self) -> Result {
+ let Self { prover, mode, pk, stdin, timeout, strategy, skip_simulation, cycle_limit } =
+ self;
+ prover
+ .request_proof_impl(pk, &stdin, mode, strategy, timeout, skip_simulation, cycle_limit)
+ .await
+ }
+
/// Run the prover with the built arguments.
///
/// # Details
@@ -251,19 +339,7 @@ impl<'a> NetworkProveBuilder<'a> {
/// .unwrap();
/// ```
pub fn run(self) -> Result {
- let Self { prover, mode, pk, stdin, timeout, strategy, mut skip_simulation } = self;
-
- // Check for deprecated environment variable
- if let Ok(val) = std::env::var("SKIP_SIMULATION") {
- eprintln!(
- "Warning: SKIP_SIMULATION environment variable is deprecated. Please use .skip_simulation() instead."
- );
- skip_simulation = matches!(val.to_lowercase().as_str(), "true" | "1");
- }
-
- sp1_dump(&pk.elf, &stdin);
-
- block_on(prover.prove_impl(pk, &stdin, mode, strategy, timeout, skip_simulation))
+ block_on(self.run_async())
}
/// Run the prover with the built arguments asynchronously.
@@ -284,7 +360,8 @@ impl<'a> NetworkProveBuilder<'a> {
/// .run_async();
/// ```
pub async fn run_async(self) -> Result {
- let Self { prover, mode, pk, stdin, timeout, strategy, mut skip_simulation } = self;
+ let Self { prover, mode, pk, stdin, timeout, strategy, mut skip_simulation, cycle_limit } =
+ self;
// Check for deprecated environment variable
if let Ok(val) = std::env::var("SKIP_SIMULATION") {
@@ -296,6 +373,6 @@ impl<'a> NetworkProveBuilder<'a> {
sp1_dump(&pk.elf, &stdin);
- prover.prove_impl(pk, &stdin, mode, strategy, timeout, skip_simulation).await
+ prover.prove_impl(pk, &stdin, mode, strategy, timeout, skip_simulation, cycle_limit).await
}
}
diff --git a/crates/sdk/src/network/prover.rs b/crates/sdk/src/network/prover.rs
index 4927acca35..515595c2f5 100644
--- a/crates/sdk/src/network/prover.rs
+++ b/crates/sdk/src/network/prover.rs
@@ -9,19 +9,19 @@ use super::prove::NetworkProveBuilder;
use super::DEFAULT_CYCLE_LIMIT;
use crate::cpu::execute::CpuExecuteBuilder;
use crate::cpu::CpuProver;
-use crate::network::{DEFAULT_PROVER_NETWORK_RPC, DEFAULT_TIMEOUT_SECS};
+use crate::network::proto::network::GetProofRequestStatusResponse;
+use crate::network::{Error, DEFAULT_NETWORK_RPC_URL, DEFAULT_TIMEOUT_SECS};
use crate::{
network::client::NetworkClient,
network::proto::network::{ExecutionStatus, FulfillmentStatus, FulfillmentStrategy, ProofMode},
Prover, SP1ProofMode, SP1ProofWithPublicValues, SP1ProvingKey, SP1VerifyingKey,
};
+use alloy_primitives::B256;
use anyhow::Result;
-use backoff::{future::retry, Error as BackoffError, ExponentialBackoff};
use serde::de::DeserializeOwned;
use sp1_core_executor::{SP1Context, SP1ContextBuilder};
use sp1_core_machine::io::SP1Stdin;
use sp1_prover::{components::CpuProverComponents, SP1Prover, SP1_CIRCUIT_VERSION};
-use tonic::Code;
use {crate::utils::block_on, tokio::time::sleep};
@@ -108,6 +108,7 @@ impl NetworkProver {
timeout: None,
strategy: FulfillmentStrategy::Hosted,
skip_simulation: false,
+ cycle_limit: None,
}
}
@@ -130,20 +131,49 @@ impl NetworkProver {
///
/// let vk_hash = client.register_program(&vk, elf);
/// ```
- pub async fn register_program(&self, vk: &SP1VerifyingKey, elf: &[u8]) -> Result> {
+ pub async fn register_program(&self, vk: &SP1VerifyingKey, elf: &[u8]) -> Result {
self.client.register_program(vk, elf).await
}
+ /// Gets the status of a proof request.
+ ///
+ /// # Details
+ /// * `request_id`: The request ID to get the status of.
+ ///
+ /// # Example
+ /// ```rust,no_run
+ /// use sp1_sdk::{ProverClient, network::B256};
+ ///
+ /// tokio_test::block_on(async {
+ /// let request_id = B256::from_slice(&vec![1u8; 32]);
+ /// let client = ProverClient::builder().network().build();
+ /// let (status, maybe_proof) = client.get_proof_status(request_id).await.unwrap();
+ /// })
+ /// ```
+ pub async fn get_proof_status(
+ &self,
+ request_id: B256,
+ ) -> Result<(GetProofRequestStatusResponse, Option)> {
+ self.client.get_proof_request_status(request_id, None).await
+ }
+
/// Requests a proof from the prover network, returning the request ID.
+ ///
+ /// # Details
+ /// * `vk_hash`: The hash of the verifying key to use for the proof.
+ /// * `stdin`: The input to use for the proof.
+ /// * `mode`: The proof mode to use for the proof.
+ /// * `strategy`: The fulfillment strategy to use for the proof.
+ /// * `cycle_limit`: The cycle limit to use for the proof.
pub(crate) async fn request_proof(
&self,
- vk_hash: &[u8],
+ vk_hash: B256,
stdin: &SP1Stdin,
mode: ProofMode,
strategy: FulfillmentStrategy,
cycle_limit: u64,
timeout: Option,
- ) -> Result> {
+ ) -> Result {
// Get the timeout.
let timeout_secs = timeout.map_or(DEFAULT_TIMEOUT_SECS, |dur| dur.as_secs());
@@ -155,36 +185,29 @@ impl NetworkProver {
log::info!("├─ Timeout: {} seconds", timeout_secs);
log::info!("└─ Circuit version: {}", SP1_CIRCUIT_VERSION);
- // Request the proof with retries.
- let response = with_retry(
- || async {
- self.client
- .request_proof(
- vk_hash,
- stdin,
- mode,
- SP1_CIRCUIT_VERSION,
- strategy,
- timeout_secs,
- cycle_limit,
- )
- .await
- },
- timeout,
- "requesting proof",
- )
- .await?;
+ // Request the proof.
+ let response = self
+ .client
+ .request_proof(
+ vk_hash,
+ stdin,
+ mode,
+ SP1_CIRCUIT_VERSION,
+ strategy,
+ timeout_secs,
+ cycle_limit,
+ )
+ .await?;
// Log the request ID and transaction hash.
- let tx_hash_hex = "0x".to_string() + &hex::encode(response.tx_hash);
- let request_id = response.body.unwrap().request_id;
- let request_id_hex = "0x".to_string() + &hex::encode(request_id.clone());
- log::info!("Created request {} in transaction {}", request_id_hex, tx_hash_hex);
+ let tx_hash = B256::from_slice(&response.tx_hash);
+ let request_id = B256::from_slice(&response.body.unwrap().request_id);
+ log::info!("Created request {} in transaction {:?}", request_id, tx_hash);
- if self.client.rpc_url == DEFAULT_PROVER_NETWORK_RPC {
+ if self.client.rpc_url == DEFAULT_NETWORK_RPC_URL {
log::info!(
"View request status at: https://network.succinct.xyz/request/{}",
- request_id_hex
+ request_id
);
}
@@ -193,9 +216,9 @@ impl NetworkProver {
/// Waits for a proof to be generated and returns the proof. If a timeout is supplied, the
/// function will return an error if the proof is not generated within the timeout.
- pub(crate) async fn wait_proof(
+ pub async fn wait_proof(
&self,
- request_id: &[u8],
+ request_id: B256,
timeout: Option,
) -> Result {
let mut is_assigned = false;
@@ -205,25 +228,32 @@ impl NetworkProver {
// Calculate the remaining timeout.
if let Some(timeout) = timeout {
if start_time.elapsed() > timeout {
- return Err(anyhow::anyhow!("proof request timed out."));
+ return Err(Error::RequestTimedOut { request_id: request_id.to_vec() }.into());
}
}
let remaining_timeout = timeout.map(|t| {
let elapsed = start_time.elapsed();
- if elapsed < t { t - elapsed } else { Duration::from_secs(0) }
+ if elapsed < t {
+ t - elapsed
+ } else {
+ Duration::from_secs(0)
+ }
});
- // Get status with retries.
- let (status, maybe_proof) = with_retry(
- || async { self.client.get_proof_request_status::
(request_id).await },
- remaining_timeout,
- "getting proof request status",
- )
- .await?;
+ // Get the status.
+ let (status, maybe_proof) =
+ self.client.get_proof_request_status(request_id, remaining_timeout).await?;
+
+ // Check the deadline.
+ if status.deadline < Instant::now().elapsed().as_secs() {
+ return Err(Error::RequestTimedOut { request_id: request_id.to_vec() }.into());
+ }
// Check the execution status.
- if status.execution_status == ExecutionStatus::Unexecutable as i32 {
- return Err(anyhow::anyhow!("proof request is unexecutable"));
+ if let Ok(ExecutionStatus::Unexecutable) =
+ ExecutionStatus::try_from(status.execution_status)
+ {
+ return Err(Error::RequestUnexecutable { request_id: request_id.to_vec() }.into());
}
// Check the fulfillment status.
@@ -233,12 +263,14 @@ impl NetworkProver {
}
Ok(FulfillmentStatus::Assigned) => {
if !is_assigned {
- log::info!("proof request assigned, proving...");
+ log::info!("Proof request assigned, proving...");
is_assigned = true;
}
}
Ok(FulfillmentStatus::Unfulfillable) => {
- return Err(anyhow::anyhow!("proof request is unfulfillable"));
+ return Err(
+ Error::RequestUnfulfillable { request_id: request_id.to_vec() }.into()
+ );
}
_ => {}
}
@@ -247,7 +279,23 @@ impl NetworkProver {
}
}
- /// Requests a proof from the prover network and waits for it to be generated.
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) async fn request_proof_impl(
+ &self,
+ pk: &SP1ProvingKey,
+ stdin: &SP1Stdin,
+ mode: SP1ProofMode,
+ strategy: FulfillmentStrategy,
+ timeout: Option,
+ skip_simulation: bool,
+ cycle_limit: Option,
+ ) -> Result {
+ let vk_hash = self.register_program(&pk.vk, &pk.elf).await?;
+ let cycle_limit = self.get_cycle_limit(cycle_limit, &pk.elf, stdin, skip_simulation)?;
+ self.request_proof(vk_hash, stdin, mode.into(), strategy, cycle_limit, timeout).await
+ }
+
+ #[allow(clippy::too_many_arguments)]
pub(crate) async fn prove_impl(
&self,
pk: &SP1ProvingKey,
@@ -256,22 +304,39 @@ impl NetworkProver {
strategy: FulfillmentStrategy,
timeout: Option,
skip_simulation: bool,
+ cycle_limit: Option,
) -> Result {
- let vk_hash = self.register_program(&pk.vk, &pk.elf).await?;
- let cycle_limit = self.get_cycle_limit(&pk.elf, stdin, skip_simulation)?;
let request_id = self
- .request_proof(&vk_hash, stdin, mode.into(), strategy, cycle_limit, timeout)
+ .request_proof_impl(pk, stdin, mode, strategy, timeout, skip_simulation, cycle_limit)
.await?;
- self.wait_proof(&request_id, timeout).await
+ self.wait_proof(request_id, timeout).await
}
- fn get_cycle_limit(&self, elf: &[u8], stdin: &SP1Stdin, skip_simulation: bool) -> Result {
+ /// The cycle limit is determined according to the following priority:
+ ///
+ /// 1. If a cycle limit was explicitly set by the requester, use the specified value.
+ /// 2. If simulation is enabled, calculate the limit by simulating the
+ /// execution of the program. This is the default behavior.
+ /// 3. Otherwise, use the default cycle limit ([`DEFAULT_CYCLE_LIMIT`]).
+ fn get_cycle_limit(
+ &self,
+ cycle_limit: Option,
+ elf: &[u8],
+ stdin: &SP1Stdin,
+ skip_simulation: bool,
+ ) -> Result {
+ if let Some(cycle_limit) = cycle_limit {
+ return Ok(cycle_limit);
+ }
+
if skip_simulation {
Ok(DEFAULT_CYCLE_LIMIT)
} else {
- let (_, report) = self.prover.inner().execute(elf, stdin, SP1Context::default())?;
- let cycles = report.total_instruction_count();
- Ok(cycles)
+ self.prover
+ .inner()
+ .execute(elf, stdin, SP1Context::default())
+ .map(|(_, report)| report.total_instruction_count())
+ .map_err(|_| Error::SimulationFailed.into())
}
}
}
@@ -291,7 +356,7 @@ impl Prover for NetworkProver {
stdin: &SP1Stdin,
mode: SP1ProofMode,
) -> Result {
- block_on(self.prove_impl(pk, stdin, mode, FulfillmentStrategy::Hosted, None, false))
+ block_on(self.prove_impl(pk, stdin, mode, FulfillmentStrategy::Hosted, None, false, None))
}
}
@@ -305,81 +370,3 @@ impl From for ProofMode {
}
}
}
-
-/// Execute an async operation with exponential backoff retries.
-pub async fn with_retry(
- operation: F,
- timeout: Option,
- operation_name: &str,
-) -> Result
-where
- F: Fn() -> Fut,
- Fut: std::future::Future