use std::sync::Arc; use cashu::{BlindSignature, BlindedMessage, Proof}; use cdk_common::error::Error; use cdk_common::mint::MintKeySetInfo; use tokio::sync::{mpsc, oneshot}; use tokio::task::JoinHandle; use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet}; enum Request { BlindSign( ( BlindedMessage, oneshot::Sender>, ), ), VerifyProof((Proof, oneshot::Sender>)), AuthKeysets(oneshot::Sender>, Error>>), Keysets(oneshot::Sender, Error>>), RotateKeyset( ( RotateKeyArguments, oneshot::Sender>, ), ), } /// Creates a service-like to wrap an implementation of the Signatory /// /// This implements the actor model, ensuring the Signatory and their private key is moved from the /// main thread to their own tokio task, and communicates with the main program by passing messages, /// an extra layer of security to move the keys to another layer. pub struct Service { pipeline: mpsc::Sender, runner: Option>, } impl Drop for Service { fn drop(&mut self) { if let Some(runner) = self.runner.take() { runner.abort(); } } } impl Service { pub fn new(handler: Arc) -> Self { let (tx, rx) = mpsc::channel(10_000); let runner = Some(tokio::spawn(Self::runner(rx, handler))); Self { pipeline: tx, runner, } } async fn runner( mut receiver: mpsc::Receiver, handler: Arc, ) { while let Some(request) = receiver.recv().await { match request { Request::BlindSign((blinded_message, response)) => { let output = handler.blind_sign(blinded_message).await; if let Err(err) = response.send(output) { tracing::error!("Error sending response: {:?}", err); } } Request::VerifyProof((proof, response)) => { let output = handler.verify_proof(proof).await; if let Err(err) = response.send(output) { tracing::error!("Error sending response: {:?}", err); } } Request::AuthKeysets(response) => { let output = handler.auth_keysets().await; if let Err(err) = response.send(output) { tracing::error!("Error sending response: {:?}", err); } } Request::Keysets(response) => { let output = handler.keysets().await; if let Err(err) = response.send(output) { tracing::error!("Error sending response: {:?}", err); } } Request::RotateKeyset((args, response)) => { let output = handler.rotate_keyset(args).await; if let Err(err) = response.send(output) { tracing::error!("Error sending response: {:?}", err); } } } } } } #[async_trait::async_trait] impl Signatory for Service { async fn blind_sign(&self, blinded_message: BlindedMessage) -> Result { let (tx, rx) = oneshot::channel(); self.pipeline .send(Request::BlindSign((blinded_message, tx))) .await .map_err(|e| Error::SendError(e.to_string()))?; rx.await.map_err(|e| Error::RecvError(e.to_string()))? } async fn verify_proof(&self, proof: Proof) -> Result<(), Error> { let (tx, rx) = oneshot::channel(); self.pipeline .send(Request::VerifyProof((proof, tx))) .await .map_err(|e| Error::SendError(e.to_string()))?; rx.await.map_err(|e| Error::RecvError(e.to_string()))? } async fn auth_keysets(&self) -> Result>, Error> { let (tx, rx) = oneshot::channel(); self.pipeline .send(Request::AuthKeysets(tx)) .await .map_err(|e| Error::SendError(e.to_string()))?; rx.await.map_err(|e| Error::RecvError(e.to_string()))? } async fn keysets(&self) -> Result, Error> { let (tx, rx) = oneshot::channel(); self.pipeline .send(Request::Keysets(tx)) .await .map_err(|e| Error::SendError(e.to_string()))?; rx.await.map_err(|e| Error::RecvError(e.to_string()))? } async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result { let (tx, rx) = oneshot::channel(); self.pipeline .send(Request::RotateKeyset((args, tx))) .await .map_err(|e| Error::SendError(e.to_string()))?; rx.await.map_err(|e| Error::RecvError(e.to_string()))? } }