wallet.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // We want Scalars to be lowercase letters, and Points and credentials
  2. // to be capital letters
  3. #![allow(non_snake_case)]
  4. use cmz::*;
  5. use curve25519_dalek::ristretto::RistrettoPoint as G;
  6. use ff::PrimeField;
  7. use group::Group;
  8. use rand_core::RngCore;
  9. use sha2::Sha512;
  10. CMZ! { Wallet: randid, balance }
  11. CMZ! { Item: serialno, price }
  12. CMZProtocol! { wallet_issue,
  13. ,
  14. W: Wallet { randid: J, balance: S },
  15. }
  16. CMZProtocol! { item_issue,
  17. ,
  18. I: Item { serialno: S, price: S },
  19. }
  20. CMZProtocol! { wallet_spend,
  21. [ W: Wallet { randid: R, balance: H },
  22. I: Item { serialno: H, price: H } ],
  23. N: Wallet { randid: J, balance: H },
  24. N.balance >= 0,
  25. W.balance = N.balance + I.price
  26. }
  27. CMZProtocol! { wallet_spend_with_fee<fee>,
  28. [ W: Wallet { randid: R, balance: H },
  29. I: Item { serialno: H, price: H } ],
  30. N: Wallet { randid: J, balance: H },
  31. N.balance >= 0,
  32. W.balance = N.balance + I.price + fee
  33. }
  34. // The issuer runs this on its own to create an Item credential for a
  35. // particular item (specified by a serial number) with a given price.
  36. fn issue_item(
  37. rng: &mut impl RngCore,
  38. serialno: u128,
  39. price: u128,
  40. privkey: &CMZPrivkey<G>,
  41. public: &CMZPubkey<G>,
  42. ) -> Result<Item, CMZError> {
  43. let (request, state) = item_issue::prepare(&mut *rng, Item::using_pubkey(public))?;
  44. let (reply, _) = item_issue::handle(
  45. &mut *rng,
  46. request,
  47. |I: &mut Item| {
  48. I.set_privkey(privkey);
  49. I.serialno = Some(serialno.into());
  50. I.price = Some(price.into());
  51. Ok(())
  52. },
  53. |I: &Item| Ok(()),
  54. )?;
  55. let res = state.finalize(reply);
  56. match res {
  57. Ok(c) => Ok(c),
  58. Err((err, _state)) => Err(err),
  59. }
  60. }
  61. // The issuer runs this on its own to create an initial wallet loaded
  62. // with funds, to sent to a client. The issuer will presumably charge
  63. // the client out of band for this loaded wallet.
  64. fn issue_wallet(
  65. rng: &mut impl RngCore,
  66. balance: u128,
  67. privkey: &CMZPrivkey<G>,
  68. public: &CMZPubkey<G>,
  69. ) -> Result<Wallet, CMZError> {
  70. let (request, state) = wallet_issue::prepare(&mut *rng, Wallet::using_pubkey(public))?;
  71. let (reply, wallet) = wallet_issue::handle(
  72. &mut *rng,
  73. request,
  74. |W: &mut Wallet| {
  75. W.set_privkey(privkey);
  76. W.balance = Some(balance.into());
  77. Ok(())
  78. },
  79. |I: &Wallet| Ok(()),
  80. )?;
  81. let res = state.finalize(reply);
  82. match res {
  83. Ok(c) => Ok(c),
  84. Err((err, _state)) => Err(err),
  85. }
  86. }
  87. #[test]
  88. fn test_wallet() -> Result<(), CMZError> {
  89. // Initialization
  90. let mut rng = rand::thread_rng();
  91. cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
  92. // Issuer: generate private and public keys for each type of
  93. // credential. (The client gets a copy of the public keys.)
  94. let (wallet_priv, wallet_pub) = Wallet::cmz_gen_keys(&mut rng);
  95. let (item_priv, item_pub) = Item::cmz_gen_keys(&mut rng);
  96. // The issuer makes some Item credentials for various items by just
  97. // executing the issuing protocol with itself
  98. let ebook_item = issue_item(&mut rng, 100, 2995, &item_priv, &item_pub)?;
  99. let album_item = issue_item(&mut rng, 200, 995, &item_priv, &item_pub)?;
  100. // In exchange for out of band funds, the issuer generates a loaded
  101. // wallet and sends it to the client.
  102. let initial_wallet = issue_wallet(&mut rng, 10000, &wallet_priv, &wallet_pub)?;
  103. // Buy an item (no fee version)
  104. // client actions
  105. let mut N = Wallet::using_pubkey(&wallet_pub);
  106. N.balance = Some(initial_wallet.balance.unwrap() - ebook_item.price.unwrap());
  107. let (request, state) = wallet_spend::prepare(&mut rng, &initial_wallet, &ebook_item, N)?;
  108. let reqbytes = request.as_bytes();
  109. // issuer actions
  110. let recvreq = wallet_spend::Request::try_from(&reqbytes[..]).unwrap();
  111. let (reply, (_W_issuer, _I_issuer, _N_issuer)) = wallet_spend::handle(
  112. &mut rng,
  113. recvreq,
  114. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  115. W.set_privkey(&wallet_priv);
  116. I.set_privkey(&item_priv);
  117. N.set_privkey(&wallet_priv);
  118. Ok(())
  119. },
  120. |W: &Wallet, I: &Item, N: &Wallet| Ok(()),
  121. )?;
  122. let replybytes = reply.as_bytes();
  123. // client actions
  124. let recvreply = wallet_spend::Reply::try_from(&replybytes[..]).unwrap();
  125. let W_issued = state.finalize(recvreply).unwrap();
  126. // The version of the protocol parameterized by a fee. The client
  127. // and issue must agree on the params.
  128. let params = wallet_spend_with_fee::Params { fee: 5u128.into() };
  129. // client actions
  130. let mut N_fee = Wallet::using_pubkey(&wallet_pub);
  131. N_fee.balance = Some(W_issued.balance.unwrap() - album_item.price.unwrap() - params.fee);
  132. let (request_fee, state_fee) =
  133. wallet_spend_with_fee::prepare(&mut rng, &W_issued, &album_item, N_fee, &params)?;
  134. let reqbytes_fee = request.as_bytes();
  135. // issuer actions
  136. let recvreq_fee = wallet_spend_with_fee::Request::try_from(&reqbytes_fee[..]).unwrap();
  137. let (reply_fee, (_W_fee_issuer, _I_fee_isser, _N_fee_issuer)) = wallet_spend_with_fee::handle(
  138. &mut rng,
  139. recvreq_fee,
  140. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  141. W.set_privkey(&wallet_priv);
  142. I.set_privkey(&item_priv);
  143. N.set_privkey(&wallet_priv);
  144. Ok(wallet_spend_with_fee::Params { fee: params.fee })
  145. },
  146. |W: &Wallet, I: &Item, N: &Wallet| Ok(()),
  147. )?;
  148. let replybytes_fee = reply_fee.as_bytes();
  149. // client actions
  150. let recvreply_fee = wallet_spend_with_fee::Reply::try_from(&replybytes_fee[..]).unwrap();
  151. let W_issued_fee = state_fee.finalize(recvreply_fee).unwrap();
  152. Ok(())
  153. }