|
@@ -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);
|