mint.rs 16 KB

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