integration.rs 31 KB

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