main.rs 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204
  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::{collections::HashMap, sync::Arc, time::Instant};
  19. use log::debug;
  20. use rand::rngs::OsRng;
  21. use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};
  22. use url::Url;
  23. use darkfi::{
  24. crypto::{
  25. proof::{ProvingKey, VerifyingKey},
  26. types::{DrkSpendHook, DrkUserData, DrkValue},
  27. },
  28. rpc::server::listen_and_serve,
  29. zk::circuit::{BurnContract, MintContract},
  30. zkas::ZkBinary,
  31. Error, Result,
  32. };
  33. use darkfi_sdk::crypto::{
  34. pedersen::pedersen_commitment_u64, poseidon_hash, Keypair, MerkleNode, PublicKey, SecretKey,
  35. TokenId,
  36. };
  37. use incrementalmerkletree::{Position, Tree};
  38. use pasta_curves::{
  39. group::{ff::Field, Group},
  40. pallas,
  41. };
  42. mod contract;
  43. mod error;
  44. mod note;
  45. mod rpc;
  46. mod util;
  47. use crate::{
  48. contract::{
  49. dao::{self, mint::wallet::DaoParams, propose::wallet::Proposal, DaoBulla},
  50. money::{self, state::OwnCoin},
  51. },
  52. error::{DaoError, DaoResult},
  53. rpc::JsonRpcInterface,
  54. util::{sign, StateRegistry, Transaction, ZkContractTable, DRK_ID, GOV_ID},
  55. };
  56. //////////////////////////////////////////////////////////////////////////
  57. //////////////////////////////////////////////////////////////////////////
  58. //// dao-demo 0.1
  59. ////
  60. //// This is a very early prototype intended to demonstrate the underlying
  61. //// crypto of fully anonymous DAOs. DAO participants can own and operate
  62. //// a collective treasury according to rules set by the DAO. Communities
  63. //// can coordinate financially in the cover of a protective darkness,
  64. //// free from surveillance and persecution.
  65. ////
  66. //// The following information is completely hidden:
  67. ////
  68. //// * DAO treasury
  69. //// * DAO parameters
  70. //// * DAO participants
  71. //// * Proposals
  72. //// * Votes
  73. ////
  74. //// The DAO enables participants to make proposals, cast votes, and spend
  75. //// money from the DAO treasury if a proposal passes. The basic operation
  76. //// involves transferring money from a treasury to a public key specified
  77. //// in a Proposal. This operation can only happen if several conditions are
  78. //// met.
  79. ////
  80. //// At its basis, the DAO is a treasury that is owned by everyone who holds
  81. //// the DAO governance token. These constraints, also known as DAO parameters,
  82. //// are configured by DAO participants and enforced by ZK cryptography.
  83. ////
  84. //// In this demo, the constraints are:
  85. ////
  86. //// 1. DAO quorum: the number of governance tokens that must be allocated
  87. //// to a proposal in order for a proposal to pass.
  88. //// 2. Proposer limit: the number of governance tokens required to make a
  89. //// proposal.
  90. //// 3. DAO approval ratio: The ratio of yes/ no votes required for a
  91. //// proposal to pass.
  92. ////
  93. //// In addition, DAO participants must prove ownership of governance tokens
  94. //// order to vote. Their vote is weighted according to the number of governance
  95. //// tokens in their wallet. In this current implementation, users do not spend
  96. //// or lock up these coins in order to vote- they simply prove ownership of them.
  97. ////
  98. //// In the current prototype, the following information is exposed:
  99. ////
  100. //// * Encrypted votes are publicly linked to the proposal identifier hash,
  101. //// meaning that it is possible to see that there is voting activity associated
  102. //// with a particular proposal identifier, but the contents of the proposal,
  103. //// how one has voted, and the associated DAO is fully private.
  104. //// * In the burn phase of casting a vote, we reveal a public value called a
  105. //// nullifier. The same public value is revealed when we spend the coins we
  106. //// used to vote, meaning you can link a vote with a user when they spend
  107. //// governance tokens. This is bad but is easily fixable. We will update the
  108. //// code to use different values in the vote (by creating an intermediate Coin
  109. //// used for voting).
  110. //// * Votes are currently encrypted to the DAO public key. This means that
  111. //// any DAO participant can decrypt votes as they come in. In the future,
  112. //// we can delay the decryption so that you cannot read votes until the final
  113. //// tally.
  114. ////
  115. //// Additionally, the dao-demo app shown below is highly limited. Namely, we use
  116. //// a single God daemon to operate all the wallets. In the next version, every user
  117. //// wallet will be a seperate daemon connecting over a network and running on a
  118. //// blockchain.
  119. ////
  120. //// /////////////////////////////////////////////////////////////////////
  121. ////
  122. //// dao-demo 0.1 TODOs:
  123. ////
  124. //// High priority:
  125. ////
  126. //// 5. vote() should pass a ProposalBulla
  127. ////
  128. //// Less priority:
  129. ////
  130. //// 1. Better document CLI/ CLI help.
  131. ////
  132. //// 2. Token id is hardcoded rn. Change this so users can specify token_id
  133. //// as either xdrk or gdrk. In dao-cli we run a match statement to link to
  134. //// the corresponding static values XDRK_ID and GDRK_ID. Note: xdrk is used
  135. //// only for the DAO treasury. gdrk is the governance token used to operate
  136. //// the DAO.
  137. ////
  138. //// 3. Implement money transfer between MoneyWallets so users can send tokens to
  139. //// eachother.
  140. ////
  141. //// 4. Make CLI usage more interactive. Example: when I cast a vote, output:
  142. //// "You voted {} with value {}." where value is the number of gDRK in a users
  143. //// wallet (and the same for making a proposal etc).
  144. ////
  145. //// 5. Currently, DaoWallet stores DaoParams, DaoBulla's and Proposal's in a
  146. //// Vector. We retrieve values through indexing, meaning that we
  147. //// cannot currently support multiple DAOs and multiple proposals.
  148. ////
  149. //// Instead, dao_wallet.create_dao() should create a struct called Dao
  150. //// which stores dao_info: HashMap<DaoBulla, DaoParams> and proposals:
  151. //// HashMap<ProposalBulla, Proposal>. Users pass the DaoBulla and
  152. //// ProposalBulla and we lookup the corresponding data. struct Dao should
  153. //// be owned by DaoWallet.
  154. ////
  155. //// 6. Error handling :)
  156. ////
  157. //////////////////////////////////////////////////////////////////////////
  158. //////////////////////////////////////////////////////////////////////////
  159. pub struct Client {
  160. dao_wallet: DaoWallet,
  161. money_wallets: HashMap<[u8; 32], MoneyWallet>,
  162. cashier_wallet: CashierWallet,
  163. states: StateRegistry,
  164. zk_bins: ZkContractTable,
  165. }
  166. impl Client {
  167. fn new() -> Self {
  168. // For this early demo we store all wallets in a single Client.
  169. let dao_wallet = DaoWallet::new();
  170. let money_wallets = HashMap::new();
  171. let cashier_wallet = CashierWallet::new();
  172. // Lookup table for smart contract states
  173. let states = StateRegistry::new();
  174. // Initialize ZK binary table
  175. let zk_bins = ZkContractTable::new();
  176. Self { dao_wallet, money_wallets, cashier_wallet, states, zk_bins }
  177. }
  178. // Load ZK contracts into the ZkContractTable and initialize the StateRegistry.
  179. fn init(&mut self) -> Result<()> {
  180. //We use these to initialize the money state.
  181. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  182. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  183. debug!(target: "demo", "Loading dao-mint.zk");
  184. let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
  185. let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
  186. self.zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
  187. debug!(target: "demo", "Loading money-transfer contracts");
  188. let start = Instant::now();
  189. let mint_pk = ProvingKey::build(11, &MintContract::default());
  190. debug!("Mint PK: [{:?}]", start.elapsed());
  191. let start = Instant::now();
  192. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  193. debug!("Burn PK: [{:?}]", start.elapsed());
  194. let start = Instant::now();
  195. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  196. debug!("Mint VK: [{:?}]", start.elapsed());
  197. let start = Instant::now();
  198. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  199. debug!("Burn VK: [{:?}]", start.elapsed());
  200. self.zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
  201. self.zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
  202. debug!(target: "demo", "Loading dao-propose-main.zk");
  203. let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
  204. let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
  205. self.zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
  206. debug!(target: "demo", "Loading dao-propose-burn.zk");
  207. let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
  208. let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
  209. self.zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
  210. debug!(target: "demo", "Loading dao-vote-main.zk");
  211. let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
  212. let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
  213. self.zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
  214. debug!(target: "demo", "Loading dao-vote-burn.zk");
  215. let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
  216. let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
  217. self.zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
  218. let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
  219. let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
  220. self.zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
  221. let cashier_signature_public = self.cashier_wallet.signature_public();
  222. let money_state =
  223. money::state::State::new(cashier_signature_public, faucet_signature_public);
  224. self.states.register(*money::CONTRACT_ID, money_state);
  225. let dao_state = dao::State::new();
  226. self.states.register(*dao::CONTRACT_ID, dao_state);
  227. Ok(())
  228. }
  229. fn create_dao(
  230. &mut self,
  231. dao_proposer_limit: u64,
  232. dao_quorum: u64,
  233. dao_approval_ratio_quot: u64,
  234. dao_approval_ratio_base: u64,
  235. token_id: TokenId,
  236. ) -> DaoResult<pallas::Base> {
  237. let tx = self.dao_wallet.mint_tx(
  238. dao_proposer_limit,
  239. dao_quorum,
  240. dao_approval_ratio_quot,
  241. dao_approval_ratio_base,
  242. token_id,
  243. &self.zk_bins,
  244. );
  245. self.validate(&tx)?;
  246. // Only witness the value once the transaction is confirmed.
  247. self.dao_wallet.update_witness(&mut self.states)?;
  248. // Retrieve DAO bulla from the state.
  249. let dao_bulla = {
  250. let func_call = &tx.func_calls[0];
  251. let call_data = func_call.call_data.as_any();
  252. let call_data = call_data.downcast_ref::<dao::mint::validate::CallData>().unwrap();
  253. call_data.dao_bulla.clone()
  254. };
  255. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  256. // We store these values in a vector we can easily retrieve DAO values for the demo.
  257. let dao_params = DaoParams {
  258. proposer_limit: dao_proposer_limit,
  259. quorum: dao_quorum,
  260. approval_ratio_quot: dao_approval_ratio_quot,
  261. approval_ratio_base: dao_approval_ratio_base,
  262. gov_token_id: token_id,
  263. public_key: self.dao_wallet.keypair.public,
  264. bulla_blind: self.dao_wallet.bulla_blind,
  265. };
  266. self.dao_wallet.params.push(dao_params);
  267. self.dao_wallet.bullas.push(dao_bulla.clone());
  268. Ok(dao_bulla.0)
  269. }
  270. fn mint_treasury(
  271. &mut self,
  272. token_id: TokenId,
  273. token_supply: u64,
  274. recipient: PublicKey,
  275. ) -> DaoResult<()> {
  276. self.dao_wallet.track(&mut self.states)?;
  277. let tx = self.cashier_wallet.mint(
  278. token_id,
  279. token_supply,
  280. self.dao_wallet.bullas[0].0,
  281. recipient,
  282. &self.zk_bins,
  283. )?;
  284. self.validate(&tx)?;
  285. self.update_wallets()?;
  286. Ok(())
  287. }
  288. fn airdrop_user(&mut self, value: u64, token_id: TokenId, addr: PublicKey) -> DaoResult<()> {
  289. // let wallet = self.money_wallets.get(&nym).unwrap();
  290. // let addr = wallet.get_public_key();
  291. let tx = self.cashier_wallet.airdrop(value, token_id, addr, &self.zk_bins)?;
  292. self.validate(&tx)?;
  293. self.update_wallets()?;
  294. Ok(())
  295. }
  296. fn validate(&mut self, tx: &Transaction) -> DaoResult<()> {
  297. debug!(target: "dao_demo::client::validate()", "commencing validate sequence");
  298. let mut updates = vec![];
  299. // Validate all function calls in the tx
  300. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  301. // So then the verifier will lookup the corresponding state_transition and apply
  302. // functions based off the func_id
  303. if func_call.func_id == *money::transfer::FUNC_ID {
  304. debug!("money_contract::transfer::state_transition()");
  305. match money::transfer::validate::state_transition(&self.states, idx, tx) {
  306. Ok(update) => {
  307. updates.push(update);
  308. }
  309. Err(e) => return Err(DaoError::StateTransitionFailed(e.to_string())),
  310. }
  311. } else if func_call.func_id == *dao::mint::FUNC_ID {
  312. debug!("dao_contract::mint::state_transition()");
  313. match dao::mint::validate::state_transition(&self.states, idx, tx) {
  314. Ok(update) => {
  315. updates.push(update);
  316. }
  317. Err(e) => return Err(DaoError::StateTransitionFailed(e.to_string())),
  318. }
  319. } else if func_call.func_id == *dao::propose::FUNC_ID {
  320. debug!(target: "demo", "dao_contract::propose::state_transition()");
  321. match dao::propose::validate::state_transition(&self.states, idx, tx) {
  322. Ok(update) => {
  323. updates.push(update);
  324. }
  325. Err(e) => return Err(DaoError::StateTransitionFailed(e.to_string())),
  326. }
  327. } else if func_call.func_id == *dao::vote::FUNC_ID {
  328. debug!(target: "demo", "dao_contract::vote::state_transition()");
  329. match dao::vote::validate::state_transition(&self.states, idx, tx) {
  330. Ok(update) => {
  331. updates.push(update);
  332. }
  333. Err(e) => return Err(DaoError::StateTransitionFailed(e.to_string())),
  334. }
  335. } else if func_call.func_id == *dao::exec::FUNC_ID {
  336. debug!("dao_contract::exec::state_transition()");
  337. match dao::exec::validate::state_transition(&self.states, idx, tx) {
  338. Ok(update) => {
  339. updates.push(update);
  340. }
  341. Err(e) => return Err(DaoError::StateTransitionFailed(e.to_string())),
  342. }
  343. }
  344. }
  345. // Atomically apply all changes
  346. for update in updates {
  347. update.apply(&mut self.states);
  348. }
  349. tx.zk_verify(&self.zk_bins)?;
  350. tx.verify_sigs();
  351. Ok(())
  352. }
  353. fn update_wallets(&mut self) -> DaoResult<()> {
  354. let state = self
  355. .states
  356. .lookup_mut::<money::State>(*money::CONTRACT_ID)
  357. .ok_or(DaoError::StateNotFound)?;
  358. let dao_coins = state.wallet_cache.get_received(&self.dao_wallet.keypair.secret);
  359. for coin in dao_coins {
  360. let note = coin.note.clone();
  361. let (pub_x, pub_y) = self.dao_wallet.keypair.public.xy();
  362. let coin_hash = poseidon_hash::<8>([
  363. pub_x,
  364. pub_y,
  365. DrkValue::from(note.value),
  366. note.token_id.inner(),
  367. note.serial,
  368. note.spend_hook,
  369. note.user_data,
  370. note.coin_blind,
  371. ]);
  372. assert_eq!(coin_hash, coin.coin.0);
  373. assert_eq!(note.spend_hook, *dao::exec::FUNC_ID);
  374. assert_eq!(note.user_data, self.dao_wallet.bullas[0].0);
  375. self.dao_wallet.own_coins.push((coin, false));
  376. debug!("DAO received a coin worth {} xDRK", note.value);
  377. }
  378. for wallet in self.money_wallets.values_mut() {
  379. let coins = state.wallet_cache.get_received(&wallet.keypair.secret);
  380. for coin in coins {
  381. let note = coin.note.clone();
  382. let (pub_x, pub_y) = wallet.keypair.public.xy();
  383. let coin_hash = poseidon_hash::<8>([
  384. pub_x,
  385. pub_y,
  386. DrkValue::from(note.value),
  387. note.token_id.inner(),
  388. note.serial,
  389. note.spend_hook,
  390. note.user_data,
  391. note.coin_blind,
  392. ]);
  393. assert_eq!(coin_hash, coin.coin.0);
  394. wallet.own_coins.push((coin, false));
  395. }
  396. }
  397. Ok(())
  398. }
  399. fn propose(
  400. &mut self,
  401. recipient: PublicKey,
  402. token_id: TokenId,
  403. amount: u64,
  404. sender: PublicKey,
  405. ) -> DaoResult<pallas::Base> {
  406. let params = self.dao_wallet.params[0].clone();
  407. let dao_leaf_position = self.dao_wallet.leaf_position;
  408. // To be able to make a proposal, we must prove we have ownership
  409. // of governance tokens, and that the quantity of governance
  410. // tokens is within the accepted proposer limit.
  411. let sender_wallet = self.money_wallets.get_mut(&sender.to_bytes());
  412. if sender_wallet.is_none() {
  413. return Err(DaoError::NoWalletFound)
  414. }
  415. let sender_wallet = sender_wallet.unwrap();
  416. let tx = sender_wallet.propose_tx(
  417. params,
  418. recipient,
  419. token_id,
  420. amount,
  421. dao_leaf_position,
  422. &self.zk_bins,
  423. &mut self.states,
  424. )?;
  425. self.validate(&tx)?;
  426. self.update_wallets()?;
  427. let proposal_bulla = self.dao_wallet.store_proposal(&tx)?;
  428. Ok(proposal_bulla)
  429. }
  430. fn cast_vote(&mut self, pubkey: PublicKey, vote: bool) -> DaoResult<()> {
  431. let dao_key = self.dao_wallet.keypair;
  432. if self.dao_wallet.proposals.is_empty() {
  433. return Err(DaoError::NoProposals)
  434. }
  435. let proposal = self.dao_wallet.proposals[0].clone();
  436. if self.dao_wallet.params.is_empty() {
  437. return Err(DaoError::DaoNotConfigured)
  438. }
  439. let dao_params = self.dao_wallet.params[0].clone();
  440. let dao_keypair = self.dao_wallet.keypair;
  441. let voter_wallet = self.money_wallets.get_mut(&pubkey.to_bytes());
  442. if voter_wallet.is_none() {
  443. return Err(DaoError::NoWalletFound)
  444. }
  445. let voter_wallet = voter_wallet.unwrap();
  446. let tx = voter_wallet.vote_tx(
  447. vote,
  448. dao_key,
  449. proposal,
  450. dao_params,
  451. dao_keypair,
  452. &self.zk_bins,
  453. &mut self.states,
  454. )?;
  455. self.validate(&tx)?;
  456. self.update_wallets()?;
  457. self.dao_wallet.store_vote(&tx)?;
  458. Ok(())
  459. }
  460. fn exec_proposal(&mut self, bulla: pallas::Base) -> DaoResult<()> {
  461. if self.dao_wallet.proposals.is_empty() {
  462. return Err(DaoError::NoProposals)
  463. }
  464. let proposal = self.dao_wallet.proposals[0].clone();
  465. if self.dao_wallet.params.is_empty() {
  466. return Err(DaoError::DaoNotConfigured)
  467. }
  468. let dao_params = self.dao_wallet.params[0].clone();
  469. let tx = self.dao_wallet.exec_tx(
  470. proposal,
  471. bulla,
  472. dao_params,
  473. &self.zk_bins,
  474. &mut self.states,
  475. )?;
  476. self.validate(&tx)?;
  477. self.update_wallets()?;
  478. Ok(())
  479. }
  480. }
  481. struct DaoWallet {
  482. keypair: Keypair,
  483. signature_secret: SecretKey,
  484. bulla_blind: pallas::Base,
  485. leaf_position: Position,
  486. proposal_bullas: Vec<pallas::Base>,
  487. bullas: Vec<DaoBulla>,
  488. params: Vec<DaoParams>,
  489. own_coins: Vec<(OwnCoin, bool)>,
  490. proposals: Vec<Proposal>,
  491. vote_notes: Vec<dao::vote::wallet::Note>,
  492. }
  493. impl DaoWallet {
  494. fn new() -> Self {
  495. let keypair = Keypair::random(&mut OsRng);
  496. let signature_secret = SecretKey::random(&mut OsRng);
  497. let bulla_blind = pallas::Base::random(&mut OsRng);
  498. let leaf_position = Position::zero();
  499. let proposal_bullas = Vec::new();
  500. let bullas = Vec::new();
  501. let params = Vec::new();
  502. let own_coins: Vec<(OwnCoin, bool)> = Vec::new();
  503. let proposals: Vec<Proposal> = Vec::new();
  504. let vote_notes = Vec::new();
  505. Self {
  506. keypair,
  507. signature_secret,
  508. bulla_blind,
  509. leaf_position,
  510. proposal_bullas,
  511. bullas,
  512. params,
  513. own_coins,
  514. proposals,
  515. vote_notes,
  516. }
  517. }
  518. fn get_public_key(&self) -> PublicKey {
  519. self.keypair.public
  520. }
  521. fn track(&self, states: &mut StateRegistry) -> DaoResult<()> {
  522. let state = states
  523. .lookup_mut::<money::State>(*money::CONTRACT_ID)
  524. .ok_or(DaoError::StateNotFound)?;
  525. state.wallet_cache.track(self.keypair.secret);
  526. Ok(())
  527. }
  528. // Mint the DAO bulla.
  529. fn mint_tx(
  530. &mut self,
  531. dao_proposer_limit: u64,
  532. dao_quorum: u64,
  533. dao_approval_ratio_quot: u64,
  534. dao_approval_ratio_base: u64,
  535. token_id: TokenId,
  536. zk_bins: &ZkContractTable,
  537. ) -> Transaction {
  538. debug!(target: "dao-demo::dao::mint_tx()", "START");
  539. let builder = dao::mint::wallet::Builder {
  540. dao_proposer_limit,
  541. dao_quorum,
  542. dao_approval_ratio_quot,
  543. dao_approval_ratio_base,
  544. gov_token_id: token_id,
  545. dao_pubkey: self.keypair.public,
  546. dao_bulla_blind: self.bulla_blind,
  547. _signature_secret: self.signature_secret,
  548. };
  549. let func_call = builder.build(zk_bins);
  550. let func_calls = vec![func_call];
  551. let mut signatures = vec![];
  552. for func_call in &func_calls {
  553. let sig = sign(vec![self.signature_secret], func_call);
  554. signatures.push(sig);
  555. }
  556. Transaction { func_calls, signatures }
  557. }
  558. fn update_witness(&mut self, states: &mut StateRegistry) -> DaoResult<()> {
  559. let state =
  560. states.lookup_mut::<dao::State>(*dao::CONTRACT_ID).ok_or(DaoError::StateNotFound)?;
  561. let path =
  562. state.dao_tree.witness().ok_or_else(|| Error::Custom("Tree is empty".to_owned()))?;
  563. self.leaf_position = path;
  564. Ok(())
  565. }
  566. fn balances(&self) -> Result<HashMap<String, u64>> {
  567. let mut ret: HashMap<String, u64> = HashMap::new();
  568. for (coin, is_spent) in &self.own_coins {
  569. if *is_spent {}
  570. if coin.note.token_id == *DRK_ID || coin.note.token_id == *GOV_ID {
  571. let token_id = format!("{}", coin.note.token_id);
  572. ret.insert(token_id, coin.note.value);
  573. }
  574. }
  575. Ok(ret)
  576. }
  577. fn store_proposal(&mut self, tx: &Transaction) -> Result<pallas::Base> {
  578. let (proposal, proposal_bulla) = {
  579. let func_call = &tx.func_calls[0];
  580. let call_data = func_call.call_data.as_any();
  581. let call_data = call_data.downcast_ref::<dao::propose::validate::CallData>().unwrap();
  582. let header = &call_data.header;
  583. let note: dao::propose::wallet::Note = header.enc_note.decrypt(&self.keypair.secret)?;
  584. // Return the proposal info
  585. (note.proposal, call_data.header.proposal_bulla)
  586. };
  587. debug!(target: "demo", "Proposal now active!");
  588. debug!(target: "demo", " destination: {:?}", proposal.dest);
  589. debug!(target: "demo", " amount: {}", proposal.amount);
  590. debug!(target: "demo", " token_id: {:?}", proposal.token_id);
  591. debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
  592. self.proposals.push(proposal);
  593. self.proposal_bullas.push(proposal_bulla);
  594. Ok(proposal_bulla)
  595. }
  596. // We decrypt the votes in a transaction and add it to the wallet.
  597. fn store_vote(&mut self, tx: &Transaction) -> Result<()> {
  598. let vote_note = {
  599. let func_call = &tx.func_calls[0];
  600. let call_data = func_call.call_data.as_any();
  601. let call_data = call_data.downcast_ref::<dao::vote::validate::CallData>().unwrap();
  602. let header = &call_data.header;
  603. let note: dao::vote::wallet::Note = header.enc_note.decrypt(&self.keypair.secret)?;
  604. note
  605. };
  606. self.vote_notes.push(vote_note);
  607. Ok(())
  608. }
  609. fn get_proposals(&self) -> &Vec<Proposal> {
  610. &self.proposals
  611. }
  612. fn get_votes(&self) -> &Vec<dao::vote::wallet::Note> {
  613. &self.vote_notes
  614. }
  615. fn get_treasury_path(
  616. &self,
  617. own_coin: &OwnCoin,
  618. states: &StateRegistry,
  619. ) -> DaoResult<(Position, Vec<MerkleNode>)> {
  620. let (money_leaf_position, money_merkle_path) = {
  621. let state = states
  622. .lookup::<money::State>(*money::CONTRACT_ID)
  623. .ok_or(DaoError::StateNotFound)?;
  624. let tree = &state.tree;
  625. let leaf_position = own_coin.leaf_position;
  626. let root = tree.root(0).ok_or_else(|| {
  627. Error::Custom(
  628. "Not enough checkpoints available to reach the requested checkpoint depth."
  629. .to_owned(),
  630. )
  631. })?;
  632. let merkle_path = tree
  633. .authentication_path(leaf_position, &root)
  634. .ok_or_else(|| Error::Custom("No available authentication path to that position or if the root does not correspond to a checkpointed root of the tree".to_owned()))?;
  635. (leaf_position, merkle_path)
  636. };
  637. Ok((money_leaf_position, money_merkle_path))
  638. }
  639. fn exec_tx(
  640. &self,
  641. proposal: Proposal,
  642. _proposal_bulla: pallas::Base,
  643. dao_params: DaoParams,
  644. zk_bins: &ZkContractTable,
  645. states: &mut StateRegistry,
  646. ) -> DaoResult<Transaction> {
  647. let dao_bulla = self.bullas[0].clone();
  648. let mut inputs = Vec::new();
  649. let mut total_input_value = 0;
  650. let tx_signature_secret = SecretKey::random(&mut OsRng);
  651. let exec_signature_secret = SecretKey::random(&mut OsRng);
  652. let user_serial = pallas::Base::random(&mut OsRng);
  653. let user_coin_blind = pallas::Base::random(&mut OsRng);
  654. let user_data_blind = pallas::Base::random(&mut OsRng);
  655. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  656. let dao_serial = pallas::Base::random(&mut OsRng);
  657. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  658. // disabled
  659. let user_spend_hook = pallas::Base::from(0);
  660. let user_data = pallas::Base::from(0);
  661. for (coin, is_spent) in &self.own_coins {
  662. let is_spent = *is_spent;
  663. if is_spent {
  664. continue
  665. }
  666. let (treasury_leaf_position, treasury_merkle_path) =
  667. self.get_treasury_path(coin, states)?;
  668. let input_value = coin.note.value;
  669. let input = {
  670. money::transfer::wallet::BuilderInputInfo {
  671. leaf_position: treasury_leaf_position,
  672. merkle_path: treasury_merkle_path,
  673. secret: self.keypair.secret,
  674. note: coin.note.clone(),
  675. user_data_blind,
  676. value_blind: input_value_blind,
  677. signature_secret: tx_signature_secret,
  678. }
  679. };
  680. total_input_value += input_value;
  681. inputs.push(input);
  682. }
  683. let builder = {
  684. money::transfer::wallet::Builder {
  685. clear_inputs: vec![],
  686. inputs,
  687. outputs: vec![
  688. // Sending money
  689. money::transfer::wallet::BuilderOutputInfo {
  690. value: proposal.amount,
  691. token_id: proposal.token_id,
  692. public: proposal.dest,
  693. serial: proposal.serial,
  694. coin_blind: proposal.blind,
  695. spend_hook: user_spend_hook,
  696. user_data,
  697. },
  698. // Change back to DAO
  699. money::transfer::wallet::BuilderOutputInfo {
  700. value: total_input_value - proposal.amount,
  701. token_id: *DRK_ID,
  702. public: self.keypair.public,
  703. serial: dao_serial,
  704. coin_blind: dao_coin_blind,
  705. spend_hook: *dao::exec::FUNC_ID,
  706. user_data: dao_bulla.0,
  707. },
  708. ],
  709. }
  710. };
  711. let transfer_func_call = builder.build(zk_bins)?;
  712. let mut yes_votes_value = 0;
  713. let mut yes_votes_blind = pallas::Scalar::from(0);
  714. let mut yes_votes_commit = pallas::Point::identity();
  715. let mut all_votes_value = 0;
  716. let mut all_votes_blind = pallas::Scalar::from(0);
  717. let mut all_votes_commit = pallas::Point::identity();
  718. for (i, note) in self.vote_notes.iter().enumerate() {
  719. let vote_commit = pedersen_commitment_u64(note.vote_value, note.vote_value_blind);
  720. all_votes_commit += vote_commit;
  721. all_votes_blind += note.vote_value_blind;
  722. let yes_vote_commit = pedersen_commitment_u64(
  723. note.vote.vote_option as u64 * note.vote_value,
  724. note.vote.vote_option_blind,
  725. );
  726. yes_votes_commit += yes_vote_commit;
  727. yes_votes_blind += note.vote.vote_option_blind;
  728. let vote_option = note.vote.vote_option;
  729. if vote_option {
  730. yes_votes_value += note.vote_value;
  731. }
  732. all_votes_value += note.vote_value;
  733. let vote_result: String =
  734. if vote_option { "yes".to_string() } else { "no".to_string() };
  735. debug!("Voter {} voted {}", i, vote_result);
  736. }
  737. debug!("Outcome = {} / {}", yes_votes_value, all_votes_value);
  738. assert!(all_votes_commit == pedersen_commitment_u64(all_votes_value, all_votes_blind));
  739. assert!(yes_votes_commit == pedersen_commitment_u64(yes_votes_value, yes_votes_blind));
  740. let builder = {
  741. dao::exec::wallet::Builder {
  742. proposal,
  743. dao: dao_params,
  744. yes_votes_value,
  745. all_votes_value,
  746. yes_votes_blind,
  747. all_votes_blind,
  748. user_serial,
  749. user_coin_blind,
  750. dao_serial,
  751. dao_coin_blind,
  752. input_value: total_input_value,
  753. input_value_blind,
  754. hook_dao_exec: *dao::exec::FUNC_ID,
  755. signature_secret: exec_signature_secret,
  756. }
  757. };
  758. let exec_func_call = builder.build(zk_bins);
  759. let func_calls = vec![transfer_func_call, exec_func_call];
  760. let mut signatures = vec![];
  761. for func_call in &func_calls {
  762. let sig = sign(vec![self.signature_secret], func_call);
  763. signatures.push(sig);
  764. }
  765. Ok(Transaction { func_calls, signatures })
  766. }
  767. }
  768. // Stores governance tokens and related secret values.
  769. struct MoneyWallet {
  770. keypair: Keypair,
  771. signature_secret: SecretKey,
  772. own_coins: Vec<(OwnCoin, bool)>,
  773. }
  774. impl MoneyWallet {
  775. // fn signature_public(&self) -> PublicKey {
  776. // PublicKey::from_secret(self.signature_secret)
  777. // }
  778. // fn get_public_key(&self) -> PublicKey {
  779. // self.keypair.public
  780. // }
  781. fn track(&self, states: &mut StateRegistry) -> DaoResult<()> {
  782. let state = states
  783. .lookup_mut::<money::State>(*money::CONTRACT_ID)
  784. .ok_or(DaoError::StateNotFound)?;
  785. state.wallet_cache.track(self.keypair.secret);
  786. Ok(())
  787. }
  788. fn balances(&self) -> Result<HashMap<String, u64>> {
  789. let mut ret: HashMap<String, u64> = HashMap::new();
  790. for (coin, is_spent) in &self.own_coins {
  791. if *is_spent {}
  792. if coin.note.token_id == *DRK_ID || coin.note.token_id == *GOV_ID {
  793. let token_id = format!("{}", coin.note.token_id);
  794. ret.insert(token_id, coin.note.value);
  795. }
  796. }
  797. Ok(ret)
  798. }
  799. fn propose_tx(
  800. &mut self,
  801. params: DaoParams,
  802. recipient: PublicKey,
  803. token_id: TokenId,
  804. amount: u64,
  805. dao_leaf_position: Position,
  806. zk_bins: &ZkContractTable,
  807. states: &mut StateRegistry,
  808. ) -> DaoResult<Transaction> {
  809. let mut inputs = Vec::new();
  810. for (coin, is_spent) in &self.own_coins {
  811. let is_spent = *is_spent;
  812. if is_spent {
  813. continue
  814. }
  815. let (money_leaf_position, money_merkle_path) = self.get_path(states, coin).unwrap();
  816. let input = {
  817. dao::propose::wallet::BuilderInput {
  818. secret: self.keypair.secret,
  819. note: coin.note.clone(),
  820. leaf_position: money_leaf_position,
  821. merkle_path: money_merkle_path,
  822. signature_secret: self.signature_secret,
  823. }
  824. };
  825. inputs.push(input);
  826. }
  827. let (dao_merkle_path, dao_merkle_root) = {
  828. let state =
  829. states.lookup::<dao::State>(*dao::CONTRACT_ID).ok_or(DaoError::StateNotFound)?;
  830. let tree = &state.dao_tree;
  831. let root = tree.root(0).ok_or_else(|| {
  832. Error::Custom(
  833. "Not enough checkpoints available to reach the requested checkpoint depth."
  834. .to_owned(),
  835. )
  836. })?;
  837. let merkle_path = tree.authentication_path(dao_leaf_position, &root)
  838. .ok_or_else(|| Error::Custom(
  839. "No available authentication path to that position or if the root does not correspond to a checkpointed root of the tree"
  840. .to_owned()
  841. ))?;
  842. (merkle_path, root)
  843. };
  844. let proposal = {
  845. dao::propose::wallet::Proposal {
  846. dest: recipient,
  847. amount,
  848. serial: pallas::Base::random(&mut OsRng),
  849. token_id,
  850. blind: pallas::Base::random(&mut OsRng),
  851. }
  852. };
  853. let builder = dao::propose::wallet::Builder {
  854. inputs,
  855. proposal,
  856. dao: params,
  857. dao_leaf_position,
  858. dao_merkle_path,
  859. dao_merkle_root,
  860. };
  861. let func_call = builder.build(zk_bins);
  862. let func_calls = vec![func_call];
  863. let mut signatures = vec![];
  864. for func_call in &func_calls {
  865. let sig = sign(vec![self.signature_secret], func_call);
  866. signatures.push(sig);
  867. }
  868. Ok(Transaction { func_calls, signatures })
  869. }
  870. fn get_path(
  871. &self,
  872. states: &StateRegistry,
  873. own_coin: &OwnCoin,
  874. ) -> DaoResult<(Position, Vec<MerkleNode>)> {
  875. let (money_leaf_position, money_merkle_path) = {
  876. let state = states
  877. .lookup::<money::State>(*money::CONTRACT_ID)
  878. .ok_or(DaoError::StateNotFound)?;
  879. let tree = &state.tree;
  880. let leaf_position = own_coin.leaf_position;
  881. let root = tree.root(0).ok_or_else(|| {
  882. Error::Custom(
  883. "Not enough checkpoints available to reach the requested checkpoint depth."
  884. .to_owned(),
  885. )
  886. })?;
  887. let merkle_path = tree.authentication_path(leaf_position, &root).ok_or_else(|| Error::Custom(
  888. "No available authentication path to that position or the root does not correspond to a checkpointed root of the tree"
  889. .to_owned()
  890. ))?;
  891. (leaf_position, merkle_path)
  892. };
  893. Ok((money_leaf_position, money_merkle_path))
  894. }
  895. fn vote_tx(
  896. &mut self,
  897. vote_option: bool,
  898. _dao_key: Keypair,
  899. proposal: Proposal,
  900. dao_params: DaoParams,
  901. dao_keypair: Keypair,
  902. zk_bins: &ZkContractTable,
  903. states: &mut StateRegistry,
  904. ) -> Result<Transaction> {
  905. let mut inputs = Vec::new();
  906. // We must prove we have sufficient governance tokens in order to vote.
  907. for (coin, _is_spent) in &self.own_coins {
  908. let (money_leaf_position, money_merkle_path) = self.get_path(states, coin).unwrap();
  909. let input = {
  910. dao::vote::wallet::BuilderInput {
  911. secret: self.keypair.secret,
  912. note: coin.note.clone(),
  913. leaf_position: money_leaf_position,
  914. merkle_path: money_merkle_path,
  915. signature_secret: self.signature_secret,
  916. }
  917. };
  918. inputs.push(input);
  919. }
  920. let builder = {
  921. dao::vote::wallet::Builder {
  922. inputs,
  923. vote: dao::vote::wallet::Vote {
  924. vote_option,
  925. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  926. },
  927. // For this demo votes are encrypted for the DAO.
  928. vote_keypair: dao_keypair,
  929. proposal,
  930. dao: dao_params,
  931. }
  932. };
  933. let func_call = builder.build(zk_bins);
  934. let func_calls = vec![func_call];
  935. let mut signatures = vec![];
  936. for func_call in &func_calls {
  937. let sig = sign(vec![self.signature_secret], func_call);
  938. signatures.push(sig);
  939. }
  940. Ok(Transaction { func_calls, signatures })
  941. }
  942. }
  943. async fn start_rpc(client: Client) -> Result<()> {
  944. let rpc_addr = Url::parse("tcp://127.0.0.1:7777")?;
  945. let rpc_client = JsonRpcInterface::new(client);
  946. let rpc_interface = Arc::new(rpc_client);
  947. listen_and_serve(rpc_addr, rpc_interface).await?;
  948. Ok(())
  949. }
  950. // Mint authority that mints the DAO treasury and airdrops governance tokens.
  951. #[derive(Clone)]
  952. struct CashierWallet {
  953. // keypair: Keypair,
  954. signature_secret: SecretKey,
  955. }
  956. impl CashierWallet {
  957. fn new() -> Self {
  958. // let keypair = Keypair::random(&mut OsRng);
  959. let signature_secret = SecretKey::random(&mut OsRng);
  960. // Self { keypair, signature_secret }
  961. Self { signature_secret }
  962. }
  963. fn signature_public(&self) -> PublicKey {
  964. PublicKey::from_secret(self.signature_secret)
  965. }
  966. fn mint(
  967. &mut self,
  968. token_id: TokenId,
  969. token_supply: u64,
  970. dao_bulla: pallas::Base,
  971. recipient: PublicKey,
  972. zk_bins: &ZkContractTable,
  973. ) -> Result<Transaction> {
  974. let spend_hook = *dao::exec::FUNC_ID;
  975. let user_data = dao_bulla;
  976. let value = token_supply;
  977. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  978. Ok(tx)
  979. }
  980. fn transfer_tx(
  981. &self,
  982. value: u64,
  983. token_id: TokenId,
  984. spend_hook: pallas::Base,
  985. user_data: pallas::Base,
  986. recipient: PublicKey,
  987. zk_bins: &ZkContractTable,
  988. ) -> Result<Transaction> {
  989. let builder = {
  990. money::transfer::wallet::Builder {
  991. clear_inputs: vec![money::transfer::wallet::BuilderClearInputInfo {
  992. value,
  993. token_id,
  994. signature_secret: self.signature_secret,
  995. }],
  996. inputs: vec![],
  997. outputs: vec![money::transfer::wallet::BuilderOutputInfo {
  998. value,
  999. token_id,
  1000. public: recipient,
  1001. serial: pallas::Base::random(&mut OsRng),
  1002. coin_blind: pallas::Base::random(&mut OsRng),
  1003. spend_hook,
  1004. user_data,
  1005. }],
  1006. }
  1007. };
  1008. let func_call = builder.build(zk_bins)?;
  1009. let func_calls = vec![func_call];
  1010. let mut signatures = vec![];
  1011. for func_call in &func_calls {
  1012. let sig = sign(vec![self.signature_secret], func_call);
  1013. signatures.push(sig);
  1014. }
  1015. Ok(Transaction { func_calls, signatures })
  1016. }
  1017. fn airdrop(
  1018. &mut self,
  1019. value: u64,
  1020. token_id: TokenId,
  1021. recipient: PublicKey,
  1022. zk_bins: &ZkContractTable,
  1023. ) -> Result<Transaction> {
  1024. // Spend hook and user data disabled
  1025. let spend_hook = DrkSpendHook::from(0);
  1026. let user_data = DrkUserData::from(0);
  1027. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  1028. Ok(tx)
  1029. }
  1030. }
  1031. #[async_std::main]
  1032. async fn main() -> Result<()> {
  1033. TermLogger::init(
  1034. LevelFilter::Debug,
  1035. simplelog::Config::default(),
  1036. TerminalMode::Mixed,
  1037. ColorChoice::Auto,
  1038. )?;
  1039. let mut client = Client::new();
  1040. client.init()?;
  1041. start_rpc(client).await?;
  1042. Ok(())
  1043. }