Skip to main content

miden_client/remote_prover/
tx_prover.rs

1use alloc::string::String;
2use alloc::sync::Arc;
3use core::time::Duration;
4
5use miden_protocol::transaction::{ProvenTransaction, TransactionInputs};
6use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable};
7use miden_protocol::vm::FutureMaybeSend;
8use miden_tx::TransactionProverError;
9use tokio::sync::Mutex;
10
11use super::api_client::ApiClient;
12use super::{RemoteProverClientError, generated as proto};
13
14// REMOTE TRANSACTION PROVER
15// ================================================================================================
16
17/// A [`RemoteTransactionProver`] is a transaction prover that sends witness data to a remote
18/// gRPC server and receives a proven transaction.
19///
20/// When compiled for the `wasm32-unknown-unknown` target, it uses the `tonic_web_wasm_client`
21/// transport. Otherwise, it uses the built-in `tonic::transport` for native platforms.
22///
23/// The transport layer connection is established lazily when the first transaction is proven.
24#[derive(Clone)]
25pub struct RemoteTransactionProver {
26    /// Lazily initialized gRPC client, populated on the first proving request.
27    client: Arc<Mutex<Option<ApiClient>>>,
28
29    /// Endpoint of the remote prover in the format `{protocol}://{hostname}:{port}`.
30    endpoint: String,
31
32    /// Timeout applied to each request sent to the remote prover.
33    timeout: Duration,
34}
35
36impl RemoteTransactionProver {
37    /// Creates a new [`RemoteTransactionProver`] with the specified gRPC server endpoint. The
38    /// endpoint should be in the format `{protocol}://{hostname}:{port}`.
39    pub fn new(endpoint: impl Into<String>) -> Self {
40        RemoteTransactionProver {
41            endpoint: endpoint.into(),
42            client: Arc::new(Mutex::new(None)),
43            timeout: Duration::from_secs(10),
44        }
45    }
46
47    /// Configures the timeout for requests to the remote prover server.
48    #[must_use]
49    pub fn with_timeout(mut self, timeout: Duration) -> Self {
50        self.timeout = timeout;
51        self
52    }
53
54    /// Establishes a connection to the remote transaction prover server. The connection is
55    /// maintained for the lifetime of the prover. If the connection is already established, this
56    /// method does nothing.
57    async fn connect(&self) -> Result<(), RemoteProverClientError> {
58        let mut client = self.client.lock().await;
59        if client.is_some() {
60            return Ok(());
61        }
62
63        *client = Some(ApiClient::new_client(self.endpoint.clone(), self.timeout).await?);
64
65        Ok(())
66    }
67
68    /// Proves the given transaction inputs on the remote prover, returning the resulting
69    /// [`ProvenTransaction`].
70    pub fn prove(
71        &self,
72        tx_inputs: &TransactionInputs,
73    ) -> impl FutureMaybeSend<Result<ProvenTransaction, TransactionProverError>> {
74        async move {
75            self.connect().await.map_err(|err| {
76                TransactionProverError::other_with_source(
77                    "failed to connect to the remote prover",
78                    err,
79                )
80            })?;
81
82            let mut client = self
83                .client
84                .lock()
85                .await
86                .as_ref()
87                .ok_or_else(|| TransactionProverError::other("client should be connected"))?
88                .clone();
89
90            let request = tonic::Request::new(tx_inputs.into());
91
92            let response = client.prove(request).await.map_err(|err| {
93                TransactionProverError::other_with_source("failed to prove transaction", err)
94            })?;
95
96            ProvenTransaction::try_from(response.into_inner()).map_err(|_| {
97                TransactionProverError::other(
98                    "failed to deserialize received response from remote transaction prover",
99                )
100            })
101        }
102    }
103}
104
105// CONVERSIONS
106// ================================================================================================
107
108impl TryFrom<proto::Proof> for ProvenTransaction {
109    type Error = DeserializationError;
110
111    fn try_from(response: proto::Proof) -> Result<Self, Self::Error> {
112        ProvenTransaction::read_from_bytes(&response.payload)
113    }
114}
115
116impl From<&TransactionInputs> for proto::ProofRequest {
117    fn from(tx_inputs: &TransactionInputs) -> Self {
118        proto::ProofRequest {
119            proof_type: proto::ProofType::Transaction.into(),
120            payload: tx_inputs.to_bytes(),
121        }
122    }
123}