regtest.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. use std::{fmt::Debug, str::FromStr, sync::Arc, time::Duration};
  2. use anyhow::{bail, Result};
  3. use bip39::Mnemonic;
  4. use cdk::{
  5. amount::{Amount, SplitTarget},
  6. cdk_database::WalletMemoryDatabase,
  7. nuts::{
  8. CurrencyUnit, MeltQuoteState, MintQuoteState, NotificationPayload, PreMintSecrets, State,
  9. },
  10. wallet::{client::HttpClient, Wallet},
  11. };
  12. use cdk_integration_tests::init_regtest::{
  13. get_mint_url, get_mint_ws_url, init_cln_client, init_lnd_client,
  14. };
  15. use futures::{SinkExt, StreamExt};
  16. use lightning_invoice::Bolt11Invoice;
  17. use ln_regtest_rs::InvoiceStatus;
  18. use serde_json::json;
  19. use tokio::time::{sleep, timeout};
  20. use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
  21. async fn get_notification<T: StreamExt<Item = Result<Message, E>> + Unpin, E: Debug>(
  22. reader: &mut T,
  23. timeout_to_wait: Duration,
  24. ) -> (String, NotificationPayload) {
  25. let msg = timeout(timeout_to_wait, reader.next())
  26. .await
  27. .expect("timeout")
  28. .unwrap()
  29. .unwrap();
  30. let mut response: serde_json::Value =
  31. serde_json::from_str(&msg.to_text().unwrap()).expect("valid json");
  32. let mut params_raw = response
  33. .as_object_mut()
  34. .expect("object")
  35. .remove("params")
  36. .expect("valid params");
  37. let params_map = params_raw.as_object_mut().expect("params is object");
  38. (
  39. params_map
  40. .remove("subId")
  41. .unwrap()
  42. .as_str()
  43. .unwrap()
  44. .to_string(),
  45. serde_json::from_value(params_map.remove("payload").unwrap()).unwrap(),
  46. )
  47. }
  48. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  49. async fn test_regtest_mint_melt_round_trip() -> Result<()> {
  50. let lnd_client = init_lnd_client().await.unwrap();
  51. let wallet = Wallet::new(
  52. &get_mint_url(),
  53. CurrencyUnit::Sat,
  54. Arc::new(WalletMemoryDatabase::default()),
  55. &Mnemonic::generate(12)?.to_seed_normalized(""),
  56. None,
  57. )?;
  58. let (ws_stream, _) = connect_async(get_mint_ws_url())
  59. .await
  60. .expect("Failed to connect");
  61. let (mut write, mut reader) = ws_stream.split();
  62. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  63. lnd_client.pay_invoice(mint_quote.request).await?;
  64. let mint_amount = wallet
  65. .mint(&mint_quote.id, SplitTarget::default(), None)
  66. .await?;
  67. assert!(mint_amount == 100.into());
  68. let invoice = lnd_client.create_invoice(50).await?;
  69. let melt = wallet.melt_quote(invoice, None).await?;
  70. write
  71. .send(Message::Text(serde_json::to_string(&json!({
  72. "jsonrpc": "2.0",
  73. "id": 2,
  74. "method": "subscribe",
  75. "params": {
  76. "kind": "bolt11_melt_quote",
  77. "filters": [
  78. melt.id.clone(),
  79. ],
  80. "subId": "test-sub",
  81. }
  82. }))?))
  83. .await?;
  84. assert_eq!(
  85. reader.next().await.unwrap().unwrap().to_text().unwrap(),
  86. r#"{"jsonrpc":"2.0","result":{"status":"OK","sub_id":"test-sub"},"id":2}"#
  87. );
  88. let melt_response = wallet.melt(&melt.id).await.unwrap();
  89. assert!(melt_response.preimage.is_some());
  90. assert!(melt_response.state == MeltQuoteState::Paid);
  91. let (sub_id, payload) = get_notification(&mut reader, Duration::from_millis(15000)).await;
  92. assert_eq!("test-sub", sub_id);
  93. let payload = match payload {
  94. NotificationPayload::MeltQuoteBolt11Response(melt) => melt,
  95. _ => panic!("Wrong payload"),
  96. };
  97. assert_eq!(payload.amount + payload.fee_reserve, 100.into());
  98. assert_eq!(payload.quote, melt.id);
  99. assert_eq!(payload.state, MeltQuoteState::Paid);
  100. Ok(())
  101. }
  102. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  103. async fn test_regtest_mint_melt() -> Result<()> {
  104. let lnd_client = init_lnd_client().await?;
  105. let wallet = Wallet::new(
  106. &get_mint_url(),
  107. CurrencyUnit::Sat,
  108. Arc::new(WalletMemoryDatabase::default()),
  109. &Mnemonic::generate(12)?.to_seed_normalized(""),
  110. None,
  111. )?;
  112. let mint_amount = Amount::from(100);
  113. let mint_quote = wallet.mint_quote(mint_amount, None).await?;
  114. assert_eq!(mint_quote.amount, mint_amount);
  115. lnd_client.pay_invoice(mint_quote.request).await?;
  116. let mint_amount = wallet
  117. .mint(&mint_quote.id, SplitTarget::default(), None)
  118. .await?;
  119. assert!(mint_amount == 100.into());
  120. Ok(())
  121. }
  122. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  123. async fn test_restore() -> Result<()> {
  124. let lnd_client = init_lnd_client().await?;
  125. let seed = Mnemonic::generate(12)?.to_seed_normalized("");
  126. let wallet = Wallet::new(
  127. &get_mint_url(),
  128. CurrencyUnit::Sat,
  129. Arc::new(WalletMemoryDatabase::default()),
  130. &seed,
  131. None,
  132. )?;
  133. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  134. lnd_client.pay_invoice(mint_quote.request).await?;
  135. let _mint_amount = wallet
  136. .mint(&mint_quote.id, SplitTarget::default(), None)
  137. .await?;
  138. assert!(wallet.total_balance().await? == 100.into());
  139. let wallet_2 = Wallet::new(
  140. &get_mint_url(),
  141. CurrencyUnit::Sat,
  142. Arc::new(WalletMemoryDatabase::default()),
  143. &seed,
  144. None,
  145. )?;
  146. assert!(wallet_2.total_balance().await? == 0.into());
  147. let restored = wallet_2.restore().await?;
  148. let proofs = wallet_2.get_unspent_proofs().await?;
  149. wallet_2
  150. .swap(None, SplitTarget::default(), proofs, None, false)
  151. .await?;
  152. assert!(restored == 100.into());
  153. assert!(wallet_2.total_balance().await? == 100.into());
  154. let proofs = wallet.get_unspent_proofs().await?;
  155. let states = wallet.check_proofs_spent(proofs).await?;
  156. for state in states {
  157. if state.state != State::Spent {
  158. bail!("All proofs should be spent");
  159. }
  160. }
  161. Ok(())
  162. }
  163. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  164. async fn test_pay_invoice_twice() -> Result<()> {
  165. let lnd_client = init_lnd_client().await?;
  166. let seed = Mnemonic::generate(12)?.to_seed_normalized("");
  167. let wallet = Wallet::new(
  168. &get_mint_url(),
  169. CurrencyUnit::Sat,
  170. Arc::new(WalletMemoryDatabase::default()),
  171. &seed,
  172. None,
  173. )?;
  174. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  175. lnd_client.pay_invoice(mint_quote.request).await?;
  176. let mint_amount = wallet
  177. .mint(&mint_quote.id, SplitTarget::default(), None)
  178. .await?;
  179. assert_eq!(mint_amount, 100.into());
  180. let invoice = lnd_client.create_invoice(10).await?;
  181. let melt_quote = wallet.melt_quote(invoice.clone(), None).await?;
  182. let melt = wallet.melt(&melt_quote.id).await.unwrap();
  183. let melt_two = wallet.melt_quote(invoice, None).await?;
  184. let melt_two = wallet.melt(&melt_two.id).await;
  185. match melt_two {
  186. Err(err) => match err {
  187. cdk::Error::RequestAlreadyPaid => (),
  188. _ => {
  189. bail!("Wrong invoice already paid");
  190. }
  191. },
  192. Ok(_) => {
  193. bail!("Should not have allowed second payment");
  194. }
  195. }
  196. let balance = wallet.total_balance().await?;
  197. assert_eq!(balance, (Amount::from(100) - melt.fee_paid - melt.amount));
  198. Ok(())
  199. }
  200. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  201. async fn test_internal_payment() -> Result<()> {
  202. let lnd_client = init_lnd_client().await?;
  203. let seed = Mnemonic::generate(12)?.to_seed_normalized("");
  204. let wallet = Wallet::new(
  205. &get_mint_url(),
  206. CurrencyUnit::Sat,
  207. Arc::new(WalletMemoryDatabase::default()),
  208. &seed,
  209. None,
  210. )?;
  211. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  212. lnd_client.pay_invoice(mint_quote.request).await?;
  213. let _mint_amount = wallet
  214. .mint(&mint_quote.id, SplitTarget::default(), None)
  215. .await?;
  216. assert!(wallet.total_balance().await? == 100.into());
  217. let seed = Mnemonic::generate(12)?.to_seed_normalized("");
  218. let wallet_2 = Wallet::new(
  219. &get_mint_url(),
  220. CurrencyUnit::Sat,
  221. Arc::new(WalletMemoryDatabase::default()),
  222. &seed,
  223. None,
  224. )?;
  225. let mint_quote = wallet_2.mint_quote(10.into(), None).await?;
  226. let melt = wallet.melt_quote(mint_quote.request.clone(), None).await?;
  227. assert_eq!(melt.amount, 10.into());
  228. let _melted = wallet.melt(&melt.id).await.unwrap();
  229. let _wallet_2_mint = wallet_2
  230. .mint(&mint_quote.id, SplitTarget::default(), None)
  231. .await
  232. .unwrap();
  233. let cln_client = init_cln_client().await?;
  234. let payment_hash = Bolt11Invoice::from_str(&mint_quote.request)?;
  235. let check_paid = cln_client
  236. .check_incoming_invoice(payment_hash.payment_hash().to_string())
  237. .await?;
  238. match check_paid {
  239. InvoiceStatus::Unpaid => (),
  240. _ => {
  241. bail!("Invoice has incorrect status: {:?}", check_paid);
  242. }
  243. }
  244. let wallet_2_balance = wallet_2.total_balance().await?;
  245. assert!(wallet_2_balance == 10.into());
  246. let wallet_1_balance = wallet.total_balance().await?;
  247. assert!(wallet_1_balance == 90.into());
  248. Ok(())
  249. }
  250. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  251. async fn test_cached_mint() -> Result<()> {
  252. let lnd_client = init_lnd_client().await.unwrap();
  253. let wallet = Wallet::new(
  254. &get_mint_url(),
  255. CurrencyUnit::Sat,
  256. Arc::new(WalletMemoryDatabase::default()),
  257. &Mnemonic::generate(12)?.to_seed_normalized(""),
  258. None,
  259. )?;
  260. let mint_amount = Amount::from(100);
  261. let quote = wallet.mint_quote(mint_amount, None).await?;
  262. lnd_client.pay_invoice(quote.request).await?;
  263. loop {
  264. let status = wallet.mint_quote_state(&quote.id).await.unwrap();
  265. println!("Quote status: {}", status.state);
  266. if status.state == MintQuoteState::Paid {
  267. break;
  268. }
  269. sleep(Duration::from_secs(5)).await;
  270. }
  271. let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
  272. let http_client = HttpClient::new();
  273. let premint_secrets =
  274. PreMintSecrets::random(active_keyset_id, 31.into(), &SplitTarget::default()).unwrap();
  275. let response = http_client
  276. .post_mint(
  277. get_mint_url().as_str().parse()?,
  278. &quote.id,
  279. premint_secrets.clone(),
  280. )
  281. .await?;
  282. let response1 = http_client
  283. .post_mint(get_mint_url().as_str().parse()?, &quote.id, premint_secrets)
  284. .await?;
  285. assert!(response == response1);
  286. Ok(())
  287. }