main.rs 42 KB

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