use alloc::string::String;
use alloc::sync::Arc;
use core::time::Duration;
use miden_protocol::transaction::{ProvenTransaction, TransactionInputs};
use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable};
use miden_protocol::vm::FutureMaybeSend;
use miden_tx::TransactionProverError;
use tokio::sync::Mutex;
use super::api_client::ApiClient;
use super::{RemoteProverClientError, generated as proto};
#[derive(Clone)]
pub struct RemoteTransactionProver {
client: Arc<Mutex<Option<ApiClient>>>,
endpoint: String,
timeout: Duration,
}
impl RemoteTransactionProver {
pub fn new(endpoint: impl Into<String>) -> Self {
RemoteTransactionProver {
endpoint: endpoint.into(),
client: Arc::new(Mutex::new(None)),
timeout: Duration::from_secs(10),
}
}
#[must_use]
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
async fn connect(&self) -> Result<(), RemoteProverClientError> {
let mut client = self.client.lock().await;
if client.is_some() {
return Ok(());
}
*client = Some(ApiClient::new_client(self.endpoint.clone(), self.timeout).await?);
Ok(())
}
pub fn prove(
&self,
tx_inputs: &TransactionInputs,
) -> impl FutureMaybeSend<Result<ProvenTransaction, TransactionProverError>> {
async move {
self.connect().await.map_err(|err| {
TransactionProverError::other_with_source(
"failed to connect to the remote prover",
err,
)
})?;
let mut client = self
.client
.lock()
.await
.as_ref()
.ok_or_else(|| TransactionProverError::other("client should be connected"))?
.clone();
let request = tonic::Request::new(tx_inputs.into());
let response = client.prove(request).await.map_err(|err| {
TransactionProverError::other_with_source("failed to prove transaction", err)
})?;
ProvenTransaction::try_from(response.into_inner()).map_err(|_| {
TransactionProverError::other(
"failed to deserialize received response from remote transaction prover",
)
})
}
}
}
impl TryFrom<proto::Proof> for ProvenTransaction {
type Error = DeserializationError;
fn try_from(response: proto::Proof) -> Result<Self, Self::Error> {
ProvenTransaction::read_from_bytes(&response.payload)
}
}
impl From<&TransactionInputs> for proto::ProofRequest {
fn from(tx_inputs: &TransactionInputs) -> Self {
proto::ProofRequest {
proof_type: proto::ProofType::Transaction.into(),
payload: tx_inputs.to_bytes(),
}
}
}