demo.rs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164
  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. let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
  245. let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
  246. zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
  247. // State for money contracts
  248. let cashier_signature_secret = SecretKey::random(&mut OsRng);
  249. let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
  250. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  251. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  252. ///////////////////////////////////////////////////
  253. let money_state =
  254. money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
  255. states.register("Money".to_string(), money_state);
  256. /////////////////////////////////////////////////////
  257. let dao_state = dao_contract::State::new();
  258. states.register("DAO".to_string(), dao_state);
  259. /////////////////////////////////////////////////////
  260. ////// Create the DAO bulla
  261. /////////////////////////////////////////////////////
  262. debug!(target: "demo", "Stage 1. Creating DAO bulla");
  263. //// Wallet
  264. //// Setup the DAO
  265. let dao_keypair = Keypair::random(&mut OsRng);
  266. let dao_bulla_blind = pallas::Base::random(&mut OsRng);
  267. // Create DAO mint tx
  268. let builder = dao_contract::mint::wallet::Builder::new(
  269. dao_proposer_limit,
  270. dao_quorum,
  271. dao_approval_ratio,
  272. gdrk_token_id,
  273. dao_keypair.public,
  274. dao_bulla_blind,
  275. );
  276. let func_call = builder.build(&zk_bins);
  277. let tx = Transaction { func_calls: vec![func_call] };
  278. //// Validator
  279. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  280. // So then the verifier will lookup the corresponding state_transition and apply
  281. // functions based off the func_id
  282. if func_call.func_id == "DAO::mint()" {
  283. debug!("dao_contract::mint::state_transition()");
  284. let update = dao_contract::mint::validate::state_transition(&states, idx, &tx)
  285. .expect("dao_contract::mint::validate::state_transition() failed!");
  286. dao_contract::mint::validate::apply(&mut states, update);
  287. }
  288. }
  289. tx.zk_verify(&zk_bins);
  290. // Wallet stuff
  291. // In your wallet, wait until you see the tx confirmed before doing anything below
  292. // So for example keep track of tx hash
  293. //assert_eq!(tx.hash(), tx_hash);
  294. // We need to witness() the value in our local merkle tree
  295. // Must be called as soon as this DAO bulla is added to the state
  296. let dao_leaf_position = {
  297. let state = states.lookup_mut::<dao_contract::State>(&"DAO".to_string()).unwrap();
  298. state.dao_tree.witness().unwrap()
  299. };
  300. // It might just be easier to hash it ourselves from keypair and blind...
  301. let dao_bulla = {
  302. assert_eq!(tx.func_calls.len(), 1);
  303. let func_call = &tx.func_calls[0];
  304. let call_data = func_call.call_data.as_any();
  305. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::mint::validate::CallData>());
  306. let call_data = call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
  307. call_data.dao_bulla.clone()
  308. };
  309. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  310. ///////////////////////////////////////////////////
  311. //// Mint the initial supply of treasury token
  312. //// and send it all to the DAO directly
  313. ///////////////////////////////////////////////////
  314. debug!(target: "demo", "Stage 2. Minting treasury token");
  315. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  316. state.wallet_cache.track(dao_keypair.secret);
  317. //// Wallet
  318. // Address of deployed contract in our example is dao_contract::exec::FUNC_ID
  319. // This field is public, you can see it's being sent to a DAO
  320. // but nothing else is visible.
  321. //
  322. // In the python code we wrote:
  323. //
  324. // spend_hook = b"0xdao_ruleset"
  325. //
  326. let spend_hook = *dao_contract::exec::FUNC_ID;
  327. // The user_data can be a simple hash of the items passed into the ZK proof
  328. // up to corresponding linked ZK proof to interpret however they need.
  329. // In out case, it's the bulla for the DAO
  330. let user_data = dao_bulla.0;
  331. let builder = money_contract::transfer::wallet::Builder {
  332. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  333. value: xdrk_supply,
  334. token_id: xdrk_token_id,
  335. signature_secret: cashier_signature_secret,
  336. }],
  337. inputs: vec![],
  338. outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
  339. value: xdrk_supply,
  340. token_id: xdrk_token_id,
  341. public: dao_keypair.public,
  342. serial: pallas::Base::random(&mut OsRng),
  343. coin_blind: pallas::Base::random(&mut OsRng),
  344. spend_hook,
  345. user_data,
  346. }],
  347. };
  348. let func_call = builder.build(&zk_bins)?;
  349. let tx = Transaction { func_calls: vec![func_call] };
  350. //// Validator
  351. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  352. // So then the verifier will lookup the corresponding state_transition and apply
  353. // functions based off the func_id
  354. if func_call.func_id == "Money::transfer()" {
  355. debug!("money_contract::transfer::state_transition()");
  356. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  357. .expect("money_contract::transfer::validate::state_transition() failed!");
  358. update.apply(&mut states);
  359. }
  360. }
  361. tx.zk_verify(&zk_bins);
  362. //// Wallet
  363. // DAO reads the money received from the encrypted note
  364. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  365. let mut recv_coins = state.wallet_cache.get_received(&dao_keypair.secret);
  366. assert_eq!(recv_coins.len(), 1);
  367. let dao_recv_coin = recv_coins.pop().unwrap();
  368. let treasury_note = dao_recv_coin.note;
  369. // Check the actual coin received is valid before accepting it
  370. let coords = dao_keypair.public.0.to_affine().coordinates().unwrap();
  371. let coin = poseidon_hash::<8>([
  372. *coords.x(),
  373. *coords.y(),
  374. DrkValue::from(treasury_note.value),
  375. treasury_note.token_id,
  376. treasury_note.serial,
  377. treasury_note.spend_hook,
  378. treasury_note.user_data,
  379. treasury_note.coin_blind,
  380. ]);
  381. assert_eq!(coin, dao_recv_coin.coin.0);
  382. assert_eq!(treasury_note.spend_hook, *dao_contract::exec::FUNC_ID);
  383. assert_eq!(treasury_note.user_data, dao_bulla.0);
  384. debug!("DAO received a coin worth {} xDRK", treasury_note.value);
  385. ///////////////////////////////////////////////////
  386. //// Mint the governance token
  387. //// Send it to three hodlers
  388. ///////////////////////////////////////////////////
  389. debug!(target: "demo", "Stage 3. Minting governance token");
  390. //// Wallet
  391. // Hodler 1
  392. let gov_keypair_1 = Keypair::random(&mut OsRng);
  393. // Hodler 2
  394. let gov_keypair_2 = Keypair::random(&mut OsRng);
  395. // Hodler 3: the tiebreaker
  396. let gov_keypair_3 = Keypair::random(&mut OsRng);
  397. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  398. state.wallet_cache.track(gov_keypair_1.secret);
  399. state.wallet_cache.track(gov_keypair_2.secret);
  400. state.wallet_cache.track(gov_keypair_3.secret);
  401. let gov_keypairs = vec![gov_keypair_1, gov_keypair_2, gov_keypair_3];
  402. // We don't use this because money-transfer expects a cashier.
  403. // let signature_secret = SecretKey::random(&mut OsRng);
  404. // Spend hook and user data disabled
  405. let spend_hook = DrkSpendHook::from(0);
  406. let user_data = DrkUserData::from(0);
  407. let output1 = money_contract::transfer::wallet::BuilderOutputInfo {
  408. value: 400000,
  409. token_id: gdrk_token_id,
  410. public: gov_keypair_1.public,
  411. serial: pallas::Base::random(&mut OsRng),
  412. coin_blind: pallas::Base::random(&mut OsRng),
  413. spend_hook,
  414. user_data,
  415. };
  416. let output2 = money_contract::transfer::wallet::BuilderOutputInfo {
  417. value: 400000,
  418. token_id: gdrk_token_id,
  419. public: gov_keypair_2.public,
  420. serial: pallas::Base::random(&mut OsRng),
  421. coin_blind: pallas::Base::random(&mut OsRng),
  422. spend_hook,
  423. user_data,
  424. };
  425. let output3 = money_contract::transfer::wallet::BuilderOutputInfo {
  426. value: 200000,
  427. token_id: gdrk_token_id,
  428. public: gov_keypair_3.public,
  429. serial: pallas::Base::random(&mut OsRng),
  430. coin_blind: pallas::Base::random(&mut OsRng),
  431. spend_hook,
  432. user_data,
  433. };
  434. assert!(2 * 400000 + 200000 == gdrk_supply);
  435. let builder = money_contract::transfer::wallet::Builder {
  436. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  437. value: gdrk_supply,
  438. token_id: gdrk_token_id,
  439. signature_secret: cashier_signature_secret,
  440. }],
  441. inputs: vec![],
  442. outputs: vec![output1, output2, output3],
  443. };
  444. let func_call = builder.build(&zk_bins)?;
  445. let tx = Transaction { func_calls: vec![func_call] };
  446. //// Validator
  447. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  448. // So then the verifier will lookup the corresponding state_transition and apply
  449. // functions based off the func_id
  450. if func_call.func_id == "Money::transfer()" {
  451. debug!("money_contract::transfer::state_transition()");
  452. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  453. .expect("money_contract::transfer::validate::state_transition() failed!");
  454. update.apply(&mut states);
  455. }
  456. }
  457. tx.zk_verify(&zk_bins);
  458. //// Wallet
  459. let mut gov_recv = vec![None, None, None];
  460. // Check that each person received one coin
  461. for (i, key) in gov_keypairs.iter().enumerate() {
  462. let gov_recv_coin = {
  463. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  464. let mut recv_coins = state.wallet_cache.get_received(&key.secret);
  465. assert_eq!(recv_coins.len(), 1);
  466. let recv_coin = recv_coins.pop().unwrap();
  467. let note = &recv_coin.note;
  468. assert_eq!(note.token_id, gdrk_token_id);
  469. // Normal payment
  470. assert_eq!(note.spend_hook, pallas::Base::from(0));
  471. assert_eq!(note.user_data, pallas::Base::from(0));
  472. let coords = key.public.0.to_affine().coordinates().unwrap();
  473. let coin = poseidon_hash::<8>([
  474. *coords.x(),
  475. *coords.y(),
  476. DrkValue::from(note.value),
  477. note.token_id,
  478. note.serial,
  479. note.spend_hook,
  480. note.user_data,
  481. note.coin_blind,
  482. ]);
  483. assert_eq!(coin, recv_coin.coin.0);
  484. debug!("Holder{} received a coin worth {} gDRK", i, note.value);
  485. recv_coin
  486. };
  487. gov_recv[i] = Some(gov_recv_coin);
  488. }
  489. // unwrap them for this demo
  490. let gov_recv: Vec<_> = gov_recv.into_iter().map(|r| r.unwrap()).collect();
  491. ///////////////////////////////////////////////////
  492. // DAO rules:
  493. // 1. gov token IDs must match on all inputs
  494. // 2. proposals must be submitted by minimum amount
  495. // 3. all votes >= quorum
  496. // 4. outcome > approval_ratio
  497. // 5. structure of outputs
  498. // output 0: value and address
  499. // output 1: change address
  500. ///////////////////////////////////////////////////
  501. ///////////////////////////////////////////////////
  502. // Propose the vote
  503. // In order to make a valid vote, first the proposer must
  504. // meet a criteria for a minimum number of gov tokens
  505. ///////////////////////////////////////////////////
  506. debug!(target: "demo", "Stage 4. Propose the vote");
  507. //// Wallet
  508. // TODO: look into proposal expiry once time for voting has finished
  509. let user_keypair = Keypair::random(&mut OsRng);
  510. let (money_leaf_position, money_merkle_path) = {
  511. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  512. let tree = &state.tree;
  513. let leaf_position = gov_recv[0].leaf_position.clone();
  514. let root = tree.root(0).unwrap();
  515. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  516. (leaf_position, merkle_path)
  517. };
  518. // TODO: is it possible for an invalid transfer() to be constructed on exec()?
  519. // need to look into this
  520. let input = dao_contract::propose::wallet::BuilderInput {
  521. secret: gov_keypair_1.secret,
  522. note: gov_recv[0].note.clone(),
  523. leaf_position: money_leaf_position,
  524. merkle_path: money_merkle_path,
  525. };
  526. let (dao_merkle_path, dao_merkle_root) = {
  527. let state = states.lookup::<dao_contract::State>(&"DAO".to_string()).unwrap();
  528. let tree = &state.dao_tree;
  529. let root = tree.root(0).unwrap();
  530. let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
  531. (merkle_path, root)
  532. };
  533. let dao_params = dao_contract::propose::wallet::DaoParams {
  534. proposer_limit: dao_proposer_limit,
  535. quorum: dao_quorum,
  536. approval_ratio: dao_approval_ratio,
  537. gov_token_id: gdrk_token_id,
  538. public_key: dao_keypair.public,
  539. bulla_blind: dao_bulla_blind,
  540. };
  541. let 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. let builder = dao_contract::propose::wallet::Builder {
  549. inputs: vec![input],
  550. proposal: proposal.clone(),
  551. dao: dao_params.clone(),
  552. dao_leaf_position,
  553. dao_merkle_path,
  554. dao_merkle_root,
  555. };
  556. let func_call = builder.build(&zk_bins);
  557. let tx = Transaction { func_calls: vec![func_call] };
  558. //// Validator
  559. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  560. if func_call.func_id == "DAO::propose()" {
  561. debug!(target: "demo", "dao_contract::propose::state_transition()");
  562. let update = dao_contract::propose::validate::state_transition(&states, idx, &tx)
  563. .expect("dao_contract::propose::validate::state_transition() failed!");
  564. dao_contract::propose::validate::apply(&mut states, update);
  565. }
  566. }
  567. tx.zk_verify(&zk_bins);
  568. //// Wallet
  569. // Read received proposal
  570. let (proposal, proposal_bulla) = {
  571. assert_eq!(tx.func_calls.len(), 1);
  572. let func_call = &tx.func_calls[0];
  573. let call_data = func_call.call_data.as_any();
  574. assert_eq!(
  575. (&*call_data).type_id(),
  576. TypeId::of::<dao_contract::propose::validate::CallData>()
  577. );
  578. let call_data =
  579. call_data.downcast_ref::<dao_contract::propose::validate::CallData>().unwrap();
  580. let header = &call_data.header;
  581. let note: dao_contract::propose::wallet::Note =
  582. header.enc_note.decrypt(&dao_keypair.secret).unwrap();
  583. // Return the proposal info
  584. (note.proposal, call_data.header.proposal_bulla)
  585. };
  586. debug!(target: "demo", "Proposal now active!");
  587. debug!(target: "demo", " destination: {:?}", proposal.dest);
  588. debug!(target: "demo", " amount: {}", proposal.amount);
  589. debug!(target: "demo", " token_id: {:?}", proposal.token_id);
  590. debug!(target: "demo", " dao_bulla: {:?}", dao_bulla.0);
  591. debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
  592. ///////////////////////////////////////////////////
  593. // Proposal is accepted!
  594. // Start the voting
  595. ///////////////////////////////////////////////////
  596. // Copying these schizo comments from python code:
  597. // Lets the voting begin
  598. // Voters have access to the proposal and dao data
  599. // vote_state = VoteState()
  600. // We don't need to copy nullifier set because it is checked from gov_state
  601. // in vote_state_transition() anyway
  602. //
  603. // TODO: what happens if voters don't unblind their vote
  604. // Answer:
  605. // 1. there is a time limit
  606. // 2. both the MPC or users can unblind
  607. //
  608. // TODO: bug if I vote then send money, then we can double vote
  609. // TODO: all timestamps missing
  610. // - timelock (future voting starts in 2 days)
  611. // Fix: use nullifiers from money gov state only from
  612. // beginning of gov period
  613. // Cannot use nullifiers from before voting period
  614. debug!(target: "demo", "Stage 5. Start voting");
  615. // We save updates here for testing.
  616. let mut updates = Vec::new();
  617. // User 1: YES
  618. let (money_leaf_position, money_merkle_path) = {
  619. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  620. let tree = &state.tree;
  621. let leaf_position = gov_recv[0].leaf_position.clone();
  622. let root = tree.root(0).unwrap();
  623. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  624. (leaf_position, merkle_path)
  625. };
  626. let input = dao_contract::vote::wallet::BuilderInput {
  627. secret: gov_keypair_1.secret,
  628. note: gov_recv[0].note.clone(),
  629. leaf_position: money_leaf_position,
  630. merkle_path: money_merkle_path,
  631. };
  632. let vote_option: bool = true;
  633. assert!(vote_option == true || vote_option == false);
  634. // We create a new keypair to encrypt the vote.
  635. let vote_keypair_1 = Keypair::random(&mut OsRng);
  636. let builder = dao_contract::vote::wallet::Builder {
  637. inputs: vec![input],
  638. vote: dao_contract::vote::wallet::Vote {
  639. vote_option,
  640. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  641. },
  642. vote_keypair: vote_keypair_1,
  643. proposal: proposal.clone(),
  644. dao: dao_params.clone(),
  645. };
  646. debug!(target: "demo", "build()...");
  647. let func_call = builder.build(&zk_bins);
  648. let tx = Transaction { func_calls: vec![func_call] };
  649. //// Validator
  650. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  651. if func_call.func_id == "DAO::vote()" {
  652. debug!(target: "demo", "dao_contract::vote::state_transition()");
  653. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  654. .expect("dao_contract::vote::validate::state_transition() failed!");
  655. dao_contract::vote::validate::apply(&mut states, update.clone());
  656. updates.push(update);
  657. }
  658. }
  659. tx.zk_verify(&zk_bins);
  660. //// Wallet
  661. // Secret vote info. Needs to be revealed at some point.
  662. // TODO: look into verifiable encryption for notes
  663. // TODO: look into timelock puzzle as a possibility
  664. let vote_note_1 = {
  665. assert_eq!(tx.func_calls.len(), 1);
  666. let func_call = &tx.func_calls[0];
  667. let call_data = func_call.call_data.as_any();
  668. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  669. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  670. let header = &call_data.header;
  671. let note: dao_contract::vote::wallet::Note =
  672. header.enc_note.decrypt(&vote_keypair_1.secret).unwrap();
  673. note
  674. };
  675. debug!(target: "demo", "User 1 voted!");
  676. debug!(target: "demo", " vote_option: {}", vote_note_1.vote.vote_option);
  677. debug!(target: "demo", " value: {}", vote_note_1.value);
  678. // User 2: NO
  679. let (money_leaf_position, money_merkle_path) = {
  680. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  681. let tree = &state.tree;
  682. let leaf_position = gov_recv[1].leaf_position.clone();
  683. let root = tree.root(0).unwrap();
  684. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  685. (leaf_position, merkle_path)
  686. };
  687. let input = dao_contract::vote::wallet::BuilderInput {
  688. secret: gov_keypair_2.secret,
  689. note: gov_recv[1].note.clone(),
  690. leaf_position: money_leaf_position,
  691. merkle_path: money_merkle_path,
  692. };
  693. let vote_option: bool = false;
  694. assert!(vote_option == true || vote_option == false);
  695. // We create a new keypair to encrypt the vote.
  696. let vote_keypair_2 = Keypair::random(&mut OsRng);
  697. let builder = dao_contract::vote::wallet::Builder {
  698. inputs: vec![input],
  699. vote: dao_contract::vote::wallet::Vote {
  700. vote_option,
  701. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  702. },
  703. vote_keypair: vote_keypair_2,
  704. proposal: proposal.clone(),
  705. dao: dao_params.clone(),
  706. };
  707. debug!(target: "demo", "build()...");
  708. let func_call = builder.build(&zk_bins);
  709. let tx = Transaction { func_calls: vec![func_call] };
  710. //// Validator
  711. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  712. if func_call.func_id == "DAO::vote()" {
  713. debug!(target: "demo", "dao_contract::vote::state_transition()");
  714. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  715. .expect("dao_contract::vote::validate::state_transition() failed!");
  716. dao_contract::vote::validate::apply(&mut states, update.clone());
  717. updates.push(update);
  718. }
  719. }
  720. tx.zk_verify(&zk_bins);
  721. //// Wallet
  722. // Secret vote info. Needs to be revealed at some point.
  723. // TODO: look into verifiable encryption for notes
  724. // TODO: look into timelock puzzle as a possibility
  725. let vote_note_2 = {
  726. assert_eq!(tx.func_calls.len(), 1);
  727. let func_call = &tx.func_calls[0];
  728. let call_data = func_call.call_data.as_any();
  729. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  730. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  731. let header = &call_data.header;
  732. let note: dao_contract::vote::wallet::Note =
  733. header.enc_note.decrypt(&vote_keypair_2.secret).unwrap();
  734. note
  735. };
  736. debug!(target: "demo", "User 2 voted!");
  737. debug!(target: "demo", " vote_option: {}", vote_note_2.vote.vote_option);
  738. debug!(target: "demo", " value: {}", vote_note_2.value);
  739. // User 3: YES
  740. let (money_leaf_position, money_merkle_path) = {
  741. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  742. let tree = &state.tree;
  743. let leaf_position = gov_recv[2].leaf_position.clone();
  744. let root = tree.root(0).unwrap();
  745. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  746. (leaf_position, merkle_path)
  747. };
  748. let input = dao_contract::vote::wallet::BuilderInput {
  749. secret: gov_keypair_3.secret,
  750. note: gov_recv[2].note.clone(),
  751. leaf_position: money_leaf_position,
  752. merkle_path: money_merkle_path,
  753. };
  754. let vote_option: bool = true;
  755. assert!(vote_option == true || vote_option == false);
  756. // We create a new keypair to encrypt the vote.
  757. let vote_keypair_3 = Keypair::random(&mut OsRng);
  758. let builder = dao_contract::vote::wallet::Builder {
  759. inputs: vec![input],
  760. vote: dao_contract::vote::wallet::Vote {
  761. vote_option,
  762. vote_option_blind: pallas::Scalar::random(&mut OsRng),
  763. },
  764. vote_keypair: vote_keypair_3,
  765. proposal: proposal.clone(),
  766. dao: dao_params.clone(),
  767. };
  768. debug!(target: "demo", "build()...");
  769. let func_call = builder.build(&zk_bins);
  770. let tx = Transaction { func_calls: vec![func_call] };
  771. //// Validator
  772. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  773. if func_call.func_id == "DAO::vote()" {
  774. debug!(target: "demo", "dao_contract::vote::state_transition()");
  775. let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
  776. .expect("dao_contract::vote::validate::state_transition() failed!");
  777. dao_contract::vote::validate::apply(&mut states, update.clone());
  778. updates.push(update);
  779. }
  780. }
  781. tx.zk_verify(&zk_bins);
  782. //// Wallet
  783. // Secret vote info. Needs to be revealed at some point.
  784. // TODO: look into verifiable encryption for notes
  785. // TODO: look into timelock puzzle as a possibility
  786. let vote_note_3 = {
  787. assert_eq!(tx.func_calls.len(), 1);
  788. let func_call = &tx.func_calls[0];
  789. let call_data = func_call.call_data.as_any();
  790. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
  791. let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
  792. let header = &call_data.header;
  793. let note: dao_contract::vote::wallet::Note =
  794. header.enc_note.decrypt(&vote_keypair_3.secret).unwrap();
  795. note
  796. };
  797. debug!(target: "demo", "User 3 voted!");
  798. debug!(target: "demo", " vote_option: {}", vote_note_3.vote.vote_option);
  799. debug!(target: "demo", " value: {}", vote_note_3.value);
  800. // Every votes produces a semi-homomorphic encryption of their vote.
  801. // Which is either yes or no
  802. // We copy the state tree for the governance token so coins can be used
  803. // to vote on other proposals at the same time.
  804. // With their vote, they produce a ZK proof + nullifier
  805. // The votes are unblinded by MPC to a selected party at the end of the
  806. // voting period.
  807. // (that's if we want votes to be hidden during voting)
  808. let mut win_votes = 0;
  809. let mut total_votes = 0;
  810. let mut total_vote_blinds = pallas::Scalar::from(0);
  811. let mut total_value_blinds = pallas::Scalar::from(0);
  812. let mut total_value_commit = pallas::Point::identity();
  813. let mut total_vote_commit = pallas::Point::identity();
  814. assert!(updates.len() == 3);
  815. for (i, (note, update)) in
  816. [vote_note_1, vote_note_2, vote_note_3].iter().zip(updates).enumerate()
  817. {
  818. let value_commit = pedersen_commitment_u64(note.value, note.value_blind);
  819. assert!(update.value_commit == value_commit);
  820. total_value_commit += value_commit;
  821. total_value_blinds += note.value_blind;
  822. let vote_commit = pedersen_commitment_u64(
  823. note.vote.vote_option as u64 * note.value,
  824. note.vote.vote_option_blind,
  825. );
  826. assert!(update.vote_commit == vote_commit);
  827. total_vote_commit += vote_commit;
  828. total_vote_blinds += note.vote.vote_option_blind;
  829. let vote_option = note.vote.vote_option;
  830. if vote_option {
  831. win_votes += note.value;
  832. }
  833. total_votes += note.value;
  834. let vote_result: String = if vote_option { "yes".to_string() } else { "no".to_string() };
  835. debug!("Voter {} voted {}", i, vote_result);
  836. }
  837. debug!("Outcome = {} / {}", win_votes, total_votes);
  838. assert!(total_value_commit == pedersen_commitment_u64(total_votes, total_value_blinds));
  839. assert!(total_vote_commit == pedersen_commitment_u64(win_votes, total_vote_blinds));
  840. ///////////////////////////////////////////////////
  841. // Execute the vote
  842. ///////////////////////////////////////////////////
  843. //// Wallet
  844. // Used to export user_data from this coin so it can be accessed by DAO::exec()
  845. let user_data_blind = pallas::Base::random(&mut OsRng);
  846. let user_serial = pallas::Base::random(&mut OsRng);
  847. let user_coin_blind = pallas::Base::random(&mut OsRng);
  848. let dao_serial = pallas::Base::random(&mut OsRng);
  849. let dao_coin_blind = pallas::Base::random(&mut OsRng);
  850. let input_value = treasury_note.value;
  851. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  852. let (treasury_leaf_position, treasury_merkle_path) = {
  853. let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
  854. let tree = &state.tree;
  855. let leaf_position = dao_recv_coin.leaf_position.clone();
  856. let root = tree.root(0).unwrap();
  857. let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
  858. (leaf_position, merkle_path)
  859. };
  860. let builder = money_contract::transfer::wallet::Builder {
  861. clear_inputs: vec![],
  862. inputs: vec![money_contract::transfer::wallet::BuilderInputInfo {
  863. leaf_position: treasury_leaf_position,
  864. merkle_path: treasury_merkle_path,
  865. secret: dao_keypair.secret,
  866. note: treasury_note,
  867. user_data_blind,
  868. value_blind: input_value_blind,
  869. }],
  870. outputs: vec![
  871. // Sending money
  872. money_contract::transfer::wallet::BuilderOutputInfo {
  873. value: 1000,
  874. token_id: xdrk_token_id,
  875. public: user_keypair.public,
  876. serial: proposal.serial,
  877. coin_blind: proposal.blind,
  878. spend_hook: pallas::Base::from(0),
  879. user_data: pallas::Base::from(0),
  880. },
  881. // Change back to DAO
  882. money_contract::transfer::wallet::BuilderOutputInfo {
  883. value: xdrk_supply - 1000,
  884. token_id: xdrk_token_id,
  885. public: dao_keypair.public,
  886. serial: dao_serial,
  887. coin_blind: dao_coin_blind,
  888. spend_hook: *dao_contract::exec::FUNC_ID,
  889. user_data: proposal_bulla,
  890. },
  891. ],
  892. };
  893. let transfer_func_call = builder.build(&zk_bins)?;
  894. let builder = dao_contract::exec::wallet::Builder {
  895. proposal,
  896. dao: dao_params,
  897. win_votes,
  898. total_votes,
  899. win_votes_blind: total_vote_blinds,
  900. total_votes_blind: total_value_blinds,
  901. user_serial,
  902. user_coin_blind,
  903. dao_serial,
  904. dao_coin_blind,
  905. input_value,
  906. input_value_blind,
  907. hook_dao_exec: *dao_contract::exec::FUNC_ID,
  908. };
  909. let exec_func_call = builder.build(&zk_bins);
  910. let tx = Transaction { func_calls: vec![transfer_func_call, exec_func_call] };
  911. {
  912. // Now the spend_hook field specifies the function DAO::exec()
  913. // so Money::transfer() must also be combined with DAO::exec()
  914. assert_eq!(tx.func_calls.len(), 2);
  915. let transfer_func_call = &tx.func_calls[0];
  916. let transfer_call_data = transfer_func_call.call_data.as_any();
  917. assert_eq!(
  918. (&*transfer_call_data).type_id(),
  919. TypeId::of::<money_contract::transfer::validate::CallData>()
  920. );
  921. let transfer_call_data =
  922. transfer_call_data.downcast_ref::<money_contract::transfer::validate::CallData>();
  923. let transfer_call_data = transfer_call_data.unwrap();
  924. // At least one input has this field value which means DAO::exec() is invoked.
  925. assert_eq!(transfer_call_data.inputs.len(), 1);
  926. let input = &transfer_call_data.inputs[0];
  927. assert_eq!(input.revealed.spend_hook, *dao_contract::exec::FUNC_ID);
  928. let user_data_enc = poseidon_hash::<2>([dao_bulla.0, user_data_blind]);
  929. assert_eq!(input.revealed.user_data_enc, user_data_enc);
  930. }
  931. //// Validator
  932. let mut updates = vec![];
  933. // Validate all function calls in the tx
  934. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  935. if func_call.func_id == "DAO::exec()" {
  936. debug!("dao_contract::exec::state_transition()");
  937. let update = dao_contract::exec::validate::state_transition(&states, idx, &tx)
  938. .expect("dao_contract::exec::validate::state_transition() failed!");
  939. updates.push(update);
  940. } else if func_call.func_id == "Money::transfer()" {
  941. debug!("money_contract::transfer::state_transition()");
  942. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  943. .expect("money_contract::transfer::validate::state_transition() failed!");
  944. updates.push(update);
  945. }
  946. }
  947. // Atomically apply all changes
  948. for update in updates {
  949. update.apply(&mut states);
  950. }
  951. // Other stuff
  952. tx.zk_verify(&zk_bins);
  953. // TODO: signature verification
  954. //// Wallet
  955. Ok(())
  956. }