mint.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. //! Mint tests
  2. use cdk::amount::{Amount, SplitTarget};
  3. use cdk::dhke::construct_proofs;
  4. use cdk::util::unix_time;
  5. use std::collections::HashMap;
  6. use std::sync::Arc;
  7. use tokio::sync::OnceCell;
  8. use anyhow::{bail, Result};
  9. use bip39::Mnemonic;
  10. use cdk::cdk_database::mint_memory::MintMemoryDatabase;
  11. use cdk::nuts::{
  12. CurrencyUnit, Id, MintBolt11Request, MintInfo, Nuts, PreMintSecrets, Proofs, SecretKey,
  13. SpendingConditions, SwapRequest,
  14. };
  15. use cdk::Mint;
  16. pub const MINT_URL: &str = "http://127.0.0.1:8088";
  17. static INSTANCE: OnceCell<Mint> = OnceCell::const_new();
  18. async fn new_mint(fee: u64) -> Mint {
  19. let mut supported_units = HashMap::new();
  20. supported_units.insert(CurrencyUnit::Sat, (fee, 32));
  21. let nuts = Nuts::new()
  22. .nut07(true)
  23. .nut08(true)
  24. .nut09(true)
  25. .nut10(true)
  26. .nut11(true)
  27. .nut12(true)
  28. .nut14(true);
  29. let mint_info = MintInfo::new().nuts(nuts);
  30. let mnemonic = Mnemonic::generate(12).unwrap();
  31. Mint::new(
  32. MINT_URL,
  33. &mnemonic.to_seed_normalized(""),
  34. mint_info,
  35. Arc::new(MintMemoryDatabase::default()),
  36. supported_units,
  37. )
  38. .await
  39. .unwrap()
  40. }
  41. async fn initialize() -> &'static Mint {
  42. INSTANCE.get_or_init(|| new_mint(0)).await
  43. }
  44. async fn mint_proofs(
  45. mint: &Mint,
  46. amount: Amount,
  47. split_target: &SplitTarget,
  48. keys: cdk::nuts::Keys,
  49. ) -> Result<Proofs> {
  50. let request_lookup = uuid::Uuid::new_v4().to_string();
  51. let mint_quote = mint
  52. .new_mint_quote(
  53. MINT_URL.parse()?,
  54. "".to_string(),
  55. CurrencyUnit::Sat,
  56. amount,
  57. unix_time() + 36000,
  58. request_lookup.to_string(),
  59. )
  60. .await?;
  61. mint.pay_mint_quote_for_request_id(&request_lookup).await?;
  62. let keyset_id = Id::from(&keys);
  63. let premint = PreMintSecrets::random(keyset_id, amount, split_target)?;
  64. let mint_request = MintBolt11Request {
  65. quote: mint_quote.id,
  66. outputs: premint.blinded_messages(),
  67. };
  68. let after_mint = mint.process_mint_request(mint_request).await?;
  69. let proofs = construct_proofs(
  70. after_mint.signatures,
  71. premint.rs(),
  72. premint.secrets(),
  73. &keys,
  74. )?;
  75. Ok(proofs)
  76. }
  77. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  78. async fn test_mint_double_spend() -> Result<()> {
  79. let mint = initialize().await;
  80. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  81. let keyset_id = Id::from(&keys);
  82. let proofs = mint_proofs(mint, 100.into(), &SplitTarget::default(), keys).await?;
  83. let preswap = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  84. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  85. let swap = mint.process_swap_request(swap_request).await;
  86. assert!(swap.is_ok());
  87. let preswap_two = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  88. let swap_two_request = SwapRequest::new(proofs, preswap_two.blinded_messages());
  89. match mint.process_swap_request(swap_two_request).await {
  90. Ok(_) => bail!("Proofs double spent"),
  91. Err(err) => match err {
  92. cdk::Error::TokenAlreadySpent => (),
  93. _ => bail!("Wrong error returned"),
  94. },
  95. }
  96. Ok(())
  97. }
  98. /// This attempts to swap for more outputs then inputs.
  99. /// This will work if the mint does not check for outputs amounts overflowing
  100. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  101. async fn test_attempt_to_swap_by_overflowing() -> Result<()> {
  102. let mint = initialize().await;
  103. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  104. let keyset_id = Id::from(&keys);
  105. let proofs = mint_proofs(mint, 100.into(), &SplitTarget::default(), keys).await?;
  106. let amount = 2_u64.pow(63);
  107. let pre_mint_amount =
  108. PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
  109. let pre_mint_amount_two =
  110. PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
  111. let mut pre_mint = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
  112. pre_mint.combine(pre_mint_amount);
  113. pre_mint.combine(pre_mint_amount_two);
  114. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  115. match mint.process_swap_request(swap_request).await {
  116. Ok(_) => bail!("Swap occurred with overflow"),
  117. Err(err) => match err {
  118. cdk::Error::NUT03(cdk::nuts::nut03::Error::Amount(_)) => (),
  119. _ => {
  120. println!("{:?}", err);
  121. bail!("Wrong error returned in swap overflow")
  122. }
  123. },
  124. }
  125. Ok(())
  126. }
  127. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  128. pub async fn test_p2pk_swap() -> Result<()> {
  129. let mint = initialize().await;
  130. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  131. let keyset_id = Id::from(&keys);
  132. let proofs = mint_proofs(mint, 100.into(), &SplitTarget::default(), keys).await?;
  133. let secret = SecretKey::generate();
  134. let spending_conditions = SpendingConditions::new_p2pk(secret.public_key(), None);
  135. let pre_swap = PreMintSecrets::with_conditions(
  136. keyset_id,
  137. 100.into(),
  138. &SplitTarget::default(),
  139. &spending_conditions,
  140. )?;
  141. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  142. let keys = mint.pubkeys().await?.keysets.first().cloned().unwrap().keys;
  143. let post_swap = mint.process_swap_request(swap_request).await?;
  144. let mut proofs = construct_proofs(
  145. post_swap.signatures,
  146. pre_swap.rs(),
  147. pre_swap.secrets(),
  148. &keys,
  149. )?;
  150. let pre_swap = PreMintSecrets::random(keyset_id, 100.into(), &SplitTarget::default())?;
  151. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  152. match mint.process_swap_request(swap_request).await {
  153. Ok(_) => bail!("Proofs spent without sig"),
  154. Err(err) => match err {
  155. cdk::Error::NUT11(cdk::nuts::nut11::Error::SignaturesNotProvided) => (),
  156. _ => {
  157. println!("{:?}", err);
  158. bail!("Wrong error returned")
  159. }
  160. },
  161. }
  162. for proof in &mut proofs {
  163. proof.sign_p2pk(secret.clone())?;
  164. }
  165. let swap_request = SwapRequest::new(proofs.clone(), pre_swap.blinded_messages());
  166. let attempt_swap = mint.process_swap_request(swap_request).await;
  167. assert!(attempt_swap.is_ok());
  168. Ok(())
  169. }
  170. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  171. async fn test_swap_unbalanced() -> Result<()> {
  172. let mint = initialize().await;
  173. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  174. let keyset_id = Id::from(&keys);
  175. let proofs = mint_proofs(mint, 100.into(), &SplitTarget::default(), keys).await?;
  176. let preswap = PreMintSecrets::random(keyset_id, 95.into(), &SplitTarget::default())?;
  177. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  178. match mint.process_swap_request(swap_request).await {
  179. Ok(_) => bail!("Swap was allowed unbalanced"),
  180. Err(err) => match err {
  181. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  182. _ => bail!("Wrong error returned"),
  183. },
  184. }
  185. let preswap = PreMintSecrets::random(keyset_id, 101.into(), &SplitTarget::default())?;
  186. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  187. match mint.process_swap_request(swap_request).await {
  188. Ok(_) => bail!("Swap was allowed unbalanced"),
  189. Err(err) => match err {
  190. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  191. _ => bail!("Wrong error returned"),
  192. },
  193. }
  194. Ok(())
  195. }
  196. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  197. async fn test_swap_overpay_underpay_fee() -> Result<()> {
  198. let mint = new_mint(1).await;
  199. mint.rotate_keyset(CurrencyUnit::Sat, 1, 32, 1).await?;
  200. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  201. let keyset_id = Id::from(&keys);
  202. let proofs = mint_proofs(&mint, 1000.into(), &SplitTarget::default(), keys).await?;
  203. let preswap = PreMintSecrets::random(keyset_id, 9998.into(), &SplitTarget::default())?;
  204. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  205. // Attempt to swap overpaying fee
  206. match mint.process_swap_request(swap_request).await {
  207. Ok(_) => bail!("Swap was allowed unbalanced"),
  208. Err(err) => match err {
  209. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  210. _ => {
  211. println!("{:?}", err);
  212. bail!("Wrong error returned")
  213. }
  214. },
  215. }
  216. let preswap = PreMintSecrets::random(keyset_id, 1000.into(), &SplitTarget::default())?;
  217. let swap_request = SwapRequest::new(proofs.clone(), preswap.blinded_messages());
  218. // Attempt to swap underpaying fee
  219. match mint.process_swap_request(swap_request).await {
  220. Ok(_) => bail!("Swap was allowed unbalanced"),
  221. Err(err) => match err {
  222. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  223. _ => {
  224. println!("{:?}", err);
  225. bail!("Wrong error returned")
  226. }
  227. },
  228. }
  229. Ok(())
  230. }
  231. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  232. async fn test_mint_enforce_fee() -> Result<()> {
  233. let mint = new_mint(1).await;
  234. let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
  235. let keyset_id = Id::from(&keys);
  236. let mut proofs = mint_proofs(&mint, 1010.into(), &SplitTarget::Value(1.into()), keys).await?;
  237. let five_proofs: Vec<_> = proofs.drain(..5).collect();
  238. let preswap = PreMintSecrets::random(keyset_id, 5.into(), &SplitTarget::default())?;
  239. let swap_request = SwapRequest::new(five_proofs.clone(), preswap.blinded_messages());
  240. // Attempt to swap underpaying fee
  241. match mint.process_swap_request(swap_request).await {
  242. Ok(_) => bail!("Swap was allowed unbalanced"),
  243. Err(err) => match err {
  244. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  245. _ => {
  246. println!("{:?}", err);
  247. bail!("Wrong error returned")
  248. }
  249. },
  250. }
  251. let preswap = PreMintSecrets::random(keyset_id, 4.into(), &SplitTarget::default())?;
  252. let swap_request = SwapRequest::new(five_proofs.clone(), preswap.blinded_messages());
  253. let _ = mint.process_swap_request(swap_request).await?;
  254. let thousnad_proofs: Vec<_> = proofs.drain(..1001).collect();
  255. let preswap = PreMintSecrets::random(keyset_id, 1000.into(), &SplitTarget::default())?;
  256. let swap_request = SwapRequest::new(thousnad_proofs.clone(), preswap.blinded_messages());
  257. // Attempt to swap underpaying fee
  258. match mint.process_swap_request(swap_request).await {
  259. Ok(_) => bail!("Swap was allowed unbalanced"),
  260. Err(err) => match err {
  261. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  262. _ => {
  263. println!("{:?}", err);
  264. bail!("Wrong error returned")
  265. }
  266. },
  267. }
  268. let preswap = PreMintSecrets::random(keyset_id, 999.into(), &SplitTarget::default())?;
  269. let swap_request = SwapRequest::new(thousnad_proofs.clone(), preswap.blinded_messages());
  270. let _ = mint.process_swap_request(swap_request).await?;
  271. Ok(())
  272. }