mint.rs 11 KB

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