Skip to main content

trustless_protocol/
client.rs

1/// Async client for communicating with a key provider process.
2///
3/// Thread-safe via an interior `Mutex` — multiple tasks can share a single client,
4/// but requests are serialized (one at a time on the wire).
5pub struct ProviderClient<R, W> {
6    inner: tokio::sync::Mutex<ProviderClientInner<R, W>>,
7}
8
9struct ProviderClientInner<R, W> {
10    reader: tokio_util::codec::FramedRead<R, tokio_util::codec::LengthDelimitedCodec>,
11    writer: tokio_util::codec::FramedWrite<W, tokio_util::codec::LengthDelimitedCodec>,
12    next_id: u64,
13}
14
15impl<R, W> ProviderClient<R, W>
16where
17    R: tokio::io::AsyncRead + Unpin + Send + 'static,
18    W: tokio::io::AsyncWrite + Unpin + Send + 'static,
19{
20    /// Create a new client from an `AsyncRead` (provider's stdout) and `AsyncWrite` (provider's stdin).
21    pub fn new(reader: R, writer: W) -> Self {
22        let reader = crate::codec::framed_read(reader);
23        let writer = crate::codec::framed_write(writer);
24
25        Self {
26            inner: tokio::sync::Mutex::new(ProviderClientInner {
27                reader,
28                writer,
29                next_id: 1,
30            }),
31        }
32    }
33
34    /// Send an `initialize` request and return the provider's certificates.
35    pub async fn initialize(
36        &self,
37    ) -> Result<crate::message::InitializeResult, crate::error::Error> {
38        let response = self
39            .send_and_recv(|id| crate::message::Request::Initialize {
40                id,
41                params: crate::message::InitializeParams {},
42            })
43            .await?;
44        match response {
45            crate::message::Response::Success(crate::message::SuccessResponse::Initialize {
46                result,
47                ..
48            }) => Ok(result),
49            crate::message::Response::Success(_) => {
50                Err(crate::error::Error::UnexpectedResponseMethod)
51            }
52            crate::message::Response::Error(crate::message::ErrorResponse { error, .. }) => {
53                Err(crate::error::Error::Provider {
54                    code: error.code,
55                    message: error.message,
56                })
57            }
58        }
59    }
60
61    /// Send a `sign` request and return the raw signature bytes.
62    pub async fn sign(
63        &self,
64        certificate_id: &str,
65        scheme: &str,
66        blob: &[u8],
67    ) -> Result<Vec<u8>, crate::error::Error> {
68        let certificate_id = certificate_id.to_owned();
69        let scheme = scheme.to_owned();
70        let blob = blob.to_vec();
71        let response = self
72            .send_and_recv(|id| crate::message::Request::Sign {
73                id,
74                params: crate::message::SignParams {
75                    certificate_id,
76                    scheme,
77                    blob,
78                },
79            })
80            .await?;
81        match response {
82            crate::message::Response::Success(crate::message::SuccessResponse::Sign {
83                result,
84                ..
85            }) => Ok(result.signature),
86            crate::message::Response::Success(_) => {
87                Err(crate::error::Error::UnexpectedResponseMethod)
88            }
89            crate::message::Response::Error(crate::message::ErrorResponse { error, .. }) => {
90                Err(crate::error::Error::Provider {
91                    code: error.code,
92                    message: error.message,
93                })
94            }
95        }
96    }
97
98    async fn send_and_recv(
99        &self,
100        build_request: impl FnOnce(u64) -> crate::message::Request,
101    ) -> Result<crate::message::Response, crate::error::Error> {
102        let mut inner = self.inner.lock().await;
103        let id = inner.next_id;
104        inner.next_id += 1;
105
106        let request = build_request(id);
107        crate::codec::send_message(&mut inner.writer, &request).await?;
108
109        let response: crate::message::Response =
110            crate::codec::recv_message(&mut inner.reader).await?;
111
112        if response.id() != id {
113            return Err(crate::error::Error::UnexpectedResponseId {
114                expected: id,
115                got: response.id(),
116            });
117        }
118
119        Ok(response)
120    }
121}