lib.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. use std::collections::HashMap;
  2. use std::sync::Arc;
  3. use std::time::Duration;
  4. use anyhow::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::nuts::{
  13. CurrencyUnit, Id, KeySet, MeltMethodSettings, MintInfo, MintMethodSettings, MintQuoteState,
  14. Nuts, PaymentMethod, PreMintSecrets, Proofs,
  15. };
  16. use cdk::wallet::client::HttpClient;
  17. use cdk::{Mint, Wallet};
  18. use cdk_axum::LnKey;
  19. use cdk_fake_wallet::FakeWallet;
  20. use futures::StreamExt;
  21. use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
  22. use tokio::time::sleep;
  23. use tower_http::cors::CorsLayer;
  24. pub mod init_regtest;
  25. pub fn create_backends_fake_wallet(
  26. ) -> HashMap<LnKey, Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>> {
  27. let fee_reserve = FeeReserve {
  28. min_fee_reserve: 1.into(),
  29. percent_fee_reserve: 1.0,
  30. };
  31. let mut ln_backends: HashMap<
  32. LnKey,
  33. Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>,
  34. > = HashMap::new();
  35. let ln_key = LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11);
  36. let wallet = Arc::new(FakeWallet::new(
  37. fee_reserve.clone(),
  38. MintMethodSettings::default(),
  39. MeltMethodSettings::default(),
  40. ));
  41. ln_backends.insert(ln_key, wallet.clone());
  42. ln_backends
  43. }
  44. pub async fn start_mint(
  45. ln_backends: HashMap<
  46. LnKey,
  47. Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>,
  48. >,
  49. supported_units: HashMap<CurrencyUnit, (u64, u8)>,
  50. ) -> Result<()> {
  51. let nuts = Nuts::new()
  52. .nut07(true)
  53. .nut08(true)
  54. .nut09(true)
  55. .nut10(true)
  56. .nut11(true)
  57. .nut12(true)
  58. .nut14(true);
  59. let mint_info = MintInfo::new().nuts(nuts);
  60. let mnemonic = Mnemonic::generate(12)?;
  61. let mint = Mint::new(
  62. &get_mint_url(),
  63. &mnemonic.to_seed_normalized(""),
  64. mint_info,
  65. Arc::new(MintMemoryDatabase::default()),
  66. supported_units,
  67. )
  68. .await?;
  69. let quote_ttl = 100000;
  70. let mint_arc = Arc::new(mint);
  71. let v1_service = cdk_axum::create_mint_router(
  72. &get_mint_url(),
  73. Arc::clone(&mint_arc),
  74. ln_backends.clone(),
  75. quote_ttl,
  76. )
  77. .await?;
  78. let mint_service = Router::new()
  79. .merge(v1_service)
  80. .layer(CorsLayer::permissive());
  81. let mint = Arc::clone(&mint_arc);
  82. for wallet in ln_backends.values() {
  83. let wallet_clone = Arc::clone(wallet);
  84. let mint = Arc::clone(&mint);
  85. tokio::spawn(async move {
  86. match wallet_clone.wait_any_invoice().await {
  87. Ok(mut stream) => {
  88. while let Some(request_lookup_id) = stream.next().await {
  89. if let Err(err) =
  90. handle_paid_invoice(Arc::clone(&mint), &request_lookup_id).await
  91. {
  92. // nosemgrep: direct-panic
  93. panic!("{:?}", err);
  94. }
  95. }
  96. }
  97. Err(err) => {
  98. // nosemgrep: direct-panic
  99. panic!("Could not get invoice stream: {}", err);
  100. }
  101. }
  102. });
  103. }
  104. axum::Server::bind(
  105. &format!("{}:{}", get_mint_addr(), get_mint_port())
  106. .as_str()
  107. .parse()?,
  108. )
  109. .serve(mint_service.into_make_service())
  110. .await?;
  111. Ok(())
  112. }
  113. /// Update mint quote when called for a paid invoice
  114. async fn handle_paid_invoice(mint: Arc<Mint>, request_lookup_id: &str) -> Result<()> {
  115. println!("Invoice with lookup id paid: {}", request_lookup_id);
  116. if let Ok(Some(mint_quote)) = mint
  117. .localstore
  118. .get_mint_quote_by_request_lookup_id(request_lookup_id)
  119. .await
  120. {
  121. println!(
  122. "Quote {} paid by lookup id {}",
  123. mint_quote.id, request_lookup_id
  124. );
  125. mint.localstore
  126. .update_mint_quote_state(&mint_quote.id, cdk::nuts::MintQuoteState::Paid)
  127. .await?;
  128. }
  129. Ok(())
  130. }
  131. pub async fn wallet_mint(
  132. wallet: Arc<Wallet>,
  133. amount: Amount,
  134. split_target: SplitTarget,
  135. description: Option<String>,
  136. ) -> Result<()> {
  137. let quote = wallet.mint_quote(amount, description).await?;
  138. loop {
  139. let status = wallet.mint_quote_state(&quote.id).await?;
  140. if status.state == MintQuoteState::Paid {
  141. break;
  142. }
  143. println!("{:?}", status);
  144. sleep(Duration::from_secs(2)).await;
  145. }
  146. let receive_amount = wallet.mint(&quote.id, split_target, None).await?;
  147. println!("Minted: {}", receive_amount);
  148. Ok(())
  149. }
  150. pub async fn mint_proofs(
  151. mint_url: &str,
  152. amount: Amount,
  153. keyset_id: Id,
  154. mint_keys: &KeySet,
  155. description: Option<String>,
  156. ) -> anyhow::Result<Proofs> {
  157. println!("Minting for ecash");
  158. println!();
  159. let wallet_client = HttpClient::new();
  160. let mint_quote = wallet_client
  161. .post_mint_quote(mint_url.parse()?, 1.into(), CurrencyUnit::Sat, description)
  162. .await?;
  163. println!("Please pay: {}", mint_quote.request);
  164. loop {
  165. let status = wallet_client
  166. .get_mint_quote_status(mint_url.parse()?, &mint_quote.quote)
  167. .await?;
  168. if status.state == MintQuoteState::Paid {
  169. break;
  170. }
  171. println!("{:?}", status.state);
  172. sleep(Duration::from_secs(2)).await;
  173. }
  174. let premint_secrets = PreMintSecrets::random(keyset_id, amount, &SplitTarget::default())?;
  175. let mint_response = wallet_client
  176. .post_mint(
  177. mint_url.parse()?,
  178. &mint_quote.quote,
  179. premint_secrets.clone(),
  180. )
  181. .await?;
  182. let pre_swap_proofs = construct_proofs(
  183. mint_response.signatures,
  184. premint_secrets.rs(),
  185. premint_secrets.secrets(),
  186. &mint_keys.clone().keys,
  187. )?;
  188. Ok(pre_swap_proofs)
  189. }