init_pure_tests.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. use std::collections::{HashMap, HashSet};
  2. use std::fmt::{Debug, Formatter};
  3. use std::str::FromStr;
  4. use std::sync::Arc;
  5. use async_trait::async_trait;
  6. use bip39::Mnemonic;
  7. use cdk::amount::SplitTarget;
  8. use cdk::cdk_database::mint_memory::MintMemoryDatabase;
  9. use cdk::cdk_database::WalletMemoryDatabase;
  10. use cdk::mint::{FeeReserve, MintBuilder, MintMeltLimits};
  11. use cdk::nuts::nut00::ProofsMethods;
  12. use cdk::nuts::{
  13. CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysetResponse,
  14. MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
  15. MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PaymentMethod,
  16. RestoreRequest, RestoreResponse, SwapRequest, SwapResponse,
  17. };
  18. use cdk::util::unix_time;
  19. use cdk::wallet::client::MintConnector;
  20. use cdk::wallet::Wallet;
  21. use cdk::{Amount, Error, Mint};
  22. use cdk_fake_wallet::FakeWallet;
  23. use tokio::sync::Notify;
  24. use uuid::Uuid;
  25. use crate::wait_for_mint_to_be_paid;
  26. pub struct DirectMintConnection {
  27. pub mint: Arc<Mint>,
  28. }
  29. impl DirectMintConnection {
  30. pub fn new(mint: Arc<Mint>) -> Self {
  31. Self { mint }
  32. }
  33. }
  34. impl Debug for DirectMintConnection {
  35. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  36. write!(
  37. f,
  38. "DirectMintConnection {{ mint_info: {:?} }}",
  39. self.mint.config.mint_info()
  40. )
  41. }
  42. }
  43. /// Implements the generic [MintConnector] (i.e. use the interface that expects to communicate
  44. /// to a generic mint, where we don't know that quote ID's are [Uuid]s) for [DirectMintConnection],
  45. /// where we know we're dealing with a mint that uses [Uuid]s for quotes.
  46. /// Convert the requests and responses between the [String] and [Uuid] variants as necessary.
  47. #[async_trait]
  48. impl MintConnector for DirectMintConnection {
  49. async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> {
  50. self.mint.pubkeys().await.map(|pks| pks.keysets)
  51. }
  52. async fn get_mint_keyset(&self, keyset_id: Id) -> Result<KeySet, Error> {
  53. self.mint
  54. .keyset(&keyset_id)
  55. .await
  56. .and_then(|res| res.ok_or(Error::UnknownKeySet))
  57. }
  58. async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> {
  59. self.mint.keysets().await
  60. }
  61. async fn post_mint_quote(
  62. &self,
  63. request: MintQuoteBolt11Request,
  64. ) -> Result<MintQuoteBolt11Response<String>, Error> {
  65. self.mint
  66. .get_mint_bolt11_quote(request)
  67. .await
  68. .map(Into::into)
  69. }
  70. async fn get_mint_quote_status(
  71. &self,
  72. quote_id: &str,
  73. ) -> Result<MintQuoteBolt11Response<String>, Error> {
  74. let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
  75. self.mint
  76. .check_mint_quote(&quote_id_uuid)
  77. .await
  78. .map(Into::into)
  79. }
  80. async fn post_mint(
  81. &self,
  82. request: MintBolt11Request<String>,
  83. ) -> Result<MintBolt11Response, Error> {
  84. let request_uuid = request.try_into().unwrap();
  85. self.mint.process_mint_request(request_uuid).await
  86. }
  87. async fn post_melt_quote(
  88. &self,
  89. request: MeltQuoteBolt11Request,
  90. ) -> Result<MeltQuoteBolt11Response<String>, Error> {
  91. self.mint
  92. .get_melt_bolt11_quote(&request)
  93. .await
  94. .map(Into::into)
  95. }
  96. async fn get_melt_quote_status(
  97. &self,
  98. quote_id: &str,
  99. ) -> Result<MeltQuoteBolt11Response<String>, Error> {
  100. let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
  101. self.mint
  102. .check_melt_quote(&quote_id_uuid)
  103. .await
  104. .map(Into::into)
  105. }
  106. async fn post_melt(
  107. &self,
  108. request: MeltBolt11Request<String>,
  109. ) -> Result<MeltQuoteBolt11Response<String>, Error> {
  110. let request_uuid = request.try_into().unwrap();
  111. self.mint.melt_bolt11(&request_uuid).await.map(Into::into)
  112. }
  113. async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> {
  114. self.mint.process_swap_request(swap_request).await
  115. }
  116. async fn get_mint_info(&self) -> Result<MintInfo, Error> {
  117. Ok(self.mint.mint_info().clone().time(unix_time()))
  118. }
  119. async fn post_check_state(
  120. &self,
  121. request: CheckStateRequest,
  122. ) -> Result<CheckStateResponse, Error> {
  123. self.mint.check_state(&request).await
  124. }
  125. async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
  126. self.mint.restore(request).await
  127. }
  128. }
  129. pub async fn create_and_start_test_mint() -> anyhow::Result<Arc<Mint>> {
  130. let mut mint_builder = MintBuilder::new();
  131. let database = MintMemoryDatabase::default();
  132. mint_builder = mint_builder.with_localstore(Arc::new(database));
  133. let fee_reserve = FeeReserve {
  134. min_fee_reserve: 1.into(),
  135. percent_fee_reserve: 1.0,
  136. };
  137. let ln_fake_backend = Arc::new(FakeWallet::new(
  138. fee_reserve.clone(),
  139. HashMap::default(),
  140. HashSet::default(),
  141. 0,
  142. ));
  143. mint_builder = mint_builder.add_ln_backend(
  144. CurrencyUnit::Sat,
  145. PaymentMethod::Bolt11,
  146. MintMeltLimits::new(1, 1_000),
  147. ln_fake_backend,
  148. );
  149. let mnemonic = Mnemonic::generate(12)?;
  150. mint_builder = mint_builder
  151. .with_name("pure test mint".to_string())
  152. .with_description("pure test mint".to_string())
  153. .with_quote_ttl(10000, 10000)
  154. .with_seed(mnemonic.to_seed_normalized("").to_vec());
  155. let mint = mint_builder.build().await?;
  156. let mint_arc = Arc::new(mint);
  157. let mint_arc_clone = Arc::clone(&mint_arc);
  158. let shutdown = Arc::new(Notify::new());
  159. tokio::spawn({
  160. let shutdown = Arc::clone(&shutdown);
  161. async move { mint_arc_clone.wait_for_paid_invoices(shutdown).await }
  162. });
  163. Ok(mint_arc)
  164. }
  165. pub fn create_test_wallet_for_mint(mint: Arc<Mint>) -> anyhow::Result<Arc<Wallet>> {
  166. let connector = DirectMintConnection::new(mint);
  167. let seed = Mnemonic::generate(12)?.to_seed_normalized("");
  168. let mint_url = "http://aa".to_string();
  169. let unit = CurrencyUnit::Sat;
  170. let localstore = WalletMemoryDatabase::default();
  171. let mut wallet = Wallet::new(&mint_url, unit, Arc::new(localstore), &seed, None)?;
  172. wallet.set_client(connector);
  173. Ok(Arc::new(wallet))
  174. }
  175. /// Creates a mint quote for the given amount and checks its state in a loop. Returns when
  176. /// amount is minted.
  177. pub async fn fund_wallet(wallet: Arc<Wallet>, amount: u64) -> anyhow::Result<Amount> {
  178. let desired_amount = Amount::from(amount);
  179. let quote = wallet.mint_quote(desired_amount, None).await?;
  180. wait_for_mint_to_be_paid(&wallet, &quote.id).await?;
  181. Ok(wallet
  182. .mint(&quote.id, SplitTarget::default(), None)
  183. .await?
  184. .total_amount()?)
  185. }