demo.rs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. use incrementalmerkletree::Tree;
  2. use log::debug;
  3. use pasta_curves::{
  4. arithmetic::CurveAffine,
  5. group::{ff::Field, Curve, Group},
  6. pallas,
  7. };
  8. use rand::rngs::OsRng;
  9. use std::{
  10. any::{Any, TypeId},
  11. collections::HashMap,
  12. time::Instant,
  13. };
  14. use darkfi::{
  15. crypto::{
  16. keypair::{Keypair, PublicKey, SecretKey},
  17. proof::{ProvingKey, VerifyingKey},
  18. types::{DrkCircuitField, DrkSpendHook, DrkUserData, DrkValue},
  19. util::pedersen_commitment_u64,
  20. Proof,
  21. },
  22. zk::{
  23. circuit::{BurnContract, MintContract},
  24. vm::ZkCircuit,
  25. vm_stack::empty_witnesses,
  26. },
  27. zkas::decoder::ZkBinary,
  28. };
  29. use crate::{dao_contract, example_contract, money_contract, util::poseidon_hash};
  30. // TODO: Anonymity leaks in this proof of concept:
  31. //
  32. // * Vote updates are linked to the proposal_bulla
  33. // * Nullifier of vote will link vote with the coin when it's spent
  34. // TODO: strategize and cleanup Result/Error usage
  35. // TODO: fix up code doc
  36. type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
  37. pub struct ZkBinaryContractInfo {
  38. pub k_param: u32,
  39. pub bincode: ZkBinary,
  40. pub proving_key: ProvingKey,
  41. pub verifying_key: VerifyingKey,
  42. }
  43. pub struct ZkNativeContractInfo {
  44. pub proving_key: ProvingKey,
  45. pub verifying_key: VerifyingKey,
  46. }
  47. pub enum ZkContractInfo {
  48. Binary(ZkBinaryContractInfo),
  49. Native(ZkNativeContractInfo),
  50. }
  51. pub struct ZkContractTable {
  52. // Key will be a hash of zk binary contract on chain
  53. table: HashMap<String, ZkContractInfo>,
  54. }
  55. impl ZkContractTable {
  56. fn new() -> Self {
  57. Self { table: HashMap::new() }
  58. }
  59. fn add_contract(&mut self, key: String, bincode: ZkBinary, k_param: u32) {
  60. let witnesses = empty_witnesses(&bincode);
  61. let circuit = ZkCircuit::new(witnesses, bincode.clone());
  62. let proving_key = ProvingKey::build(k_param, &circuit);
  63. let verifying_key = VerifyingKey::build(k_param, &circuit);
  64. let info = ZkContractInfo::Binary(ZkBinaryContractInfo {
  65. k_param,
  66. bincode,
  67. proving_key,
  68. verifying_key,
  69. });
  70. self.table.insert(key, info);
  71. }
  72. fn add_native(&mut self, key: String, proving_key: ProvingKey, verifying_key: VerifyingKey) {
  73. self.table.insert(
  74. key,
  75. ZkContractInfo::Native(ZkNativeContractInfo { proving_key, verifying_key }),
  76. );
  77. }
  78. pub fn lookup(&self, key: &String) -> Option<&ZkContractInfo> {
  79. self.table.get(key)
  80. }
  81. }
  82. pub struct Transaction {
  83. pub func_calls: Vec<FuncCall>,
  84. }
  85. impl Transaction {
  86. /// Verify ZK contracts for the entire tx
  87. /// In real code, we could parallelize this for loop
  88. /// TODO: fix use of unwrap with Result type stuff
  89. fn zk_verify(&self, zk_bins: &ZkContractTable) {
  90. for func_call in &self.func_calls {
  91. let proofs_public_vals = &func_call.call_data.zk_public_values();
  92. assert_eq!(
  93. proofs_public_vals.len(),
  94. func_call.proofs.len(),
  95. "proof_public_vals.len()={} and func_call.proofs.len()={} do not match",
  96. proofs_public_vals.len(),
  97. func_call.proofs.len()
  98. );
  99. for (i, (proof, (key, public_vals))) in
  100. func_call.proofs.iter().zip(proofs_public_vals.iter()).enumerate()
  101. {
  102. match zk_bins.lookup(key).unwrap() {
  103. ZkContractInfo::Binary(info) => {
  104. let verifying_key = &info.verifying_key;
  105. let verify_result = proof.verify(&verifying_key, public_vals);
  106. assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key);
  107. }
  108. ZkContractInfo::Native(info) => {
  109. let verifying_key = &info.verifying_key;
  110. let verify_result = proof.verify(&verifying_key, public_vals);
  111. assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key);
  112. }
  113. };
  114. debug!(target: "demo", "zk_verify({}) passed [i={}]", key, i);
  115. }
  116. }
  117. }
  118. }
  119. // These would normally be a hash or sth
  120. type ContractId = String;
  121. type FuncId = String;
  122. pub struct FuncCall {
  123. pub contract_id: ContractId,
  124. pub func_id: FuncId,
  125. pub call_data: Box<dyn CallDataBase>,
  126. pub proofs: Vec<Proof>,
  127. }
  128. pub trait CallDataBase {
  129. // Public values for verifying the proofs
  130. // Needed so we can convert internal types so they can be used in Proof::verify()
  131. fn zk_public_values(&self) -> Vec<(String, Vec<DrkCircuitField>)>;
  132. // For upcasting to CallData itself so it can be read in state_transition()
  133. fn as_any(&self) -> &dyn Any;
  134. }
  135. type GenericContractState = Box<dyn Any>;
  136. pub struct StateRegistry {
  137. pub states: HashMap<ContractId, GenericContractState>,
  138. }
  139. impl StateRegistry {
  140. fn new() -> Self {
  141. Self { states: HashMap::new() }
  142. }
  143. fn register(&mut self, contract_id: ContractId, state: GenericContractState) {
  144. debug!(target: "StateRegistry::register()", "contract_id: {:?}", contract_id);
  145. self.states.insert(contract_id, state);
  146. }
  147. pub fn lookup_mut<'a, S: 'static>(&'a mut self, contract_id: &ContractId) -> Option<&'a mut S> {
  148. self.states.get_mut(contract_id).and_then(|state| state.downcast_mut())
  149. }
  150. pub fn lookup<'a, S: 'static>(&'a self, contract_id: &ContractId) -> Option<&'a S> {
  151. self.states.get(contract_id).and_then(|state| state.downcast_ref())
  152. }
  153. }
  154. pub trait UpdateBase {
  155. fn apply(self: Box<Self>, states: &mut StateRegistry);
  156. }
  157. ///////////////////////////////////////////////////
  158. ///// Example contract
  159. ///////////////////////////////////////////////////
  160. pub async fn example() -> Result<()> {
  161. debug!(target: "demo", "Stage 0. Example contract");
  162. // Lookup table for smart contract states
  163. let mut states = StateRegistry::new();
  164. // Initialize ZK binary table
  165. let mut zk_bins = ZkContractTable::new();
  166. let zk_example_foo_bincode = include_bytes!("../proof/foo.zk.bin");
  167. let zk_example_foo_bin = ZkBinary::decode(zk_example_foo_bincode)?;
  168. zk_bins.add_contract("example-foo".to_string(), zk_example_foo_bin, 13);
  169. let example_state = example_contract::state::State::new();
  170. states.register("Example".to_string(), example_state);
  171. //// Wallet
  172. let foo = example_contract::foo::wallet::Foo { a: 5, b: 10 };
  173. let builder = example_contract::foo::wallet::Builder { foo };
  174. let func_call = builder.build(&zk_bins);
  175. let tx = Transaction { func_calls: vec![func_call] };
  176. //// Validator
  177. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  178. if func_call.func_id == "Example::foo()" {
  179. debug!("example_contract::foo::state_transition()");
  180. // TODO: separate this 2 things
  181. let update = example_contract::foo::validate::state_transition(&states, idx, &tx)
  182. .expect("example_contract::foo::validate::state_transition() failed!");
  183. example_contract::foo::validate::apply(&mut states, update);
  184. }
  185. }
  186. tx.zk_verify(&zk_bins);
  187. Ok(())
  188. }
  189. pub async fn demo() -> Result<()> {
  190. // Example smart contract
  191. //// TODO: this will be moved to a different file
  192. example().await?;
  193. // Money parameters
  194. let xdrk_supply = 1_000_000;
  195. let xdrk_token_id = pallas::Base::random(&mut OsRng);
  196. // Governance token parameters
  197. let gdrk_supply = 1_000_000;
  198. let gdrk_token_id = pallas::Base::random(&mut OsRng);
  199. // DAO parameters
  200. let dao_proposer_limit = 110;
  201. let dao_quorum = 110;
  202. let dao_approval_ratio = 2;
  203. // Lookup table for smart contract states
  204. let mut states = StateRegistry::new();
  205. // Initialize ZK binary table
  206. let mut zk_bins = ZkContractTable::new();
  207. debug!(target: "demo", "Loading dao-mint.zk");
  208. let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
  209. let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
  210. zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
  211. debug!(target: "demo", "Loading money-transfer contracts");
  212. {
  213. let start = Instant::now();
  214. let mint_pk = ProvingKey::build(11, &MintContract::default());
  215. debug!("Mint PK: [{:?}]", start.elapsed());
  216. let start = Instant::now();
  217. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  218. debug!("Burn PK: [{:?}]", start.elapsed());
  219. let start = Instant::now();
  220. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  221. debug!("Mint VK: [{:?}]", start.elapsed());
  222. let start = Instant::now();
  223. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  224. debug!("Burn VK: [{:?}]", start.elapsed());
  225. zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
  226. zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
  227. }
  228. debug!(target: "demo", "Loading dao-propose-main.zk");
  229. let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
  230. let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
  231. zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
  232. debug!(target: "demo", "Loading dao-propose-burn.zk");
  233. let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
  234. let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
  235. zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
  236. debug!(target: "demo", "Loading dao-vote-main.zk");
  237. let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
  238. let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
  239. zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
  240. debug!(target: "demo", "Loading dao-vote-burn.zk");
  241. let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
  242. let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
  243. zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
  244. // State for money contracts
  245. let cashier_signature_secret = SecretKey::random(&mut OsRng);
  246. let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
  247. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  248. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  249. ///////////////////////////////////////////////////
  250. let money_state =
  251. money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
  252. states.register("Money".to_string(), money_state);
  253. /////////////////////////////////////////////////////
  254. let dao_state = dao_contract::State::new();
  255. states.register("DAO".to_string(), dao_state);
  256. /////////////////////////////////////////////////////
  257. ////// Create the DAO bulla
  258. /////////////////////////////////////////////////////
  259. debug!(target: "demo", "Stage 1. Creating DAO bulla");
  260. //// Wallet
  261. //// Setup the DAO
  262. let dao_keypair = Keypair::random(&mut OsRng);
  263. let dao_bulla_blind = pallas::Base::random(&mut OsRng);
  264. // Create DAO mint tx
  265. let builder = dao_contract::mint::wallet::Builder::new(
  266. dao_proposer_limit,
  267. dao_quorum,
  268. dao_approval_ratio,
  269. gdrk_token_id,
  270. dao_keypair.public,
  271. dao_bulla_blind,
  272. );
  273. let func_call = builder.build(&zk_bins);
  274. let tx = Transaction { func_calls: vec![func_call] };
  275. //// Validator
  276. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  277. // So then the verifier will lookup the corresponding state_transition and apply
  278. // functions based off the func_id
  279. if func_call.func_id == "DAO::mint()" {
  280. debug!("dao_contract::mint::state_transition()");
  281. let update = dao_contract::mint::validate::state_transition(&states, idx, &tx)
  282. .expect("dao_contract::mint::validate::state_transition() failed!");
  283. dao_contract::mint::validate::apply(&mut states, update);
  284. }
  285. }
  286. tx.zk_verify(&zk_bins);
  287. // Wallet stuff
  288. // In your wallet, wait until you see the tx confirmed before doing anything below
  289. // So for example keep track of tx hash
  290. //assert_eq!(tx.hash(), tx_hash);
  291. // We need to witness() the value in our local merkle tree
  292. // Must be called as soon as this DAO bulla is added to the state
  293. let dao_leaf_position = {
  294. let state = states.lookup_mut::<dao_contract::State>(&"DAO".to_string()).unwrap();
  295. state.dao_tree.witness().unwrap()
  296. };
  297. // It might just be easier to hash it ourselves from keypair and blind...
  298. let dao_bulla = {
  299. assert_eq!(tx.func_calls.len(), 1);
  300. let func_call = &tx.func_calls[0];
  301. let call_data = func_call.call_data.as_any();
  302. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::mint::validate::CallData>());
  303. let call_data = call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
  304. call_data.dao_bulla.clone()
  305. };
  306. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  307. ///////////////////////////////////////////////////
  308. //// Mint the initial supply of treasury token
  309. //// and send it all to the DAO directly
  310. ///////////////////////////////////////////////////
  311. debug!(target: "demo", "Stage 2. Minting treasury token");
  312. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  313. state.wallet_cache.track(dao_keypair.secret);
  314. //// Wallet
  315. // Address of deployed contract in our example is hook_dao_exec
  316. // This field is public, you can see it's being sent to a DAO
  317. // but nothing else is visible.
  318. //
  319. // In the python code we wrote:
  320. //
  321. // spend_hook = b"0xdao_ruleset"
  322. //
  323. let hook_dao_exec = DrkSpendHook::random(&mut OsRng);
  324. let spend_hook = hook_dao_exec;
  325. // The user_data can be a simple hash of the items passed into the ZK proof
  326. // up to corresponding linked ZK proof to interpret however they need.
  327. // In out case, it's the bulla for the DAO
  328. let user_data = dao_bulla.0;
  329. let builder = money_contract::transfer::wallet::Builder {
  330. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  331. value: xdrk_supply,
  332. token_id: xdrk_token_id,
  333. signature_secret: cashier_signature_secret,
  334. }],
  335. inputs: vec![],
  336. outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
  337. value: xdrk_supply,
  338. token_id: xdrk_token_id,
  339. public: dao_keypair.public,
  340. serial: pallas::Base::random(&mut OsRng),
  341. coin_blind: pallas::Base::random(&mut OsRng),
  342. spend_hook,
  343. user_data,
  344. }],
  345. };
  346. let func_call = builder.build(&zk_bins)?;
  347. let tx = Transaction { func_calls: vec![func_call] };
  348. //// Validator
  349. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  350. // So then the verifier will lookup the corresponding state_transition and apply
  351. // functions based off the func_id
  352. if func_call.func_id == "Money::transfer()" {
  353. debug!("money_contract::transfer::state_transition()");
  354. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  355. .expect("money_contract::transfer::validate::state_transition() failed!");
  356. update.apply(&mut states);
  357. }
  358. }
  359. tx.zk_verify(&zk_bins);
  360. //// Wallet
  361. // DAO reads the money received from the encrypted note
  362. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  363. let mut recv_coins = state.wallet_cache.get_received(&dao_keypair.secret);
  364. assert_eq!(recv_coins.len(), 1);
  365. let dao_recv_coin = recv_coins.pop().unwrap();
  366. let treasury_note = dao_recv_coin.note;
  367. // Check the actual coin received is valid before accepting it
  368. let coords = dao_keypair.public.0.to_affine().coordinates().unwrap();
  369. let coin = poseidon_hash::<8>([
  370. *coords.x(),
  371. *coords.y(),
  372. DrkValue::from(treasury_note.value),
  373. treasury_note.token_id,
  374. treasury_note.serial,
  375. treasury_note.spend_hook,
  376. treasury_note.user_data,
  377. treasury_note.coin_blind,
  378. ]);
  379. assert_eq!(coin, dao_recv_coin.coin.0);
  380. assert_eq!(treasury_note.spend_hook, hook_dao_exec);
  381. assert_eq!(treasury_note.user_data, dao_bulla.0);
  382. debug!("DAO received a coin worth {} xDRK", treasury_note.value);
  383. ///////////////////////////////////////////////////
  384. //// Mint the governance token
  385. //// Send it to three hodlers
  386. ///////////////////////////////////////////////////
  387. debug!(target: "demo", "Stage 3. Minting governance token");
  388. //// Wallet
  389. // Hodler 1
  390. let gov_keypair_1 = Keypair::random(&mut OsRng);
  391. // Hodler 2
  392. let gov_keypair_2 = Keypair::random(&mut OsRng);
  393. // Hodler 3: the tiebreaker
  394. let gov_keypair_3 = Keypair::random(&mut OsRng);
  395. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  396. state.wallet_cache.track(gov_keypair_1.secret);
  397. state.wallet_cache.track(gov_keypair_2.secret);
  398. state.wallet_cache.track(gov_keypair_3.secret);
  399. let gov_keypairs = vec![gov_keypair_1, gov_keypair_2, gov_keypair_3];
  400. // We don't use this because money-transfer expects a cashier.
  401. // let signature_secret = SecretKey::random(&mut OsRng);
  402. // Spend hook and user data disabled
  403. let spend_hook = DrkSpendHook::from(0);
  404. let user_data = DrkUserData::from(0);
  405. let output1 = money_contract::transfer::wallet::BuilderOutputInfo {
  406. value: 400000,
  407. token_id: gdrk_token_id,
  408. public: gov_keypair_1.public,
  409. serial: pallas::Base::random(&mut OsRng),
  410. coin_blind: pallas::Base::random(&mut OsRng),
  411. spend_hook,
  412. user_data,
  413. };
  414. let output2 = money_contract::transfer::wallet::BuilderOutputInfo {
  415. value: 400000,
  416. token_id: gdrk_token_id,
  417. public: gov_keypair_2.public,
  418. serial: pallas::Base::random(&mut OsRng),
  419. coin_blind: pallas::Base::random(&mut OsRng),
  420. spend_hook,
  421. user_data,
  422. };
  423. let output3 = money_contract::transfer::wallet::BuilderOutputInfo {
  424. value: 200000,
  425. token_id: gdrk_token_id,
  426. public: gov_keypair_3.public,
  427. serial: pallas::Base::random(&mut OsRng),
  428. coin_blind: pallas::Base::random(&mut OsRng),
  429. spend_hook,
  430. user_data,
  431. };
  432. assert!(2 * 400000 + 200000 == gdrk_supply);
  433. let builder = money_contract::transfer::wallet::Builder {
  434. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  435. value: gdrk_supply,
  436. token_id: gdrk_token_id,
  437. signature_secret: cashier_signature_secret,
  438. }],
  439. inputs: vec![],
  440. outputs: vec![output1, output2, output3],
  441. };
  442. let func_call = builder.build(&zk_bins)?;
  443. let tx = Transaction { func_calls: vec![func_call] };
  444. //// Validator
  445. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  446. // So then the verifier will lookup the corresponding state_transition and apply
  447. // functions based off the func_id
  448. if func_call.func_id == "Money::transfer()" {
  449. debug!("money_contract::transfer::state_transition()");
  450. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  451. .expect("money_contract::transfer::validate::state_transition() failed!");
  452. update.apply(&mut states);
  453. }
  454. }
  455. tx.zk_verify(&zk_bins);
  456. //// Wallet
  457. let mut gov_recv = vec![None, None, None];
  458. // Check that each person received one coin
  459. for (i, key) in gov_keypairs.iter().enumerate() {
  460. let gov_recv_coin = {
  461. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  462. let mut recv_coins = state.wallet_cache.get_received(&key.secret);
  463. assert_eq!(recv_coins.len(), 1);
  464. let recv_coin = recv_coins.pop().unwrap();
  465. let note = &recv_coin.note;
  466. assert_eq!(note.token_id, gdrk_token_id);
  467. // Normal payment
  468. assert_eq!(note.spend_hook, pallas::Base::from(0));
  469. assert_eq!(note.user_data, pallas::Base::from(0));
  470. let coords = key.public.0.to_affine().coordinates().unwrap();
  471. let coin = poseidon_hash::<8>([
  472. *coords.x(),
  473. *coords.y(),
  474. DrkValue::from(note.value),
  475. note.token_id,
  476. note.serial,
  477. note.spend_hook,
  478. note.user_data,
  479. note.coin_blind,
  480. ]);
  481. assert_eq!(coin, recv_coin.coin.0);
  482. debug!("Holder{} received a coin worth {} gDRK", i, note.value);
  483. recv_coin
  484. };
  485. gov_recv[i] = Some(gov_recv_coin);
  486. }
  487. // unwrap them for this demo
  488. let gov_recv: Vec<_> = gov_recv.into_iter().map(|r| r.unwrap()).collect();
  489. ///////////////////////////////////////////////////
  490. // DAO rules:
  491. // 1. gov token IDs must match on all inputs
  492. // 2. proposals must be submitted by minimum amount
  493. // 3. all votes >= quorum
  494. // 4. outcome > approval_ratio
  495. // 5. structure of outputs
  496. // output 0: value and address
  497. // output 1: change address
  498. ///////////////////////////////////////////////////
  499. ///////////////////////////////////////////////////
  500. // Propose the vote
  501. // In order to make a valid vote, first the proposer must
  502. // meet a criteria for a minimum number of gov tokens
  503. ///////////////////////////////////////////////////
  504. debug!(target: "demo", "Stage 4. Propose the vote");
  505. //// Wallet
  506. // TODO: look into proposal expiry once time for voting has finished
  507. let user_keypair = Keypair::random(&mut OsRng);
  508. let (money_leaf_position, money_merkle_path) = {
  509. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  510. let tree = &state.tree;
  511. let leaf_position = gov_recv[0].leaf_position.clone();
  512. let root = tree.root(0).unwrap();
  513. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  514. (leaf_position, merkle_path)
  515. };
  516. // TODO: is it possible for an invalid transfer() to be constructed on exec()?
  517. // need to look into this
  518. let input = dao_contract::propose::wallet::BuilderInput {
  519. secret: gov_keypair_1.secret,
  520. note: gov_recv[0].note.clone(),
  521. leaf_position: money_leaf_position,
  522. merkle_path: money_merkle_path,
  523. };
  524. let (dao_merkle_path, dao_merkle_root) = {
  525. let state = states.lookup::<dao_contract::State>(&"DAO".to_string()).unwrap();
  526. let tree = &state.dao_tree;
  527. let root = tree.root(0).unwrap();
  528. let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
  529. (merkle_path, root)
  530. };
  531. let dao_params = dao_contract::propose::wallet::DaoParams {
  532. proposer_limit: dao_proposer_limit,
  533. quorum: dao_quorum,
  534. approval_ratio: dao_approval_ratio,
  535. gov_token_id: gdrk_token_id,
  536. public_key: dao_keypair.public,
  537. bulla_blind: dao_bulla_blind,
  538. };
  539. let builder = dao_contract::propose::wallet::Builder {
  540. inputs: vec![input],
  541. proposal: dao_contract::propose::wallet::Proposal {
  542. dest: user_keypair.public,
  543. amount: 1000,
  544. serial: pallas::Base::random(&mut OsRng),
  545. token_id: xdrk_token_id,
  546. blind: pallas::Base::random(&mut OsRng),
  547. },
  548. dao: dao_params.clone(),
  549. dao_leaf_position,
  550. dao_merkle_path,
  551. dao_merkle_root,
  552. };
  553. let func_call = builder.build(&zk_bins);
  554. let tx = Transaction { func_calls: vec![func_call] };
  555. //// Validator
  556. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  557. if func_call.func_id == "DAO::propose()" {
  558. debug!(target: "demo", "dao_contract::propose::state_transition()");
  559. let update = dao_contract::propose::validate::state_transition(&states, idx, &tx)
  560. .expect("dao_contract::propose::validate::state_transition() failed!");
  561. dao_contract::propose::validate::apply(&mut states, update);
  562. }
  563. }
  564. tx.zk_verify(&zk_bins);
  565. //// Wallet
  566. // Read received proposal
  567. let (proposal, proposal_bulla) = {
  568. assert_eq!(tx.func_calls.len(), 1);
  569. let func_call = &tx.func_calls[0];
  570. let call_data = func_call.call_data.as_any();
  571. assert_eq!(
  572. (&*call_data).type_id(),
  573. TypeId::of::<dao_contract::propose::validate::CallData>()
  574. );
  575. let call_data =
  576. call_data.downcast_ref::<dao_contract::propose::validate::CallData>().unwrap();
  577. let header = &call_data.header;
  578. let note: dao_contract::propose::wallet::Note =
  579. header.enc_note.decrypt(&dao_keypair.secret).unwrap();
  580. // Return the proposal info
  581. (note.proposal, call_data.header.proposal_bulla)
  582. };
  583. debug!(target: "demo", "Proposal now active!");
  584. debug!(target: "demo", " destination: {:?}", proposal.dest);
  585. debug!(target: "demo", " amount: {}", proposal.amount);
  586. debug!(target: "demo", " token_id: {:?}", proposal.token_id);
  587. debug!(target: "demo", " dao_bulla: {:?}", dao_bulla.0);
  588. debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
  589. ///////////////////////////////////////////////////
  590. // Proposal is accepted!
  591. // Start the voting
  592. ///////////////////////////////////////////////////
  593. // Copying these schizo comments from python code:
  594. // Lets the voting begin
  595. // Voters have access to the proposal and dao data
  596. // vote_state = VoteState()
  597. // We don't need to copy nullifier set because it is checked from gov_state
  598. // in vote_state_transition() anyway
  599. //
  600. // TODO: what happens if voters don't unblind their vote
  601. // Answer:
  602. // 1. there is a time limit
  603. // 2. both the MPC or users can unblind
  604. //
  605. // TODO: bug if I vote then send money, then we can double vote
  606. // TODO: all timestamps missing
  607. // - timelock (future voting starts in 2 days)
  608. // Fix: use nullifiers from money gov state only from
  609. // beginning of gov period
  610. // Cannot use nullifiers from before voting period
  611. debug!(target: "demo", "Stage 5. Start voting");
  612. // We save updates here for testing.
  613. let mut updates = Vec::new();
  614. // User 1: YES
  615. let (money_leaf_position, money_merkle_path) = {
  616. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  617. let tree = &state.tree;
  618. let leaf_position = gov_recv[0].leaf_position.clone();
  619. let root = tree.root(0).unwrap();
  620. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  621. (leaf_position, merkle_path)
  622. };
  623. let input = dao_contract::vote::wallet::BuilderInput {
  624. secret: gov_keypair_1.secret,
  625. note: gov_recv[0].note.clone(),
  626. leaf_position: money_leaf_position,
  627. merkle_path: money_merkle_path,
  628. };
  629. let vote_option: bool = true;
  630. assert!(vote_option == true || vote_option == false);
  631. // We create a new keypair to encrypt the vote.
  632. let vote_keypair_1 = Keypair::random(&mut OsRng);
  633. let builder = dao_contract::vote::wallet::Builder {
  634. inputs: vec![input],
  635. vote: dao_contract::vote::wallet::Vote {
  636. vote_option,
  637. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  638. },
  639. vote_keypair: vote_keypair_1,
  640. proposal: proposal.clone(),
  641. dao: dao_params.clone(),
  642. };
  643. debug!(target: "demo", "build()...");
  644. let func_call = builder.build(&zk_bins);
  645. let tx = Transaction { func_calls: vec![func_call] };
  646. //// Validator
  647. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  648. if func_call.func_id == "DAO::vote()" {
  649. debug!(target: "demo", "dao_contract::vote::state_transition()");
  650. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  651. .expect("dao_contract::vote::validate::state_transition() failed!");
  652. dao_contract::vote::validate::apply(&mut states, update.clone());
  653. updates.push(update);
  654. }
  655. }
  656. tx.zk_verify(&zk_bins);
  657. //// Wallet
  658. // Secret vote info. Needs to be revealed at some point.
  659. // TODO: look into verifiable encryption for notes
  660. // TODO: look into timelock puzzle as a possibility
  661. let vote_note_1 = {
  662. assert_eq!(tx.func_calls.len(), 1);
  663. let func_call = &tx.func_calls[0];
  664. let call_data = func_call.call_data.as_any();
  665. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  666. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  667. let header = &call_data.header;
  668. let note: dao_contract::vote::wallet::Note =
  669. header.enc_note.decrypt(&vote_keypair_1.secret).unwrap();
  670. note
  671. };
  672. debug!(target: "demo", "User 1 voted!");
  673. debug!(target: "demo", " vote_option: {}", vote_note_1.vote.vote_option);
  674. debug!(target: "demo", " value: {}", vote_note_1.value);
  675. // User 2: NO
  676. let (money_leaf_position, money_merkle_path) = {
  677. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  678. let tree = &state.tree;
  679. let leaf_position = gov_recv[1].leaf_position.clone();
  680. let root = tree.root(0).unwrap();
  681. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  682. (leaf_position, merkle_path)
  683. };
  684. let input = dao_contract::vote::wallet::BuilderInput {
  685. secret: gov_keypair_2.secret,
  686. note: gov_recv[1].note.clone(),
  687. leaf_position: money_leaf_position,
  688. merkle_path: money_merkle_path,
  689. };
  690. let vote_option: bool = false;
  691. assert!(vote_option == true || vote_option == false);
  692. // We create a new keypair to encrypt the vote.
  693. let vote_keypair_2 = Keypair::random(&mut OsRng);
  694. let builder = dao_contract::vote::wallet::Builder {
  695. inputs: vec![input],
  696. vote: dao_contract::vote::wallet::Vote {
  697. vote_option,
  698. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  699. },
  700. vote_keypair: vote_keypair_2,
  701. proposal: proposal.clone(),
  702. dao: dao_params.clone(),
  703. };
  704. debug!(target: "demo", "build()...");
  705. let func_call = builder.build(&zk_bins);
  706. let tx = Transaction { func_calls: vec![func_call] };
  707. //// Validator
  708. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  709. if func_call.func_id == "DAO::vote()" {
  710. debug!(target: "demo", "dao_contract::vote::state_transition()");
  711. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  712. .expect("dao_contract::vote::validate::state_transition() failed!");
  713. dao_contract::vote::validate::apply(&mut states, update.clone());
  714. updates.push(update);
  715. }
  716. }
  717. tx.zk_verify(&zk_bins);
  718. //// Wallet
  719. // Secret vote info. Needs to be revealed at some point.
  720. // TODO: look into verifiable encryption for notes
  721. // TODO: look into timelock puzzle as a possibility
  722. let vote_note_2 = {
  723. assert_eq!(tx.func_calls.len(), 1);
  724. let func_call = &tx.func_calls[0];
  725. let call_data = func_call.call_data.as_any();
  726. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  727. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  728. let header = &call_data.header;
  729. let note: dao_contract::vote::wallet::Note =
  730. header.enc_note.decrypt(&vote_keypair_2.secret).unwrap();
  731. note
  732. };
  733. debug!(target: "demo", "User 2 voted!");
  734. debug!(target: "demo", " vote_option: {}", vote_note_2.vote.vote_option);
  735. debug!(target: "demo", " value: {}", vote_note_2.value);
  736. // User 3: YES
  737. let (money_leaf_position, money_merkle_path) = {
  738. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  739. let tree = &state.tree;
  740. let leaf_position = gov_recv[2].leaf_position.clone();
  741. let root = tree.root(0).unwrap();
  742. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  743. (leaf_position, merkle_path)
  744. };
  745. let input = dao_contract::vote::wallet::BuilderInput {
  746. secret: gov_keypair_3.secret,
  747. note: gov_recv[2].note.clone(),
  748. leaf_position: money_leaf_position,
  749. merkle_path: money_merkle_path,
  750. };
  751. let vote_option: bool = true;
  752. assert!(vote_option == true || vote_option == false);
  753. // We create a new keypair to encrypt the vote.
  754. let vote_keypair_3 = Keypair::random(&mut OsRng);
  755. let builder = dao_contract::vote::wallet::Builder {
  756. inputs: vec![input],
  757. vote: dao_contract::vote::wallet::Vote {
  758. vote_option,
  759. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  760. },
  761. vote_keypair: vote_keypair_3,
  762. proposal: proposal.clone(),
  763. dao: dao_params.clone(),
  764. };
  765. debug!(target: "demo", "build()...");
  766. let func_call = builder.build(&zk_bins);
  767. let tx = Transaction { func_calls: vec![func_call] };
  768. //// Validator
  769. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  770. if func_call.func_id == "DAO::vote()" {
  771. debug!(target: "demo", "dao_contract::vote::state_transition()");
  772. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  773. .expect("dao_contract::vote::validate::state_transition() failed!");
  774. dao_contract::vote::validate::apply(&mut states, update.clone());
  775. updates.push(update);
  776. }
  777. }
  778. tx.zk_verify(&zk_bins);
  779. //// Wallet
  780. // Secret vote info. Needs to be revealed at some point.
  781. // TODO: look into verifiable encryption for notes
  782. // TODO: look into timelock puzzle as a possibility
  783. let vote_note_3 = {
  784. assert_eq!(tx.func_calls.len(), 1);
  785. let func_call = &tx.func_calls[0];
  786. let call_data = func_call.call_data.as_any();
  787. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  788. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  789. let header = &call_data.header;
  790. let note: dao_contract::vote::wallet::Note =
  791. header.enc_note.decrypt(&vote_keypair_3.secret).unwrap();
  792. note
  793. };
  794. debug!(target: "demo", "User 3 voted!");
  795. debug!(target: "demo", " vote_option: {}", vote_note_3.vote.vote_option);
  796. debug!(target: "demo", " value: {}", vote_note_3.value);
  797. // Every votes produces a semi-homomorphic encryption of their vote.
  798. // Which is either yes or no
  799. // We copy the state tree for the governance token so coins can be used
  800. // to vote on other proposals at the same time.
  801. // With their vote, they produce a ZK proof + nullifier
  802. // The votes are unblinded by MPC to a selected party at the end of the
  803. // voting period.
  804. // (that's if we want votes to be hidden during voting)
  805. let mut win_votes = 0;
  806. let mut total_votes = 0;
  807. let mut total_vote_blinds = pallas::Scalar::from(0);
  808. let mut total_value_blinds = pallas::Scalar::from(0);
  809. let mut total_value_commit = pallas::Point::identity();
  810. let mut total_vote_commit = pallas::Point::identity();
  811. assert!(updates.len() == 3);
  812. for (i, (note, update)) in
  813. [vote_note_1, vote_note_2, vote_note_3].iter().zip(updates).enumerate()
  814. {
  815. let value_commit = pedersen_commitment_u64(note.value, note.value_blind);
  816. assert!(update.value_commit == value_commit);
  817. total_value_commit += value_commit;
  818. total_value_blinds += note.value_blind;
  819. let vote_commit = pedersen_commitment_u64(
  820. note.vote.vote_option as u64 * note.value,
  821. note.vote.vote_option_blind,
  822. );
  823. assert!(update.vote_commit == vote_commit);
  824. total_vote_commit += vote_commit;
  825. total_vote_blinds += note.vote.vote_option_blind;
  826. let vote_option = note.vote.vote_option;
  827. if vote_option {
  828. win_votes += note.value;
  829. }
  830. total_votes += note.value;
  831. let vote_result: String = if vote_option { "yes".to_string() } else { "no".to_string() };
  832. debug!("Voter {} voted {}", i, vote_result);
  833. }
  834. debug!("Outcome = {} / {}", win_votes, total_votes);
  835. assert!(total_value_commit == pedersen_commitment_u64(total_votes, total_value_blinds));
  836. assert!(total_vote_commit == pedersen_commitment_u64(win_votes, total_vote_blinds));
  837. ///////////////////////////////////////////////////
  838. // Execute the vote
  839. ///////////////////////////////////////////////////
  840. //// Wallet
  841. // Used to export user_data from this coin so it can be accessed by DAO::exec()
  842. let user_data_blind = pallas::Base::random(&mut OsRng);
  843. let user_serial = pallas::Base::random(&mut OsRng);
  844. let user_coin_blind = pallas::Base::random(&mut OsRng);
  845. let dao_serial = pallas::Base::random(&mut OsRng);
  846. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  847. let (treasury_leaf_position, treasury_merkle_path) = {
  848. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  849. let tree = &state.tree;
  850. let leaf_position = dao_recv_coin.leaf_position.clone();
  851. let root = tree.root(0).unwrap();
  852. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  853. (leaf_position, merkle_path)
  854. };
  855. let builder = money_contract::transfer::wallet::Builder {
  856. clear_inputs: vec![],
  857. inputs: vec![money_contract::transfer::wallet::BuilderInputInfo {
  858. leaf_position: treasury_leaf_position,
  859. merkle_path: treasury_merkle_path,
  860. secret: dao_keypair.secret,
  861. note: treasury_note,
  862. user_data_blind,
  863. }],
  864. outputs: vec![
  865. // Sending money
  866. money_contract::transfer::wallet::BuilderOutputInfo {
  867. value: 1000,
  868. token_id: xdrk_token_id,
  869. public: user_keypair.public,
  870. serial: user_serial,
  871. coin_blind: user_coin_blind,
  872. spend_hook: pallas::Base::from(0),
  873. user_data: pallas::Base::from(0),
  874. },
  875. // Change back to DAO
  876. money_contract::transfer::wallet::BuilderOutputInfo {
  877. value: xdrk_supply - 1000,
  878. token_id: xdrk_token_id,
  879. public: dao_keypair.public,
  880. serial: dao_serial,
  881. coin_blind: dao_coin_blind,
  882. spend_hook: hook_dao_exec,
  883. user_data: dao_bulla.0,
  884. },
  885. ],
  886. };
  887. let transfer_func_call = builder.build(&zk_bins)?;
  888. let foo = dao_contract::exec::wallet::Foo { a: 5, b: 10 };
  889. let builder = dao_contract::exec::wallet::Builder { foo };
  890. let exec_func_call = builder.build(&zk_bins);
  891. let tx = Transaction { func_calls: vec![transfer_func_call, exec_func_call] };
  892. {
  893. // Now the spend_hook field specifies the function DAO::exec()
  894. // so Money::transfer() must also be combined with DAO::exec()
  895. assert_eq!(tx.func_calls.len(), 2);
  896. let transfer_func_call = &tx.func_calls[0];
  897. let transfer_call_data = transfer_func_call.call_data.as_any();
  898. assert_eq!(
  899. (&*transfer_call_data).type_id(),
  900. TypeId::of::<money_contract::transfer::validate::CallData>()
  901. );
  902. let transfer_call_data =
  903. transfer_call_data.downcast_ref::<money_contract::transfer::validate::CallData>();
  904. let transfer_call_data = transfer_call_data.unwrap();
  905. // At least one input has this field value which means DAO::exec() is invoked.
  906. assert_eq!(transfer_call_data.inputs.len(), 1);
  907. let input = &transfer_call_data.inputs[0];
  908. assert_eq!(input.revealed.spend_hook, hook_dao_exec);
  909. let user_data_enc = poseidon_hash::<2>([dao_bulla.0, user_data_blind]);
  910. assert_eq!(input.revealed.user_data_enc, user_data_enc);
  911. }
  912. //// Validator
  913. let mut updates = vec![];
  914. // Validate all function calls in the tx
  915. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  916. if func_call.func_id == "DAO::exec()" {
  917. debug!("dao_contract::exec::state_transition()");
  918. let update = dao_contract::exec::validate::state_transition(&states, idx, &tx)
  919. .expect("dao_contract::exec::validate::state_transition() failed!");
  920. updates.push(update);
  921. } else if func_call.func_id == "Money::transfer()" {
  922. debug!("money_contract::transfer::state_transition()");
  923. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  924. .expect("money_contract::transfer::validate::state_transition() failed!");
  925. updates.push(update);
  926. }
  927. }
  928. // Atomically apply all changes
  929. for update in updates {
  930. update.apply(&mut states);
  931. }
  932. // Other stuff
  933. tx.zk_verify(&zk_bins);
  934. // TODO: signature verification
  935. //// Wallet
  936. Ok(())
  937. }