lib.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_sdk::{
  19. crypto::{ContractId, MerkleNode, MerkleTree, PublicKey},
  20. db::{db_init, db_lookup, db_set},
  21. define_contract,
  22. error::ContractResult,
  23. merkle::merkle_add,
  24. msg,
  25. pasta::{arithmetic::CurveAffine, group::Curve, pallas},
  26. tx::ContractCall,
  27. util::set_return_data,
  28. };
  29. use darkfi_serial::{
  30. deserialize, serialize, Encodable, SerialDecodable, SerialEncodable, WriteExt,
  31. };
  32. #[repr(u8)]
  33. pub enum MoneyFunction {
  34. Transfer = 0x00,
  35. }
  36. impl From<u8> for MoneyFunction {
  37. fn from(b: u8) -> Self {
  38. match b {
  39. 0x00 => Self::Transfer,
  40. _ => panic!("Invalid function ID: {:#04x?}", b),
  41. }
  42. }
  43. }
  44. #[derive(SerialEncodable, SerialDecodable)]
  45. pub struct MoneyTransferParams {
  46. /// Clear inputs
  47. pub clear_inputs: Vec<ClearInput>,
  48. /// Anonymous inputs
  49. pub inputs: Vec<Input>,
  50. /// Anonymous outputs
  51. pub outputs: Vec<Output>,
  52. }
  53. #[derive(SerialEncodable, SerialDecodable)]
  54. pub struct MoneyTransferUpdate {
  55. /// Nullifiers
  56. pub nullifiers: Vec<pallas::Base>,
  57. /// Coins
  58. pub coins: Vec<pallas::Base>,
  59. }
  60. /// A transaction's clear input
  61. #[derive(SerialEncodable, SerialDecodable)]
  62. pub struct ClearInput {
  63. /// Input's value (amount)
  64. pub value: u64,
  65. /// Input's token ID
  66. pub token_id: pallas::Base,
  67. /// Blinding factor for `value`
  68. pub value_blind: pallas::Scalar,
  69. /// Blinding factor for `token_id`
  70. pub token_blind: pallas::Scalar,
  71. /// Public key for the signature
  72. pub signature_public: PublicKey,
  73. }
  74. /// A transaction's anonymous input
  75. #[derive(SerialEncodable, SerialDecodable)]
  76. pub struct Input {
  77. // Public inputs for the zero-knowledge proof
  78. pub value_commit: pallas::Point,
  79. pub token_commit: pallas::Point,
  80. pub nullifier: pallas::Base,
  81. pub merkle_root: pallas::Base,
  82. pub spend_hook: pallas::Base,
  83. pub user_data_enc: pallas::Base,
  84. pub signature_public: PublicKey,
  85. }
  86. /// A transaction's anonymous output
  87. #[derive(SerialEncodable, SerialDecodable)]
  88. pub struct Output {
  89. // Public inputs for the zero-knowledge proof
  90. pub value_commit: pallas::Point,
  91. pub token_commit: pallas::Point,
  92. pub coin: pallas::Base,
  93. /// The encrypted note ciphertext
  94. pub ciphertext: Vec<u8>,
  95. pub ephem_public: PublicKey,
  96. }
  97. define_contract!(
  98. init: init_contract,
  99. exec: process_instruction,
  100. apply: process_update,
  101. metadata: get_metadata
  102. );
  103. fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
  104. let info_db = db_init(cid, "info")?;
  105. let _ = db_init(cid, "coin_roots")?;
  106. let coin_tree = MerkleTree::new(100);
  107. let mut coin_tree_data = Vec::new();
  108. coin_tree_data.write_u32(0)?;
  109. coin_tree.encode(&mut coin_tree_data)?;
  110. db_set(info_db, &serialize(&"coin_tree".to_string()), &coin_tree_data)?;
  111. let _ = db_init(cid, "nulls")?;
  112. Ok(())
  113. }
  114. fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
  115. let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
  116. assert!(call_idx < call.len() as u32);
  117. let self_ = &call[call_idx as usize];
  118. match MoneyFunction::from(self_.data[0]) {
  119. MoneyFunction::Transfer => {
  120. let data = &self_.data[1..];
  121. let params: MoneyTransferParams = deserialize(data)?;
  122. let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = Vec::new();
  123. let mut signature_public_keys: Vec<pallas::Point> = Vec::new();
  124. for input in &params.clear_inputs {
  125. signature_public_keys.push(input.signature_public.inner());
  126. }
  127. for input in &params.inputs {
  128. let value_coords = input.value_commit.to_affine().coordinates().unwrap();
  129. let token_coords = input.token_commit.to_affine().coordinates().unwrap();
  130. let (sig_x, sig_y) = input.signature_public.xy();
  131. zk_public_values.push((
  132. "money-transfer-burn".to_string(),
  133. vec![
  134. input.nullifier,
  135. *value_coords.x(),
  136. *value_coords.y(),
  137. *token_coords.x(),
  138. *token_coords.y(),
  139. input.merkle_root,
  140. input.user_data_enc,
  141. sig_x,
  142. sig_y,
  143. ],
  144. ));
  145. signature_public_keys.push(input.signature_public.inner());
  146. }
  147. for output in &params.outputs {
  148. let value_coords = output.value_commit.to_affine().coordinates().unwrap();
  149. let token_coords = output.token_commit.to_affine().coordinates().unwrap();
  150. zk_public_values.push((
  151. "money-transfer-mint".to_string(),
  152. vec![
  153. output.coin,
  154. *value_coords.x(),
  155. *value_coords.y(),
  156. *token_coords.x(),
  157. *token_coords.y(),
  158. ],
  159. ));
  160. }
  161. let mut metadata = Vec::new();
  162. zk_public_values.encode(&mut metadata)?;
  163. signature_public_keys.encode(&mut metadata)?;
  164. set_return_data(&metadata)?;
  165. }
  166. }
  167. Ok(())
  168. }
  169. fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
  170. let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
  171. assert!(call_idx < call.len() as u32);
  172. let self_ = &call[call_idx as usize];
  173. match MoneyFunction::from(self_.data[0]) {
  174. MoneyFunction::Transfer => {
  175. let data = &self_.data[1..];
  176. let params: MoneyTransferParams = deserialize(data)?;
  177. // TODO: implement state_transition() checks here
  178. let update = MoneyTransferUpdate {
  179. nullifiers: params.inputs.iter().map(|input| input.nullifier).collect(),
  180. coins: params.outputs.iter().map(|output| output.coin).collect(),
  181. };
  182. let mut update_data = Vec::new();
  183. update_data.write_u8(MoneyFunction::Transfer as u8)?;
  184. update.encode(&mut update_data)?;
  185. set_return_data(&update_data)?;
  186. msg!("update is set!");
  187. }
  188. }
  189. Ok(())
  190. }
  191. fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
  192. match MoneyFunction::from(update_data[0]) {
  193. MoneyFunction::Transfer => {
  194. let data = &update_data[1..];
  195. let update: MoneyTransferUpdate = deserialize(data)?;
  196. let db_info = db_lookup(cid, "info")?;
  197. let db_nulls = db_lookup(cid, "nulls")?;
  198. for nullifier in update.nullifiers {
  199. db_set(db_nulls, &serialize(&nullifier), &[])?;
  200. }
  201. let db_roots = db_lookup(cid, "coin_roots")?;
  202. for coin in update.coins {
  203. let node = MerkleNode::new(coin);
  204. // TODO: merkle_add() should take a list of coins and batch add them
  205. // for efficiency
  206. merkle_add(db_info, db_roots, &serialize(&"coin_tree".to_string()), &node)?;
  207. }
  208. }
  209. }
  210. Ok(())
  211. }