Skip to main content

cdk_signatory/
embedded.rs

1//! Run a Signatory in a embedded environment, inside a CDK instance, but this wrapper makes sure to
2//! run the Signatory in another thread, isolated form the main CDK, communicating through messages
3use std::sync::Arc;
4
5use cdk_common::{BlindSignature, BlindedMessage, Error, Proof};
6use tokio::sync::{mpsc, oneshot, watch};
7use tokio::task::JoinHandle;
8
9use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
10
11enum Request {
12    BlindSign(
13        (
14            Vec<BlindedMessage>,
15            oneshot::Sender<Result<Vec<BlindSignature>, Error>>,
16        ),
17    ),
18    VerifyProof((Vec<Proof>, oneshot::Sender<Result<(), Error>>)),
19    Keysets(oneshot::Sender<Result<SignatoryKeysets, Error>>),
20    SubscribeKeysets(oneshot::Sender<Result<watch::Receiver<SignatoryKeysets>, Error>>),
21    RotateKeyset(
22        (
23            RotateKeyArguments,
24            oneshot::Sender<Result<SignatoryKeySet, Error>>,
25        ),
26    ),
27}
28
29/// Creates a service-like to wrap an implementation of the Signatory
30///
31/// This implements the actor model, ensuring the Signatory and their private key is moved from the
32/// main thread to their own tokio task, and communicates with the main program by passing messages,
33/// an extra layer of security to move the keys to another layer.
34#[allow(missing_debug_implementations)]
35pub struct Service {
36    pipeline: mpsc::Sender<Request>,
37    runner: Option<JoinHandle<()>>,
38}
39
40impl Drop for Service {
41    fn drop(&mut self) {
42        if let Some(runner) = self.runner.take() {
43            runner.abort();
44        }
45    }
46}
47
48impl Service {
49    /// Takes a signatory and spawns it into a Tokio task, isolating its implementation with the
50    /// main thread, communicating with it through messages
51    pub fn new(handler: Arc<dyn Signatory + Send + Sync>) -> Self {
52        let (tx, rx) = mpsc::channel(10_000);
53        let runner = Some(tokio::spawn(Self::runner(rx, handler)));
54
55        Self {
56            pipeline: tx,
57            runner,
58        }
59    }
60
61    #[tracing::instrument(skip_all)]
62    async fn runner(
63        mut receiver: mpsc::Receiver<Request>,
64        handler: Arc<dyn Signatory + Send + Sync>,
65    ) {
66        while let Some(request) = receiver.recv().await {
67            match request {
68                Request::BlindSign((blinded_message, response)) => {
69                    let output = handler.blind_sign(blinded_message).await;
70                    if response.send(output).is_err() {
71                        tracing::error!("Error sending blind-sign response: receiver dropped");
72                    }
73                }
74                Request::VerifyProof((proof, response)) => {
75                    let output = handler.verify_proofs(proof).await;
76                    if response.send(output).is_err() {
77                        tracing::error!(
78                            "Error sending proof-verification response: receiver dropped"
79                        );
80                    }
81                }
82                Request::Keysets(response) => {
83                    let output = handler.keysets().await;
84                    if response.send(output).is_err() {
85                        tracing::error!("Error sending keysets response: receiver dropped");
86                    }
87                }
88                Request::SubscribeKeysets(response) => {
89                    let output = handler.subscribe_keysets().await;
90                    if response.send(output).is_err() {
91                        tracing::error!("Error sending keyset subscription");
92                    }
93                }
94                Request::RotateKeyset((args, response)) => {
95                    let output = handler.rotate_keyset(args).await;
96                    if response.send(output).is_err() {
97                        tracing::error!("Error sending keyset-rotation response: receiver dropped");
98                    }
99                }
100            }
101        }
102    }
103}
104
105#[async_trait::async_trait]
106impl Signatory for Service {
107    fn name(&self) -> String {
108        "Embedded".to_owned()
109    }
110
111    #[tracing::instrument(skip_all)]
112    async fn blind_sign(
113        &self,
114        blinded_messages: Vec<BlindedMessage>,
115    ) -> Result<Vec<BlindSignature>, Error> {
116        let (tx, rx) = oneshot::channel();
117        self.pipeline
118            .send(Request::BlindSign((blinded_messages, tx)))
119            .await
120            .map_err(|e| Error::SendError(e.to_string()))?;
121
122        rx.await.map_err(|e| Error::RecvError(e.to_string()))?
123    }
124
125    #[tracing::instrument(skip_all)]
126    async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error> {
127        let (tx, rx) = oneshot::channel();
128        self.pipeline
129            .send(Request::VerifyProof((proofs, tx)))
130            .await
131            .map_err(|e| Error::SendError(e.to_string()))?;
132
133        rx.await.map_err(|e| Error::RecvError(e.to_string()))?
134    }
135
136    #[tracing::instrument(skip_all)]
137    async fn keysets(&self) -> Result<SignatoryKeysets, Error> {
138        let (tx, rx) = oneshot::channel();
139        self.pipeline
140            .send(Request::Keysets(tx))
141            .await
142            .map_err(|e| Error::SendError(e.to_string()))?;
143
144        rx.await.map_err(|e| Error::RecvError(e.to_string()))?
145    }
146
147    #[tracing::instrument(skip_all)]
148    async fn subscribe_keysets(&self) -> Result<watch::Receiver<SignatoryKeysets>, Error> {
149        let (tx, rx) = oneshot::channel();
150        self.pipeline
151            .send(Request::SubscribeKeysets(tx))
152            .await
153            .map_err(|e| Error::SendError(e.to_string()))?;
154
155        rx.await.map_err(|e| Error::RecvError(e.to_string()))?
156    }
157
158    #[tracing::instrument(skip(self))]
159    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error> {
160        let (tx, rx) = oneshot::channel();
161        self.pipeline
162            .send(Request::RotateKeyset((args, tx)))
163            .await
164            .map_err(|e| Error::SendError(e.to_string()))?;
165
166        rx.await.map_err(|e| Error::RecvError(e.to_string()))?
167    }
168}