receive.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. use std::collections::HashSet;
  2. use std::str::FromStr;
  3. use std::sync::Arc;
  4. use anyhow::{anyhow, Result};
  5. use cdk::cdk_database::{self, WalletDatabase};
  6. use cdk::nuts::{SecretKey, Token};
  7. use cdk::util::unix_time;
  8. use cdk::wallet::multi_mint_wallet::MultiMintWallet;
  9. use cdk::wallet::types::WalletKey;
  10. use cdk::wallet::Wallet;
  11. use cdk::Amount;
  12. use clap::Args;
  13. use nostr_sdk::nips::nip04;
  14. use nostr_sdk::{Filter, Keys, Kind, Timestamp};
  15. #[derive(Args)]
  16. pub struct ReceiveSubCommand {
  17. /// Cashu Token
  18. token: Option<String>,
  19. /// Signing Key
  20. #[arg(short, long, action = clap::ArgAction::Append)]
  21. signing_key: Vec<String>,
  22. /// Nostr key
  23. #[arg(short, long)]
  24. nostr_key: Option<String>,
  25. /// Nostr relay
  26. #[arg(short, long, action = clap::ArgAction::Append)]
  27. relay: Vec<String>,
  28. /// Unix time to to query nostr from
  29. #[arg(long)]
  30. since: Option<u64>,
  31. /// Preimage
  32. #[arg(short, long, action = clap::ArgAction::Append)]
  33. preimage: Vec<String>,
  34. }
  35. pub async fn receive(
  36. multi_mint_wallet: &MultiMintWallet,
  37. localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
  38. seed: &[u8],
  39. sub_command_args: &ReceiveSubCommand,
  40. ) -> Result<()> {
  41. let mut signing_keys = Vec::new();
  42. if !sub_command_args.signing_key.is_empty() {
  43. let mut s_keys: Vec<SecretKey> = sub_command_args
  44. .signing_key
  45. .iter()
  46. .map(|s| {
  47. if s.starts_with("nsec") {
  48. let nostr_key = nostr_sdk::SecretKey::from_str(s).expect("Invalid secret key");
  49. SecretKey::from_str(&nostr_key.to_secret_hex())
  50. } else {
  51. SecretKey::from_str(s)
  52. }
  53. })
  54. .collect::<Result<Vec<SecretKey>, _>>()?;
  55. signing_keys.append(&mut s_keys);
  56. }
  57. let amount = match &sub_command_args.token {
  58. Some(token_str) => {
  59. receive_token(
  60. multi_mint_wallet,
  61. localstore,
  62. seed,
  63. token_str,
  64. &signing_keys,
  65. &sub_command_args.preimage,
  66. )
  67. .await?
  68. }
  69. None => {
  70. //wallet.add_p2pk_signing_key(nostr_signing_key).await;
  71. let nostr_key = match sub_command_args.nostr_key.as_ref() {
  72. Some(nostr_key) => {
  73. let secret_key = nostr_sdk::SecretKey::from_str(nostr_key)?;
  74. let secret_key = SecretKey::from_str(&secret_key.to_secret_hex())?;
  75. Some(secret_key)
  76. }
  77. None => None,
  78. };
  79. let nostr_key =
  80. nostr_key.ok_or(anyhow!("Nostr key required if token is not provided"))?;
  81. signing_keys.push(nostr_key.clone());
  82. let relays = sub_command_args.relay.clone();
  83. let since = localstore
  84. .get_nostr_last_checked(&nostr_key.public_key())
  85. .await?;
  86. let tokens = nostr_receive(relays, nostr_key.clone(), since).await?;
  87. let mut total_amount = Amount::ZERO;
  88. for token_str in &tokens {
  89. match receive_token(
  90. multi_mint_wallet,
  91. localstore.clone(),
  92. seed,
  93. token_str,
  94. &signing_keys,
  95. &sub_command_args.preimage,
  96. )
  97. .await
  98. {
  99. Ok(amount) => {
  100. total_amount += amount;
  101. }
  102. Err(err) => {
  103. println!("{}", err);
  104. }
  105. }
  106. }
  107. localstore
  108. .add_nostr_last_checked(nostr_key.public_key(), unix_time() as u32)
  109. .await?;
  110. total_amount
  111. }
  112. };
  113. println!("Received: {}", amount);
  114. Ok(())
  115. }
  116. async fn receive_token(
  117. multi_mint_wallet: &MultiMintWallet,
  118. localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
  119. seed: &[u8],
  120. token_str: &str,
  121. signing_keys: &[SecretKey],
  122. preimage: &[String],
  123. ) -> Result<Amount> {
  124. let token: Token = Token::from_str(token_str)?;
  125. let mint_url = token.mint_url()?;
  126. let wallet_key = WalletKey::new(mint_url.clone(), token.unit().unwrap_or_default());
  127. if multi_mint_wallet.get_wallet(&wallet_key).await.is_none() {
  128. let wallet = Wallet::new(
  129. &mint_url.to_string(),
  130. token.unit().unwrap_or_default(),
  131. localstore,
  132. seed,
  133. None,
  134. )?;
  135. multi_mint_wallet.add_wallet(wallet).await;
  136. }
  137. let amount = multi_mint_wallet
  138. .receive(token_str, signing_keys, preimage)
  139. .await?;
  140. Ok(amount)
  141. }
  142. /// Receive tokens sent to nostr pubkey via dm
  143. async fn nostr_receive(
  144. relays: Vec<String>,
  145. nostr_signing_key: SecretKey,
  146. since: Option<u32>,
  147. ) -> Result<HashSet<String>> {
  148. let verifying_key = nostr_signing_key.public_key();
  149. let x_only_pubkey = verifying_key.x_only_public_key();
  150. let nostr_pubkey = nostr_sdk::PublicKey::from_hex(x_only_pubkey.to_string())?;
  151. let since = since.map(|s| Timestamp::from(s as u64));
  152. let filter = match since {
  153. Some(since) => Filter::new()
  154. .pubkey(nostr_pubkey)
  155. .kind(Kind::EncryptedDirectMessage)
  156. .since(since),
  157. None => Filter::new()
  158. .pubkey(nostr_pubkey)
  159. .kind(Kind::EncryptedDirectMessage),
  160. };
  161. let client = nostr_sdk::Client::default();
  162. client.connect().await;
  163. let events = client
  164. .get_events_of(
  165. vec![filter],
  166. nostr_sdk::EventSource::Relays {
  167. timeout: None,
  168. specific_relays: Some(relays),
  169. },
  170. )
  171. .await?;
  172. let mut tokens: HashSet<String> = HashSet::new();
  173. let keys = Keys::from_str(&(nostr_signing_key).to_secret_hex())?;
  174. for event in events {
  175. if event.kind == Kind::EncryptedDirectMessage {
  176. if let Ok(msg) = nip04::decrypt(keys.secret_key(), &event.pubkey, event.content) {
  177. if let Some(token) = cdk::wallet::util::token_from_text(&msg) {
  178. tokens.insert(token.to_string());
  179. }
  180. } else {
  181. tracing::error!("Impossible to decrypt direct message");
  182. }
  183. }
  184. }
  185. Ok(tokens)
  186. }