integration.rs 33 KB

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