main.rs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  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 leaf_position = Position::zero();
  111. let money_wallet = MoneyWallet { keypair, signature_secret, leaf_position };
  112. self.money_wallets.insert(key, money_wallet);
  113. }
  114. // TODO: user passes DAO approval ratio: 1/2
  115. // we parse that into dao_approval_ratio_base and dao_approval_ratio_quot
  116. fn create_dao(
  117. &mut self,
  118. dao_proposer_limit: u64,
  119. dao_quorum: u64,
  120. dao_approval_ratio_quot: u64,
  121. dao_approval_ratio_base: u64,
  122. token_id: pallas::Base,
  123. ) -> Result<pallas::Base> {
  124. let tx = self.dao_wallet.mint_tx(
  125. dao_proposer_limit,
  126. dao_quorum,
  127. dao_approval_ratio_quot,
  128. dao_approval_ratio_base,
  129. token_id,
  130. &self.zk_bins,
  131. );
  132. // TODO: Proper error handling.
  133. // Only witness the value once the transaction is confirmed.
  134. match self.validate(&tx) {
  135. Ok(v) => self.dao_wallet.update_witness(&mut self.states)?,
  136. Err(e) => {}
  137. }
  138. // Retrieve DAO bulla from the state.
  139. let dao_bulla = {
  140. let func_call = &tx.func_calls[0];
  141. let call_data = func_call.call_data.as_any();
  142. let call_data =
  143. call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
  144. call_data.dao_bulla.clone()
  145. };
  146. // TODO: instead of this print statement, return DAO bulla to CLI
  147. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  148. // We create a hashmap so we can easily retrieve DAO values for the demo.
  149. let dao_params = DaoParams {
  150. proposer_limit: dao_proposer_limit,
  151. quorum: dao_quorum,
  152. approval_ratio_quot: dao_approval_ratio_quot,
  153. approval_ratio_base: dao_approval_ratio_base,
  154. gov_token_id: token_id,
  155. public_key: self.dao_wallet.keypair.public,
  156. bulla_blind: self.dao_wallet.bulla_blind,
  157. };
  158. self.dao_wallet.params.insert(HashableBase(dao_bulla.0), dao_params);
  159. Ok(dao_bulla.0)
  160. }
  161. // TODO: Change these into errors instead of expects.
  162. fn validate(&mut self, tx: &Transaction) -> Result<()> {
  163. let mut updates = vec![];
  164. // Validate all function calls in the tx
  165. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  166. // So then the verifier will lookup the corresponding state_transition and apply
  167. // functions based off the func_id
  168. if func_call.func_id == *money_contract::transfer::FUNC_ID {
  169. debug!("money_contract::transfer::state_transition()");
  170. let update =
  171. money_contract::transfer::validate::state_transition(&self.states, idx, &tx)
  172. .expect("money_contract::transfer::validate::state_transition() failed!");
  173. updates.push(update);
  174. } else if func_call.func_id == *dao_contract::mint::FUNC_ID {
  175. debug!("dao_contract::mint::state_transition()");
  176. let update = dao_contract::mint::validate::state_transition(&self.states, idx, &tx)
  177. .expect("dao_contract::mint::validate::state_transition() failed!");
  178. updates.push(update);
  179. } else if func_call.func_id == *dao_contract::propose::FUNC_ID {
  180. debug!(target: "demo", "dao_contract::propose::state_transition()");
  181. let update =
  182. dao_contract::propose::validate::state_transition(&self.states, idx, &tx)
  183. .expect("dao_contract::propose::validate::state_transition() failed!");
  184. updates.push(update);
  185. } else if func_call.func_id == *dao_contract::vote::FUNC_ID {
  186. debug!(target: "demo", "dao_contract::vote::state_transition()");
  187. let update = dao_contract::vote::validate::state_transition(&self.states, idx, &tx)
  188. .expect("dao_contract::vote::validate::state_transition() failed!");
  189. updates.push(update);
  190. } else if func_call.func_id == *dao_contract::exec::FUNC_ID {
  191. debug!("dao_contract::exec::state_transition()");
  192. let update = dao_contract::exec::validate::state_transition(&self.states, idx, &tx)
  193. .expect("dao_contract::exec::validate::state_transition() failed!");
  194. updates.push(update);
  195. }
  196. }
  197. // Atomically apply all changes
  198. for update in updates {
  199. update.apply(&mut self.states);
  200. }
  201. tx.zk_verify(&self.zk_bins);
  202. tx.verify_sigs();
  203. Ok(())
  204. }
  205. // TODO: error handling
  206. fn propose(
  207. &mut self,
  208. params: DaoParams,
  209. recipient: PublicKey,
  210. token_id: pallas::Base,
  211. amount: u64,
  212. key: String,
  213. ) -> Result<()> {
  214. let dao_leaf_position = self.dao_wallet.leaf_position;
  215. let mut money_wallet = self.money_wallets.get_mut(&key).unwrap();
  216. let tx = money_wallet.propose_tx(
  217. params,
  218. recipient,
  219. token_id,
  220. amount,
  221. dao_leaf_position,
  222. &self.zk_bins,
  223. &mut self.states,
  224. )?;
  225. self.validate(&tx)?;
  226. self.dao_wallet.read_proposal(&tx)?;
  227. Ok(())
  228. }
  229. }
  230. struct DaoWallet {
  231. keypair: Keypair,
  232. signature_secret: SecretKey,
  233. bulla_blind: pallas::Base,
  234. leaf_position: Position,
  235. params: HashMap<HashableBase, DaoParams>,
  236. vote_notes: Vec<dao_contract::vote::wallet::Note>,
  237. }
  238. impl DaoWallet {
  239. fn new() -> Self {
  240. let keypair = Keypair::random(&mut OsRng);
  241. let signature_secret = SecretKey::random(&mut OsRng);
  242. let bulla_blind = pallas::Base::random(&mut OsRng);
  243. let leaf_position = Position::zero();
  244. let params: HashMap<HashableBase, DaoParams> = HashMap::default();
  245. let vote_notes = Vec::new();
  246. Self { keypair, signature_secret, bulla_blind, leaf_position, params, vote_notes }
  247. }
  248. // Mint the DAO bulla.
  249. fn mint_tx(
  250. &mut self,
  251. dao_proposer_limit: u64,
  252. dao_quorum: u64,
  253. dao_approval_ratio_quot: u64,
  254. dao_approval_ratio_base: u64,
  255. token_id: pallas::Base,
  256. zk_bins: &ZkContractTable,
  257. ) -> Transaction {
  258. let builder = dao_contract::mint::wallet::Builder {
  259. dao_proposer_limit,
  260. dao_quorum,
  261. dao_approval_ratio_quot,
  262. dao_approval_ratio_base,
  263. gov_token_id: *GDRK_ID,
  264. dao_pubkey: self.keypair.public,
  265. dao_bulla_blind: self.bulla_blind,
  266. _signature_secret: self.signature_secret,
  267. };
  268. let func_call = builder.build(zk_bins);
  269. let func_calls = vec![func_call];
  270. let signatures = sign(vec![self.signature_secret], &func_calls);
  271. Transaction { func_calls, signatures }
  272. }
  273. // TODO: error handling
  274. fn update_witness(&mut self, states: &mut StateRegistry) -> Result<()> {
  275. let state = states.lookup_mut::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
  276. let path = state.dao_tree.witness();
  277. match path {
  278. Some(path) => {
  279. self.leaf_position = path;
  280. }
  281. None => {}
  282. }
  283. Ok(())
  284. }
  285. fn balances(&self, states: &mut StateRegistry) -> Result<OwnCoin> {
  286. let state =
  287. states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  288. let mut recv_coins = state.wallet_cache.get_received(&self.keypair.secret);
  289. let dao_recv_coin = recv_coins.pop().unwrap();
  290. let treasury_note = dao_recv_coin.note.clone();
  291. let coords = self.keypair.public.0.to_affine().coordinates().unwrap();
  292. let coin = poseidon_hash::<8>([
  293. *coords.x(),
  294. *coords.y(),
  295. DrkValue::from(treasury_note.value),
  296. treasury_note.token_id,
  297. treasury_note.serial,
  298. treasury_note.spend_hook,
  299. treasury_note.user_data,
  300. treasury_note.coin_blind,
  301. ]);
  302. // TODO: Error handling
  303. if coin == dao_recv_coin.coin.0 {
  304. // return Ok(dao_recv_coin)
  305. }
  306. // else {
  307. // return Err::InvalidCoin
  308. // }
  309. // TODO: this is the CLI output.
  310. debug!("DAO received a coin worth {} xDRK", treasury_note.value);
  311. // TODO: just return the value of the coin, not OwnCoin.
  312. Ok(dao_recv_coin)
  313. }
  314. fn read_proposal(&self, tx: &Transaction) -> Result<()> {
  315. let (proposal, proposal_bulla) = {
  316. let func_call = &tx.func_calls[0];
  317. let call_data = func_call.call_data.as_any();
  318. let call_data =
  319. call_data.downcast_ref::<dao_contract::propose::validate::CallData>().unwrap();
  320. let header = &call_data.header;
  321. let note: dao_contract::propose::wallet::Note =
  322. header.enc_note.decrypt(&self.keypair.secret).unwrap();
  323. // Return the proposal info
  324. (note.proposal, call_data.header.proposal_bulla)
  325. };
  326. // TODO: this should print from the CLI rather than use debug statements.
  327. debug!(target: "demo", "Proposal now active!");
  328. debug!(target: "demo", " destination: {:?}", proposal.dest);
  329. debug!(target: "demo", " amount: {}", proposal.amount);
  330. debug!(target: "demo", " token_id: {:?}", proposal.token_id);
  331. debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
  332. Ok(())
  333. // TODO: encode Proposal as base58 and return to cli
  334. }
  335. // We decrypt the votes in a transaction and add it to the wallet.
  336. fn read_vote(&mut self, tx: &Transaction) -> Result<()> {
  337. let vote_note = {
  338. let func_call = &tx.func_calls[0];
  339. let call_data = func_call.call_data.as_any();
  340. let call_data =
  341. call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  342. let header = &call_data.header;
  343. let note: dao_contract::vote::wallet::Note =
  344. header.enc_note.decrypt(&self.keypair.secret).unwrap();
  345. note
  346. };
  347. self.vote_notes.push(vote_note);
  348. // TODO: this should print from the CLI rather than use debug statements.
  349. // TODO: maybe this its own method? get votes
  350. //debug!(target: "demo", "User voted!");
  351. //debug!(target: "demo", " vote_option: {}", vote_note.vote.vote_option);
  352. //debug!(target: "demo", " value: {}", vote_note.vote_value);
  353. Ok(())
  354. }
  355. // TODO: Explicit error handling.
  356. fn get_treasury_path(
  357. &self,
  358. own_coin: &OwnCoin,
  359. states: &StateRegistry,
  360. ) -> Result<(Position, Vec<MerkleNode>)> {
  361. let (money_leaf_position, money_merkle_path) = {
  362. let state =
  363. states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  364. let tree = &state.tree;
  365. let leaf_position = own_coin.leaf_position.clone();
  366. let root = tree.root(0).unwrap();
  367. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  368. (leaf_position, merkle_path)
  369. };
  370. Ok((money_leaf_position, money_merkle_path))
  371. }
  372. fn build_exec_tx(
  373. &self,
  374. proposal: Proposal,
  375. proposal_bulla: pallas::Base,
  376. dao_params: DaoParams,
  377. zk_bins: &ZkContractTable,
  378. states: &mut StateRegistry,
  379. ) -> Result<Transaction> {
  380. // TODO: move these to DAO struct?
  381. let tx_signature_secret = SecretKey::random(&mut OsRng);
  382. let exec_signature_secret = SecretKey::random(&mut OsRng);
  383. // We must prove we have sufficient governance tokens to execute this.
  384. let own_coin = self.balances(states)?;
  385. let (treasury_leaf_position, treasury_merkle_path) =
  386. self.get_treasury_path(&own_coin, states)?;
  387. let input_value = own_coin.note.value;
  388. // TODO: not sure what this is doing
  389. // Should this be moved into a different struct?
  390. let user_serial = pallas::Base::random(&mut OsRng);
  391. let user_coin_blind = pallas::Base::random(&mut OsRng);
  392. let user_data_blind = pallas::Base::random(&mut OsRng);
  393. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  394. let dao_serial = pallas::Base::random(&mut OsRng);
  395. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  396. let input = {
  397. money_contract::transfer::wallet::BuilderInputInfo {
  398. leaf_position: treasury_leaf_position,
  399. merkle_path: treasury_merkle_path,
  400. secret: self.keypair.secret,
  401. note: own_coin.note.clone(),
  402. user_data_blind,
  403. value_blind: input_value_blind,
  404. // TODO: in schema, we create random signatures here. why?
  405. signature_secret: tx_signature_secret,
  406. }
  407. };
  408. let builder = {
  409. money_contract::transfer::wallet::Builder {
  410. clear_inputs: vec![],
  411. inputs: vec![input],
  412. outputs: vec![
  413. // Sending money
  414. money_contract::transfer::wallet::BuilderOutputInfo {
  415. value: proposal.amount,
  416. token_id: proposal.token_id,
  417. public: proposal.dest,
  418. serial: proposal.serial,
  419. coin_blind: proposal.blind,
  420. spend_hook: pallas::Base::from(0),
  421. user_data: pallas::Base::from(0),
  422. },
  423. // Change back to DAO
  424. money_contract::transfer::wallet::BuilderOutputInfo {
  425. value: own_coin.note.value - proposal.amount,
  426. token_id: own_coin.note.token_id,
  427. public: self.keypair.public,
  428. // ?
  429. serial: dao_serial,
  430. coin_blind: dao_coin_blind,
  431. spend_hook: *dao_contract::exec::FUNC_ID,
  432. user_data: proposal_bulla,
  433. },
  434. ],
  435. }
  436. };
  437. let transfer_func_call = builder.build(zk_bins)?;
  438. let mut yes_votes_value = 0;
  439. let mut yes_votes_blind = pallas::Scalar::from(0);
  440. let mut all_votes_value = 0;
  441. let mut all_votes_blind = pallas::Scalar::from(0);
  442. for note in &self.vote_notes {
  443. if note.vote.vote_option {
  444. // this is a yes vote
  445. yes_votes_value += note.vote_value;
  446. yes_votes_blind += note.vote_value_blind;
  447. }
  448. all_votes_value += note.vote_value;
  449. all_votes_blind += note.vote_value_blind;
  450. }
  451. let builder = {
  452. dao_contract::exec::wallet::Builder {
  453. proposal: proposal.clone(),
  454. dao: dao_params.clone(),
  455. yes_votes_value,
  456. all_votes_value,
  457. yes_votes_blind,
  458. all_votes_blind,
  459. user_serial,
  460. user_coin_blind,
  461. dao_serial,
  462. dao_coin_blind,
  463. input_value: proposal.amount,
  464. input_value_blind,
  465. hook_dao_exec: *dao_contract::exec::FUNC_ID,
  466. signature_secret: exec_signature_secret,
  467. }
  468. };
  469. let exec_func_call = builder.build(zk_bins);
  470. let func_calls = vec![transfer_func_call, exec_func_call];
  471. let signatures = sign(vec![tx_signature_secret, exec_signature_secret], &func_calls);
  472. Ok(Transaction { func_calls, signatures })
  473. }
  474. }
  475. // Stores governance tokens and related secret values.
  476. #[derive(Clone)]
  477. struct MoneyWallet {
  478. keypair: Keypair,
  479. signature_secret: SecretKey,
  480. leaf_position: Position,
  481. }
  482. impl MoneyWallet {
  483. fn signature_public(&self) -> PublicKey {
  484. PublicKey::from_secret(self.signature_secret)
  485. }
  486. fn balances(&self, states: &mut StateRegistry) -> Result<OwnCoin> {
  487. let state =
  488. states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  489. let mut recv_coins = state.wallet_cache.get_received(&self.keypair.secret);
  490. let recv_coin = recv_coins.pop().unwrap();
  491. let note = recv_coin.note.clone();
  492. // TODO: this should output to command line
  493. debug!("User received a coin worth {} gDRK", note.value);
  494. // TODO: don't return the coin, just return the value
  495. Ok(recv_coin)
  496. }
  497. fn propose_tx(
  498. &mut self,
  499. params: DaoParams,
  500. recipient: PublicKey,
  501. token_id: pallas::Base,
  502. amount: u64,
  503. dao_leaf_position: Position,
  504. zk_bins: &ZkContractTable,
  505. states: &mut StateRegistry,
  506. ) -> Result<Transaction> {
  507. // To be able to make a proposal, we must prove we have ownership of governance tokens,
  508. // and that the quantity of governance tokens is within the accepted proposal limit.
  509. let own_coin = self.balances(states)?;
  510. let (money_leaf_position, money_merkle_path) = self.get_path(&states, &own_coin)?;
  511. let signature_secret = SecretKey::random(&mut OsRng);
  512. let input = {
  513. dao_contract::propose::wallet::BuilderInput {
  514. secret: self.keypair.secret,
  515. note: own_coin.note.clone(),
  516. leaf_position: money_leaf_position,
  517. merkle_path: money_merkle_path,
  518. signature_secret,
  519. }
  520. };
  521. let (dao_merkle_path, dao_merkle_root) = {
  522. let state = states.lookup::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
  523. let tree = &state.dao_tree;
  524. let root = tree.root(0).unwrap();
  525. let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
  526. (merkle_path, root)
  527. };
  528. let proposal = {
  529. dao_contract::propose::wallet::Proposal {
  530. dest: recipient,
  531. amount,
  532. serial: pallas::Base::random(&mut OsRng),
  533. token_id,
  534. blind: pallas::Base::random(&mut OsRng),
  535. }
  536. };
  537. let builder = dao_contract::propose::wallet::Builder {
  538. inputs: vec![input],
  539. proposal,
  540. dao: params.clone(),
  541. dao_leaf_position,
  542. dao_merkle_path,
  543. dao_merkle_root,
  544. };
  545. let func_call = builder.build(zk_bins);
  546. let func_calls = vec![func_call];
  547. let signatures = sign(vec![signature_secret], &func_calls);
  548. Ok(Transaction { func_calls, signatures })
  549. }
  550. // TODO: Explicit error handling.
  551. fn get_path(
  552. &self,
  553. states: &StateRegistry,
  554. own_coin: &OwnCoin,
  555. ) -> Result<(Position, Vec<MerkleNode>)> {
  556. let (money_leaf_position, money_merkle_path) = {
  557. let state =
  558. states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
  559. let tree = &state.tree;
  560. let leaf_position = own_coin.leaf_position.clone();
  561. let root = tree.root(0).unwrap();
  562. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  563. (leaf_position, merkle_path)
  564. };
  565. Ok((money_leaf_position, money_merkle_path))
  566. }
  567. // TODO: User must have the values Proposal and DaoParams in order to cast a vote.
  568. // These should be encoded to base58 and printed to command-line when a DAO is made (DaoParams)
  569. // and a Proposal is made (Proposal). Then the user loads a base58 string into the vote request.
  570. fn vote_tx(
  571. &mut self,
  572. vote_option: bool,
  573. dao_key: Keypair,
  574. proposal: Proposal,
  575. dao_params: DaoParams,
  576. zk_bins: &ZkContractTable,
  577. states: &mut StateRegistry,
  578. ) -> Result<Transaction> {
  579. // We must prove we have governance tokens in order to vote.
  580. let own_coin = self.balances(states)?;
  581. let (money_leaf_position, money_merkle_path) = self.get_path(states, &own_coin)?;
  582. let input = {
  583. dao_contract::vote::wallet::BuilderInput {
  584. secret: self.keypair.secret,
  585. note: own_coin.note.clone(),
  586. leaf_position: money_leaf_position,
  587. merkle_path: money_merkle_path,
  588. signature_secret: self.signature_secret,
  589. }
  590. };
  591. let builder = {
  592. dao_contract::vote::wallet::Builder {
  593. inputs: vec![input],
  594. vote: dao_contract::vote::wallet::Vote {
  595. vote_option,
  596. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  597. },
  598. vote_keypair: self.keypair,
  599. proposal: proposal.clone(),
  600. dao: dao_params.clone(),
  601. }
  602. };
  603. let func_call = builder.build(zk_bins);
  604. let func_calls = vec![func_call];
  605. let signatures = sign(vec![self.signature_secret], &func_calls);
  606. Ok(Transaction { func_calls, signatures })
  607. }
  608. }
  609. async fn start_rpc(client: Client) -> Result<()> {
  610. let rpc_addr = Url::parse("tcp://127.0.0.1:7777")?;
  611. let rpc_client = JsonRpcInterface::new(client);
  612. let rpc_interface = Arc::new(rpc_client);
  613. listen_and_serve(rpc_addr, rpc_interface).await?;
  614. Ok(())
  615. }
  616. // Mint authority that mints the DAO treasury and airdrops governance tokens.
  617. #[derive(Clone)]
  618. struct Cashier {
  619. keypair: Keypair,
  620. signature_secret: SecretKey,
  621. }
  622. impl Cashier {
  623. fn new() -> Self {
  624. let keypair = Keypair::random(&mut OsRng);
  625. let signature_secret = SecretKey::random(&mut OsRng);
  626. Self { keypair, signature_secret }
  627. }
  628. fn signature_public(&self) -> PublicKey {
  629. PublicKey::from_secret(self.signature_secret)
  630. }
  631. fn mint_treasury(
  632. &mut self,
  633. token_id: pallas::Base,
  634. token_supply: u64,
  635. dao_bulla: pallas::Base,
  636. recipient: PublicKey,
  637. zk_bins: &ZkContractTable,
  638. ) -> Result<Transaction> {
  639. let spend_hook = *dao_contract::exec::FUNC_ID;
  640. let user_data = dao_bulla;
  641. let value = token_supply;
  642. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  643. Ok(tx)
  644. }
  645. fn transfer_tx(
  646. &self,
  647. value: u64,
  648. token_id: pallas::Base,
  649. spend_hook: pallas::Base,
  650. user_data: pallas::Base,
  651. recipient: PublicKey,
  652. zk_bins: &ZkContractTable,
  653. ) -> Result<Transaction> {
  654. let builder = {
  655. money_contract::transfer::wallet::Builder {
  656. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  657. value,
  658. token_id,
  659. signature_secret: self.signature_secret,
  660. }],
  661. inputs: vec![],
  662. outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
  663. value,
  664. token_id,
  665. public: recipient,
  666. serial: pallas::Base::random(&mut OsRng),
  667. coin_blind: pallas::Base::random(&mut OsRng),
  668. spend_hook,
  669. user_data,
  670. }],
  671. }
  672. };
  673. let func_call = builder.build(zk_bins)?;
  674. let func_calls = vec![func_call];
  675. let signatures = sign(vec![self.signature_secret], &func_calls);
  676. Ok(Transaction { func_calls, signatures })
  677. }
  678. fn airdrop(
  679. &mut self,
  680. value: u64,
  681. token_id: pallas::Base,
  682. recipient: PublicKey,
  683. zk_bins: &ZkContractTable,
  684. ) -> Result<Transaction> {
  685. // Spend hook and user data disabled
  686. let spend_hook = DrkSpendHook::from(0);
  687. let user_data = DrkUserData::from(0);
  688. let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
  689. Ok(tx)
  690. }
  691. }
  692. #[async_std::main]
  693. async fn main() -> Result<()> {
  694. TermLogger::init(
  695. LevelFilter::Debug,
  696. simplelog::Config::default(),
  697. TerminalMode::Mixed,
  698. ColorChoice::Auto,
  699. )?;
  700. let mut client = Client::new();
  701. client.init();
  702. start_rpc(client).await?;
  703. Ok(())
  704. }