miden_client/remote_prover/
tx_prover.rs1use alloc::string::String;
2use alloc::sync::Arc;
3use core::time::Duration;
4
5use miden_objects::{BuildUnchecked, DecodeMessage};
6use miden_protocol::transaction::{ProvenTransaction, TransactionInputs};
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#[derive(Clone)]
25pub struct RemoteTransactionProver {
26 client: Arc<Mutex<Option<ApiClient>>>,
28
29 endpoint: String,
31
32 timeout: Duration,
34}
35
36impl RemoteTransactionProver {
37 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 #[must_use]
49 pub fn with_timeout(mut self, timeout: Duration) -> Self {
50 self.timeout = timeout;
51 self
52 }
53
54 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 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())
97 }
98 }
99}
100
101impl TryFrom<proto::Proof> for ProvenTransaction {
105 type Error = TransactionProverError;
106
107 fn try_from(response: proto::Proof) -> Result<Self, Self::Error> {
108 match response.proof {
109 Some(proto::proof::Proof::Transaction(transaction)) => transaction
110 .decode_fields()
111 .map_err(|err| {
112 TransactionProverError::other_with_source(
113 "failed to decode the transaction proof",
114 err,
115 )
116 })?
117 .build_unchecked()
118 .map_err(|err| {
119 TransactionProverError::other_with_source(
120 "failed to build the transaction proof",
121 err,
122 )
123 }),
124 Some(proto::proof::Proof::Batch(_)) => Err(TransactionProverError::other(
125 "expected a transaction proof, got a batch proof",
126 )),
127 Some(proto::proof::Proof::Block(_)) => Err(TransactionProverError::other(
128 "expected a transaction proof, got a block proof",
129 )),
130 None => Err(TransactionProverError::other("prover returned no proof")),
131 }
132 }
133}
134
135impl From<&TransactionInputs> for proto::ProofRequest {
136 fn from(tx_inputs: &TransactionInputs) -> Self {
137 proto::ProofRequest {
138 request: Some(proto::proof_request::Request::Transaction(tx_inputs.into())),
139 }
140 }
141}