rpc_dao.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  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 anyhow::{anyhow, Result};
  19. use darkfi::{
  20. tx::Transaction,
  21. zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
  22. zkas::ZkBinary,
  23. };
  24. use darkfi_dao_contract::{
  25. dao_client,
  26. dao_client::{DaoInfo, DaoProposalInfo, DaoVoteCall, DaoVoteInput},
  27. dao_model::DaoBlindAggregateVote,
  28. money_client, DaoFunction, DAO_CONTRACT_ZKAS_DAO_EXEC_NS, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
  29. DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
  30. DAO_CONTRACT_ZKAS_DAO_VOTE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS,
  31. };
  32. use darkfi_money_contract::{
  33. client::OwnCoin, MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
  34. };
  35. use darkfi_sdk::{
  36. crypto::{
  37. pedersen_commitment_u64, Keypair, PublicKey, SecretKey, TokenId, DAO_CONTRACT_ID,
  38. MONEY_CONTRACT_ID,
  39. },
  40. incrementalmerkletree::Tree,
  41. pasta::pallas,
  42. ContractCall,
  43. };
  44. use darkfi_serial::Encodable;
  45. use rand::rngs::OsRng;
  46. use super::Drk;
  47. use crate::wallet_dao::{Dao, DaoProposal};
  48. impl Drk {
  49. /// Mint a DAO on-chain
  50. pub async fn dao_mint(&self, dao_id: u64) -> Result<Transaction> {
  51. let dao = self.get_dao_by_id(dao_id).await?;
  52. if dao.tx_hash.is_some() {
  53. return Err(anyhow!("This DAO seems to have already been minted on-chain"))
  54. }
  55. let dao_info = DaoInfo {
  56. proposer_limit: dao.proposer_limit,
  57. quorum: dao.quorum,
  58. approval_ratio_base: dao.approval_ratio_base,
  59. approval_ratio_quot: dao.approval_ratio_quot,
  60. gov_token_id: dao.gov_token_id,
  61. public_key: PublicKey::from_secret(dao.secret_key),
  62. bulla_blind: dao.bulla_blind,
  63. };
  64. let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
  65. let Some(dao_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_MINT_NS) else {
  66. return Err(anyhow!("DAO Mint circuit not found"));
  67. };
  68. let dao_mint_zkbin = ZkBinary::decode(&dao_mint_zkbin.1)?;
  69. let k = 13;
  70. let dao_mint_circuit =
  71. ZkCircuit::new(empty_witnesses(&dao_mint_zkbin), dao_mint_zkbin.clone());
  72. eprintln!("Creating DAO Mint proving key");
  73. let dao_mint_pk = ProvingKey::build(k, &dao_mint_circuit);
  74. let (params, proofs) =
  75. dao_client::make_mint_call(&dao_info, &dao.secret_key, &dao_mint_zkbin, &dao_mint_pk)?;
  76. let mut data = vec![DaoFunction::Mint as u8];
  77. params.encode(&mut data)?;
  78. let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
  79. let proofs = vec![proofs];
  80. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  81. let sigs = tx.create_sigs(&mut OsRng, &[dao.secret_key])?;
  82. tx.signatures = vec![sigs];
  83. Ok(tx)
  84. }
  85. /// Create a DAO proposal
  86. pub async fn dao_propose(
  87. &self,
  88. dao_id: u64,
  89. recipient: PublicKey,
  90. amount: u64,
  91. token_id: TokenId,
  92. ) -> Result<Transaction> {
  93. let Ok(dao) = self.get_dao_by_id(dao_id).await else {
  94. return Err(anyhow!("DAO not found in wallet"))
  95. };
  96. if dao.leaf_position.is_none() || dao.tx_hash.is_none() {
  97. return Err(anyhow!("DAO seems to not have been deployed yet"))
  98. }
  99. let bulla = dao.bulla();
  100. let owncoins = self.get_coins(false).await?;
  101. let mut dao_owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
  102. dao_owncoins.retain(|x| {
  103. x.note.token_id == token_id &&
  104. x.note.spend_hook == DAO_CONTRACT_ID.inner() &&
  105. x.note.user_data == bulla.inner()
  106. });
  107. let mut gov_owncoins: Vec<OwnCoin> = owncoins.iter().map(|x| x.0.clone()).collect();
  108. gov_owncoins.retain(|x| x.note.token_id == dao.gov_token_id);
  109. if dao_owncoins.is_empty() {
  110. return Err(anyhow!("Did not find any {} coins owned by this DAO", token_id))
  111. }
  112. if gov_owncoins.is_empty() {
  113. return Err(anyhow!("Did not find any governance {} coins in wallet", dao.gov_token_id))
  114. }
  115. if dao_owncoins.iter().map(|x| x.note.value).sum::<u64>() < amount {
  116. return Err(anyhow!("Not enough DAO balance for token ID: {}", token_id))
  117. }
  118. if gov_owncoins.iter().map(|x| x.note.value).sum::<u64>() < dao.proposer_limit {
  119. return Err(anyhow!("Not enough gov token {} balance to propose", dao.gov_token_id))
  120. }
  121. // FIXME: Here we're looking for a coin == proposer_limit but this shouldn't have to
  122. // be the case {
  123. let Some(gov_coin) = gov_owncoins.iter().find(|x| x.note.value == dao.proposer_limit) else {
  124. return Err(anyhow!("Did not find a single gov coin of value {}", dao.proposer_limit));
  125. };
  126. // }
  127. // Lookup the zkas bins
  128. let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
  129. let Some(propose_burn_zkbin) =
  130. zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS) else
  131. {
  132. return Err(anyhow!("Propose Burn circuit not found"))
  133. };
  134. let Some(propose_main_zkbin) =
  135. zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS) else
  136. {
  137. return Err(anyhow!("Propose Main circuit not found"))
  138. };
  139. let propose_burn_zkbin = ZkBinary::decode(&propose_burn_zkbin.1)?;
  140. let propose_main_zkbin = ZkBinary::decode(&propose_main_zkbin.1)?;
  141. let k = 13;
  142. let propose_burn_circuit =
  143. ZkCircuit::new(empty_witnesses(&propose_burn_zkbin), propose_burn_zkbin.clone());
  144. let propose_main_circuit =
  145. ZkCircuit::new(empty_witnesses(&propose_main_zkbin), propose_main_zkbin.clone());
  146. eprintln!("Creating Propose Burn circuit proving key");
  147. let propose_burn_pk = ProvingKey::build(k, &propose_burn_circuit);
  148. eprintln!("Creating Propose Main circuit proving key");
  149. let propose_main_pk = ProvingKey::build(k, &propose_main_circuit);
  150. // Now create the parameters for the proposal tx
  151. let signature_secret = SecretKey::random(&mut OsRng);
  152. // Get the Merkle path for the gov coin in the money tree
  153. let money_merkle_tree = self.get_money_tree().await?;
  154. let root = money_merkle_tree.root(0).unwrap();
  155. let gov_coin_merkle_path =
  156. money_merkle_tree.authentication_path(gov_coin.leaf_position, &root).unwrap();
  157. // Fetch the daos Merkle tree
  158. let (daos_tree, _) = self.get_dao_trees().await?;
  159. let input = dao_client::DaoProposeStakeInput {
  160. secret: gov_coin.secret, // <-- TODO: Is this correct?
  161. note: gov_coin.note.clone(),
  162. leaf_position: gov_coin.leaf_position,
  163. merkle_path: gov_coin_merkle_path,
  164. signature_secret,
  165. };
  166. let (dao_merkle_path, dao_merkle_root) = {
  167. let root = daos_tree.root(0).unwrap();
  168. let leaf_pos = dao.leaf_position.unwrap();
  169. let dao_merkle_path = daos_tree.authentication_path(leaf_pos, &root).unwrap();
  170. (dao_merkle_path, root)
  171. };
  172. let proposal_blind = pallas::Base::random(&mut OsRng);
  173. let proposal = dao_client::DaoProposalInfo {
  174. dest: recipient,
  175. amount,
  176. token_id,
  177. blind: proposal_blind,
  178. };
  179. let daoinfo = DaoInfo {
  180. proposer_limit: dao.proposer_limit,
  181. quorum: dao.quorum,
  182. approval_ratio_quot: dao.approval_ratio_quot,
  183. approval_ratio_base: dao.approval_ratio_base,
  184. gov_token_id: dao.gov_token_id,
  185. public_key: PublicKey::from_secret(dao.secret_key),
  186. bulla_blind: dao.bulla_blind,
  187. };
  188. let call = dao_client::DaoProposeCall {
  189. inputs: vec![input],
  190. proposal,
  191. dao: daoinfo,
  192. dao_leaf_position: dao.leaf_position.unwrap(),
  193. dao_merkle_path,
  194. dao_merkle_root,
  195. };
  196. eprintln!("Creating ZK proofs...");
  197. let (params, proofs) = call.make(
  198. &propose_burn_zkbin,
  199. &propose_burn_pk,
  200. &propose_main_zkbin,
  201. &propose_main_pk,
  202. )?;
  203. let mut data = vec![DaoFunction::Propose as u8];
  204. params.encode(&mut data)?;
  205. let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
  206. let proofs = vec![proofs];
  207. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  208. let sigs = tx.create_sigs(&mut OsRng, &[signature_secret])?;
  209. tx.signatures = vec![sigs];
  210. Ok(tx)
  211. }
  212. /// Vote on a DAO proposal
  213. pub async fn dao_vote(
  214. &self,
  215. dao_id: u64,
  216. proposal_id: u64,
  217. vote_option: bool,
  218. weight: u64,
  219. ) -> Result<Transaction> {
  220. let dao = self.get_dao_by_id(dao_id).await?;
  221. let proposals = self.get_dao_proposals(dao_id).await?;
  222. let Some(proposal) = proposals.iter().find(|x| x.id == proposal_id) else {
  223. return Err(anyhow!("Proposal ID not found"))
  224. };
  225. let money_tree = self.get_money_tree().await?;
  226. let mut coins: Vec<OwnCoin> =
  227. self.get_coins(false).await?.iter().map(|x| x.0.clone()).collect();
  228. coins.retain(|x| x.note.token_id == dao.gov_token_id);
  229. coins.retain(|x| x.note.spend_hook == pallas::Base::zero());
  230. if coins.iter().map(|x| x.note.value).sum::<u64>() < weight {
  231. return Err(anyhow!("Not enough balance for vote weight"))
  232. }
  233. // TODO: The spent coins need to either be marked as spent here, and/or on scan
  234. let mut spent_value = 0;
  235. let mut spent_coins = vec![];
  236. let mut inputs = vec![];
  237. let mut input_secrets = vec![];
  238. // FIXME: We don't take back any change so it's possible to vote with > requested weight.
  239. for coin in coins {
  240. if spent_value >= weight {
  241. break
  242. }
  243. spent_value += coin.note.value;
  244. spent_coins.push(coin.clone());
  245. let signature_secret = SecretKey::random(&mut OsRng);
  246. input_secrets.push(signature_secret);
  247. let root = money_tree.root(0).unwrap();
  248. let leaf_position = coin.leaf_position;
  249. let merkle_path = money_tree.authentication_path(coin.leaf_position, &root).unwrap();
  250. let input = DaoVoteInput {
  251. secret: coin.secret,
  252. note: coin.note.clone(),
  253. leaf_position,
  254. merkle_path,
  255. signature_secret,
  256. };
  257. inputs.push(input);
  258. }
  259. // We use the DAO secret to encrypt the vote.
  260. let vote_keypair = Keypair::new(dao.secret_key);
  261. let proposal_info = DaoProposalInfo {
  262. dest: proposal.recipient,
  263. amount: proposal.amount,
  264. token_id: proposal.token_id,
  265. blind: proposal.bulla_blind,
  266. };
  267. let dao_info = DaoInfo {
  268. proposer_limit: dao.proposer_limit,
  269. quorum: dao.quorum,
  270. approval_ratio_quot: dao.approval_ratio_quot,
  271. approval_ratio_base: dao.approval_ratio_base,
  272. gov_token_id: dao.gov_token_id,
  273. public_key: PublicKey::from_secret(dao.secret_key),
  274. bulla_blind: dao.bulla_blind,
  275. };
  276. let call = DaoVoteCall {
  277. inputs,
  278. vote_option,
  279. yes_vote_blind: pallas::Scalar::random(&mut OsRng),
  280. vote_keypair,
  281. proposal: proposal_info,
  282. dao: dao_info,
  283. };
  284. let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
  285. let Some(dao_vote_burn_zkbin) =
  286. zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_VOTE_BURN_NS) else
  287. {
  288. return Err(anyhow!("DAO Vote Burn circuit not found"))
  289. };
  290. let Some(dao_vote_main_zkbin) =
  291. zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS) else
  292. {
  293. return Err(anyhow!("DAO Vote Main circuit not found"))
  294. };
  295. let dao_vote_burn_zkbin = ZkBinary::decode(&dao_vote_burn_zkbin.1)?;
  296. let dao_vote_main_zkbin = ZkBinary::decode(&dao_vote_main_zkbin.1)?;
  297. let k = 13;
  298. let dao_vote_burn_circuit =
  299. ZkCircuit::new(empty_witnesses(&dao_vote_burn_zkbin), dao_vote_burn_zkbin.clone());
  300. let dao_vote_main_circuit =
  301. ZkCircuit::new(empty_witnesses(&dao_vote_main_zkbin), dao_vote_main_zkbin.clone());
  302. eprintln!("Creating DAO Vote Burn proving key");
  303. let dao_vote_burn_pk = ProvingKey::build(k, &dao_vote_burn_circuit);
  304. eprintln!("Creating DAO Vote Main proving key");
  305. let dao_vote_main_pk = ProvingKey::build(k, &dao_vote_main_circuit);
  306. let (params, proofs) = call.make(
  307. &dao_vote_burn_zkbin,
  308. &dao_vote_burn_pk,
  309. &dao_vote_main_zkbin,
  310. &dao_vote_main_pk,
  311. )?;
  312. let mut data = vec![DaoFunction::Vote as u8];
  313. params.encode(&mut data)?;
  314. let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
  315. let proofs = vec![proofs];
  316. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  317. let sigs = tx.create_sigs(&mut OsRng, &input_secrets)?;
  318. tx.signatures = vec![sigs];
  319. Ok(tx)
  320. }
  321. /// Import given DAO votes into the wallet
  322. /// This function is really bad but I'm also really tired and annoyed.
  323. pub async fn dao_exec(&self, dao: Dao, proposal: DaoProposal) -> Result<Transaction> {
  324. let dao_bulla = dao.bulla();
  325. eprintln!("Fetching proposal's votes");
  326. let votes = self.get_dao_proposal_votes(proposal.id).await?;
  327. // Find the treasury coins that can be used for this proposal
  328. let mut coins: Vec<OwnCoin> =
  329. self.get_coins(false).await?.iter().map(|x| x.0.clone()).collect();
  330. coins.retain(|x| x.note.spend_hook == DAO_CONTRACT_ID.inner());
  331. coins.retain(|x| x.note.user_data == dao_bulla.inner());
  332. coins.retain(|x| x.note.token_id == proposal.token_id);
  333. if coins.iter().map(|x| x.note.value).sum::<u64>() < proposal.amount {
  334. return Err(anyhow!("Not enough balance in DAO treasury to execute proposal"))
  335. }
  336. // Used to export user_data from this coin so it can be accessed by DAO::exec()
  337. let user_data_blind = pallas::Base::random(&mut OsRng);
  338. let user_serial = pallas::Base::random(&mut OsRng);
  339. let user_coin_blind = pallas::Base::random(&mut OsRng);
  340. let dao_serial = pallas::Base::random(&mut OsRng);
  341. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  342. // TODO: FIXME: Clean this up and create an API
  343. let exec_signature_secret = SecretKey::random(&mut OsRng);
  344. let mut xfer_signature_secrets = vec![];
  345. let mut xfer_inputs = vec![];
  346. let mut input_coins = vec![];
  347. let mut input_amount = 0;
  348. for coin in coins {
  349. input_amount += coin.note.value;
  350. input_coins.push(coin);
  351. if input_amount >= proposal.amount {
  352. break
  353. }
  354. }
  355. let money_merkle_tree = self.get_money_tree().await?;
  356. let money_merkle_root = money_merkle_tree.root(0).unwrap();
  357. let mut input_value_blind = pallas::Scalar::from(0);
  358. for coin in &input_coins {
  359. let value_blind = pallas::Scalar::random(&mut OsRng);
  360. let sig_secret = SecretKey::random(&mut OsRng);
  361. xfer_signature_secrets.push(sig_secret);
  362. xfer_inputs.push(money_client::TransferInput {
  363. leaf_position: coin.leaf_position,
  364. merkle_path: money_merkle_tree
  365. .authentication_path(coin.leaf_position, &money_merkle_root)
  366. .unwrap(),
  367. secret: dao.secret_key,
  368. note: coin.note.clone(),
  369. user_data_blind,
  370. value_blind,
  371. signature_secret: sig_secret,
  372. });
  373. input_value_blind += value_blind;
  374. }
  375. let input_sum = input_coins.iter().map(|x| x.note.value).sum::<u64>();
  376. let xfer_outputs = vec![
  377. // Proposal send
  378. money_client::TransferOutput {
  379. value: proposal.amount,
  380. token_id: proposal.token_id,
  381. public: proposal.recipient,
  382. serial: user_serial,
  383. coin_blind: user_coin_blind,
  384. spend_hook: pallas::Base::zero(),
  385. user_data: pallas::Base::zero(),
  386. },
  387. // Change
  388. money_client::TransferOutput {
  389. value: input_sum - proposal.amount,
  390. token_id: proposal.token_id,
  391. public: PublicKey::from_secret(dao.secret_key),
  392. serial: dao_serial,
  393. coin_blind: dao_coin_blind,
  394. spend_hook: DAO_CONTRACT_ID.inner(),
  395. user_data: dao_bulla.inner(),
  396. },
  397. ];
  398. let xfer_call = money_client::TransferCall {
  399. clear_inputs: vec![],
  400. inputs: xfer_inputs,
  401. outputs: xfer_outputs,
  402. };
  403. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
  404. let Some(mint_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1) else {
  405. return Err(anyhow!("Money Mint circuit not found"))
  406. };
  407. let Some(burn_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1) else {
  408. return Err(anyhow!("Money Burn circuit not found"))
  409. };
  410. let mint_zkbin = ZkBinary::decode(&mint_zkbin.1)?;
  411. let burn_zkbin = ZkBinary::decode(&burn_zkbin.1)?;
  412. let k = 13;
  413. let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin), mint_zkbin.clone());
  414. let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin), burn_zkbin.clone());
  415. eprintln!("Creating Money Mint circuit proving key");
  416. let mint_pk = ProvingKey::build(k, &mint_circuit);
  417. eprintln!("Creating Money Burn circuit proving key");
  418. let burn_pk = ProvingKey::build(k, &burn_circuit);
  419. let (xfer_params, xfer_proofs) =
  420. xfer_call.make(&mint_zkbin, &mint_pk, &burn_zkbin, &burn_pk)?;
  421. let mut data = vec![MoneyFunction::TransferV1 as u8];
  422. xfer_params.encode(&mut data)?;
  423. let xfer_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  424. let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
  425. let Some(exec_zkbin) = zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_EXEC_NS) else {
  426. return Err(anyhow!("DAO Exec circuit not found"))
  427. };
  428. let exec_zkbin = ZkBinary::decode(&exec_zkbin.1)?;
  429. let exec_circuit = ZkCircuit::new(empty_witnesses(&exec_zkbin), exec_zkbin.clone());
  430. eprintln!("Creating DAO Exec circuit proving key");
  431. let exec_pk = ProvingKey::build(k, &exec_circuit);
  432. // Count votes
  433. let mut total_yes_vote_value = 0;
  434. let mut total_all_vote_value = 0;
  435. let mut blind_total_vote = DaoBlindAggregateVote::default();
  436. let mut total_yes_vote_blind = pallas::Scalar::zero();
  437. let mut total_all_vote_blind = pallas::Scalar::zero();
  438. for (_, vote) in votes.iter().enumerate() {
  439. total_yes_vote_blind += vote.yes_vote_blind;
  440. total_all_vote_blind += vote.all_vote_blind;
  441. let yes_vote_value = vote.vote_option as u64 * vote.all_vote_value;
  442. eprintln!("yes_vote = {}", yes_vote_value);
  443. total_yes_vote_value += yes_vote_value;
  444. total_all_vote_value += vote.all_vote_value;
  445. let yes_vote_commit = pedersen_commitment_u64(yes_vote_value, vote.yes_vote_blind);
  446. let all_vote_commit = pedersen_commitment_u64(vote.all_vote_value, vote.all_vote_blind);
  447. let blind_vote = DaoBlindAggregateVote { yes_vote_commit, all_vote_commit };
  448. blind_total_vote.aggregate(blind_vote);
  449. }
  450. eprintln!("yes = {}, all = {}", total_yes_vote_value, total_all_vote_value);
  451. let prop_t = DaoProposalInfo {
  452. dest: proposal.recipient,
  453. amount: proposal.amount,
  454. token_id: proposal.token_id,
  455. blind: proposal.bulla_blind, // <-- FIXME: wtf
  456. };
  457. // TODO: allvote/yesvote is 11 weirdly
  458. let dao_t = DaoInfo {
  459. proposer_limit: dao.proposer_limit,
  460. quorum: dao.quorum,
  461. approval_ratio_quot: dao.approval_ratio_quot,
  462. approval_ratio_base: dao.approval_ratio_base,
  463. gov_token_id: dao.gov_token_id,
  464. public_key: PublicKey::from_secret(dao.secret_key),
  465. bulla_blind: dao.bulla_blind,
  466. };
  467. let dao_exec_call = dao_client::DaoExecCall {
  468. proposal: prop_t,
  469. dao: dao_t,
  470. yes_vote_value: total_yes_vote_value,
  471. all_vote_value: total_all_vote_value,
  472. yes_vote_blind: total_yes_vote_blind,
  473. all_vote_blind: total_all_vote_blind,
  474. user_serial,
  475. user_coin_blind,
  476. dao_serial,
  477. dao_coin_blind,
  478. input_value: input_sum, // <-- FIXME
  479. input_value_blind, // <-- FIXME
  480. hook_dao_exec: DAO_CONTRACT_ID.inner(),
  481. signature_secret: exec_signature_secret,
  482. };
  483. let (exec_params, exec_proofs) = dao_exec_call.make(&exec_zkbin, &exec_pk)?;
  484. let mut data = vec![DaoFunction::Exec as u8];
  485. exec_params.encode(&mut data)?;
  486. let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
  487. let mut tx = Transaction {
  488. calls: vec![xfer_call, exec_call],
  489. proofs: vec![xfer_proofs, exec_proofs],
  490. signatures: vec![],
  491. };
  492. let xfer_sigs = tx.create_sigs(&mut OsRng, &xfer_signature_secrets)?;
  493. let exec_sigs = tx.create_sigs(&mut OsRng, &[exec_signature_secret])?;
  494. tx.signatures = vec![xfer_sigs, exec_sigs];
  495. Ok(tx)
  496. }
  497. }