lib.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. use std::collections::{HashMap, HashSet};
  2. use std::str::FromStr;
  3. use std::sync::Arc;
  4. use anyhow::{bail, Result};
  5. use axum::Router;
  6. use bip39::Mnemonic;
  7. use cdk::amount::{Amount, SplitTarget};
  8. use cdk::cdk_database::mint_memory::MintMemoryDatabase;
  9. use cdk::cdk_lightning::MintLightning;
  10. use cdk::dhke::construct_proofs;
  11. use cdk::mint::FeeReserve;
  12. use cdk::mint_url::MintUrl;
  13. use cdk::nuts::nut17::Params;
  14. use cdk::nuts::{
  15. CurrencyUnit, Id, KeySet, MintBolt11Request, MintInfo, MintQuoteBolt11Request, MintQuoteState,
  16. NotificationPayload, Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
  17. };
  18. use cdk::types::{LnKey, QuoteTTL};
  19. use cdk::wallet::client::{HttpClient, MintConnector};
  20. use cdk::wallet::subscription::SubscriptionManager;
  21. use cdk::wallet::WalletSubscription;
  22. use cdk::{Mint, Wallet};
  23. use cdk_fake_wallet::FakeWallet;
  24. use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
  25. use tokio::sync::Notify;
  26. use tower_http::cors::CorsLayer;
  27. pub mod init_fake_wallet;
  28. pub mod init_regtest;
  29. pub fn create_backends_fake_wallet(
  30. ) -> HashMap<LnKey, Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>> {
  31. let fee_reserve = FeeReserve {
  32. min_fee_reserve: 1.into(),
  33. percent_fee_reserve: 1.0,
  34. };
  35. let mut ln_backends: HashMap<
  36. LnKey,
  37. Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>,
  38. > = HashMap::new();
  39. let ln_key = LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11);
  40. let wallet = Arc::new(FakeWallet::new(
  41. fee_reserve.clone(),
  42. HashMap::default(),
  43. HashSet::default(),
  44. 0,
  45. ));
  46. ln_backends.insert(ln_key, wallet.clone());
  47. ln_backends
  48. }
  49. pub async fn start_mint(
  50. ln_backends: HashMap<
  51. LnKey,
  52. Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>,
  53. >,
  54. supported_units: HashMap<CurrencyUnit, (u64, u8)>,
  55. ) -> Result<()> {
  56. let nuts = Nuts::new()
  57. .nut07(true)
  58. .nut08(true)
  59. .nut09(true)
  60. .nut10(true)
  61. .nut11(true)
  62. .nut12(true)
  63. .nut14(true);
  64. let mint_info = MintInfo::new().nuts(nuts);
  65. let mnemonic = Mnemonic::generate(12)?;
  66. let quote_ttl = QuoteTTL::new(10000, 10000);
  67. let mint = Mint::new(
  68. &get_mint_url(),
  69. &mnemonic.to_seed_normalized(""),
  70. mint_info,
  71. quote_ttl,
  72. Arc::new(MintMemoryDatabase::default()),
  73. ln_backends.clone(),
  74. supported_units,
  75. HashMap::new(),
  76. )
  77. .await?;
  78. let mint_arc = Arc::new(mint);
  79. let v1_service = cdk_axum::create_mint_router(Arc::clone(&mint_arc)).await?;
  80. let mint_service = Router::new()
  81. .merge(v1_service)
  82. .layer(CorsLayer::permissive());
  83. let mint = Arc::clone(&mint_arc);
  84. let shutdown = Arc::new(Notify::new());
  85. tokio::spawn({
  86. let shutdown = Arc::clone(&shutdown);
  87. async move { mint.wait_for_paid_invoices(shutdown).await }
  88. });
  89. axum::Server::bind(
  90. &format!("{}:{}", get_mint_addr(), get_mint_port())
  91. .as_str()
  92. .parse()?,
  93. )
  94. .serve(mint_service.into_make_service())
  95. .await?;
  96. Ok(())
  97. }
  98. pub async fn wallet_mint(
  99. wallet: Arc<Wallet>,
  100. amount: Amount,
  101. split_target: SplitTarget,
  102. description: Option<String>,
  103. ) -> Result<()> {
  104. let quote = wallet.mint_quote(amount, description).await?;
  105. let mut subscription = wallet
  106. .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![quote
  107. .id
  108. .clone()]))
  109. .await;
  110. while let Some(msg) = subscription.recv().await {
  111. if let NotificationPayload::MintQuoteBolt11Response(response) = msg {
  112. if response.state == MintQuoteState::Paid {
  113. break;
  114. }
  115. }
  116. }
  117. let receive_amount = wallet.mint(&quote.id, split_target, None).await?;
  118. println!("Minted: {}", receive_amount);
  119. Ok(())
  120. }
  121. pub async fn mint_proofs(
  122. mint_url: &str,
  123. amount: Amount,
  124. keyset_id: Id,
  125. mint_keys: &KeySet,
  126. description: Option<String>,
  127. ) -> anyhow::Result<Proofs> {
  128. println!("Minting for ecash");
  129. println!();
  130. let wallet_client = HttpClient::new(MintUrl::from_str(mint_url)?);
  131. let request = MintQuoteBolt11Request {
  132. amount,
  133. unit: CurrencyUnit::Sat,
  134. description,
  135. };
  136. let mint_quote = wallet_client.post_mint_quote(request).await?;
  137. println!("Please pay: {}", mint_quote.request);
  138. let subscription_client = SubscriptionManager::new(Arc::new(wallet_client.clone()));
  139. let mut subscription = subscription_client
  140. .subscribe(
  141. mint_url.parse()?,
  142. Params {
  143. filters: vec![mint_quote.quote.clone()],
  144. kind: cdk::nuts::nut17::Kind::Bolt11MintQuote,
  145. id: "sub".into(),
  146. },
  147. )
  148. .await;
  149. while let Some(msg) = subscription.recv().await {
  150. if let NotificationPayload::MintQuoteBolt11Response(response) = msg {
  151. if response.state == MintQuoteState::Paid {
  152. break;
  153. }
  154. }
  155. }
  156. let premint_secrets = PreMintSecrets::random(keyset_id, amount, &SplitTarget::default())?;
  157. let request = MintBolt11Request {
  158. quote: mint_quote.quote,
  159. outputs: premint_secrets.blinded_messages(),
  160. };
  161. let mint_response = wallet_client.post_mint(request).await?;
  162. let pre_swap_proofs = construct_proofs(
  163. mint_response.signatures,
  164. premint_secrets.rs(),
  165. premint_secrets.secrets(),
  166. &mint_keys.clone().keys,
  167. )?;
  168. Ok(pre_swap_proofs)
  169. }
  170. // Get all pending from wallet and attempt to swap
  171. // Will panic if there are no pending
  172. // Will return Ok if swap fails as expected
  173. pub async fn attempt_to_swap_pending(wallet: &Wallet) -> Result<()> {
  174. let pending = wallet
  175. .localstore
  176. .get_proofs(None, None, Some(vec![State::Pending]), None)
  177. .await?;
  178. assert!(!pending.is_empty());
  179. let swap = wallet
  180. .swap(
  181. None,
  182. SplitTarget::None,
  183. pending.into_iter().map(|p| p.proof).collect(),
  184. None,
  185. false,
  186. )
  187. .await;
  188. match swap {
  189. Ok(_swap) => {
  190. bail!("These proofs should be pending")
  191. }
  192. Err(err) => match err {
  193. cdk::error::Error::TokenPending => (),
  194. _ => {
  195. println!("{:?}", err);
  196. bail!("Wrong error")
  197. }
  198. },
  199. }
  200. Ok(())
  201. }