Bläddra i källkod

Implementing https://github.com/cashubtc/nuts/pull/250

Cesar Rodas 1 månad sedan
förälder
incheckning
1c86f2cf3f

+ 3 - 3
crates/cdk-integration-tests/tests/integration_tests_pure.rs

@@ -513,7 +513,7 @@ async fn test_swap_overpay_underpay_fee() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 1, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
         .await
         .unwrap();
 
@@ -575,7 +575,7 @@ async fn test_mint_enforce_fee() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 1, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
         .await
         .unwrap();
 
@@ -659,7 +659,7 @@ async fn test_mint_change_with_fee_melt() {
         .expect("Failed to create test mint");
 
     mint_bob
-        .rotate_keyset(CurrencyUnit::Sat, 1, 32, 1)
+        .rotate_keyset(CurrencyUnit::Sat, 32, 1)
         .await
         .unwrap();
 

+ 2 - 6
crates/cdk-integration-tests/tests/mint.rs

@@ -59,12 +59,8 @@ async fn test_correct_keyset() {
     let quote_ttl = QuoteTTL::new(10000, 10000);
     localstore.set_quote_ttl(quote_ttl).await.unwrap();
 
-    mint.rotate_next_keyset(CurrencyUnit::Sat, 32, 0)
-        .await
-        .unwrap();
-    mint.rotate_next_keyset(CurrencyUnit::Sat, 32, 0)
-        .await
-        .unwrap();
+    mint.rotate_keyset(CurrencyUnit::Sat, 32, 0).await.unwrap();
+    mint.rotate_keyset(CurrencyUnit::Sat, 32, 0).await.unwrap();
 
     let active = mint.get_active_keysets();
 

+ 1 - 1
crates/cdk-mint-rpc/src/proto/server.rs

@@ -673,7 +673,7 @@ impl CdkMint for MintRPCServer {
 
         let keyset_info = self
             .mint
-            .rotate_next_keyset(
+            .rotate_keyset(
                 unit,
                 request.max_order.map(|a| a as u8).unwrap_or(32),
                 request.input_fee_ppk.unwrap_or(0),

+ 87 - 69
crates/cdk-signatory/proto/signatory.proto

@@ -2,57 +2,85 @@ syntax = "proto3";
 
 package signatory;
 
+/// TODO: research about the stream, to notify when RotateKeyset is triggered.
 service Signatory {
-    rpc BlindSign(BlindedMessage) returns (BlindSignature);
+  rpc BlindSign (BlindedMessages) returns (BlindSignResponse);
 
-    rpc VerifyProof(Proof) returns (Empty);
+  rpc VerifyProofs (Proofs) returns (BooleanResponse);
 
-    rpc Keysets(Empty) returns (VecSignatoryKeySet);
+  rpc Keysets (EmptyRequest) returns (KeysResponse);
 
-    rpc RotateKeyset(RotateKeyArguments) returns (MintKeySetInfo);
+  rpc RotateKeyset (RotationRequest) returns (KeyRotationResponse);
 }
 
-message Empty {}
+message BlindSignResponse {
+    oneof result {
+        BlindSignatures sigs  = 1;
+        Error error = 2;
+    }
+}
 
-message VecSignatoryKeySet {
-    repeated SignatoryKeySet keysets = 1;
+message BlindedMessages {
+    repeated BlindedMessage blinded_messages = 1;
+}
 
-    optional bool is_none = 2;
+// Represents a blinded message
+message BlindedMessage {
+    uint64 amount = 1;
+    string keyset_id = 2;
+    bytes blinded_secret = 3;
 }
 
-message SignatoryKeySet {
-    KeySet key = 1;
-    MintKeySetInfo info = 2;
+message BooleanResponse {
+    oneof result {
+        bool success  = 1;
+        Error error = 2;
+    }
 }
 
-message KeySet {
-    Id id = 1;
-    CurrencyUnit unit = 2;
-    Keys keys = 3;
+message KeyRotationResponse {
+    oneof result {
+        KeySet keyset  = 1;
+        Error error = 2;
+    }
 }
 
-message Keys {
-    map<uint64, bytes> keys = 1;
+message KeysResponse {
+    oneof result {
+        SignatoryKeysets keysets = 1;
+        Error error = 2;
+    }
 }
 
+message SignatoryKeysets {
+    bytes pubkey = 1;
+    repeated KeySet keysets = 2;
+}
 
-message RotateKeyArguments {
-    CurrencyUnit unit = 1;
-    optional uint32 derivation_path_index = 2;
-    uint32 max_order = 3;
+message KeySet {
+    string id = 1;
+    CurrencyUnit unit = 2;
+    bool active = 3;
     uint64 input_fee_ppk = 4;
+    Keys keys = 5;
 }
 
-message CustomDerivationPath {
-    CurrencyUnit unit = 1;
-    repeated DerivationPath derivation_path = 2;
+message Keys {
+    map<uint64, bytes> keys = 1;
+}
+
+message RotationRequest {
+    CurrencyUnit unit  = 1;
+    uint64 input_fee_ppk  = 2;
+    uint64 max_order = 3;
 }
 
 enum CurrencyUnitType {
-  SAT = 0;
-  MSAT = 1;
-  USD = 2;
-  EUR = 3;
+    SAT = 0;
+    MSAT = 1;
+    USD = 2;
+    EUR = 3;
+    AUTH = 4;
 }
 
 message CurrencyUnit {
@@ -62,13 +90,15 @@ message CurrencyUnit {
     }
 }
 
+message Proofs {
+    repeated Proof proof = 1;
+}
+
 message Proof {
     uint64 amount = 1;
     string keyset_id = 2;
-    string secret = 3;
+    bytes secret = 3;
     bytes C = 4;
-    optional Witness witness = 5;
-    optional ProofDLEQ dleq = 6;
 }
 
 message ProofDLEQ {
@@ -77,6 +107,16 @@ message ProofDLEQ {
     bytes r = 3;
 }
 
+message SigningResponse {
+    oneof result {
+        BlindSignatures blind_signatures = 1;
+        Error error = 2;
+    }
+}
+message BlindSignatures {
+    repeated BlindSignature blind_signatures = 1;
+}
+
 message BlindSignature {
     uint64 amount = 1;
     string keyset_id = 2;
@@ -89,13 +129,6 @@ message BlindSignatureDLEQ {
     bytes s = 2;
 }
 
-message KeySetInfo {
-    Id id = 1;
-    CurrencyUnit unit = 2;
-    bool active = 3;
-    uint64 input_fee_ppk = 4;
-}
-
 // Witness type
 message Witness {
     oneof witness_type {
@@ -114,42 +147,27 @@ message P2PKWitness {
 message HTLCWitness {
     // Preimage
     string preimage = 1;
-
     // List of signatures
     repeated string signatures = 2;
 }
 
-message BlindedMessage {
-    uint64 amount = 1;
-    string keyset_id = 2;
-    bytes blinded_secret = 3;
-    optional Witness witness = 4; // This field is optional by default in proto3
-}
-
-message KeysResponse {
-    repeated KeySet keysets = 1;
-}
-
-
-message Id {
-    bytes inner = 1;
+enum ErrorCode {
+    UNKNOWN = 0;
+    AMOUNT_OUTSIDE_LIMIT = 1;
+    DUPLICATE_INPUTS_PROVIDED = 2;
+    DUPLICATE_OUTPUTS_PROVIDED = 3;
+    KEYSET_NOT_KNOWN = 4;
+    KEYSET_INACTIVE = 5;
+    MINTING_DISABLED = 6;
+    COULD_NOT_ROTATE_KEYSET = 7;
+    INVALID_PROOF = 8;
+    INVALID_BLIND_MESSAGE = 9;
+    UNIT_NOT_SUPPORTED = 10;
 }
 
-message DerivationPath {
-    oneof child_number {
-        uint32 normal = 1;
-        uint32 hardened = 2;
-    }
+message Error {
+  ErrorCode code = 1;
+  string detail = 2;
 }
 
-message MintKeySetInfo {
-    Id id = 1;
-    CurrencyUnit unit = 2;
-    bool active = 3;
-    uint64 valid_from = 4;
-    optional uint64 valid_to = 5;
-    repeated DerivationPath derivation_path = 6;
-    optional uint32 derivation_path_index = 7;
-    uint32 max_order = 8;
-    uint64 input_fee_ppk = 9;
-}
+message EmptyRequest {}

+ 72 - 59
crates/cdk-signatory/src/db_signatory.rs

@@ -3,6 +3,7 @@ use std::sync::Arc;
 
 use bitcoin::bip32::{DerivationPath, Xpriv};
 use bitcoin::secp256k1::{self, Secp256k1};
+use cashu::PublicKey;
 use cdk_common::dhke::{sign_message, verify_message};
 use cdk_common::mint::MintKeySetInfo;
 use cdk_common::nuts::{BlindSignature, BlindedMessage, CurrencyUnit, Id, MintKeySet, Proof};
@@ -10,7 +11,7 @@ use cdk_common::{database, Error};
 use tokio::sync::RwLock;
 
 use crate::common::{create_new_keyset, derivation_path_from_unit, init_keysets};
-use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet};
+use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
 
 /// In-memory Signatory
 ///
@@ -27,6 +28,7 @@ pub struct DbSignatory {
     secp_ctx: Secp256k1<secp256k1::All>,
     custom_paths: HashMap<CurrencyUnit, DerivationPath>,
     xpriv: Xpriv,
+    xpub: PublicKey,
 }
 
 impl DbSignatory {
@@ -107,9 +109,10 @@ impl DbSignatory {
             keysets: Default::default(),
             active_keysets: Default::default(),
             auth_localstore,
-            secp_ctx,
             localstore,
             custom_paths,
+            xpub: xpriv.to_keypair(&secp_ctx).public_key().into(),
+            secp_ctx,
             xpriv,
         };
         keys.reload_keys_from_db().await?;
@@ -175,74 +178,84 @@ impl DbSignatory {
 
 #[async_trait::async_trait]
 impl Signatory for DbSignatory {
-    async fn blind_sign(&self, blinded_message: BlindedMessage) -> Result<BlindSignature, Error> {
-        let BlindedMessage {
-            amount,
-            blinded_secret,
-            keyset_id,
-            ..
-        } = blinded_message;
-
+    async fn blind_sign(
+        &self,
+        blinded_messages: Vec<BlindedMessage>,
+    ) -> Result<Vec<BlindSignature>, Error> {
         let keysets = self.keysets.read().await;
-        let (info, key) = keysets.get(&keyset_id).ok_or(Error::UnknownKeySet)?;
-        if !info.active {
-            return Err(Error::InactiveKeyset);
-        }
 
-        let key_pair = key.keys.get(&amount).ok_or(Error::UnknownKeySet)?;
-        let c = sign_message(&key_pair.secret_key, &blinded_secret)?;
+        blinded_messages
+            .into_iter()
+            .map(|blinded_message| {
+                let BlindedMessage {
+                    amount,
+                    blinded_secret,
+                    keyset_id,
+                    ..
+                } = blinded_message;
+
+                let (info, key) = keysets.get(&keyset_id).ok_or(Error::UnknownKeySet)?;
+                if !info.active {
+                    return Err(Error::InactiveKeyset);
+                }
+
+                let key_pair = key.keys.get(&amount).ok_or(Error::UnknownKeySet)?;
+                let c = sign_message(&key_pair.secret_key, &blinded_secret)?;
 
-        let blinded_signature = BlindSignature::new(
-            amount,
-            c,
-            keyset_id,
-            &blinded_message.blinded_secret,
-            key_pair.secret_key.clone(),
-        )?;
+                let blinded_signature = BlindSignature::new(
+                    amount,
+                    c,
+                    keyset_id,
+                    &blinded_message.blinded_secret,
+                    key_pair.secret_key.clone(),
+                )?;
 
-        Ok(blinded_signature)
+                Ok(blinded_signature)
+            })
+            .collect::<Result<Vec<_>, _>>()
     }
 
-    async fn verify_proof(&self, proof: Proof) -> Result<(), Error> {
+    async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error> {
         let keysets = self.keysets.read().await;
-        let (_, key) = keysets.get(&proof.keyset_id).ok_or(Error::UnknownKeySet)?;
-        let key_pair = key.keys.get(&proof.amount).ok_or(Error::UnknownKeySet)?;
-        verify_message(&key_pair.secret_key, proof.c, proof.secret.as_bytes())?;
 
-        Ok(())
+        proofs.into_iter().try_for_each(|proof| {
+            let (_, key) = keysets.get(&proof.keyset_id).ok_or(Error::UnknownKeySet)?;
+            let key_pair = key.keys.get(&proof.amount).ok_or(Error::UnknownKeySet)?;
+            verify_message(&key_pair.secret_key, proof.c, proof.secret.as_bytes())?;
+            Ok(())
+        })
     }
 
-    async fn keysets(&self) -> Result<Vec<SignatoryKeySet>, Error> {
-        Ok(self
-            .keysets
-            .read()
-            .await
-            .values()
-            .map(|k| k.into())
-            .collect::<Vec<_>>())
+    async fn keysets(&self) -> Result<SignatoryKeysets, Error> {
+        Ok(SignatoryKeysets {
+            pubkey: self.xpub,
+            keysets: self
+                .keysets
+                .read()
+                .await
+                .values()
+                .map(|k| k.into())
+                .collect::<Vec<_>>(),
+        })
     }
 
     /// Add current keyset to inactive keysets
     /// Generate new keyset
     #[tracing::instrument(skip(self))]
-    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<MintKeySetInfo, Error> {
-        let path_index = if let Some(path_index) = args.derivation_path_index {
-            path_index
-        } else {
-            let current_keyset_id = self
-                .localstore
-                .get_active_keyset_id(&args.unit)
-                .await?
-                .ok_or(Error::UnsupportedUnit)?;
-
-            let keyset_info = self
-                .localstore
-                .get_keyset_info(&current_keyset_id)
-                .await?
-                .ok_or(Error::UnknownKeySet)?;
-
-            keyset_info.derivation_path_index.unwrap_or(1) + 1
-        };
+    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error> {
+        let current_keyset_id = self
+            .localstore
+            .get_active_keyset_id(&args.unit)
+            .await?
+            .ok_or(Error::UnsupportedUnit)?;
+
+        let keyset_info = self
+            .localstore
+            .get_keyset_info(&current_keyset_id)
+            .await?
+            .ok_or(Error::UnknownKeySet)?;
+
+        let path_index = keyset_info.derivation_path_index.unwrap_or(1) + 1;
 
         let derivation_path = match self.custom_paths.get(&args.unit) {
             Some(path) => path.clone(),
@@ -250,7 +263,7 @@ impl Signatory for DbSignatory {
                 .ok_or(Error::UnsupportedUnit)?,
         };
 
-        let (_, keyset_info) = create_new_keyset(
+        let (keyset, info) = create_new_keyset(
             &self.secp_ctx,
             self.xpriv,
             derivation_path,
@@ -259,13 +272,13 @@ impl Signatory for DbSignatory {
             args.max_order,
             args.input_fee_ppk,
         );
-        let id = keyset_info.id;
-        self.localstore.add_keyset_info(keyset_info.clone()).await?;
+        let id = info.id;
+        self.localstore.add_keyset_info(info.clone()).await?;
         self.localstore.set_active_keyset(args.unit, id).await?;
 
         self.reload_keys_from_db().await?;
 
-        Ok(keyset_info)
+        Ok((&(info, keyset)).into())
     }
 }
 

+ 16 - 14
crates/cdk-signatory/src/embedded.rs

@@ -3,26 +3,25 @@
 use std::sync::Arc;
 
 use cashu::{BlindSignature, BlindedMessage, Proof};
-use cdk_common::mint::MintKeySetInfo;
 use cdk_common::Error;
 use tokio::sync::{mpsc, oneshot};
 use tokio::task::JoinHandle;
 
-use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet};
+use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
 
 enum Request {
     BlindSign(
         (
-            BlindedMessage,
-            oneshot::Sender<Result<BlindSignature, Error>>,
+            Vec<BlindedMessage>,
+            oneshot::Sender<Result<Vec<BlindSignature>, Error>>,
         ),
     ),
-    VerifyProof((Proof, oneshot::Sender<Result<(), Error>>)),
-    Keysets(oneshot::Sender<Result<Vec<SignatoryKeySet>, Error>>),
+    VerifyProof((Vec<Proof>, oneshot::Sender<Result<(), Error>>)),
+    Keysets(oneshot::Sender<Result<SignatoryKeysets, Error>>),
     RotateKeyset(
         (
             RotateKeyArguments,
-            oneshot::Sender<Result<MintKeySetInfo, Error>>,
+            oneshot::Sender<Result<SignatoryKeySet, Error>>,
         ),
     ),
 }
@@ -69,7 +68,7 @@ impl Service {
                     }
                 }
                 Request::VerifyProof((proof, response)) => {
-                    let output = handler.verify_proof(proof).await;
+                    let output = handler.verify_proofs(proof).await;
                     if let Err(err) = response.send(output) {
                         tracing::error!("Error sending response: {:?}", err);
                     }
@@ -93,27 +92,30 @@ impl Service {
 
 #[async_trait::async_trait]
 impl Signatory for Service {
-    async fn blind_sign(&self, blinded_message: BlindedMessage) -> Result<BlindSignature, Error> {
+    async fn blind_sign(
+        &self,
+        blinded_messages: Vec<BlindedMessage>,
+    ) -> Result<Vec<BlindSignature>, Error> {
         let (tx, rx) = oneshot::channel();
         self.pipeline
-            .send(Request::BlindSign((blinded_message, tx)))
+            .send(Request::BlindSign((blinded_messages, 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> {
+    async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error> {
         let (tx, rx) = oneshot::channel();
         self.pipeline
-            .send(Request::VerifyProof((proof, tx)))
+            .send(Request::VerifyProof((proofs, 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<Vec<SignatoryKeySet>, Error> {
+    async fn keysets(&self) -> Result<SignatoryKeysets, Error> {
         let (tx, rx) = oneshot::channel();
         self.pipeline
             .send(Request::Keysets(tx))
@@ -123,7 +125,7 @@ impl Signatory for Service {
         rx.await.map_err(|e| Error::RecvError(e.to_string()))?
     }
 
-    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<MintKeySetInfo, Error> {
+    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error> {
         let (tx, rx) = oneshot::channel();
         self.pipeline
             .send(Request::RotateKeyset((args, tx)))

+ 64 - 19
crates/cdk-signatory/src/proto/client.rs

@@ -1,9 +1,9 @@
 use cdk_common::error::Error;
-use cdk_common::mint::MintKeySetInfo;
 use cdk_common::{BlindSignature, BlindedMessage, Proof};
 
+use super::{blind_sign_response, boolean_response, key_rotation_response, keys_response};
 use crate::proto::signatory_client::SignatoryClient;
-use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet};
+use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
 
 /// A client for the Signatory service.
 pub struct SignatoryRpcClient {
@@ -21,49 +21,94 @@ impl SignatoryRpcClient {
 
 #[async_trait::async_trait]
 impl Signatory for SignatoryRpcClient {
-    async fn blind_sign(&self, request: BlindedMessage) -> Result<BlindSignature, Error> {
-        let req: super::BlindedMessage = request.into();
+    async fn blind_sign(&self, request: Vec<BlindedMessage>) -> Result<Vec<BlindSignature>, Error> {
+        let req = super::BlindedMessages {
+            blinded_messages: request
+                .into_iter()
+                .map(|blind_message| blind_message.into())
+                .collect(),
+        };
+
         self.client
             .clone()
             .blind_sign(req)
             .await
-            .map(|response| response.into_inner().try_into())
+            .map(|response| {
+                match response
+                    .into_inner()
+                    .result
+                    .ok_or(Error::Custom("Internal error".to_owned()))?
+                {
+                    blind_sign_response::Result::Sigs(sigs) => sigs
+                        .blind_signatures
+                        .into_iter()
+                        .map(|blinded_signature| blinded_signature.try_into())
+                        .collect(),
+                    blind_sign_response::Result::Error(err) => Err(err.into()),
+                }
+            })
             .map_err(|e| Error::Custom(e.to_string()))?
     }
 
-    async fn verify_proof(&self, proof: Proof) -> Result<(), Error> {
-        let req: super::Proof = proof.into();
+    async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error> {
+        let req: super::Proofs = proofs.into();
         self.client
             .clone()
-            .verify_proof(req)
+            .verify_proofs(req)
             .await
-            .map(|response| response.into_inner().try_into())
+            .map(|response| {
+                match response
+                    .into_inner()
+                    .result
+                    .ok_or(Error::Custom("Internal error".to_owned()))?
+                {
+                    boolean_response::Result::Success(bool) => {
+                        if bool {
+                            Ok(())
+                        } else {
+                            Err(Error::SignatureMissingOrInvalid)
+                        }
+                    }
+                    boolean_response::Result::Error(err) => Err(err.into()),
+                }
+            })
             .map_err(|e| Error::Custom(e.to_string()))?
     }
 
-    async fn keysets(&self) -> Result<Vec<SignatoryKeySet>, Error> {
+    async fn keysets(&self) -> Result<SignatoryKeysets, Error> {
         self.client
             .clone()
-            .keysets(super::Empty {})
+            .keysets(super::EmptyRequest {})
             .await
             .map(|response| {
-                response
+                match response
                     .into_inner()
-                    .keysets
-                    .into_iter()
-                    .map(|x| x.try_into())
-                    .collect::<Result<Vec<SignatoryKeySet>, _>>()
+                    .result
+                    .ok_or(Error::Custom("Internal error".to_owned()))?
+                {
+                    keys_response::Result::Keysets(keyset) => keyset.try_into(),
+                    keys_response::Result::Error(err) => Err(err.into()),
+                }
             })
             .map_err(|e| Error::Custom(e.to_string()))?
     }
 
-    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<MintKeySetInfo, Error> {
-        let req: super::RotateKeyArguments = args.into();
+    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error> {
+        let req: super::RotationRequest = args.into();
         self.client
             .clone()
             .rotate_keyset(req)
             .await
-            .map(|response| response.into_inner().try_into())
+            .map(|response| {
+                match response
+                    .into_inner()
+                    .result
+                    .ok_or(Error::Custom("Internal error".to_owned()))?
+                {
+                    key_rotation_response::Result::Keyset(keyset) => keyset.try_into(),
+                    key_rotation_response::Result::Error(err) => Err(err.into()),
+                }
+            })
             .map_err(|e| Error::Custom(e.to_string()))?
     }
 }

+ 130 - 194
crates/cdk-signatory/src/proto/convert.rs

@@ -1,26 +1,112 @@
 //! Type conversions between Rust types and the generated protobuf types.
 use std::collections::BTreeMap;
-use std::str::FromStr;
 
 use cashu::secret::Secret;
+use cashu::util::hex;
+use cashu::{Amount, PublicKey};
 use cdk_common::{HTLCWitness, P2PKWitness};
 use tonic::Status;
 
 use super::*;
 
-impl From<cashu::Id> for Id {
-    fn from(value: cashu::Id) -> Self {
-        Id {
-            inner: value.to_bytes().to_vec(),
+const INTERNAL_ERROR: &str = "Missing property";
+
+impl From<crate::signatory::SignatoryKeysets> for SignatoryKeysets {
+    fn from(keyset: crate::signatory::SignatoryKeysets) -> Self {
+        Self {
+            pubkey: keyset.pubkey.to_bytes().to_vec(),
+            keysets: keyset
+                .keysets
+                .into_iter()
+                .map(|keyset| keyset.into())
+                .collect(),
         }
     }
 }
 
-impl TryInto<cashu::Id> for Id {
-    type Error = cdk_common::error::Error;
+impl TryInto<crate::signatory::SignatoryKeysets> for SignatoryKeysets {
+    type Error = cdk_common::Error;
+
+    fn try_into(self) -> Result<crate::signatory::SignatoryKeysets, Self::Error> {
+        Ok(crate::signatory::SignatoryKeysets {
+            pubkey: PublicKey::from_slice(&self.pubkey)?,
+            keysets: self
+                .keysets
+                .into_iter()
+                .map(|keyset| keyset.try_into())
+                .collect::<Result<Vec<_>, _>>()?,
+        })
+    }
+}
 
-    fn try_into(self) -> Result<cashu::Id, Self::Error> {
-        Ok(cashu::Id::from_bytes(&self.inner)?)
+impl TryInto<crate::signatory::SignatoryKeySet> for KeySet {
+    type Error = cdk_common::Error;
+
+    fn try_into(self) -> Result<crate::signatory::SignatoryKeySet, Self::Error> {
+        Ok(crate::signatory::SignatoryKeySet {
+            id: self.id.parse()?,
+            unit: self
+                .unit
+                .ok_or(cdk_common::Error::Custom(INTERNAL_ERROR.to_owned()))?
+                .try_into()
+                .map_err(|_| cdk_common::Error::Custom("Invalid currency unit".to_owned()))?,
+            active: self.active,
+            input_fee_ppk: self.input_fee_ppk,
+            keys: cdk_common::Keys::new(
+                self.keys
+                    .ok_or(cdk_common::Error::Custom(INTERNAL_ERROR.to_owned()))?
+                    .keys
+                    .into_iter()
+                    .map(|(amount, pk)| PublicKey::from_slice(&pk).map(|pk| (amount.into(), pk)))
+                    .collect::<Result<BTreeMap<Amount, _>, _>>()?,
+            ),
+        })
+    }
+}
+
+impl From<crate::signatory::SignatoryKeySet> for KeySet {
+    fn from(keyset: crate::signatory::SignatoryKeySet) -> Self {
+        Self {
+            id: keyset.id.to_string(),
+            unit: Some(keyset.unit.into()),
+            active: keyset.active,
+            input_fee_ppk: keyset.input_fee_ppk,
+            keys: Some(Keys {
+                keys: keyset
+                    .keys
+                    .iter()
+                    .map(|(key, value)| ((*key).into(), value.to_bytes().to_vec()))
+                    .collect(),
+            }),
+        }
+    }
+}
+
+impl From<cdk_common::Error> for Error {
+    fn from(err: cdk_common::Error) -> Self {
+        let code = match err {
+            cdk_common::Error::AmountError(_) => ErrorCode::AmountOutsideLimit,
+            cdk_common::Error::DuplicateInputs => ErrorCode::DuplicateInputsProvided,
+            cdk_common::Error::DuplicateOutputs => ErrorCode::DuplicateInputsProvided,
+            cdk_common::Error::UnknownKeySet => ErrorCode::KeysetNotKnown,
+            cdk_common::Error::InactiveKeyset => ErrorCode::KeysetInactive,
+            _ => ErrorCode::Unknown,
+        };
+
+        Error {
+            code: code.into(),
+            detail: err.to_string(),
+        }
+    }
+}
+
+impl From<Error> for cdk_common::Error {
+    fn from(val: Error) -> Self {
+        match val.code.try_into().expect("valid code") {
+            ErrorCode::DuplicateInputsProvided => cdk_common::Error::DuplicateInputs,
+            ErrorCode::Unknown => cdk_common::Error::Custom(val.detail),
+            _ => todo!(),
+        }
     }
 }
 
@@ -43,36 +129,6 @@ impl TryInto<cdk_common::BlindSignatureDleq> for BlindSignatureDleq {
     }
 }
 
-impl From<crate::signatory::SignatoryKeySet> for SignatoryKeySet {
-    fn from(value: crate::signatory::SignatoryKeySet) -> Self {
-        SignatoryKeySet {
-            key: Some(value.key.into()),
-            info: Some(value.info.into()),
-        }
-    }
-}
-
-impl TryInto<crate::signatory::SignatoryKeySet> for SignatoryKeySet {
-    type Error = cdk_common::error::Error;
-
-    fn try_into(self) -> Result<crate::signatory::SignatoryKeySet, Self::Error> {
-        Ok(crate::signatory::SignatoryKeySet {
-            key: self
-                .key
-                .ok_or(cdk_common::Error::RecvError(
-                    "Missing property key".to_owned(),
-                ))?
-                .try_into()?,
-            info: self
-                .info
-                .ok_or(cdk_common::Error::RecvError(
-                    "Missing property info".to_owned(),
-                ))?
-                .try_into()?,
-        })
-    }
-}
-
 impl From<cdk_common::BlindSignature> for BlindSignature {
     fn from(value: cdk_common::BlindSignature) -> Self {
         BlindSignature {
@@ -84,15 +140,21 @@ impl From<cdk_common::BlindSignature> for BlindSignature {
     }
 }
 
+impl From<Vec<cdk_common::Proof>> for Proofs {
+    fn from(value: Vec<cdk_common::Proof>) -> Self {
+        Proofs {
+            proof: value.into_iter().map(|x| x.into()).collect(),
+        }
+    }
+}
+
 impl From<cdk_common::Proof> for Proof {
     fn from(value: cdk_common::Proof) -> Self {
         Proof {
             amount: value.amount.into(),
             keyset_id: value.keyset_id.to_string(),
-            secret: value.secret.to_string(),
+            secret: value.secret.to_bytes(),
             c: value.c.to_bytes().to_vec(),
-            witness: value.witness.map(|w| w.into()),
-            dleq: value.dleq.map(|dleq| dleq.into()),
         }
     }
 }
@@ -100,17 +162,23 @@ impl From<cdk_common::Proof> for Proof {
 impl TryInto<cdk_common::Proof> for Proof {
     type Error = Status;
     fn try_into(self) -> Result<cdk_common::Proof, Self::Error> {
+        let secret = if let Ok(str) = String::from_utf8(self.secret.clone()) {
+            str
+        } else {
+            hex::encode(&self.secret)
+        };
+
         Ok(cdk_common::Proof {
             amount: self.amount.into(),
             keyset_id: self
                 .keyset_id
                 .parse()
                 .map_err(|e| Status::from_error(Box::new(e)))?,
-            secret: Secret::from_str(&self.secret).map_err(|e| Status::from_error(Box::new(e)))?,
+            secret: Secret::new(secret),
             c: cdk_common::PublicKey::from_slice(&self.c)
                 .map_err(|e| Status::from_error(Box::new(e)))?,
-            witness: self.witness.map(|w| w.try_into()).transpose()?,
-            dleq: self.dleq.map(|x| x.try_into()).transpose()?,
+            witness: None,
+            dleq: None,
         })
     }
 }
@@ -159,7 +227,6 @@ impl From<cdk_common::BlindedMessage> for BlindedMessage {
             amount: value.amount.into(),
             keyset_id: value.keyset_id.to_string(),
             blinded_secret: value.blinded_secret.to_bytes().to_vec(),
-            witness: value.witness.map(|x| x.into()),
         }
     }
 }
@@ -175,7 +242,7 @@ impl TryInto<cdk_common::BlindedMessage> for BlindedMessage {
                 .map_err(|e| Status::from_error(Box::new(e)))?,
             blinded_secret: cdk_common::PublicKey::from_slice(&self.blinded_secret)
                 .map_err(|e| Status::from_error(Box::new(e)))?,
-            witness: self.witness.map(|x| x.try_into()).transpose()?,
+            witness: None,
         })
     }
 }
@@ -222,13 +289,13 @@ impl TryInto<cdk_common::Witness> for Witness {
     }
 }
 
-impl From<()> for Empty {
+impl From<()> for EmptyRequest {
     fn from(_: ()) -> Self {
-        Empty {}
+        EmptyRequest {}
     }
 }
 
-impl TryInto<()> for Empty {
+impl TryInto<()> for EmptyRequest {
     type Error = cdk_common::error::Error;
 
     fn try_into(self) -> Result<(), Self::Error> {
@@ -280,6 +347,7 @@ impl TryInto<cashu::CurrencyUnit> for CurrencyUnit {
                 CurrencyUnitType::Msat => Ok(cashu::CurrencyUnit::Msat),
                 CurrencyUnitType::Usd => Ok(cashu::CurrencyUnit::Usd),
                 CurrencyUnitType::Eur => Ok(cashu::CurrencyUnit::Eur),
+                CurrencyUnitType::Auth => Ok(cashu::CurrencyUnit::Auth),
             },
             Some(currency_unit::CurrencyUnit::CustomUnit(name)) => {
                 Ok(cashu::CurrencyUnit::Custom(name))
@@ -289,128 +357,22 @@ impl TryInto<cashu::CurrencyUnit> for CurrencyUnit {
     }
 }
 
-impl From<&bitcoin::bip32::ChildNumber> for derivation_path::ChildNumber {
-    fn from(value: &bitcoin::bip32::ChildNumber) -> Self {
-        match value {
-            bitcoin::bip32::ChildNumber::Normal { index } => {
-                derivation_path::ChildNumber::Normal(*index)
-            }
-            bitcoin::bip32::ChildNumber::Hardened { index } => {
-                derivation_path::ChildNumber::Hardened(*index)
-            }
-        }
-    }
-}
-
-impl TryInto<bitcoin::bip32::ChildNumber> for derivation_path::ChildNumber {
-    type Error = cdk_common::error::Error;
-
-    fn try_into(self) -> Result<bitcoin::bip32::ChildNumber, Self::Error> {
-        Ok(match self {
-            derivation_path::ChildNumber::Normal(index) => {
-                bitcoin::bip32::ChildNumber::Normal { index }
-            }
-            derivation_path::ChildNumber::Hardened(index) => {
-                bitcoin::bip32::ChildNumber::Hardened { index }
-            }
-        })
-    }
-}
-
-impl From<cdk_common::mint::MintKeySetInfo> for MintKeySetInfo {
-    fn from(value: cdk_common::mint::MintKeySetInfo) -> Self {
-        Self {
-            id: Some(value.id.into()),
-            unit: Some(value.unit.into()),
-            active: value.active,
-            valid_from: value.valid_from,
-            valid_to: value.valid_to,
-            derivation_path: value
-                .derivation_path
-                .into_iter()
-                .map(|x| DerivationPath {
-                    child_number: Some(x.into()),
-                })
-                .collect(),
-            derivation_path_index: value.derivation_path_index,
-            max_order: value.max_order.into(),
-            input_fee_ppk: value.input_fee_ppk,
-        }
-    }
-}
-
-impl TryInto<cdk_common::mint::MintKeySetInfo> for MintKeySetInfo {
-    type Error = cdk_common::error::Error;
-
-    fn try_into(self) -> Result<cdk_common::mint::MintKeySetInfo, Self::Error> {
-        Ok(cdk_common::mint::MintKeySetInfo {
-            id: self
-                .id
-                .ok_or(cdk_common::error::Error::Custom("id not set".to_owned()))?
-                .try_into()?,
-            unit: self
-                .unit
-                .ok_or(cdk_common::error::Error::Custom("unit not set".to_owned()))?
-                .try_into()
-                .map_err(|_| cdk_common::Error::Custom("Invalid unit encoding".to_owned()))?,
-            active: self.active,
-            valid_from: self.valid_from,
-            valid_to: self.valid_to,
-            max_order: self
-                .max_order
-                .try_into()
-                .map_err(|_| cdk_common::Error::Custom("Invalid max_order".to_owned()))?,
-            input_fee_ppk: self.input_fee_ppk,
-            derivation_path: self
-                .derivation_path
-                .into_iter()
-                .map(|derivation_path| {
-                    derivation_path
-                        .child_number
-                        .ok_or(cdk_common::error::Error::Custom(
-                            "child_number not set".to_owned(),
-                        ))?
-                        .try_into()
-                })
-                .collect::<Result<Vec<bitcoin::bip32::ChildNumber>, _>>()?
-                .into(),
-            derivation_path_index: self.derivation_path_index,
-        })
-    }
-}
-
-impl From<cashu::KeySet> for KeySet {
-    fn from(value: cashu::KeySet) -> Self {
-        Self {
-            id: Some(value.id.into()),
-            unit: Some(value.unit.into()),
-            keys: Some(Keys {
-                keys: value
-                    .keys
-                    .iter()
-                    .map(|(amount, pk)| (*(amount.as_ref()), pk.to_bytes().to_vec()))
-                    .collect(),
-            }),
-        }
-    }
-}
-
 impl TryInto<cashu::KeySet> for KeySet {
     type Error = cdk_common::error::Error;
     fn try_into(self) -> Result<cashu::KeySet, Self::Error> {
         Ok(cashu::KeySet {
             id: self
                 .id
-                .ok_or(cdk_common::error::Error::Custom("id not set".to_owned()))?
-                .try_into()?,
+                .parse()
+                .map_err(|_| cdk_common::error::Error::Custom("Invalid ID".to_owned()))?,
             unit: self
                 .unit
-                .ok_or(cdk_common::error::Error::Custom("unit not set".to_owned()))?
+                .ok_or(cdk_common::error::Error::Custom(INTERNAL_ERROR.to_owned()))?
                 .try_into()
                 .map_err(|_| cdk_common::Error::Custom("Invalid unit encoding".to_owned()))?,
             keys: cashu::Keys::new(
                 self.keys
-                    .ok_or(cdk_common::error::Error::Custom("keys not set".to_owned()))?
+                    .ok_or(cdk_common::error::Error::Custom(INTERNAL_ERROR.to_owned()))?
                     .keys
                     .into_iter()
                     .map(|(k, v)| cdk_common::PublicKey::from_slice(&v).map(|pk| (k.into(), pk)))
@@ -420,40 +382,17 @@ impl TryInto<cashu::KeySet> for KeySet {
     }
 }
 
-impl From<cashu::KeysResponse> for KeysResponse {
-    fn from(value: cashu::KeysResponse) -> Self {
-        Self {
-            keysets: value.keysets.into_iter().map(|x| x.into()).collect(),
-        }
-    }
-}
-
-impl TryInto<cashu::KeysResponse> for KeysResponse {
-    type Error = cdk_common::error::Error;
-
-    fn try_into(self) -> Result<cashu::KeysResponse, Self::Error> {
-        Ok(cashu::KeysResponse {
-            keysets: self
-                .keysets
-                .into_iter()
-                .map(|x| x.try_into())
-                .collect::<Result<Vec<cashu::KeySet>, _>>()?,
-        })
-    }
-}
-
-impl From<crate::signatory::RotateKeyArguments> for RotateKeyArguments {
+impl From<crate::signatory::RotateKeyArguments> for RotationRequest {
     fn from(value: crate::signatory::RotateKeyArguments) -> Self {
         Self {
             unit: Some(value.unit.into()),
-            derivation_path_index: value.derivation_path_index,
             max_order: value.max_order.into(),
             input_fee_ppk: value.input_fee_ppk,
         }
     }
 }
 
-impl TryInto<crate::signatory::RotateKeyArguments> for RotateKeyArguments {
+impl TryInto<crate::signatory::RotateKeyArguments> for RotationRequest {
     type Error = Status;
 
     fn try_into(self) -> Result<crate::signatory::RotateKeyArguments, Self::Error> {
@@ -462,7 +401,6 @@ impl TryInto<crate::signatory::RotateKeyArguments> for RotateKeyArguments {
                 .unit
                 .ok_or(Status::invalid_argument("unit not set"))?
                 .try_into()?,
-            derivation_path_index: self.derivation_path_index,
             max_order: self
                 .max_order
                 .try_into()
@@ -472,29 +410,27 @@ impl TryInto<crate::signatory::RotateKeyArguments> for RotateKeyArguments {
     }
 }
 
-impl From<cdk_common::KeySetInfo> for KeySetInfo {
+impl From<cdk_common::KeySetInfo> for KeySet {
     fn from(value: cdk_common::KeySetInfo) -> Self {
         Self {
-            id: Some(value.id.into()),
+            id: value.id.into(),
             unit: Some(value.unit.into()),
             active: value.active,
             input_fee_ppk: value.input_fee_ppk,
+            keys: Default::default(),
         }
     }
 }
 
-impl TryInto<cdk_common::KeySetInfo> for KeySetInfo {
+impl TryInto<cdk_common::KeySetInfo> for KeySet {
     type Error = cdk_common::Error;
 
     fn try_into(self) -> Result<cdk_common::KeySetInfo, Self::Error> {
         Ok(cdk_common::KeySetInfo {
-            id: self
-                .id
-                .ok_or(cdk_common::Error::Custom("id not set".to_owned()))?
-                .try_into()?,
+            id: self.id.try_into()?,
             unit: self
                 .unit
-                .ok_or(cdk_common::Error::Custom("unit not set".to_owned()))?
+                .ok_or(cdk_common::Error::Custom(INTERNAL_ERROR.to_owned()))?
                 .try_into()
                 .map_err(|_| cdk_common::Error::Custom("Invalid unit encoding".to_owned()))?,
             active: self.active,

+ 71 - 28
crates/cdk-signatory/src/proto/server.rs

@@ -3,6 +3,7 @@ use std::net::SocketAddr;
 use tonic::transport::{Error, Server};
 use tonic::{Request, Response, Status};
 
+use super::{boolean_response, key_rotation_response, keys_response, BooleanResponse};
 use crate::proto::{self, signatory_server};
 use crate::signatory::Signatory;
 
@@ -20,52 +21,94 @@ where
 {
     async fn blind_sign(
         &self,
-        request: Request<proto::BlindedMessage>,
-    ) -> Result<Response<proto::BlindSignature>, Status> {
-        let blind_signature = self
+        request: Request<proto::BlindedMessages>,
+    ) -> Result<Response<proto::BlindSignResponse>, Status> {
+        let result = match self
             .inner
-            .blind_sign(request.into_inner().try_into()?)
+            .blind_sign(
+                request
+                    .into_inner()
+                    .blinded_messages
+                    .into_iter()
+                    .map(|blind_message| blind_message.try_into())
+                    .collect::<Result<Vec<_>, _>>()?,
+            )
             .await
-            .map_err(|e| Status::from_error(Box::new(e)))?;
-        Ok(Response::new(blind_signature.into()))
+        {
+            Ok(blind_signatures) => {
+                proto::blind_sign_response::Result::Sigs(proto::BlindSignatures {
+                    blind_signatures: blind_signatures
+                        .into_iter()
+                        .map(|blind_sign| blind_sign.into())
+                        .collect(),
+                })
+            }
+            Err(err) => proto::blind_sign_response::Result::Error(err.into()),
+        };
+
+        //.map_err(|e| Status::from_error(Box::new(e)))?;
+        Ok(Response::new(proto::BlindSignResponse {
+            result: Some(result),
+        }))
     }
 
-    async fn verify_proof(
+    async fn verify_proofs(
         &self,
-        request: Request<proto::Proof>,
-    ) -> Result<Response<proto::Empty>, Status> {
-        self.inner
-            .verify_proof(request.into_inner().try_into()?)
+        request: Request<proto::Proofs>,
+    ) -> Result<Response<proto::BooleanResponse>, Status> {
+        let result = match self
+            .inner
+            .verify_proofs(
+                request
+                    .into_inner()
+                    .proof
+                    .into_iter()
+                    .map(|x| x.try_into())
+                    .collect::<Result<Vec<_>, _>>()?,
+            )
             .await
-            .map_err(|e| Status::from_error(Box::new(e)))?;
-        Ok(Response::new(proto::Empty {}))
+        {
+            Ok(()) => boolean_response::Result::Success(true),
+
+            Err(cdk_common::Error::DHKE(_)) => boolean_response::Result::Success(false),
+            Err(err) => boolean_response::Result::Error(err.into()),
+        };
+
+        Ok(Response::new(BooleanResponse {
+            result: Some(result),
+        }))
     }
 
     async fn keysets(
         &self,
-        _request: Request<proto::Empty>,
-    ) -> Result<Response<proto::VecSignatoryKeySet>, Status> {
-        let keys_response = self
-            .inner
-            .keysets()
-            .await
-            .map_err(|e| Status::from_error(Box::new(e)))?;
-        Ok(Response::new(proto::VecSignatoryKeySet {
-            keysets: keys_response.into_iter().map(|k| k.into()).collect(),
-            is_none: Some(false),
+        _request: Request<proto::EmptyRequest>,
+    ) -> Result<Response<proto::KeysResponse>, Status> {
+        let result = match self.inner.keysets().await {
+            Ok(result) => keys_response::Result::Keysets(result.into()),
+            Err(err) => keys_response::Result::Error(err.into()),
+        };
+
+        Ok(Response::new(proto::KeysResponse {
+            result: Some(result),
         }))
     }
 
     async fn rotate_keyset(
         &self,
-        request: Request<proto::RotateKeyArguments>,
-    ) -> Result<Response<proto::MintKeySetInfo>, Status> {
-        let mint_keyset_info = self
+        request: Request<proto::RotationRequest>,
+    ) -> Result<Response<proto::KeyRotationResponse>, Status> {
+        let mint_keyset_info = match self
             .inner
             .rotate_keyset(request.into_inner().try_into()?)
             .await
-            .map_err(|e| Status::from_error(Box::new(e)))?;
-        Ok(Response::new(mint_keyset_info.into()))
+        {
+            Ok(result) => key_rotation_response::Result::Keyset(result.into()),
+            Err(err) => key_rotation_response::Result::Error(err.into()),
+        };
+
+        Ok(Response::new(proto::KeyRotationResponse {
+            result: Some(mint_keyset_info),
+        }))
     }
 }
 

+ 70 - 12
crates/cdk-signatory/src/signatory.rs

@@ -6,7 +6,9 @@
 //! There is an in memory implementation, when the keys are stored in memory, in the same process,
 //! but it is isolated from the rest of the application, and they communicate through a channel with
 //! the defined API.
-use cashu::{BlindSignature, BlindedMessage, CurrencyUnit, Id, KeySet, MintKeySet, Proof};
+use cashu::{
+    BlindSignature, BlindedMessage, CurrencyUnit, Id, KeySet, Keys, MintKeySet, Proof, PublicKey,
+};
 use cdk_common::error::Error;
 use cdk_common::mint::MintKeySetInfo;
 
@@ -36,29 +38,82 @@ impl From<CurrencyUnit> for KeysetIdentifier {
 /// This struct is used to pass the arguments to the rotate_keyset function
 #[derive(Debug, Clone)]
 pub struct RotateKeyArguments {
+    /// Unit
     pub unit: CurrencyUnit,
-    pub derivation_path_index: Option<u32>,
+    /// Max order
     pub max_order: u8,
+    /// Input fee
     pub input_fee_ppk: u64,
 }
 
 #[derive(Debug, Clone)]
+/// Signatory keysets
+pub struct SignatoryKeysets {
+    /// The public key
+    pub pubkey: PublicKey,
+    /// The list of keysets
+    pub keysets: Vec<SignatoryKeySet>,
+}
+
+#[derive(Debug, Clone)]
 /// SignatoryKeySet
 ///
 /// This struct is used to represent a keyset and its info, pretty much all the information but the
 /// private key, that will never leave the signatory
 pub struct SignatoryKeySet {
-    /// KeySet
-    pub key: KeySet,
-    /// MintSetInfo
-    pub info: MintKeySetInfo,
+    pub id: Id,
+    pub unit: CurrencyUnit,
+    pub active: bool,
+    pub keys: Keys,
+    pub input_fee_ppk: u64,
+}
+
+impl From<&SignatoryKeySet> for KeySet {
+    fn from(val: &SignatoryKeySet) -> Self {
+        val.to_owned().into()
+    }
+}
+
+impl From<SignatoryKeySet> for KeySet {
+    fn from(val: SignatoryKeySet) -> Self {
+        KeySet {
+            id: val.id,
+            unit: val.unit,
+            keys: val.keys,
+        }
+    }
+}
+
+impl From<&SignatoryKeySet> for MintKeySetInfo {
+    fn from(val: &SignatoryKeySet) -> Self {
+        val.to_owned().into()
+    }
+}
+
+impl From<SignatoryKeySet> for MintKeySetInfo {
+    fn from(val: SignatoryKeySet) -> Self {
+        MintKeySetInfo {
+            id: val.id,
+            unit: val.unit,
+            active: val.active,
+            input_fee_ppk: val.input_fee_ppk,
+            derivation_path: Default::default(),
+            derivation_path_index: Default::default(),
+            max_order: 0,
+            valid_to: None,
+            valid_from: 0,
+        }
+    }
 }
 
 impl From<&(MintKeySetInfo, MintKeySet)> for SignatoryKeySet {
     fn from((info, key): &(MintKeySetInfo, MintKeySet)) -> Self {
         Self {
-            key: key.clone().into(),
-            info: info.clone(),
+            id: info.id,
+            unit: key.unit.clone(),
+            active: info.active,
+            input_fee_ppk: info.input_fee_ppk,
+            keys: key.keys.clone().into(),
         }
     }
 }
@@ -69,15 +124,18 @@ pub trait Signatory {
     /// Blind sign a message.
     ///
     /// The message can be for a coin or an auth token.
-    async fn blind_sign(&self, blinded_message: BlindedMessage) -> Result<BlindSignature, Error>;
+    async fn blind_sign(
+        &self,
+        blinded_messages: Vec<BlindedMessage>,
+    ) -> Result<Vec<BlindSignature>, Error>;
 
     /// Verify [`Proof`] meets conditions and is signed
-    async fn verify_proof(&self, proofs: Proof) -> Result<(), Error>;
+    async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error>;
 
     /// Retrieve the list of all mint keysets
-    async fn keysets(&self) -> Result<Vec<SignatoryKeySet>, Error>;
+    async fn keysets(&self) -> Result<SignatoryKeysets, Error>;
 
     /// Add current keyset to inactive keysets
     /// Generate new keyset
-    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<MintKeySetInfo, Error>;
+    async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error>;
 }

+ 6 - 2
crates/cdk/src/mint/auth/mod.rs

@@ -35,7 +35,7 @@ impl Mint {
     #[instrument(skip(self, token))]
     pub async fn verify_blind_auth(&self, token: &BlindAuthToken) -> Result<(), Error> {
         self.signatory
-            .verify_proof(token.auth_proof.clone().into())
+            .verify_proofs(vec![token.auth_proof.clone().into()])
             .await
     }
 
@@ -214,6 +214,10 @@ impl Mint {
         &self,
         blinded_message: &BlindedMessage,
     ) -> Result<BlindSignature, Error> {
-        self.signatory.blind_sign(blinded_message.to_owned()).await
+        self.signatory
+            .blind_sign(vec![blinded_message.to_owned()])
+            .await?
+            .pop()
+            .ok_or(Error::Internal)
     }
 }

+ 10 - 5
crates/cdk/src/mint/keysets/auth.rs

@@ -1,6 +1,6 @@
 //! Auth keyset functions
 
-use cdk_common::CurrencyUnit;
+use cdk_common::{CurrencyUnit, KeySetInfo};
 use tracing::instrument;
 
 use crate::mint::{KeysResponse, KeysetResponse};
@@ -15,12 +15,12 @@ impl Mint {
             .keysets
             .load()
             .iter()
-            .find(|key| key.info.unit == CurrencyUnit::Auth)
+            .find(|key| key.unit == CurrencyUnit::Auth)
             .ok_or(Error::NoActiveKeyset)?
             .clone();
 
         Ok(KeysResponse {
-            keysets: vec![key.key],
+            keysets: vec![key.into()],
         })
     }
 
@@ -33,8 +33,13 @@ impl Mint {
                 .load()
                 .iter()
                 .filter_map(|key| {
-                    if key.info.unit == CurrencyUnit::Auth {
-                        Some(key.info.clone().into())
+                    if key.unit == CurrencyUnit::Auth {
+                        Some(KeySetInfo {
+                            id: key.id,
+                            unit: key.unit.clone(),
+                            active: key.active,
+                            input_fee_ppk: key.input_fee_ppk,
+                        })
                     } else {
                         None
                     }

+ 13 - 39
crates/cdk/src/mint/keysets/mod.rs

@@ -17,10 +17,10 @@ impl Mint {
         self.keysets
             .load()
             .iter()
-            .find(|keyset| &keyset.key.id == keyset_id)
+            .find(|keyset| &keyset.id == keyset_id)
             .ok_or(Error::UnknownKeySet)
             .map(|key| KeysResponse {
-                keysets: vec![key.key.clone()],
+                keysets: vec![key.into()],
             })
     }
 
@@ -33,8 +33,8 @@ impl Mint {
                 .keysets
                 .load()
                 .iter()
-                .filter(|keyset| keyset.info.active && keyset.info.unit != CurrencyUnit::Auth)
-                .map(|key| key.key.clone())
+                .filter(|keyset| keyset.active && keyset.unit != CurrencyUnit::Auth)
+                .map(|key| key.into())
                 .collect::<Vec<_>>(),
         }
     }
@@ -47,12 +47,12 @@ impl Mint {
                 .keysets
                 .load()
                 .iter()
-                .filter(|k| k.key.unit != CurrencyUnit::Auth)
+                .filter(|k| k.unit != CurrencyUnit::Auth)
                 .map(|k| KeySetInfo {
-                    id: k.key.id,
-                    unit: k.key.unit.clone(),
-                    active: k.info.active,
-                    input_fee_ppk: k.info.input_fee_ppk,
+                    id: k.id,
+                    unit: k.unit.clone(),
+                    active: k.active,
+                    input_fee_ppk: k.input_fee_ppk,
                 })
                 .collect(),
         }
@@ -64,8 +64,8 @@ impl Mint {
         self.keysets
             .load()
             .iter()
-            .find(|key| &key.key.id == id)
-            .map(|x| x.key.clone())
+            .find(|key| &key.id == id)
+            .map(|x| x.into())
     }
 
     /// Add current keyset to inactive keysets
@@ -74,7 +74,6 @@ impl Mint {
     pub async fn rotate_keyset(
         &self,
         unit: CurrencyUnit,
-        derivation_path_index: u32,
         max_order: u8,
         input_fee_ppk: u64,
     ) -> Result<MintKeySetInfo, Error> {
@@ -82,39 +81,14 @@ impl Mint {
             .signatory
             .rotate_keyset(RotateKeyArguments {
                 unit,
-                derivation_path_index: Some(derivation_path_index),
                 max_order,
                 input_fee_ppk,
             })
             .await?;
 
         let new_keyset = self.signatory.keysets().await?;
-        self.keysets.store(new_keyset.into());
+        self.keysets.store(new_keyset.keysets.into());
 
-        Ok(result)
-    }
-
-    /// Rotate to next keyset for unit
-    #[instrument(skip(self))]
-    pub async fn rotate_next_keyset(
-        &self,
-        unit: CurrencyUnit,
-        max_order: u8,
-        input_fee_ppk: u64,
-    ) -> Result<MintKeySetInfo, Error> {
-        let result = self
-            .signatory
-            .rotate_keyset(RotateKeyArguments {
-                unit,
-                max_order,
-                derivation_path_index: None,
-                input_fee_ppk,
-            })
-            .await?;
-
-        let new_keyset = self.signatory.keysets().await?;
-        self.keysets.store(new_keyset.into());
-
-        Ok(result)
+        Ok(result.into())
     }
 }

+ 39 - 28
crates/cdk/src/mint/mod.rs

@@ -164,7 +164,7 @@ impl Mint {
             ln,
             #[cfg(feature = "auth")]
             auth_localstore,
-            keysets: Arc::new(ArcSwap::new(keysets.into())),
+            keysets: Arc::new(ArcSwap::new(keysets.keysets.into())),
         })
     }
 
@@ -319,8 +319,8 @@ impl Mint {
             .load()
             .iter()
             .filter_map(|keyset| {
-                if keyset.info.active {
-                    Some((keyset.info.unit.clone(), keyset.info.id))
+                if keyset.active {
+                    Some((keyset.unit.clone(), keyset.id))
                 } else {
                     None
                 }
@@ -334,8 +334,8 @@ impl Mint {
             .load()
             .iter()
             .filter_map(|keyset| {
-                if keyset.info.id == *id {
-                    Some(keyset.info.clone())
+                if keyset.id == *id {
+                    Some(keyset.into())
                 } else {
                     None
                 }
@@ -349,32 +349,43 @@ impl Mint {
         &self,
         blinded_message: &BlindedMessage,
     ) -> Result<BlindSignature, Error> {
-        self.signatory.blind_sign(blinded_message.to_owned()).await
+        self.signatory
+            .blind_sign(vec![blinded_message.to_owned()])
+            .await?
+            .pop()
+            .ok_or(Error::Internal)
     }
 
     /// Verify [`Proof`] meets conditions and is signed
     #[instrument(skip_all)]
-    pub async fn verify_proof(&self, proof: &Proof) -> Result<(), Error> {
-        // Check if secret is a nut10 secret with conditions
-        if let Ok(secret) =
-            <&secret::Secret as TryInto<nuts::nut10::Secret>>::try_into(&proof.secret)
-        {
-            // Checks and verifies known secret kinds.
-            // If it is an unknown secret kind it will be treated as a normal secret.
-            // Spending conditions will **not** be check. It is up to the wallet to ensure
-            // only supported secret kinds are used as there is no way for the mint to
-            // enforce only signing supported secrets as they are blinded at
-            // that point.
-            match secret.kind {
-                Kind::P2PK => {
-                    proof.verify_p2pk()?;
-                }
-                Kind::HTLC => {
-                    proof.verify_htlc()?;
+    pub async fn verify_proofs(&self, proofs: &[Proof]) -> Result<(), Error> {
+        proofs
+            .iter()
+            .map(|proof| {
+                // Check if secret is a nut10 secret with conditions
+                if let Ok(secret) =
+                    <&secret::Secret as TryInto<nuts::nut10::Secret>>::try_into(&proof.secret)
+                {
+                    // Checks and verifies known secret kinds.
+                    // If it is an unknown secret kind it will be treated as a normal secret.
+                    // Spending conditions will **not** be check. It is up to the wallet to ensure
+                    // only supported secret kinds are used as there is no way for the mint to
+                    // enforce only signing supported secrets as they are blinded at
+                    // that point.
+                    match secret.kind {
+                        Kind::P2PK => {
+                            proof.verify_p2pk()?;
+                        }
+                        Kind::HTLC => {
+                            proof.verify_htlc()?;
+                        }
+                    }
                 }
-            }
-        }
-        self.signatory.verify_proof(proof.to_owned()).await
+                Ok(())
+            })
+            .collect::<Result<Vec<()>, Error>>()?;
+
+        self.signatory.verify_proofs(proofs.to_owned()).await
     }
 
     /// Verify melt request is valid
@@ -642,7 +653,7 @@ mod tests {
         assert!(keysets.keysets.is_empty());
 
         // generate the first keyset and set it to active
-        mint.rotate_keyset(CurrencyUnit::default(), 0, 1, 1)
+        mint.rotate_keyset(CurrencyUnit::default(), 1, 1)
             .await
             .expect("test");
 
@@ -652,7 +663,7 @@ mod tests {
         let first_keyset_id = keysets.keysets[0].id;
 
         // set the first keyset to inactive and generate a new keyset
-        mint.rotate_keyset(CurrencyUnit::default(), 1, 1, 1)
+        mint.rotate_keyset(CurrencyUnit::default(), 1, 1)
             .await
             .expect("test");
 

+ 1 - 3
crates/cdk/src/mint/verification.rs

@@ -203,9 +203,7 @@ impl Mint {
         let unit = self.verify_inputs_keyset(inputs).await?;
         let amount = inputs.total_amount()?;
 
-        for proof in inputs {
-            self.verify_proof(proof).await?;
-        }
+        self.verify_proofs(inputs).await?;
 
         Ok(Verification {
             amount,