diff --git a/bindings/cdk-js/src/wallet.rs b/bindings/cdk-js/src/wallet.rs index ccfcb33d..6394f4b1 100644 --- a/bindings/cdk-js/src/wallet.rs +++ b/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 for JsWallet { #[wasm_bindgen(js_class = Wallet)] impl JsWallet { #[wasm_bindgen(constructor)] - pub async fn new(seed: Vec) -> Self { + pub async fn new(seed: Vec, p2pk_signing_keys: Vec) -> 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 { - let signing_keys: Option> = serde_wasm_bindgen::from_value(signing_keys)?; + pub async fn receive(&mut self, encoded_token: String, preimages: JsValue) -> Result { let preimages: Option> = 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()) diff --git a/crates/cdk/src/wallet.rs b/crates/cdk/src/wallet.rs index fa46e01e..9e25e8b1 100644 --- a/crates/cdk/src/wallet.rs +++ b/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 + Send + Sync>, xpriv: ExtendedPrivKey, + p2pk_signing_keys: Arc>>, #[cfg(feature = "nostr")] nostr_client: nostr_sdk::Client, } @@ -115,6 +119,7 @@ impl Wallet { pub fn new( localstore: Arc + Send + Sync>, seed: &[u8], + p2pk_signing_keys: Vec, ) -> 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 { + 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>, preimages: Option>, ) -> Result { 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 = 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);