lib.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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, MintBolt11Request, MintInfo, MintMethodSettings,
  14. MintQuoteBolt11Request, MintQuoteState, Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
  15. };
  16. use cdk::types::{LnKey, QuoteTTL};
  17. use cdk::wallet::client::{HttpClient, HttpClientMethods};
  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. HashMap::new(),
  75. )
  76. .await?;
  77. let cache_time_to_live = 3600;
  78. let cache_time_to_idle = 3600;
  79. let mint_arc = Arc::new(mint);
  80. let v1_service = cdk_axum::create_mint_router(
  81. Arc::clone(&mint_arc),
  82. cache_time_to_live,
  83. cache_time_to_idle,
  84. )
  85. .await?;
  86. let mint_service = Router::new()
  87. .merge(v1_service)
  88. .layer(CorsLayer::permissive());
  89. let mint = Arc::clone(&mint_arc);
  90. let shutdown = Arc::new(Notify::new());
  91. tokio::spawn({
  92. let shutdown = Arc::clone(&shutdown);
  93. async move { mint.wait_for_paid_invoices(shutdown).await }
  94. });
  95. axum::Server::bind(
  96. &format!("{}:{}", get_mint_addr(), get_mint_port())
  97. .as_str()
  98. .parse()?,
  99. )
  100. .serve(mint_service.into_make_service())
  101. .await?;
  102. Ok(())
  103. }
  104. pub async fn wallet_mint(
  105. wallet: Arc<Wallet>,
  106. amount: Amount,
  107. split_target: SplitTarget,
  108. description: Option<String>,
  109. ) -> Result<()> {
  110. let quote = wallet.mint_quote(amount, description).await?;
  111. loop {
  112. let status = wallet.mint_quote_state(&quote.id).await?;
  113. if status.state == MintQuoteState::Paid {
  114. break;
  115. }
  116. println!("{:?}", status);
  117. sleep(Duration::from_secs(2)).await;
  118. }
  119. let receive_amount = wallet.mint(&quote.id, split_target, None).await?;
  120. println!("Minted: {}", receive_amount);
  121. Ok(())
  122. }
  123. pub async fn mint_proofs(
  124. mint_url: &str,
  125. amount: Amount,
  126. keyset_id: Id,
  127. mint_keys: &KeySet,
  128. description: Option<String>,
  129. ) -> anyhow::Result<Proofs> {
  130. println!("Minting for ecash");
  131. println!();
  132. let wallet_client = HttpClient::new();
  133. let request = MintQuoteBolt11Request {
  134. amount,
  135. unit: CurrencyUnit::Sat,
  136. description,
  137. };
  138. let mint_quote = wallet_client
  139. .post_mint_quote(mint_url.parse()?, request)
  140. .await?;
  141. println!("Please pay: {}", mint_quote.request);
  142. loop {
  143. let status = wallet_client
  144. .get_mint_quote_status(mint_url.parse()?, &mint_quote.quote)
  145. .await?;
  146. if status.state == MintQuoteState::Paid {
  147. break;
  148. }
  149. println!("{:?}", status.state);
  150. sleep(Duration::from_secs(2)).await;
  151. }
  152. let premint_secrets = PreMintSecrets::random(keyset_id, amount, &SplitTarget::default())?;
  153. let request = MintBolt11Request {
  154. quote: mint_quote.quote,
  155. outputs: premint_secrets.blinded_messages(),
  156. };
  157. let mint_response = wallet_client.post_mint(mint_url.parse()?, request).await?;
  158. let pre_swap_proofs = construct_proofs(
  159. mint_response.signatures,
  160. premint_secrets.rs(),
  161. premint_secrets.secrets(),
  162. &mint_keys.clone().keys,
  163. )?;
  164. Ok(pre_swap_proofs)
  165. }
  166. // Get all pending from wallet and attempt to swap
  167. // Will panic if there are no pending
  168. // Will return Ok if swap fails as expected
  169. pub async fn attempt_to_swap_pending(wallet: &Wallet) -> Result<()> {
  170. let pending = wallet
  171. .localstore
  172. .get_proofs(None, None, Some(vec![State::Pending]), None)
  173. .await?;
  174. assert!(!pending.is_empty());
  175. let swap = wallet
  176. .swap(
  177. None,
  178. SplitTarget::None,
  179. pending.into_iter().map(|p| p.proof).collect(),
  180. None,
  181. false,
  182. )
  183. .await;
  184. match swap {
  185. Ok(_swap) => {
  186. bail!("These proofs should be pending")
  187. }
  188. Err(err) => match err {
  189. cdk::error::Error::TokenPending => (),
  190. _ => {
  191. println!("{:?}", err);
  192. bail!("Wrong error")
  193. }
  194. },
  195. }
  196. Ok(())
  197. }