mint.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. //! Mint tests
  2. use std::collections::{HashMap, HashSet};
  3. use std::ops::Deref;
  4. use std::sync::Arc;
  5. use std::time::Duration;
  6. use anyhow::{bail, Result};
  7. use bip39::Mnemonic;
  8. use cdk::amount::{Amount, SplitTarget};
  9. use cdk::cdk_database::mint_memory::MintMemoryDatabase;
  10. use cdk::cdk_database::MintDatabase;
  11. use cdk::dhke::construct_proofs;
  12. use cdk::mint::signatory::SignatoryManager;
  13. use cdk::mint::{FeeReserve, MemorySignatory, MintBuilder, MintMeltLimits, MintQuote};
  14. use cdk::nuts::nut00::ProofsMethods;
  15. use cdk::nuts::{
  16. CurrencyUnit, Id, MintBolt11Request, MintInfo, NotificationPayload, Nuts, PaymentMethod,
  17. PreMintSecrets, ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest,
  18. };
  19. use cdk::subscription::{IndexableParams, Params};
  20. use cdk::types::QuoteTTL;
  21. use cdk::util::unix_time;
  22. use cdk::Mint;
  23. use cdk_fake_wallet::FakeWallet;
  24. use tokio::sync::OnceCell;
  25. use tokio::time::sleep;
  26. pub const MINT_URL: &str = "http://127.0.0.1:8088";
  27. static INSTANCE: OnceCell<Arc<Mint>> = OnceCell::const_new();
  28. async fn new_mint(fee: u64) -> Mint {
  29. let mut supported_units = HashMap::new();
  30. supported_units.insert(CurrencyUnit::Sat, (fee, 32));
  31. let nuts = Nuts::new()
  32. .nut07(true)
  33. .nut08(true)
  34. .nut09(true)
  35. .nut10(true)
  36. .nut11(true)
  37. .nut12(true)
  38. .nut14(true);
  39. let mint_info = MintInfo::new().nuts(nuts);
  40. let localstore = MintMemoryDatabase::default();
  41. localstore
  42. .set_mint_info(mint_info)
  43. .await
  44. .expect("Could not set mint info");
  45. let mnemonic = Mnemonic::generate(12).unwrap();
  46. let localstore = Arc::new(MintMemoryDatabase::default());
  47. let seed = mnemonic.to_seed_normalized("");
  48. let signatory_manager = Arc::new(SignatoryManager::new(Arc::new(
  49. MemorySignatory::new(localstore.clone(), &seed, supported_units, HashMap::new())
  50. .await
  51. .expect("valid signatory"),
  52. )));
  53. Mint::new(
  54. localstore,
  55. HashMap::new(),
  56. signatory_manager,
  57. HashMap::new(),
  58. )
  59. .await
  60. .unwrap()
  61. }
  62. async fn initialize() -> Arc<Mint> {
  63. INSTANCE
  64. .get_or_init(|| async { Arc::new(new_mint(0).await) })
  65. .await
  66. .clone()
  67. }
  68. async fn mint_proofs(
  69. mint: &Mint,
  70. amount: Amount,
  71. split_target: &SplitTarget,
  72. keys: cdk::nuts::Keys,
  73. ) -> Result<Proofs> {
  74. let request_lookup = uuid::Uuid::new_v4().to_string();
  75. let quote = MintQuote::new(
  76. "".to_string(),
  77. CurrencyUnit::Sat,
  78. amount,
  79. unix_time() + 36000,
  80. request_lookup.to_string(),
  81. None,
  82. );
  83. mint.localstore.add_mint_quote(quote.clone()).await?;
  84. mint.pay_mint_quote_for_request_id(&request_lookup).await?;
  85. let keyset_id = Id::from(&keys);
  86. let premint = PreMintSecrets::random(keyset_id, amount, split_target)?;
  87. let mint_request = MintBolt11Request {
  88. quote: quote.id,
  89. outputs: premint.blinded_messages(),
  90. signature: None,
  91. };
  92. let after_mint = mint.process_mint_request(mint_request).await?;
  93. let proofs = construct_proofs(
  94. after_mint.signatures,
  95. premint.rs(),
  96. premint.secrets(),
  97. &keys,
  98. )?;
  99. Ok(proofs)
  100. }
  101. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  102. async fn test_mint_double_spend() -> Result<()> {
  103. let mint = initialize().await;
  104. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  105. let keyset_id = Id::from(&keys);
  106. let proofs = mint_proofs(&mint, 100.into(), &SplitTarget::default(), keys).await?;
  107. let preswap = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  108. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  109. let swap = mint.process_swap_request(swap_request).await;
  110. assert!(swap.is_ok());
  111. let preswap_two = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  112. let swap_two_request = SwapRequest::new(proofs, preswap_two.blinded_messages());
  113. match mint.process_swap_request(swap_two_request).await {
  114. Ok(_) => bail!("Proofs double spent"),
  115. Err(err) => match err {
  116. cdk::Error::TokenAlreadySpent => (),
  117. _ => bail!("Wrong error returned"),
  118. },
  119. }
  120. Ok(())
  121. }
  122. /// This attempts to swap for more outputs then inputs.
  123. /// This will work if the mint does not check for outputs amounts overflowing
  124. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  125. async fn test_attempt_to_swap_by_overflowing() -> Result<()> {
  126. let mint = initialize().await;
  127. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  128. let keyset_id = Id::from(&keys);
  129. let proofs = mint_proofs(&mint, 100.into(), &SplitTarget::default(), keys).await?;
  130. let amount = 2_u64.pow(63);
  131. let pre_mint_amount =
  132. PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
  133. let pre_mint_amount_two =
  134. PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
  135. let mut pre_mint = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
  136. pre_mint.combine(pre_mint_amount);
  137. pre_mint.combine(pre_mint_amount_two);
  138. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  139. match mint.process_swap_request(swap_request).await {
  140. Ok(_) => bail!("Swap occurred with overflow"),
  141. Err(err) => match err {
  142. cdk::Error::NUT03(cdk::nuts::nut03::Error::Amount(_)) => (),
  143. cdk::Error::AmountOverflow => (),
  144. cdk::Error::AmountError(_) => (),
  145. _ => {
  146. println!("{:?}", err);
  147. bail!("Wrong error returned in swap overflow")
  148. }
  149. },
  150. }
  151. Ok(())
  152. }
  153. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  154. pub async fn test_p2pk_swap() -> Result<()> {
  155. let mint = initialize().await;
  156. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  157. let keyset_id = Id::from(&keys);
  158. let proofs = mint_proofs(&mint, 100.into(), &SplitTarget::default(), keys).await?;
  159. let secret = SecretKey::generate();
  160. let spending_conditions = SpendingConditions::new_p2pk(secret.public_key(), None);
  161. let pre_swap = PreMintSecrets::with_conditions(
  162. keyset_id,
  163. 100.into(),
  164. &SplitTarget::default(),
  165. &spending_conditions,
  166. )?;
  167. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  168. let keys = mint.pubkeys().await?.keysets.first().cloned().unwrap().keys;
  169. let post_swap = mint.process_swap_request(swap_request).await?;
  170. let mut proofs = construct_proofs(
  171. post_swap.signatures,
  172. pre_swap.rs(),
  173. pre_swap.secrets(),
  174. &keys,
  175. )?;
  176. let pre_swap = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  177. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  178. let public_keys_to_listen: Vec<_> = swap_request
  179. .inputs
  180. .ys()
  181. .expect("key")
  182. .into_iter()
  183. .enumerate()
  184. .filter_map(|(key, pk)| {
  185. if key % 2 == 0 {
  186. // Only expect messages from every other key
  187. Some(pk.to_string())
  188. } else {
  189. None
  190. }
  191. })
  192. .collect();
  193. let mut listener = mint
  194. .pubsub_manager
  195. .try_subscribe::<IndexableParams>(
  196. Params {
  197. kind: cdk::nuts::nut17::Kind::ProofState,
  198. filters: public_keys_to_listen.clone(),
  199. id: "test".into(),
  200. }
  201. .into(),
  202. )
  203. .await
  204. .expect("valid subscription");
  205. match mint.process_swap_request(swap_request).await {
  206. Ok(_) => bail!("Proofs spent without sig"),
  207. Err(err) => match err {
  208. cdk::Error::NUT11(cdk::nuts::nut11::Error::SignaturesNotProvided) => (),
  209. _ => {
  210. println!("{:?}", err);
  211. bail!("Wrong error returned")
  212. }
  213. },
  214. }
  215. for proof in &mut proofs {
  216. proof.sign_p2pk(secret.clone())?;
  217. }
  218. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  219. let attempt_swap = mint.process_swap_request(swap_request).await;
  220. assert!(attempt_swap.is_ok());
  221. sleep(Duration::from_millis(10)).await;
  222. let mut msgs = HashMap::new();
  223. while let Ok((sub_id, msg)) = listener.try_recv() {
  224. assert_eq!(sub_id, "test".into());
  225. match msg {
  226. NotificationPayload::ProofState(ProofState { y, state, .. }) => {
  227. let pk = y.to_string();
  228. msgs.get_mut(&pk)
  229. .map(|x: &mut Vec<State>| {
  230. x.push(state);
  231. })
  232. .unwrap_or_else(|| {
  233. msgs.insert(pk, vec![state]);
  234. });
  235. }
  236. _ => bail!("Wrong message received"),
  237. }
  238. }
  239. for keys in public_keys_to_listen {
  240. let statuses = msgs.remove(&keys).expect("some events");
  241. assert_eq!(statuses, vec![State::Pending, State::Spent]);
  242. }
  243. assert!(listener.try_recv().is_err(), "no other event is happening");
  244. assert!(msgs.is_empty(), "Only expected key events are received");
  245. Ok(())
  246. }
  247. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  248. async fn test_swap_unbalanced() -> Result<()> {
  249. let mint = initialize().await;
  250. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  251. let keyset_id = Id::from(&keys);
  252. let proofs = mint_proofs(&mint, 100.into(), &SplitTarget::default(), keys).await?;
  253. let preswap = PreMintSecrets::random(keyset_id, 95.into(), &SplitTarget::default())?;
  254. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  255. match mint.process_swap_request(swap_request).await {
  256. Ok(_) => bail!("Swap was allowed unbalanced"),
  257. Err(err) => match err {
  258. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  259. _ => bail!("Wrong error returned"),
  260. },
  261. }
  262. let preswap = PreMintSecrets::random(keyset_id, 101.into(), &SplitTarget::default())?;
  263. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  264. match mint.process_swap_request(swap_request).await {
  265. Ok(_) => bail!("Swap was allowed unbalanced"),
  266. Err(err) => match err {
  267. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  268. _ => bail!("Wrong error returned"),
  269. },
  270. }
  271. Ok(())
  272. }
  273. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  274. async fn test_swap_overpay_underpay_fee() -> Result<()> {
  275. let mint = new_mint(1).await;
  276. mint.rotate_keyset(CurrencyUnit::Sat, 1, 32, 1, &HashMap::new())
  277. .await?;
  278. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  279. let keyset_id = Id::from(&keys);
  280. let proofs = mint_proofs(&mint, 1000.into(), &SplitTarget::default(), keys).await?;
  281. let preswap = PreMintSecrets::random(keyset_id, 9998.into(), &SplitTarget::default())?;
  282. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  283. // Attempt to swap overpaying fee
  284. match mint.process_swap_request(swap_request).await {
  285. Ok(_) => bail!("Swap was allowed unbalanced"),
  286. Err(err) => match err {
  287. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  288. _ => {
  289. println!("{:?}", err);
  290. bail!("Wrong error returned")
  291. }
  292. },
  293. }
  294. let preswap = PreMintSecrets::random(keyset_id, 1000.into(), &SplitTarget::default())?;
  295. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  296. // Attempt to swap underpaying fee
  297. match mint.process_swap_request(swap_request).await {
  298. Ok(_) => bail!("Swap was allowed unbalanced"),
  299. Err(err) => match err {
  300. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  301. _ => {
  302. println!("{:?}", err);
  303. bail!("Wrong error returned")
  304. }
  305. },
  306. }
  307. Ok(())
  308. }
  309. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  310. async fn test_mint_enforce_fee() -> Result<()> {
  311. let mint = new_mint(1).await;
  312. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  313. let keyset_id = Id::from(&keys);
  314. let mut proofs = mint_proofs(&mint, 1010.into(), &SplitTarget::Value(1.into()), keys).await?;
  315. let five_proofs: Vec<_> = proofs.drain(..5).collect();
  316. let preswap = PreMintSecrets::random(keyset_id, 5.into(), &SplitTarget::default())?;
  317. let swap_request = SwapRequest::new(five_proofs.clone(), preswap.blinded_messages());
  318. // Attempt to swap underpaying fee
  319. match mint.process_swap_request(swap_request).await {
  320. Ok(_) => bail!("Swap was allowed unbalanced"),
  321. Err(err) => match err {
  322. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  323. _ => {
  324. println!("{:?}", err);
  325. bail!("Wrong error returned")
  326. }
  327. },
  328. }
  329. let preswap = PreMintSecrets::random(keyset_id, 4.into(), &SplitTarget::default())?;
  330. let swap_request = SwapRequest::new(five_proofs.clone(), preswap.blinded_messages());
  331. let _ = mint.process_swap_request(swap_request).await?;
  332. let thousnad_proofs: Vec<_> = proofs.drain(..1001).collect();
  333. let preswap = PreMintSecrets::random(keyset_id, 1000.into(), &SplitTarget::default())?;
  334. let swap_request = SwapRequest::new(thousnad_proofs.clone(), preswap.blinded_messages());
  335. // Attempt to swap underpaying fee
  336. match mint.process_swap_request(swap_request).await {
  337. Ok(_) => bail!("Swap was allowed unbalanced"),
  338. Err(err) => match err {
  339. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  340. _ => {
  341. println!("{:?}", err);
  342. bail!("Wrong error returned")
  343. }
  344. },
  345. }
  346. let preswap = PreMintSecrets::random(keyset_id, 999.into(), &SplitTarget::default())?;
  347. let swap_request = SwapRequest::new(thousnad_proofs.clone(), preswap.blinded_messages());
  348. let _ = mint.process_swap_request(swap_request).await?;
  349. Ok(())
  350. }
  351. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  352. async fn test_correct_keyset() -> Result<()> {
  353. let mnemonic = Mnemonic::generate(12)?;
  354. let fee_reserve = FeeReserve {
  355. min_fee_reserve: 1.into(),
  356. percent_fee_reserve: 1.0,
  357. };
  358. let database = MintMemoryDatabase::default();
  359. let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0);
  360. let mut mint_builder = MintBuilder::new();
  361. let localstore = Arc::new(database);
  362. mint_builder = mint_builder.with_localstore(localstore.clone());
  363. mint_builder = mint_builder.add_ln_backend(
  364. CurrencyUnit::Sat,
  365. PaymentMethod::Bolt11,
  366. MintMeltLimits::new(1, 5_000),
  367. Arc::new(fake_wallet),
  368. );
  369. mint_builder = mint_builder
  370. .with_name("regtest mint".to_string())
  371. .with_description("regtest mint".to_string())
  372. .with_seed(mnemonic.to_seed_normalized("").to_vec());
  373. let mint = mint_builder.clone().build().await?;
  374. localstore
  375. .set_mint_info(mint_builder.mint_info.clone())
  376. .await?;
  377. let quote_ttl = QuoteTTL::new(10000, 10000);
  378. localstore.set_quote_ttl(quote_ttl).await?;
  379. mint.rotate_next_keyset(CurrencyUnit::Sat, 32, 0).await?;
  380. mint.rotate_next_keyset(CurrencyUnit::Sat, 32, 0).await?;
  381. let active = mint.localstore.get_active_keysets().await?;
  382. let active = active
  383. .get(&CurrencyUnit::Sat)
  384. .expect("There is a keyset for unit");
  385. let keyset_info = mint
  386. .localstore
  387. .get_keyset_info(active)
  388. .await?
  389. .expect("There is keyset");
  390. assert!(keyset_info.derivation_path_index == Some(2));
  391. let mint = mint_builder
  392. .with_signatory(mint.signatory.deref().deref().to_owned())
  393. .build()
  394. .await?;
  395. let active = mint.localstore.get_active_keysets().await?;
  396. let active = active
  397. .get(&CurrencyUnit::Sat)
  398. .expect("There is a keyset for unit");
  399. let keyset_info = mint
  400. .localstore
  401. .get_keyset_info(active)
  402. .await?
  403. .expect("There is keyset");
  404. assert!(keyset_info.derivation_path_index == Some(2));
  405. Ok(())
  406. }