integration.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  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::{tx::Transaction, Result};
  19. use darkfi_sdk::{
  20. crypto::{
  21. merkle_prelude::*, pallas, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Coin,
  22. Keypair, MerkleNode, MerkleTree, SecretKey, TokenId, DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
  23. },
  24. ContractCall,
  25. };
  26. use darkfi_serial::{Decodable, Encodable};
  27. use log::debug;
  28. use rand::rngs::OsRng;
  29. use darkfi_dao_contract::{dao_client, money_client, note, wallet_cache::WalletCache, DaoFunction};
  30. use darkfi_money_contract::{client::EncryptedNote, state::MoneyTransferParams, MoneyFunction};
  31. mod harness;
  32. use harness::{init_logger, DaoTestHarness};
  33. // TODO: Anonymity leaks in this proof of concept:
  34. //
  35. // * Vote updates are linked to the proposal_bulla
  36. // * Nullifier of vote will link vote with the coin when it's spent
  37. // TODO: strategize and cleanup Result/Error usage
  38. // TODO: fix up code doc
  39. // TODO: db_* errors returned from runtime should be more specific.
  40. // TODO: db_* functions should be consistently ordered
  41. // TODO: migrate rest of func calls below to make() format and cleanup
  42. #[async_std::test]
  43. async fn integration_test() -> Result<()> {
  44. init_logger()?;
  45. let dao_th = DaoTestHarness::new().await?;
  46. // Money parameters
  47. let xdrk_supply = 1_000_000;
  48. let xdrk_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
  49. // Governance token parameters
  50. let gdrk_supply = 1_000_000;
  51. let gdrk_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
  52. // DAO parameters
  53. let dao = dao_client::DaoInfo {
  54. proposer_limit: 110,
  55. quorum: 110,
  56. approval_ratio_base: 2,
  57. approval_ratio_quot: 1,
  58. gov_token_id: gdrk_token_id,
  59. public_key: dao_th.dao_kp.public,
  60. bulla_blind: pallas::Base::random(&mut OsRng),
  61. };
  62. // We use this to receive coins
  63. let mut cache = WalletCache::new();
  64. // =======================================================
  65. // Dao::Mint
  66. //
  67. // Create the DAO bulla
  68. // =======================================================
  69. debug!(target: "dao", "Stage 1. Creating DAO bulla");
  70. let (params, proofs) =
  71. dao_client::make_mint_call(&dao, &dao_th.dao_mint_zkbin, &dao_th.dao_mint_pk)?;
  72. let mut data = vec![DaoFunction::Mint as u8];
  73. params.encode(&mut data)?;
  74. let calls = vec![ContractCall { contract_id: dao_th.dao_contract_id, data }];
  75. let proofs = vec![proofs];
  76. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  77. let sigs = tx.create_sigs(&mut OsRng, &[])?;
  78. tx.signatures = vec![sigs];
  79. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  80. // TODO: Witness and add to wallet merkle tree?
  81. let mut dao_tree = MerkleTree::new(100);
  82. let dao_leaf_position = {
  83. let node = MerkleNode::from(params.dao_bulla.inner());
  84. dao_tree.append(&node);
  85. dao_tree.witness().unwrap()
  86. };
  87. let dao_bulla = params.dao_bulla;
  88. debug!(target: "dao", "Created DAO bulla: {:?}", dao_bulla.inner());
  89. // =======================================================
  90. // Money::Transfer
  91. //
  92. // Mint the initial supply of treasury token
  93. // and send it all to the DAO directly
  94. // =======================================================
  95. debug!(target: "dao", "Stage 2. Minting treasury token");
  96. cache.track(dao_th.dao_kp.secret);
  97. // Address of deployed contract in our example is dao::exec::FUNC_ID
  98. // This field is public, you can see it's being sent to a DAO
  99. // but nothing else is visible.
  100. //
  101. // In the python code we wrote:
  102. //
  103. // spend_hook = b"0xdao_ruleset"
  104. //
  105. // TODO: this should be the contract/func ID
  106. let spend_hook = DAO_CONTRACT_ID.inner();
  107. // The user_data can be a simple hash of the items passed into the ZK proof
  108. // up to corresponding linked ZK proof to interpret however they need.
  109. // In out case, it's the bulla for the DAO
  110. let user_data = dao_bulla.inner();
  111. let call = money_client::TransferCall {
  112. clear_inputs: vec![money_client::TransferClearInput {
  113. value: xdrk_supply,
  114. token_id: xdrk_token_id,
  115. signature_secret: dao_th.faucet_kp.secret,
  116. }],
  117. inputs: vec![],
  118. outputs: vec![money_client::TransferOutput {
  119. value: xdrk_supply,
  120. token_id: xdrk_token_id,
  121. public: dao_th.dao_kp.public,
  122. serial: pallas::Base::random(&mut OsRng),
  123. coin_blind: pallas::Base::random(&mut OsRng),
  124. spend_hook,
  125. user_data,
  126. }],
  127. };
  128. let (params, proofs) = call.make(
  129. &dao_th.money_mint_zkbin,
  130. &dao_th.money_mint_pk,
  131. &dao_th.money_burn_zkbin,
  132. &dao_th.money_burn_pk,
  133. )?;
  134. let contract_id = *MONEY_CONTRACT_ID;
  135. let mut data = vec![MoneyFunction::Transfer as u8];
  136. params.encode(&mut data)?;
  137. let calls = vec![ContractCall { contract_id, data }];
  138. let proofs = vec![proofs];
  139. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  140. let sigs = tx.create_sigs(&mut OsRng, &vec![dao_th.faucet_kp.secret])?;
  141. tx.signatures = vec![sigs];
  142. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  143. // Wallet stuff
  144. // DAO reads the money received from the encrypted note
  145. {
  146. assert_eq!(tx.calls.len(), 1);
  147. let calldata = &tx.calls[0].data;
  148. let params_data = &calldata[1..];
  149. let params: MoneyTransferParams = Decodable::decode(params_data)?;
  150. for output in params.outputs {
  151. let coin = output.coin;
  152. let enc_note =
  153. EncryptedNote { ciphertext: output.ciphertext, ephem_public: output.ephem_public };
  154. let coin = Coin(coin);
  155. cache.try_decrypt_note(coin, &enc_note);
  156. }
  157. }
  158. let mut recv_coins = cache.get_received(&dao_th.dao_kp.secret);
  159. assert_eq!(recv_coins.len(), 1);
  160. let dao_recv_coin = recv_coins.pop().unwrap();
  161. let treasury_note = dao_recv_coin.note;
  162. // Check the actual coin received is valid before accepting it
  163. let coords = dao_th.dao_kp.public.inner().to_affine().coordinates().unwrap();
  164. let coin = poseidon_hash::<8>([
  165. *coords.x(),
  166. *coords.y(),
  167. pallas::Base::from(treasury_note.value),
  168. treasury_note.token_id.inner(),
  169. treasury_note.serial,
  170. treasury_note.spend_hook,
  171. treasury_note.user_data,
  172. treasury_note.coin_blind,
  173. ]);
  174. assert_eq!(coin, dao_recv_coin.coin.0);
  175. assert_eq!(treasury_note.spend_hook, spend_hook);
  176. assert_eq!(treasury_note.user_data, dao_bulla.inner());
  177. debug!(target: "dao", "DAO received a coin worth {} xDRK", treasury_note.value);
  178. // =======================================================
  179. // Money::Transfer
  180. //
  181. // Mint the governance token
  182. // Send it to three hodlers
  183. // =======================================================
  184. debug!(target: "dao", "Stage 3. Minting governance token");
  185. cache.track(dao_th.alice_kp.secret);
  186. cache.track(dao_th.bob_kp.secret);
  187. cache.track(dao_th.charlie_kp.secret);
  188. // Spend hook and user data disabled
  189. let spend_hook = pallas::Base::from(0);
  190. let user_data = pallas::Base::from(0);
  191. let output1 = money_client::TransferOutput {
  192. value: 400000,
  193. token_id: gdrk_token_id,
  194. public: dao_th.alice_kp.public,
  195. serial: pallas::Base::random(&mut OsRng),
  196. coin_blind: pallas::Base::random(&mut OsRng),
  197. spend_hook,
  198. user_data,
  199. };
  200. let output2 = money_client::TransferOutput {
  201. value: 400000,
  202. token_id: gdrk_token_id,
  203. public: dao_th.bob_kp.public,
  204. serial: pallas::Base::random(&mut OsRng),
  205. coin_blind: pallas::Base::random(&mut OsRng),
  206. spend_hook,
  207. user_data,
  208. };
  209. let output3 = money_client::TransferOutput {
  210. value: 200000,
  211. token_id: gdrk_token_id,
  212. public: dao_th.charlie_kp.public,
  213. serial: pallas::Base::random(&mut OsRng),
  214. coin_blind: pallas::Base::random(&mut OsRng),
  215. spend_hook,
  216. user_data,
  217. };
  218. assert!(2 * 400000 + 200000 == gdrk_supply);
  219. let call = money_client::TransferCall {
  220. clear_inputs: vec![money_client::TransferClearInput {
  221. value: gdrk_supply,
  222. token_id: gdrk_token_id,
  223. // This might be different for various tokens but lets reuse it here
  224. signature_secret: dao_th.faucet_kp.secret,
  225. }],
  226. inputs: vec![],
  227. outputs: vec![output1, output2, output3],
  228. };
  229. let (params, proofs) = call.make(
  230. &dao_th.money_mint_zkbin,
  231. &dao_th.money_mint_pk,
  232. &dao_th.money_burn_zkbin,
  233. &dao_th.money_burn_pk,
  234. )?;
  235. let contract_id = *MONEY_CONTRACT_ID;
  236. let mut data = vec![MoneyFunction::Transfer as u8];
  237. params.encode(&mut data)?;
  238. let calls = vec![ContractCall { contract_id, data }];
  239. let proofs = vec![proofs];
  240. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  241. let sigs = tx.create_sigs(&mut OsRng, &vec![dao_th.faucet_kp.secret])?;
  242. tx.signatures = vec![sigs];
  243. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  244. // Wallet
  245. {
  246. assert_eq!(tx.calls.len(), 1);
  247. let calldata = &tx.calls[0].data;
  248. let params_data = &calldata[1..];
  249. let params: MoneyTransferParams = Decodable::decode(params_data)?;
  250. for output in params.outputs {
  251. let coin = output.coin;
  252. let enc_note =
  253. EncryptedNote { ciphertext: output.ciphertext, ephem_public: output.ephem_public };
  254. let coin = Coin(coin);
  255. cache.try_decrypt_note(coin, &enc_note);
  256. }
  257. }
  258. let gov_keypairs = vec![dao_th.alice_kp, dao_th.bob_kp, dao_th.charlie_kp];
  259. let mut gov_recv = vec![None, None, None];
  260. // Check that each person received one coin
  261. for (i, key) in gov_keypairs.iter().enumerate() {
  262. let gov_recv_coin = {
  263. let mut recv_coins = cache.get_received(&key.secret);
  264. assert_eq!(recv_coins.len(), 1);
  265. let recv_coin = recv_coins.pop().unwrap();
  266. let note = &recv_coin.note;
  267. assert_eq!(note.token_id, gdrk_token_id);
  268. // Normal payment
  269. assert_eq!(note.spend_hook, pallas::Base::from(0));
  270. assert_eq!(note.user_data, pallas::Base::from(0));
  271. let (pub_x, pub_y) = key.public.xy();
  272. let coin = poseidon_hash::<8>([
  273. pub_x,
  274. pub_y,
  275. pallas::Base::from(note.value),
  276. note.token_id.inner(),
  277. note.serial,
  278. note.spend_hook,
  279. note.user_data,
  280. note.coin_blind,
  281. ]);
  282. assert_eq!(coin, recv_coin.coin.0);
  283. debug!(target: "dao", "Holder{} received a coin worth {} gDRK", i, note.value);
  284. recv_coin
  285. };
  286. gov_recv[i] = Some(gov_recv_coin);
  287. }
  288. // unwrap them for this demo
  289. let gov_recv: Vec<_> = gov_recv.into_iter().map(|r| r.unwrap()).collect();
  290. // =======================================================
  291. // Dao::Propose
  292. //
  293. // Propose the vote
  294. // In order to make a valid vote, first the proposer must
  295. // meet a criteria for a minimum number of gov tokens
  296. //
  297. // DAO rules:
  298. // 1. gov token IDs must match on all inputs
  299. // 2. proposals must be submitted by minimum amount
  300. // 3. all votes >= quorum
  301. // 4. outcome > approval_ratio
  302. // 5. structure of outputs
  303. // output 0: value and address
  304. // output 1: change address
  305. // =======================================================
  306. debug!(target: "dao", "Stage 4. Propose the vote");
  307. // TODO: look into proposal expiry once time for voting has finished
  308. let receiver_keypair = Keypair::random(&mut OsRng);
  309. let (money_leaf_position, money_merkle_path) = {
  310. let tree = &cache.tree;
  311. let leaf_position = gov_recv[0].leaf_position;
  312. let root = tree.root(0).unwrap();
  313. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  314. (leaf_position, merkle_path)
  315. };
  316. // TODO: is it possible for an invalid transfer() to be constructed on exec()?
  317. // need to look into this
  318. let signature_secret = SecretKey::random(&mut OsRng);
  319. let input = dao_client::ProposeStakeInput {
  320. secret: dao_th.alice_kp.secret,
  321. note: gov_recv[0].note.clone(),
  322. leaf_position: money_leaf_position,
  323. merkle_path: money_merkle_path,
  324. signature_secret,
  325. };
  326. let (dao_merkle_path, dao_merkle_root) = {
  327. let tree = &dao_tree;
  328. let root = tree.root(0).unwrap();
  329. let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
  330. (merkle_path, root)
  331. };
  332. let proposal = dao_client::ProposalInfo {
  333. dest: receiver_keypair.public,
  334. amount: 1000,
  335. serial: pallas::Base::random(&mut OsRng),
  336. token_id: xdrk_token_id,
  337. blind: pallas::Base::random(&mut OsRng),
  338. };
  339. let call = dao_client::ProposeCall {
  340. inputs: vec![input],
  341. proposal,
  342. dao: dao.clone(),
  343. dao_leaf_position,
  344. dao_merkle_path,
  345. dao_merkle_root,
  346. };
  347. let (params, proofs) = call.make(
  348. &dao_th.dao_propose_burn_zkbin,
  349. &dao_th.dao_propose_burn_pk,
  350. &dao_th.dao_propose_main_zkbin,
  351. &dao_th.dao_propose_main_pk,
  352. )?;
  353. let contract_id = *DAO_CONTRACT_ID;
  354. let mut data = vec![DaoFunction::Propose as u8];
  355. params.encode(&mut data)?;
  356. let calls = vec![ContractCall { contract_id, data }];
  357. let proofs = vec![proofs];
  358. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  359. let sigs = tx.create_sigs(&mut OsRng, &vec![signature_secret])?;
  360. tx.signatures = vec![sigs];
  361. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  362. //// Wallet
  363. // Read received proposal
  364. let (proposal, proposal_bulla) = {
  365. // TODO: EncryptedNote should be accessible by wasm and put in the structs directly
  366. let enc_note = note::EncryptedNote2 {
  367. ciphertext: params.ciphertext,
  368. ephem_public: params.ephem_public,
  369. };
  370. let note: dao_client::ProposeNote = enc_note.decrypt(&dao_th.dao_kp.secret).unwrap();
  371. // TODO: check it belongs to DAO bulla
  372. // Return the proposal info
  373. (note.proposal, params.proposal_bulla)
  374. };
  375. debug!(target: "dao", "Proposal now active!");
  376. debug!(target: "dao", " destination: {:?}", proposal.dest);
  377. debug!(target: "dao", " amount: {}", proposal.amount);
  378. debug!(target: "dao", " token_id: {:?}", proposal.token_id);
  379. debug!(target: "dao", " dao_bulla: {:?}", dao_bulla.inner());
  380. debug!(target: "dao", "Proposal bulla: {:?}", proposal_bulla);
  381. // =======================================================
  382. // Proposal is accepted!
  383. // Start the voting
  384. // =======================================================
  385. // Copying these schizo comments from python code:
  386. // Lets the voting begin
  387. // Voters have access to the proposal and dao data
  388. // vote_state = VoteState()
  389. // We don't need to copy nullifier set because it is checked from gov_state
  390. // in vote_state_transition() anyway
  391. //
  392. // TODO: what happens if voters don't unblind their vote
  393. // Answer:
  394. // 1. there is a time limit
  395. // 2. both the MPC or users can unblind
  396. //
  397. // TODO: bug if I vote then send money, then we can double vote
  398. // TODO: all timestamps missing
  399. // - timelock (future voting starts in 2 days)
  400. // Fix: use nullifiers from money gov state only from
  401. // beginning of gov period
  402. // Cannot use nullifiers from before voting period
  403. debug!(target: "dao", "Stage 5. Start voting");
  404. // We were previously saving updates here for testing
  405. // let mut updates = vec![];
  406. // User 1: YES
  407. let (money_leaf_position, money_merkle_path) = {
  408. let tree = &cache.tree;
  409. let leaf_position = gov_recv[0].leaf_position;
  410. let root = tree.root(0).unwrap();
  411. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  412. (leaf_position, merkle_path)
  413. };
  414. let signature_secret = SecretKey::random(&mut OsRng);
  415. let input = dao_client::VoteInput {
  416. secret: dao_th.alice_kp.secret,
  417. note: gov_recv[0].note.clone(),
  418. leaf_position: money_leaf_position,
  419. merkle_path: money_merkle_path,
  420. signature_secret,
  421. };
  422. let vote_option: bool = true;
  423. // assert!(vote_option || !vote_option); // wtf
  424. // We create a new keypair to encrypt the vote.
  425. // For the demo MVP, you can just use the dao_keypair secret
  426. let vote_keypair_1 = Keypair::random(&mut OsRng);
  427. let call = dao_client::VoteCall {
  428. inputs: vec![input],
  429. vote: dao_client::VoteInfo {
  430. vote_option,
  431. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  432. },
  433. vote_keypair: vote_keypair_1,
  434. proposal: proposal.clone(),
  435. dao: dao.clone(),
  436. };
  437. let (params, proofs) = call.make(
  438. &dao_th.dao_vote_burn_zkbin,
  439. &dao_th.dao_vote_burn_pk,
  440. &dao_th.dao_vote_main_zkbin,
  441. &dao_th.dao_vote_main_pk,
  442. )?;
  443. let contract_id = *DAO_CONTRACT_ID;
  444. let mut data = vec![DaoFunction::Vote as u8];
  445. params.encode(&mut data)?;
  446. let calls = vec![ContractCall { contract_id, data }];
  447. let proofs = vec![proofs];
  448. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  449. let sigs = tx.create_sigs(&mut OsRng, &vec![signature_secret])?;
  450. tx.signatures = vec![sigs];
  451. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  452. // Secret vote info. Needs to be revealed at some point.
  453. // TODO: look into verifiable encryption for notes
  454. // TODO: look into timelock puzzle as a possibility
  455. let vote_note_1 = {
  456. let enc_note = note::EncryptedNote2 {
  457. ciphertext: params.ciphertext,
  458. ephem_public: params.ephem_public,
  459. };
  460. let note: dao_client::VoteNote = enc_note.decrypt(&vote_keypair_1.secret).unwrap();
  461. note
  462. };
  463. debug!(target: "dao", "User 1 voted!");
  464. debug!(target: "dao", " vote_option: {}", vote_note_1.vote.vote_option);
  465. debug!(target: "dao", " value: {}", vote_note_1.vote_value);
  466. // User 2: NO
  467. let (money_leaf_position, money_merkle_path) = {
  468. let tree = &cache.tree;
  469. let leaf_position = gov_recv[1].leaf_position;
  470. let root = tree.root(0).unwrap();
  471. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  472. (leaf_position, merkle_path)
  473. };
  474. let signature_secret = SecretKey::random(&mut OsRng);
  475. let input = dao_client::VoteInput {
  476. //secret: gov_keypair_2.secret,
  477. secret: dao_th.bob_kp.secret,
  478. note: gov_recv[1].note.clone(),
  479. leaf_position: money_leaf_position,
  480. merkle_path: money_merkle_path,
  481. signature_secret,
  482. };
  483. let vote_option: bool = false;
  484. // assert!(vote_option || !vote_option); // wtf
  485. // We create a new keypair to encrypt the vote.
  486. let vote_keypair_2 = Keypair::random(&mut OsRng);
  487. let call = dao_client::VoteCall {
  488. inputs: vec![input],
  489. vote: dao_client::VoteInfo {
  490. vote_option,
  491. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  492. },
  493. vote_keypair: vote_keypair_2,
  494. proposal: proposal.clone(),
  495. dao: dao.clone(),
  496. };
  497. let (params, proofs) = call.make(
  498. &dao_th.dao_vote_burn_zkbin,
  499. &dao_th.dao_vote_burn_pk,
  500. &dao_th.dao_vote_main_zkbin,
  501. &dao_th.dao_vote_main_pk,
  502. )?;
  503. let contract_id = *DAO_CONTRACT_ID;
  504. let mut data = vec![DaoFunction::Vote as u8];
  505. params.encode(&mut data)?;
  506. let calls = vec![ContractCall { contract_id, data }];
  507. let proofs = vec![proofs];
  508. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  509. let sigs = tx.create_sigs(&mut OsRng, &vec![signature_secret])?;
  510. tx.signatures = vec![sigs];
  511. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  512. let vote_note_2 = {
  513. let enc_note = note::EncryptedNote2 {
  514. ciphertext: params.ciphertext,
  515. ephem_public: params.ephem_public,
  516. };
  517. let note: dao_client::VoteNote = enc_note.decrypt(&vote_keypair_2.secret).unwrap();
  518. note
  519. };
  520. debug!(target: "dao", "User 2 voted!");
  521. debug!(target: "dao", " vote_option: {}", vote_note_2.vote.vote_option);
  522. debug!(target: "dao", " value: {}", vote_note_2.vote_value);
  523. // User 3: YES
  524. let (money_leaf_position, money_merkle_path) = {
  525. let tree = &cache.tree;
  526. let leaf_position = gov_recv[2].leaf_position;
  527. let root = tree.root(0).unwrap();
  528. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  529. (leaf_position, merkle_path)
  530. };
  531. let signature_secret = SecretKey::random(&mut OsRng);
  532. let input = dao_client::VoteInput {
  533. //secret: gov_keypair_3.secret,
  534. secret: dao_th.charlie_kp.secret,
  535. note: gov_recv[2].note.clone(),
  536. leaf_position: money_leaf_position,
  537. merkle_path: money_merkle_path,
  538. signature_secret,
  539. };
  540. let vote_option: bool = true;
  541. // assert!(vote_option || !vote_option); // wtf
  542. // We create a new keypair to encrypt the vote.
  543. let vote_keypair_3 = Keypair::random(&mut OsRng);
  544. let call = dao_client::VoteCall {
  545. inputs: vec![input],
  546. vote: dao_client::VoteInfo {
  547. vote_option,
  548. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  549. },
  550. vote_keypair: vote_keypair_3,
  551. proposal: proposal.clone(),
  552. dao: dao.clone(),
  553. };
  554. let (params, proofs) = call.make(
  555. &dao_th.dao_vote_burn_zkbin,
  556. &dao_th.dao_vote_burn_pk,
  557. &dao_th.dao_vote_main_zkbin,
  558. &dao_th.dao_vote_main_pk,
  559. )?;
  560. let contract_id = *DAO_CONTRACT_ID;
  561. let mut data = vec![DaoFunction::Vote as u8];
  562. params.encode(&mut data)?;
  563. let calls = vec![ContractCall { contract_id, data }];
  564. let proofs = vec![proofs];
  565. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  566. let sigs = tx.create_sigs(&mut OsRng, &vec![signature_secret])?;
  567. tx.signatures = vec![sigs];
  568. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  569. // Secret vote info. Needs to be revealed at some point.
  570. // TODO: look into verifiable encryption for notes
  571. // TODO: look into timelock puzzle as a possibility
  572. let vote_note_3 = {
  573. let enc_note = note::EncryptedNote2 {
  574. ciphertext: params.ciphertext,
  575. ephem_public: params.ephem_public,
  576. };
  577. let note: dao_client::VoteNote = enc_note.decrypt(&vote_keypair_3.secret).unwrap();
  578. note
  579. };
  580. debug!(target: "dao", "User 3 voted!");
  581. debug!(target: "dao", " vote_option: {}", vote_note_3.vote.vote_option);
  582. debug!(target: "dao", " value: {}", vote_note_3.vote_value);
  583. // Every votes produces a semi-homomorphic encryption of their vote.
  584. // Which is either yes or no
  585. // We copy the state tree for the governance token so coins can be used
  586. // to vote on other proposals at the same time.
  587. // With their vote, they produce a ZK proof + nullifier
  588. // The votes are unblinded by MPC to a selected party at the end of the
  589. // voting period.
  590. // (that's if we want votes to be hidden during voting)
  591. let mut yes_votes_value = 0;
  592. let mut yes_votes_blind = pallas::Scalar::from(0);
  593. let mut yes_votes_commit = pallas::Point::identity();
  594. let mut all_votes_value = 0;
  595. let mut all_votes_blind = pallas::Scalar::from(0);
  596. let mut all_votes_commit = pallas::Point::identity();
  597. // We were previously saving votes to a Vec<Update> for testing.
  598. // However since Update is now UpdateBase it gets moved into update.apply().
  599. // So we need to think of another way to run these tests.
  600. //assert!(updates.len() == 3);
  601. for (i, note /* update*/) in [vote_note_1, vote_note_2, vote_note_3]
  602. .iter() /*.zip(updates)*/
  603. .enumerate()
  604. {
  605. let vote_commit = pedersen_commitment_u64(note.vote_value, note.vote_value_blind);
  606. //assert!(update.value_commit == all_vote_value_commit);
  607. all_votes_commit += vote_commit;
  608. all_votes_blind += note.vote_value_blind;
  609. let yes_vote_commit = pedersen_commitment_u64(
  610. note.vote.vote_option as u64 * note.vote_value,
  611. note.vote.vote_option_blind,
  612. );
  613. //assert!(update.yes_vote_commit == yes_vote_commit);
  614. yes_votes_commit += yes_vote_commit;
  615. yes_votes_blind += note.vote.vote_option_blind;
  616. let vote_option = note.vote.vote_option;
  617. if vote_option {
  618. yes_votes_value += note.vote_value;
  619. }
  620. all_votes_value += note.vote_value;
  621. let vote_result: String = if vote_option { "yes".to_string() } else { "no".to_string() };
  622. debug!(target: "dao", "Voter {} voted {}", i, vote_result);
  623. }
  624. debug!(target: "dao", "Outcome = {} / {}", yes_votes_value, all_votes_value);
  625. assert!(all_votes_commit == pedersen_commitment_u64(all_votes_value, all_votes_blind));
  626. assert!(yes_votes_commit == pedersen_commitment_u64(yes_votes_value, yes_votes_blind));
  627. // =======================================================
  628. // Execute the vote
  629. // =======================================================
  630. debug!(target: "dao", "Stage 6. Execute vote");
  631. // Used to export user_data from this coin so it can be accessed by DAO::exec()
  632. let user_data_blind = pallas::Base::random(&mut OsRng);
  633. let user_serial = pallas::Base::random(&mut OsRng);
  634. let user_coin_blind = pallas::Base::random(&mut OsRng);
  635. let dao_serial = pallas::Base::random(&mut OsRng);
  636. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  637. let input_value = treasury_note.value;
  638. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  639. let tx_signature_secret = SecretKey::random(&mut OsRng);
  640. let exec_signature_secret = SecretKey::random(&mut OsRng);
  641. let (treasury_leaf_position, treasury_merkle_path) = {
  642. let tree = &cache.tree;
  643. let leaf_position = dao_recv_coin.leaf_position;
  644. let root = tree.root(0).unwrap();
  645. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  646. (leaf_position, merkle_path)
  647. };
  648. // TODO: this should be the contract/func ID
  649. //let spend_hook = pallas::Base::from(110);
  650. let spend_hook = DAO_CONTRACT_ID.inner();
  651. // The user_data can be a simple hash of the items passed into the ZK proof
  652. // up to corresponding linked ZK proof to interpret however they need.
  653. // In out case, it's the bulla for the DAO
  654. let user_data = dao_bulla.inner();
  655. let xfer_call = money_client::TransferCall {
  656. clear_inputs: vec![],
  657. inputs: vec![money_client::TransferInput {
  658. leaf_position: treasury_leaf_position,
  659. merkle_path: treasury_merkle_path,
  660. secret: dao_th.dao_kp.secret,
  661. note: treasury_note,
  662. user_data_blind,
  663. value_blind: input_value_blind,
  664. signature_secret: tx_signature_secret,
  665. }],
  666. outputs: vec![
  667. // Sending money
  668. money_client::TransferOutput {
  669. value: 1000,
  670. token_id: xdrk_token_id,
  671. //public: user_keypair.public,
  672. public: receiver_keypair.public,
  673. serial: proposal.serial,
  674. coin_blind: proposal.blind,
  675. spend_hook: pallas::Base::from(0),
  676. user_data: pallas::Base::from(0),
  677. },
  678. // Change back to DAO
  679. money_client::TransferOutput {
  680. value: xdrk_supply - 1000,
  681. token_id: xdrk_token_id,
  682. public: dao_th.dao_kp.public,
  683. serial: dao_serial,
  684. coin_blind: dao_coin_blind,
  685. spend_hook,
  686. user_data,
  687. },
  688. ],
  689. };
  690. let (xfer_params, xfer_proofs) = xfer_call.make(
  691. &dao_th.money_mint_zkbin,
  692. &dao_th.money_mint_pk,
  693. &dao_th.money_burn_zkbin,
  694. &dao_th.money_burn_pk,
  695. )?;
  696. let mut data = vec![MoneyFunction::Transfer as u8];
  697. xfer_params.encode(&mut data)?;
  698. let xfer_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  699. let call = dao_client::ExecCall {
  700. proposal,
  701. dao,
  702. yes_votes_value,
  703. all_votes_value,
  704. yes_votes_blind,
  705. all_votes_blind,
  706. user_serial,
  707. user_coin_blind,
  708. dao_serial,
  709. dao_coin_blind,
  710. input_value,
  711. input_value_blind,
  712. hook_dao_exec: spend_hook,
  713. signature_secret: exec_signature_secret,
  714. };
  715. let (exec_params, exec_proofs) = call.make(&dao_th.dao_exec_zkbin, &dao_th.dao_exec_pk)?;
  716. let mut data = vec![DaoFunction::Exec as u8];
  717. exec_params.encode(&mut data)?;
  718. let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
  719. let mut tx = Transaction {
  720. calls: vec![xfer_call, exec_call],
  721. proofs: vec![xfer_proofs, exec_proofs],
  722. signatures: vec![],
  723. };
  724. let xfer_sigs = tx.create_sigs(&mut OsRng, &vec![tx_signature_secret])?;
  725. let exec_sigs = tx.create_sigs(&mut OsRng, &vec![exec_signature_secret])?;
  726. tx.signatures = vec![xfer_sigs, exec_sigs];
  727. dao_th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
  728. Ok(())
  729. }