Procházet zdrojové kódy

refactor: signing keys as part of wallet

thesimplekid před 9 měsíci
rodič
revize
6103bb1d87
2 změnil soubory, kde provedl 57 přidání a 39 odebrání
  1. 14 16
      bindings/cdk-js/src/wallet.rs
  2. 43 23
      crates/cdk/src/wallet.rs

+ 14 - 16
bindings/cdk-js/src/wallet.rs

@@ -3,7 +3,7 @@ use std::str::FromStr;
 use std::sync::Arc;
 
 use cdk::amount::SplitTarget;
-use cdk::nuts::{Proofs, SecretKey};
+use cdk::nuts::Proofs;
 use cdk::url::UncheckedUrl;
 use cdk::wallet::Wallet;
 use cdk::Amount;
@@ -11,6 +11,7 @@ use cdk_rexie::RexieWalletDatabase;
 use wasm_bindgen::prelude::*;
 
 use crate::error::{into_err, Result};
+use crate::nuts::nut01::JsSecretKey;
 use crate::nuts::nut04::JsMintQuoteBolt11Response;
 use crate::nuts::nut05::JsMeltQuoteBolt11Response;
 use crate::nuts::nut11::JsP2PKSpendingConditions;
@@ -40,10 +41,18 @@ impl From<Wallet> for JsWallet {
 #[wasm_bindgen(js_class = Wallet)]
 impl JsWallet {
     #[wasm_bindgen(constructor)]
-    pub async fn new(seed: Vec<u8>) -> Self {
+    pub async fn new(seed: Vec<u8>, p2pk_signing_keys: Vec<JsSecretKey>) -> Self {
         let db = RexieWalletDatabase::new().await.unwrap();
 
-        Wallet::new(Arc::new(db), &seed).into()
+        Wallet::new(
+            Arc::new(db),
+            &seed,
+            p2pk_signing_keys
+                .into_iter()
+                .map(|s| s.deref().clone())
+                .collect(),
+        )
+        .into()
     }
 
     #[wasm_bindgen(js_name = unitBalance)]
@@ -253,23 +262,12 @@ impl JsWallet {
     }
 
     #[wasm_bindgen(js_name = receive)]
-    pub async fn receive(
-        &mut self,
-        encoded_token: String,
-        signing_keys: JsValue,
-        preimages: JsValue,
-    ) -> Result<JsAmount> {
-        let signing_keys: Option<Vec<SecretKey>> = serde_wasm_bindgen::from_value(signing_keys)?;
+    pub async fn receive(&mut self, encoded_token: String, preimages: JsValue) -> Result<JsAmount> {
         let preimages: Option<Vec<String>> = serde_wasm_bindgen::from_value(preimages)?;
 
         Ok(self
             .inner
-            .receive(
-                &encoded_token,
-                &SplitTarget::default(),
-                signing_keys,
-                preimages,
-            )
+            .receive(&encoded_token, &SplitTarget::default(), preimages)
             .await
             .map_err(into_err)?
             .into())

+ 43 - 23
crates/cdk/src/wallet.rs

@@ -2,18 +2,21 @@
 
 use std::collections::{HashMap, HashSet};
 use std::num::ParseIntError;
+use std::ops::Deref;
 use std::str::FromStr;
 use std::sync::Arc;
 
 use bitcoin::bip32::ExtendedPrivKey;
 use bitcoin::hashes::sha256::Hash as Sha256Hash;
 use bitcoin::hashes::Hash;
+use bitcoin::secp256k1::XOnlyPublicKey;
 use bitcoin::Network;
 #[cfg(feature = "nostr")]
 use nostr_sdk::nips::nip04;
 #[cfg(feature = "nostr")]
 use nostr_sdk::{Filter, Timestamp};
 use thiserror::Error;
+use tokio::sync::RwLock;
 use tracing::instrument;
 
 use crate::amount::SplitTarget;
@@ -107,6 +110,7 @@ pub struct Wallet {
     pub client: HttpClient,
     pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
     xpriv: ExtendedPrivKey,
+    p2pk_signing_keys: Arc<RwLock<HashMap<XOnlyPublicKey, SecretKey>>>,
     #[cfg(feature = "nostr")]
     nostr_client: nostr_sdk::Client,
 }
@@ -115,6 +119,7 @@ impl Wallet {
     pub fn new(
         localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
         seed: &[u8],
+        p2pk_signing_keys: Vec<SecretKey>,
     ) -> Self {
         let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, seed)
             .expect("Could not create master key");
@@ -123,11 +128,38 @@ impl Wallet {
             client: HttpClient::new(),
             localstore,
             xpriv,
+            p2pk_signing_keys: Arc::new(RwLock::new(
+                p2pk_signing_keys
+                    .into_iter()
+                    .map(|s| (s.public_key().x_only_public_key(), s))
+                    .collect(),
+            )),
             #[cfg(feature = "nostr")]
             nostr_client: nostr_sdk::Client::default(),
         }
     }
 
+    /// Add P2PK signing key to wallet
+    #[instrument(skip_all)]
+    pub async fn add_p2pk_signing_key(&self, signing_key: SecretKey) {
+        self.p2pk_signing_keys
+            .write()
+            .await
+            .insert(signing_key.public_key().x_only_public_key(), signing_key);
+    }
+
+    /// Remove P2PK signing key from wallet
+    #[instrument(skip_all)]
+    pub async fn remove_p2pk_signing_key(&self, x_only_pubkey: &XOnlyPublicKey) {
+        self.p2pk_signing_keys.write().await.remove(x_only_pubkey);
+    }
+
+    /// P2PK keys available in wallet
+    #[instrument(skip(self))]
+    pub async fn available_p2pk_signing_keys(&self) -> HashMap<XOnlyPublicKey, SecretKey> {
+        self.p2pk_signing_keys.read().await.deref().clone()
+    }
+
     /// Add nostr relays to client
     #[cfg(feature = "nostr")]
     #[instrument(skip(self))]
@@ -1299,7 +1331,6 @@ impl Wallet {
         &self,
         encoded_token: &str,
         amount_split_target: &SplitTarget,
-        signing_keys: Option<Vec<SecretKey>>,
         preimages: Option<Vec<String>>,
     ) -> Result<Amount, Error> {
         let token_data = Token::from_str(encoded_token)?;
@@ -1333,14 +1364,6 @@ impl Wallet {
 
             let mut sig_flag = SigFlag::SigInputs;
 
-            let pubkey_secret_key = match &signing_keys {
-                Some(signing_keys) => signing_keys
-                    .iter()
-                    .map(|s| (s.public_key().x_only_public_key(), s))
-                    .collect(),
-                None => HashMap::new(),
-            };
-
             // Map hash of preimage to preimage
             let hashed_to_preimage = match preimages {
                 Some(ref preimages) => preimages
@@ -1353,6 +1376,8 @@ impl Wallet {
                 None => HashMap::new(),
             };
 
+            let p2pk_signing_keys = self.p2pk_signing_keys.read().await;
+
             for proof in &mut proofs {
                 // Verify that proof DLEQ is valid
                 if proof.dleq.is_some() {
@@ -1386,7 +1411,7 @@ impl Wallet {
                         }
                         for pubkey in pubkeys {
                             if let Some(signing) =
-                                pubkey_secret_key.get(&pubkey.x_only_public_key())
+                                p2pk_signing_keys.get(&pubkey.x_only_public_key())
                             {
                                 proof.sign_p2pk(signing.to_owned().clone())?;
                             }
@@ -1412,7 +1437,7 @@ impl Wallet {
 
             if sig_flag.eq(&SigFlag::SigAll) {
                 for blinded_message in &mut pre_swap.swap_request.outputs {
-                    for signing_key in pubkey_secret_key.values() {
+                    for signing_key in p2pk_signing_keys.values() {
                         blinded_message.sign_p2pk(signing_key.to_owned().clone())?
                     }
                 }
@@ -1463,8 +1488,12 @@ impl Wallet {
 
         let verifying_key = nostr_signing_key.public_key();
 
-        let nostr_pubkey =
-            nostr_sdk::PublicKey::from_hex(verifying_key.x_only_public_key().to_string())?;
+        let x_only_pubkey = verifying_key.x_only_public_key();
+
+        let nostr_pubkey = nostr_sdk::PublicKey::from_hex(x_only_pubkey.to_string())?;
+
+        let keys = Keys::from_str(&(nostr_signing_key).to_secret_hex())?;
+        self.add_p2pk_signing_key(nostr_signing_key).await;
 
         let filter = match self
             .localstore
@@ -1484,7 +1513,6 @@ impl Wallet {
 
         let events = self.nostr_client.get_events_of(vec![filter], None).await?;
 
-        let keys = Keys::from_str(&nostr_signing_key.to_secret_hex()).unwrap();
         let mut tokens: HashSet<String> = HashSet::new();
 
         for event in events {
@@ -1503,15 +1531,7 @@ impl Wallet {
 
         let mut total_received = Amount::ZERO;
         for token in tokens.iter() {
-            match self
-                .receive(
-                    token,
-                    &amount_split_target,
-                    Some(vec![nostr_signing_key.clone()]),
-                    None,
-                )
-                .await
-            {
+            match self.receive(token, &amount_split_target, None).await {
                 Ok(amount) => total_received += amount,
                 Err(err) => {
                     tracing::error!("Could not receive token: {}", err);