lib.rs 5.8 KB

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