main.rs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. use std::{any::TypeId, collections::HashMap, sync::Arc, time::Instant};
  2. use incrementalmerkletree::{Position, Tree};
  3. use log::debug;
  4. use pasta_curves::{
  5. arithmetic::CurveAffine,
  6. group::{ff::Field, Curve, Group},
  7. pallas, Fp, Fq,
  8. };
  9. use rand::rngs::OsRng;
  10. use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};
  11. use url::Url;
  12. use darkfi::{
  13. crypto::{
  14. keypair::{Keypair, PublicKey, SecretKey},
  15. merkle_node::MerkleNode,
  16. proof::{ProvingKey, VerifyingKey},
  17. types::{DrkSpendHook, DrkUserData, DrkValue},
  18. util::{pedersen_commitment_u64, poseidon_hash},
  19. },
  20. rpc::server::listen_and_serve,
  21. zk::circuit::{BurnContract, MintContract},
  22. zkas::ZkBinary,
  23. Result,
  24. };
  25. mod contract;
  26. mod note;
  27. mod rpc;
  28. mod util;
  29. use crate::{
  30. contract::{
  31. dao_contract::{self, mint::wallet::DaoParams, propose::wallet::Proposal, DaoBulla},
  32. money_contract::{self, state::OwnCoin, transfer::Note},
  33. },
  34. rpc::JsonRpcInterface,
  35. util::{
  36. sign, FuncCall, HashableBase, StateRegistry, Transaction, ZkContractTable, GDRK_ID, XDRK_ID,
  37. },
  38. };
  39. pub struct Client {
  40. cashier: Cashier,
  41. dao_wallet: DaoWallet,
  42. money_wallets: HashMap<String, MoneyWallet>,
  43. states: StateRegistry,
  44. zk_bins: ZkContractTable,
  45. }
  46. impl Client {
  47. fn new() -> Self {
  48. let dao_wallet = DaoWallet::new();
  49. let money_wallets = HashMap::default();
  50. let cashier = Cashier::new();
  51. // Lookup table for smart contract states
  52. let mut states = StateRegistry::new();
  53. // Initialize ZK binary table
  54. let mut zk_bins = ZkContractTable::new();
  55. Self { cashier, dao_wallet, money_wallets, states, zk_bins }
  56. }
  57. fn init(&mut self) -> Result<()> {
  58. //We use these to initialize the money state.
  59. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  60. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  61. debug!(target: "demo", "Loading dao-mint.zk");
  62. let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
  63. let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
  64. self.zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
  65. debug!(target: "demo", "Loading money-transfer contracts");
  66. let start = Instant::now();
  67. let mint_pk = ProvingKey::build(11, &MintContract::default());
  68. debug!("Mint PK: [{:?}]", start.elapsed());
  69. let start = Instant::now();
  70. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  71. debug!("Burn PK: [{:?}]", start.elapsed());
  72. let start = Instant::now();
  73. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  74. debug!("Mint VK: [{:?}]", start.elapsed());
  75. let start = Instant::now();
  76. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  77. debug!("Burn VK: [{:?}]", start.elapsed());
  78. self.zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
  79. self.zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
  80. debug!(target: "demo", "Loading dao-propose-main.zk");
  81. let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
  82. let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
  83. self.zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
  84. debug!(target: "demo", "Loading dao-propose-burn.zk");
  85. let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
  86. let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
  87. self.zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
  88. debug!(target: "demo", "Loading dao-vote-main.zk");
  89. let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
  90. let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
  91. self.zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
  92. debug!(target: "demo", "Loading dao-vote-burn.zk");
  93. let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
  94. let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
  95. self.zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
  96. let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
  97. let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
  98. self.zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
  99. let cashier_signature_public = self.cashier.signature_public();
  100. let money_state =
  101. money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
  102. self.states.register(*money_contract::CONTRACT_ID, money_state);
  103. let dao_state = dao_contract::State::new();
  104. self.states.register(*dao_contract::CONTRACT_ID, dao_state);
  105. Ok(())
  106. }
  107. fn new_money_wallet(&mut self, key: String) {
  108. let keypair = Keypair::random(&mut OsRng);
  109. let signature_secret = SecretKey::random(&mut OsRng);
  110. let own_coins: Vec<(OwnCoin, bool)> = Vec::new();
  111. let money_wallet = MoneyWallet { keypair, signature_secret, own_coins };
  112. money_wallet.track(&mut self.states);
  113. self.money_wallets.insert(key.clone(), money_wallet);
  114. debug!(target: "dao-demo::client::new_money_wallet()", "created wallet with key {}", &key);
  115. }
  116. // TODO: user passes DAO approval ratio: 1/2
  117. // we parse that into dao_approval_ratio_base and dao_approval_ratio_quot
  118. fn create_dao(
  119. &mut self,
  120. dao_proposer_limit: u64,
  121. dao_quorum: u64,
  122. dao_approval_ratio_quot: u64,
  123. dao_approval_ratio_base: u64,
  124. token_id: pallas::Base,
  125. ) -> Result<pallas::Base> {
  126. debug!(target: "dao-demo::client::create_dao()", "START");
  127. let tx = self.dao_wallet.mint_tx(
  128. dao_proposer_limit,
  129. dao_quorum,
  130. dao_approval_ratio_quot,
  131. dao_approval_ratio_base,
  132. token_id,
  133. &self.zk_bins,
  134. );
  135. // TODO: Proper error handling.
  136. // Only witness the value once the transaction is confirmed.
  137. self.validate(&tx).unwrap();
  138. self.dao_wallet.update_witness(&mut self.states).unwrap();
  139. // Retrieve DAO bulla from the state.
  140. let dao_bulla = {
  141. let func_call = &tx.func_calls[0];
  142. let call_data = func_call.call_data.as_any();
  143. let call_data =
  144. call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
  145. call_data.dao_bulla.clone()
  146. };
  147. // TODO: instead of this print statement, return DAO bulla to CLI
  148. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  149. // We create a hashmap so we can easily retrieve DAO values for the demo.
  150. let dao_params = DaoParams {
  151. proposer_limit: dao_proposer_limit,
  152. quorum: dao_quorum,
  153. approval_ratio_quot: dao_approval_ratio_quot,
  154. approval_ratio_base: dao_approval_ratio_base,
  155. gov_token_id: token_id,
  156. public_key: self.dao_wallet.keypair.public,
  157. bulla_blind: self.dao_wallet.bulla_blind,
  158. };
  159. self.dao_wallet.params.push(dao_params);
  160. self.dao_wallet.bullas.push(dao_bulla.clone());
  161. Ok(dao_bulla.0)
  162. }
  163. fn mint_treasury(
  164. &mut self,
  165. token_id: pallas::Base,
  166. token_supply: u64,
  167. recipient: PublicKey,
  168. ) -> Result<()> {
  169. self.dao_wallet.track(&mut self.states);
  170. let tx = self
  171. .cashier
  172. .mint(*XDRK_ID, token_supply, self.dao_wallet.bullas[0].0, recipient, &self.zk_bins)
  173. .unwrap();
  174. self.validate(&tx).unwrap();
  175. self.update_wallets().unwrap();
  176. Ok(())
  177. }
  178. fn airdrop_user(&mut self, value: u64, token_id: pallas::Base, nym: String) -> Result<()> {
  179. let wallet = self.money_wallets.get(&nym).unwrap();
  180. let addr = wallet.get_public_key();
  181. let tx = self.cashier.airdrop(value, token_id, addr, &self.zk_bins)?;
  182. self.validate(&tx)?;
  183. self.update_wallets().unwrap();
  184. Ok(())
  185. }
  186. // TODO: Change these into errors instead of expects.
  187. fn validate(&mut self, tx: &Transaction) -> Result<()> {
  188. debug!(target: "dao_demo::client::validate()", "commencing validate sequence");
  189. let mut updates = vec![];
  190. // Validate all function calls in the tx
  191. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  192. // So then the verifier will lookup the corresponding state_transition and apply
  193. // functions based off the func_id
  194. if func_call.func_id == *money_contract::transfer::FUNC_ID {
  195. debug!("money_contract::transfer::state_transition()");
  196. let update =
  197. money_contract::transfer::validate::state_transition(&self.states, idx, &tx)
  198. .expect("money_contract::transfer::validate::state_transition() failed!");
  199. updates.push(update);
  200. } else if func_call.func_id == *dao_contract::mint::FUNC_ID {
  201. debug!("dao_contract::mint::state_transition()");
  202. let update = dao_contract::mint::validate::state_transition(&self.states, idx, &tx)
  203. .expect("dao_contract::mint::validate::state_transition() failed!");
  204. updates.push(update);
  205. } else if func_call.func_id == *dao_contract::propose::FUNC_ID {
  206. debug!(target: "demo", "dao_contract::propose::state_transition()");
  207. let update =
  208. dao_contract::propose::validate::state_transition(&self.states, idx, &tx)
  209. .expect("dao_contract::propose::validate::state_transition() failed!");
  210. updates.push(update);
  211. } else if func_call.func_id == *dao_contract::vote::FUNC_ID {
  212. debug!(target: "demo", "dao_contract::vote::state_transition()");
  213. let update = dao_contract::vote::validate::state_transition(&self.states, idx, &tx)
  214. .expect("dao_contract::vote::validate::state_transition() failed!");
  215. updates.push(update);
  216. } else if func_call.func_id == *dao_contract::exec::FUNC_ID {
  217. debug!("dao_contract::exec::state_transition()");
  218. let update = dao_contract::exec::validate::state_transition(&self.states, idx, &tx)
  219. .expect("dao_contract::exec::validate::state_transition() failed!");
  220. updates.push(update);
  221. }
  222. }
  223. // Atomically apply all changes
  224. for update in updates {
  225. update.apply(&mut self.states);
  226. }
  227. tx.zk_verify(&self.zk_bins);
  228. tx.verify_sigs();
  229. Ok(())
  230. }
  231. fn update_wallets(&mut self) -> Result<()> {
  232. let state =
  233. self.states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  234. let mut dao_coins = state.wallet_cache.get_received(&self.dao_wallet.keypair.secret);
  235. for coin in dao_coins {
  236. let note = coin.note.clone();
  237. let coords = self.dao_wallet.keypair.public.0.to_affine().coordinates().unwrap();
  238. let coin_hash = poseidon_hash::<8>([
  239. *coords.x(),
  240. *coords.y(),
  241. DrkValue::from(note.value),
  242. note.token_id,
  243. note.serial,
  244. note.spend_hook,
  245. note.user_data,
  246. note.coin_blind,
  247. ]);
  248. assert_eq!(coin_hash, coin.coin.0);
  249. assert_eq!(note.spend_hook, *dao_contract::exec::FUNC_ID);
  250. assert_eq!(note.user_data, self.dao_wallet.bullas[0].0);
  251. self.dao_wallet.own_coins.push((coin, false));
  252. debug!("DAO received a coin worth {} xDRK", note.value);
  253. }
  254. for (key, wallet) in &mut self.money_wallets {
  255. let mut coins = state.wallet_cache.get_received(&wallet.keypair.secret);
  256. for coin in coins {
  257. let note = coin.note.clone();
  258. let coords = wallet.keypair.public.0.to_affine().coordinates().unwrap();
  259. let coin_hash = poseidon_hash::<8>([
  260. *coords.x(),
  261. *coords.y(),
  262. DrkValue::from(note.value),
  263. note.token_id,
  264. note.serial,
  265. note.spend_hook,
  266. note.user_data,
  267. note.coin_blind,
  268. ]);
  269. assert_eq!(coin_hash, coin.coin.0);
  270. wallet.own_coins.push((coin, false));
  271. }
  272. }
  273. Ok(())
  274. }
  275. // TODO: error handling
  276. fn propose(
  277. &mut self,
  278. recipient: PublicKey,
  279. token_id: pallas::Base,
  280. amount: u64,
  281. sender: String,
  282. ) -> Result<(Proposal, pallas::Base)> {
  283. let params = self.dao_wallet.params[0].clone();
  284. let dao_leaf_position = self.dao_wallet.leaf_position;
  285. // To be able to make a proposal, we must prove we have ownership of governance tokens,
  286. // and that the quantity of governance tokens is within the accepted proposal limit.
  287. let mut sender_wallet = self.money_wallets.get_mut(&sender).unwrap();
  288. //let own_coin = sender_wallet.balances()?;
  289. //let (money_leaf_position, money_merkle_path) =
  290. // sender_wallet.get_path(&self.states, &own_coin)?;
  291. let tx = sender_wallet.propose_tx(
  292. params.clone(),
  293. recipient,
  294. token_id,
  295. amount,
  296. dao_leaf_position,
  297. &self.zk_bins,
  298. &mut self.states,
  299. )?;
  300. // bang!
  301. self.validate(&tx)?;
  302. self.update_wallets().unwrap();
  303. let (proposal, proposal_bulla) = self.dao_wallet.read_proposal(&tx)?;
  304. Ok((proposal, proposal_bulla))
  305. }
  306. fn get_addr_from_nym(&self, nym: String) -> Result<PublicKey> {
  307. let wallet = self.money_wallets.get(&nym).unwrap();
  308. Ok(wallet.get_public_key())
  309. }
  310. }
  311. struct DaoWallet {
  312. keypair: Keypair,
  313. signature_secret: SecretKey,
  314. bulla_blind: pallas::Base,
  315. leaf_position: Position,
  316. bullas: Vec<DaoBulla>,
  317. params: Vec<DaoParams>,
  318. own_coins: Vec<(OwnCoin, bool)>,
  319. vote_notes: Vec<dao_contract::vote::wallet::Note>,
  320. }
  321. impl DaoWallet {
  322. fn new() -> Self {
  323. let keypair = Keypair::random(&mut OsRng);
  324. let signature_secret = SecretKey::random(&mut OsRng);
  325. let bulla_blind = pallas::Base::random(&mut OsRng);
  326. let leaf_position = Position::zero();
  327. let bullas = Vec::new();
  328. let params = Vec::new();
  329. let own_coins: Vec<(OwnCoin, bool)> = Vec::new();
  330. let vote_notes = Vec::new();
  331. Self {
  332. keypair,
  333. signature_secret,
  334. bulla_blind,
  335. leaf_position,
  336. bullas,
  337. params,
  338. own_coins,
  339. vote_notes,
  340. }
  341. }
  342. fn get_public_key(&self) -> PublicKey {
  343. self.keypair.public
  344. }
  345. fn track(&self, states: &mut StateRegistry) -> Result<()> {
  346. let state =
  347. states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  348. state.wallet_cache.track(self.keypair.secret);
  349. Ok(())
  350. }
  351. // Mint the DAO bulla.
  352. fn mint_tx(
  353. &mut self,
  354. dao_proposer_limit: u64,
  355. dao_quorum: u64,
  356. dao_approval_ratio_quot: u64,
  357. dao_approval_ratio_base: u64,
  358. token_id: pallas::Base,
  359. zk_bins: &ZkContractTable,
  360. ) -> Transaction {
  361. debug!(target: "dao-demo::dao::mint_tx()", "START");
  362. let builder = dao_contract::mint::wallet::Builder {
  363. dao_proposer_limit,
  364. dao_quorum,
  365. dao_approval_ratio_quot,
  366. dao_approval_ratio_base,
  367. gov_token_id: *GDRK_ID,
  368. dao_pubkey: self.keypair.public,
  369. dao_bulla_blind: self.bulla_blind,
  370. _signature_secret: self.signature_secret,
  371. };
  372. let func_call = builder.build(zk_bins);
  373. let func_calls = vec![func_call];
  374. let signatures = sign(vec![self.signature_secret], &func_calls);
  375. Transaction { func_calls, signatures }
  376. }
  377. // TODO: error handling
  378. fn update_witness(&mut self, states: &mut StateRegistry) -> Result<()> {
  379. let state = states.lookup_mut::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
  380. let path = state.dao_tree.witness();
  381. match path {
  382. Some(path) => {
  383. self.leaf_position = path;
  384. }
  385. None => {}
  386. }
  387. Ok(())
  388. }
  389. fn balances(&self) -> Result<u64> {
  390. let mut balances = 0;
  391. for (coin, is_spent) in &self.own_coins {
  392. if *is_spent {
  393. continue
  394. }
  395. balances += coin.note.value;
  396. }
  397. Ok(balances)
  398. }
  399. fn read_proposal(&self, tx: &Transaction) -> Result<(Proposal, pallas::Base)> {
  400. let (proposal, proposal_bulla) = {
  401. let func_call = &tx.func_calls[0];
  402. let call_data = func_call.call_data.as_any();
  403. let call_data =
  404. call_data.downcast_ref::<dao_contract::propose::validate::CallData>().unwrap();
  405. let header = &call_data.header;
  406. let note: dao_contract::propose::wallet::Note =
  407. header.enc_note.decrypt(&self.keypair.secret).unwrap();
  408. // Return the proposal info
  409. (note.proposal, call_data.header.proposal_bulla)
  410. };
  411. debug!(target: "demo", "Proposal now active!");
  412. debug!(target: "demo", " destination: {:?}", proposal.dest);
  413. debug!(target: "demo", " amount: {}", proposal.amount);
  414. debug!(target: "demo", " token_id: {:?}", proposal.token_id);
  415. debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
  416. Ok((proposal, proposal_bulla))
  417. }
  418. // We decrypt the votes in a transaction and add it to the wallet.
  419. fn read_vote(&mut self, tx: &Transaction) -> Result<()> {
  420. let vote_note = {
  421. let func_call = &tx.func_calls[0];
  422. let call_data = func_call.call_data.as_any();
  423. let call_data =
  424. call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  425. let header = &call_data.header;
  426. let note: dao_contract::vote::wallet::Note =
  427. header.enc_note.decrypt(&self.keypair.secret).unwrap();
  428. note
  429. };
  430. self.vote_notes.push(vote_note);
  431. // TODO: this should print from the CLI rather than use debug statements.
  432. // TODO: maybe this its own method? get votes
  433. //debug!(target: "demo", "User voted!");
  434. //debug!(target: "demo", " vote_option: {}", vote_note.vote.vote_option);
  435. //debug!(target: "demo", " value: {}", vote_note.vote_value);
  436. Ok(())
  437. }
  438. // TODO: Explicit error handling.
  439. fn get_treasury_path(
  440. &self,
  441. own_coin: &OwnCoin,
  442. states: &StateRegistry,
  443. ) -> Result<(Position, Vec<MerkleNode>)> {
  444. let (money_leaf_position, money_merkle_path) = {
  445. let state =
  446. states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  447. let tree = &state.tree;
  448. let leaf_position = own_coin.leaf_position.clone();
  449. let root = tree.root(0).unwrap();
  450. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  451. (leaf_position, merkle_path)
  452. };
  453. Ok((money_leaf_position, money_merkle_path))
  454. }
  455. fn build_exec_tx(
  456. &self,
  457. proposal: Proposal,
  458. proposal_bulla: pallas::Base,
  459. dao_params: DaoParams,
  460. zk_bins: &ZkContractTable,
  461. states: &mut StateRegistry,
  462. ) -> Result<Transaction> {
  463. let mut inputs = Vec::new();
  464. let mut total_input_value = 0;
  465. // TODO: move these to DAO struct?
  466. let tx_signature_secret = SecretKey::random(&mut OsRng);
  467. let exec_signature_secret = SecretKey::random(&mut OsRng);
  468. // TODO: not sure what this is doing
  469. // Should this be moved into a different struct?
  470. let user_serial = pallas::Base::random(&mut OsRng);
  471. let user_coin_blind = pallas::Base::random(&mut OsRng);
  472. let user_data_blind = pallas::Base::random(&mut OsRng);
  473. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  474. let dao_serial = pallas::Base::random(&mut OsRng);
  475. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  476. // We must prove we have sufficient governance tokens to execute this.
  477. for (coin, is_spent) in &self.own_coins {
  478. let is_spent = is_spent.clone();
  479. if is_spent {
  480. continue
  481. }
  482. let (treasury_leaf_position, treasury_merkle_path) =
  483. self.get_treasury_path(&coin, states)?;
  484. let input_value = coin.note.value;
  485. let input = {
  486. money_contract::transfer::wallet::BuilderInputInfo {
  487. leaf_position: treasury_leaf_position,
  488. merkle_path: treasury_merkle_path,
  489. secret: self.keypair.secret,
  490. note: coin.note.clone(),
  491. user_data_blind,
  492. value_blind: input_value_blind,
  493. // TODO: in schema, we create random signatures here. why?
  494. signature_secret: tx_signature_secret,
  495. }
  496. };
  497. total_input_value += input_value;
  498. inputs.push(input);
  499. }
  500. let builder = {
  501. money_contract::transfer::wallet::Builder {
  502. clear_inputs: vec![],
  503. inputs,
  504. outputs: vec![
  505. // Sending money
  506. money_contract::transfer::wallet::BuilderOutputInfo {
  507. value: proposal.amount,
  508. token_id: proposal.token_id,
  509. public: proposal.dest,
  510. serial: proposal.serial,
  511. coin_blind: proposal.blind,
  512. spend_hook: pallas::Base::from(0),
  513. user_data: pallas::Base::from(0),
  514. },
  515. // Change back to DAO
  516. money_contract::transfer::wallet::BuilderOutputInfo {
  517. value: total_input_value - proposal.amount,
  518. // TODO: this should be the token id of the treasury,
  519. // rather than the token id of the proposal.
  520. // total_id: own_coin.token_id,
  521. token_id: proposal.token_id,
  522. public: self.keypair.public,
  523. // ?
  524. serial: dao_serial,
  525. coin_blind: dao_coin_blind,
  526. spend_hook: *dao_contract::exec::FUNC_ID,
  527. user_data: proposal_bulla,
  528. },
  529. ],
  530. }
  531. };
  532. let transfer_func_call = builder.build(zk_bins)?;
  533. let mut yes_votes_value = 0;
  534. let mut yes_votes_blind = pallas::Scalar::from(0);
  535. let mut all_votes_value = 0;
  536. let mut all_votes_blind = pallas::Scalar::from(0);
  537. for note in &self.vote_notes {
  538. if note.vote.vote_option {
  539. // this is a yes vote
  540. yes_votes_value += note.vote_value;
  541. yes_votes_blind += note.vote_value_blind;
  542. }
  543. all_votes_value += note.vote_value;
  544. all_votes_blind += note.vote_value_blind;
  545. }
  546. let builder = {
  547. dao_contract::exec::wallet::Builder {
  548. proposal: proposal.clone(),
  549. dao: dao_params.clone(),
  550. yes_votes_value,
  551. all_votes_value,
  552. yes_votes_blind,
  553. all_votes_blind,
  554. user_serial,
  555. user_coin_blind,
  556. dao_serial,
  557. dao_coin_blind,
  558. input_value: proposal.amount,
  559. input_value_blind,
  560. hook_dao_exec: *dao_contract::exec::FUNC_ID,
  561. signature_secret: exec_signature_secret,
  562. }
  563. };
  564. let exec_func_call = builder.build(zk_bins);
  565. let func_calls = vec![transfer_func_call, exec_func_call];
  566. let signatures = sign(vec![tx_signature_secret, exec_signature_secret], &func_calls);
  567. Ok(Transaction { func_calls, signatures })
  568. }
  569. }
  570. // Stores governance tokens and related secret values.
  571. struct MoneyWallet {
  572. keypair: Keypair,
  573. signature_secret: SecretKey,
  574. own_coins: Vec<(OwnCoin, bool)>,
  575. //leaf_position: Position,
  576. }
  577. impl MoneyWallet {
  578. fn signature_public(&self) -> PublicKey {
  579. PublicKey::from_secret(self.signature_secret)
  580. }
  581. fn get_public_key(&self) -> PublicKey {
  582. self.keypair.public
  583. }
  584. fn track(&self, states: &mut StateRegistry) -> Result<()> {
  585. let state =
  586. states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  587. state.wallet_cache.track(self.keypair.secret);
  588. Ok(())
  589. }
  590. fn balances(&self) -> Result<u64> {
  591. let mut balances = 0;
  592. for (coin, is_spent) in &self.own_coins {
  593. if *is_spent {}
  594. balances += coin.note.value;
  595. }
  596. Ok(balances)
  597. }
  598. fn propose_tx(
  599. &mut self,
  600. params: DaoParams,
  601. recipient: PublicKey,
  602. token_id: pallas::Base,
  603. amount: u64,
  604. dao_leaf_position: Position,
  605. zk_bins: &ZkContractTable,
  606. states: &mut StateRegistry,
  607. ) -> Result<Transaction> {
  608. let mut inputs = Vec::new();
  609. for (coin, is_spent) in &self.own_coins {
  610. let is_spent = is_spent.clone();
  611. if is_spent {
  612. continue
  613. }
  614. let (money_leaf_position, money_merkle_path) = self.get_path(&states, &coin)?;
  615. let input = {
  616. dao_contract::propose::wallet::BuilderInput {
  617. secret: self.keypair.secret,
  618. note: coin.note.clone(),
  619. leaf_position: money_leaf_position,
  620. merkle_path: money_merkle_path,
  621. signature_secret: self.signature_secret,
  622. }
  623. };
  624. inputs.push(input);
  625. }
  626. let (dao_merkle_path, dao_merkle_root) = {
  627. let state = states.lookup::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
  628. let tree = &state.dao_tree;
  629. let root = tree.root(0).unwrap();
  630. let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
  631. (merkle_path, root)
  632. };
  633. let proposal = {
  634. dao_contract::propose::wallet::Proposal {
  635. dest: recipient,
  636. amount,
  637. serial: pallas::Base::random(&mut OsRng),
  638. token_id,
  639. blind: pallas::Base::random(&mut OsRng),
  640. }
  641. };
  642. let builder = dao_contract::propose::wallet::Builder {
  643. inputs,
  644. proposal,
  645. dao: params.clone(),
  646. dao_leaf_position,
  647. dao_merkle_path,
  648. dao_merkle_root,
  649. };
  650. let func_call = builder.build(zk_bins);
  651. let func_calls = vec![func_call];
  652. debug!(target: "dao-demo::money_wallet::propose", "signature_public {:?}", self.signature_public());
  653. let signatures = sign(vec![self.signature_secret], &func_calls);
  654. Ok(Transaction { func_calls, signatures })
  655. }
  656. // TODO: Explicit error handling.
  657. fn get_path(
  658. &self,
  659. states: &StateRegistry,
  660. own_coin: &OwnCoin,
  661. ) -> Result<(Position, Vec<MerkleNode>)> {
  662. let (money_leaf_position, money_merkle_path) = {
  663. let state =
  664. states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  665. let tree = &state.tree;
  666. let leaf_position = own_coin.leaf_position.clone();
  667. let root = tree.root(0).unwrap();
  668. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  669. (leaf_position, merkle_path)
  670. };
  671. Ok((money_leaf_position, money_merkle_path))
  672. }
  673. // TODO: User must have the values Proposal and DaoParams in order to cast a vote.
  674. // These should be encoded to base58 and printed to command-line when a DAO is made (DaoParams)
  675. // and a Proposal is made (Proposal). Then the user loads a base58 string into the vote request.
  676. fn vote_tx(
  677. &mut self,
  678. vote_option: bool,
  679. dao_key: Keypair,
  680. proposal: Proposal,
  681. dao_params: DaoParams,
  682. zk_bins: &ZkContractTable,
  683. states: &mut StateRegistry,
  684. ) -> Result<Transaction> {
  685. let mut inputs = Vec::new();
  686. // We must prove we have sufficient governance tokens in order to vote.
  687. for (coin, is_spent) in &self.own_coins {
  688. let (money_leaf_position, money_merkle_path) = self.get_path(states, &coin)?;
  689. let input = {
  690. dao_contract::vote::wallet::BuilderInput {
  691. secret: self.keypair.secret,
  692. note: coin.note.clone(),
  693. leaf_position: money_leaf_position,
  694. merkle_path: money_merkle_path,
  695. signature_secret: self.signature_secret,
  696. }
  697. };
  698. inputs.push(input);
  699. }
  700. let builder = {
  701. dao_contract::vote::wallet::Builder {
  702. inputs,
  703. vote: dao_contract::vote::wallet::Vote {
  704. vote_option,
  705. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  706. },
  707. vote_keypair: self.keypair,
  708. proposal: proposal.clone(),
  709. dao: dao_params.clone(),
  710. }
  711. };
  712. let func_call = builder.build(zk_bins);
  713. let func_calls = vec![func_call];
  714. let signatures = sign(vec![self.signature_secret], &func_calls);
  715. Ok(Transaction { func_calls, signatures })
  716. }
  717. }
  718. async fn start_rpc(client: Client) -> Result<()> {
  719. let rpc_addr = Url::parse("tcp://127.0.0.1:7777")?;
  720. let rpc_client = JsonRpcInterface::new(client);
  721. let rpc_interface = Arc::new(rpc_client);
  722. listen_and_serve(rpc_addr, rpc_interface).await?;
  723. Ok(())
  724. }
  725. // Mint authority that mints the DAO treasury and airdrops governance tokens.
  726. #[derive(Clone)]
  727. struct Cashier {
  728. keypair: Keypair,
  729. signature_secret: SecretKey,
  730. }
  731. impl Cashier {
  732. fn new() -> Self {
  733. let keypair = Keypair::random(&mut OsRng);
  734. let signature_secret = SecretKey::random(&mut OsRng);
  735. Self { keypair, signature_secret }
  736. }
  737. fn signature_public(&self) -> PublicKey {
  738. PublicKey::from_secret(self.signature_secret)
  739. }
  740. fn mint(
  741. &mut self,
  742. token_id: pallas::Base,
  743. token_supply: u64,
  744. dao_bulla: pallas::Base,
  745. recipient: PublicKey,
  746. zk_bins: &ZkContractTable,
  747. ) -> Result<Transaction> {
  748. let spend_hook = *dao_contract::exec::FUNC_ID;
  749. let user_data = dao_bulla;
  750. let value = token_supply;
  751. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  752. Ok(tx)
  753. }
  754. fn transfer_tx(
  755. &self,
  756. value: u64,
  757. token_id: pallas::Base,
  758. spend_hook: pallas::Base,
  759. user_data: pallas::Base,
  760. recipient: PublicKey,
  761. zk_bins: &ZkContractTable,
  762. ) -> Result<Transaction> {
  763. let builder = {
  764. money_contract::transfer::wallet::Builder {
  765. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  766. value,
  767. token_id,
  768. signature_secret: self.signature_secret,
  769. }],
  770. inputs: vec![],
  771. outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
  772. value,
  773. token_id,
  774. public: recipient,
  775. serial: pallas::Base::random(&mut OsRng),
  776. coin_blind: pallas::Base::random(&mut OsRng),
  777. spend_hook,
  778. user_data,
  779. }],
  780. }
  781. };
  782. let func_call = builder.build(zk_bins)?;
  783. let func_calls = vec![func_call];
  784. let signatures = sign(vec![self.signature_secret], &func_calls);
  785. Ok(Transaction { func_calls, signatures })
  786. }
  787. fn airdrop(
  788. &mut self,
  789. value: u64,
  790. token_id: pallas::Base,
  791. recipient: PublicKey,
  792. zk_bins: &ZkContractTable,
  793. ) -> Result<Transaction> {
  794. // Spend hook and user data disabled
  795. let spend_hook = DrkSpendHook::from(0);
  796. let user_data = DrkUserData::from(0);
  797. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  798. Ok(tx)
  799. }
  800. }
  801. #[async_std::main]
  802. async fn main() -> Result<()> {
  803. TermLogger::init(
  804. LevelFilter::Debug,
  805. simplelog::Config::default(),
  806. TerminalMode::Mixed,
  807. ColorChoice::Auto,
  808. )?;
  809. let mut client = Client::new();
  810. client.init();
  811. start_rpc(client).await?;
  812. Ok(())
  813. }