demo.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. #![allow(unused)]
  2. use halo2_gadgets::poseidon::primitives as poseidon;
  3. use halo2_proofs::circuit::Value;
  4. use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
  5. use log::debug;
  6. use pasta_curves::{
  7. arithmetic::CurveAffine,
  8. group::{ff::Field, Curve},
  9. pallas,
  10. };
  11. use rand::rngs::OsRng;
  12. use std::{
  13. any::{Any, TypeId},
  14. collections::HashMap,
  15. time::Instant,
  16. };
  17. use darkfi::{
  18. crypto::{
  19. constants::MERKLE_DEPTH,
  20. keypair::{Keypair, PublicKey, SecretKey},
  21. merkle_node::MerkleNode,
  22. note::{EncryptedNote, Note},
  23. nullifier::Nullifier,
  24. proof::{ProvingKey, VerifyingKey},
  25. token_id::generate_id,
  26. types::{DrkCircuitField, DrkSpendHook, DrkUserData, DrkValue},
  27. OwnCoin, OwnCoins, Proof,
  28. },
  29. node::state::{ProgramState, StateUpdate},
  30. tx::builder::{
  31. TransactionBuilder, TransactionBuilderClearInputInfo, TransactionBuilderInputInfo,
  32. TransactionBuilderOutputInfo,
  33. },
  34. util::NetworkName,
  35. zk::{
  36. circuit::{BurnContract, MintContract},
  37. vm::{Witness, ZkCircuit},
  38. vm_stack::empty_witnesses,
  39. },
  40. zkas::decoder::ZkBinary,
  41. };
  42. use crate::{dao_contract, money_contract};
  43. // TODO: reenable unused vars warning and fix it
  44. // TODO: strategize and cleanup Result/Error usage
  45. // TODO: fix up code doc
  46. type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
  47. pub struct ZkBinaryContractInfo {
  48. pub k_param: u32,
  49. pub bincode: ZkBinary,
  50. pub proving_key: ProvingKey,
  51. pub verifying_key: VerifyingKey,
  52. }
  53. pub struct ZkNativeContractInfo {
  54. pub proving_key: ProvingKey,
  55. pub verifying_key: VerifyingKey,
  56. }
  57. pub enum ZkContractInfo {
  58. Binary(ZkBinaryContractInfo),
  59. Native(ZkNativeContractInfo),
  60. }
  61. pub struct ZkContractTable {
  62. // Key will be a hash of zk binary contract on chain
  63. table: HashMap<String, ZkContractInfo>,
  64. }
  65. impl ZkContractTable {
  66. fn new() -> Self {
  67. Self { table: HashMap::new() }
  68. }
  69. fn add_contract(&mut self, key: String, bincode: ZkBinary, k_param: u32) {
  70. let witnesses = empty_witnesses(&bincode);
  71. let circuit = ZkCircuit::new(witnesses, bincode.clone());
  72. let proving_key = ProvingKey::build(k_param, &circuit);
  73. let verifying_key = VerifyingKey::build(k_param, &circuit);
  74. let info = ZkContractInfo::Binary(ZkBinaryContractInfo {
  75. k_param,
  76. bincode,
  77. proving_key,
  78. verifying_key,
  79. });
  80. self.table.insert(key, info);
  81. }
  82. fn add_native(&mut self, key: String, proving_key: ProvingKey, verifying_key: VerifyingKey) {
  83. self.table.insert(
  84. key,
  85. ZkContractInfo::Native(ZkNativeContractInfo { proving_key, verifying_key }),
  86. );
  87. }
  88. pub fn lookup(&self, key: &String) -> Option<&ZkContractInfo> {
  89. self.table.get(key)
  90. }
  91. }
  92. macro_rules! zip {
  93. ($x: expr) => ($x);
  94. ($x: expr, $($y: expr), +) => (
  95. $x.iter().zip(
  96. zip!($($y), +))
  97. )
  98. }
  99. pub struct Transaction {
  100. pub func_calls: Vec<FuncCall>,
  101. }
  102. impl Transaction {
  103. /// Verify ZK contracts for the entire tx
  104. /// In real code, we could parallelize this for loop
  105. /// TODO: fix use of unwrap with Result type stuff
  106. fn zk_verify(&self, zk_bins: &ZkContractTable) {
  107. for func_call in &self.func_calls {
  108. let proofs_public_vals = &func_call.call_data.zk_public_values();
  109. let proofs_keys = &func_call.call_data.zk_proof_addrs();
  110. assert_eq!(proofs_public_vals.len(), proofs_keys.len());
  111. assert_eq!(proofs_keys.len(), func_call.proofs.len());
  112. for (key, (proof, public_vals)) in
  113. zip!(proofs_keys, &func_call.proofs, proofs_public_vals)
  114. {
  115. match zk_bins.lookup(key).unwrap() {
  116. ZkContractInfo::Binary(info) => {
  117. let verifying_key = &info.verifying_key;
  118. proof.verify(&verifying_key, public_vals).expect("verify zk proof failed!");
  119. }
  120. ZkContractInfo::Native(info) => {
  121. let verifying_key = &info.verifying_key;
  122. proof.verify(&verifying_key, public_vals).expect("verify zk proof failed!");
  123. }
  124. };
  125. debug!("zk_verify({}) passed", key);
  126. }
  127. }
  128. }
  129. }
  130. // These would normally be a hash or sth
  131. type ContractId = String;
  132. type FuncId = String;
  133. pub struct FuncCall {
  134. pub contract_id: ContractId,
  135. pub func_id: FuncId,
  136. pub call_data: Box<dyn CallDataBase>,
  137. pub proofs: Vec<Proof>,
  138. }
  139. pub trait CallDataBase {
  140. // Public values for verifying the proofs
  141. // Needed so we can convert internal types so they can be used in Proof::verify()
  142. fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>>;
  143. // The zk contract ID needed to lookup in the table
  144. fn zk_proof_addrs(&self) -> Vec<String>;
  145. // For upcasting to CallData itself so it can be read in state_transition()
  146. fn as_any(&self) -> &dyn Any;
  147. }
  148. type GenericContractState = Box<dyn Any>;
  149. pub struct StateRegistry {
  150. pub states: HashMap<ContractId, GenericContractState>,
  151. }
  152. impl StateRegistry {
  153. fn new() -> Self {
  154. Self { states: HashMap::new() }
  155. }
  156. fn register(&mut self, contract_id: ContractId, state: GenericContractState) {
  157. debug!(target: "StateRegistry::register()", "contract_id: {:?}", contract_id);
  158. self.states.insert(contract_id, state);
  159. }
  160. pub fn lookup_mut<'a, S: 'static>(&'a mut self, contract_id: &ContractId) -> Option<&'a mut S> {
  161. self.states.get_mut(contract_id).and_then(|state| state.downcast_mut())
  162. }
  163. pub fn lookup<'a, S: 'static>(&'a self, contract_id: &ContractId) -> Option<&'a S> {
  164. self.states.get(contract_id).and_then(|state| state.downcast_ref())
  165. }
  166. }
  167. pub async fn demo() -> Result<()> {
  168. // Money parameters
  169. let xdrk_supply = 1_000_000;
  170. let xdrk_token_id = pallas::Base::random(&mut OsRng);
  171. // Governance token parameters
  172. let gdrk_supply = 1_000_000;
  173. let gdrk_token_id = pallas::Base::random(&mut OsRng);
  174. // DAO parameters
  175. let dao_proposer_limit = 110;
  176. let dao_quorum = 110;
  177. let dao_approval_ratio = 2;
  178. // Lookup table for smart contract states
  179. let mut states = StateRegistry::new();
  180. // Initialize ZK binary table
  181. let mut zk_bins = ZkContractTable::new();
  182. let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
  183. let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
  184. zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
  185. {
  186. let start = Instant::now();
  187. let mint_pk = ProvingKey::build(11, &MintContract::default());
  188. debug!("Mint PK: [{:?}]", start.elapsed());
  189. let start = Instant::now();
  190. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  191. debug!("Burn PK: [{:?}]", start.elapsed());
  192. let start = Instant::now();
  193. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  194. debug!("Mint VK: [{:?}]", start.elapsed());
  195. let start = Instant::now();
  196. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  197. debug!("Burn VK: [{:?}]", start.elapsed());
  198. zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
  199. zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
  200. }
  201. // State for money contracts
  202. let cashier_signature_secret = SecretKey::random(&mut OsRng);
  203. let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
  204. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  205. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  206. ///////////////////////////////////////////////////
  207. let money_state =
  208. money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
  209. states.register("Money".to_string(), money_state);
  210. /////////////////////////////////////////////////////
  211. let dao_state = dao_contract::State::new();
  212. states.register("DAO".to_string(), dao_state);
  213. // For this demo lets create 10 random preexisting DAO bullas
  214. for _ in 0..10 {
  215. let bulla = pallas::Base::random(&mut OsRng);
  216. }
  217. /////////////////////////////////////////////////////
  218. ////// Create the DAO bulla
  219. /////////////////////////////////////////////////////
  220. //// Wallet
  221. //// Setup the DAO
  222. let dao_keypair = Keypair::random(&mut OsRng);
  223. let dao_bulla_blind = pallas::Base::random(&mut OsRng);
  224. // Create DAO mint tx
  225. let builder = dao_contract::mint::wallet::Builder::new(
  226. dao_proposer_limit,
  227. dao_quorum,
  228. dao_approval_ratio,
  229. gdrk_token_id,
  230. dao_keypair.public,
  231. dao_bulla_blind,
  232. );
  233. let func_call = builder.build(&zk_bins);
  234. let tx = Transaction { func_calls: vec![func_call] };
  235. //// Validator
  236. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  237. // So then the verifier will lookup the corresponding state_transition and apply
  238. // functions based off the func_id
  239. if func_call.func_id == "DAO::mint()" {
  240. debug!("dao_contract::mint::state_transition()");
  241. let update = dao_contract::mint::validate::state_transition(&states, idx, &tx)
  242. .expect("dao_contract::mint::validate::state_transition() failed!");
  243. dao_contract::mint::validate::apply(&mut states, update);
  244. }
  245. }
  246. tx.zk_verify(&zk_bins);
  247. // Wallet stuff
  248. // It might just be easier to hash it ourselves from keypair and blind...
  249. let dao_bulla = {
  250. assert_eq!(tx.func_calls.len(), 1);
  251. let func_call = &tx.func_calls[0];
  252. let call_data = func_call.call_data.as_any();
  253. assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::mint::validate::CallData>());
  254. let call_data = call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
  255. call_data.dao_bulla.clone()
  256. };
  257. ///////////////////////////////////////////////////
  258. //// Mint the initial supply of treasury token
  259. //// and send it all to the DAO directly
  260. ///////////////////////////////////////////////////
  261. //// Wallet
  262. // Address of deployed contract in our example is hook_dao_exec
  263. // This field is public, you can see it's being sent to a DAO
  264. // but nothing else is visible.
  265. //
  266. // In the python code we wrote:
  267. //
  268. // spend_hook = b"0xdao_ruleset"
  269. //
  270. let hook_dao_exec = DrkSpendHook::random(&mut OsRng);
  271. let spend_hook = hook_dao_exec;
  272. // The user_data can be a simple hash of the items passed into the ZK proof
  273. // up to corresponding linked ZK proof to interpret however they need.
  274. // In out case, it's the bulla for the DAO
  275. let user_data = dao_bulla.0;
  276. let builder = money_contract::transfer::wallet::Builder {
  277. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  278. value: xdrk_supply,
  279. token_id: xdrk_token_id,
  280. signature_secret: cashier_signature_secret,
  281. }],
  282. inputs: vec![],
  283. outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
  284. value: xdrk_supply,
  285. token_id: xdrk_token_id,
  286. public: dao_keypair.public,
  287. spend_hook,
  288. user_data,
  289. }],
  290. };
  291. let func_call = builder.build(&zk_bins)?;
  292. let tx = Transaction { func_calls: vec![func_call] };
  293. //// Validator
  294. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  295. // So then the verifier will lookup the corresponding state_transition and apply
  296. // functions based off the func_id
  297. if func_call.func_id == "Money::transfer()" {
  298. debug!("money_contract::transfer::state_transition()");
  299. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  300. .expect("money_contract::state_transition() failed!");
  301. money_contract::transfer::validate::apply(&mut states, update);
  302. }
  303. }
  304. tx.zk_verify(&zk_bins);
  305. //// Wallet
  306. // DAO reads the money received from the encrypted note
  307. let dao_recv = {
  308. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  309. let mut recv_coins = state.wallet_cache.get_received(&dao_keypair.secret);
  310. assert_eq!(recv_coins.len(), 1);
  311. let recv_coin = recv_coins.pop().unwrap();
  312. let note = &recv_coin.note;
  313. // Check the actual coin received is valid before accepting it
  314. let coords = dao_keypair.public.0.to_affine().coordinates().unwrap();
  315. let coin = poseidon_hash::<8>([
  316. *coords.x(),
  317. *coords.y(),
  318. DrkValue::from(note.value),
  319. note.token_id,
  320. note.serial,
  321. note.spend_hook,
  322. note.user_data,
  323. note.coin_blind,
  324. ]);
  325. assert_eq!(coin, recv_coin.coin.0);
  326. assert_eq!(note.spend_hook, hook_dao_exec);
  327. assert_eq!(note.user_data, dao_bulla.0);
  328. debug!("DAO received a coin worth {} xDRK", note.value);
  329. recv_coin
  330. };
  331. ///////////////////////////////////////////////////
  332. //// Mint the governance token
  333. //// Send it to three hodlers
  334. ///////////////////////////////////////////////////
  335. //// Wallet
  336. // Hodler 1
  337. let gov_keypair_1 = Keypair::random(&mut OsRng);
  338. // Hodler 2
  339. let gov_keypair_2 = Keypair::random(&mut OsRng);
  340. // Hodler 3: the tiebreaker
  341. let gov_keypair_3 = Keypair::random(&mut OsRng);
  342. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  343. state.wallet_cache.track(gov_keypair_1.secret);
  344. state.wallet_cache.track(gov_keypair_2.secret);
  345. state.wallet_cache.track(gov_keypair_3.secret);
  346. let gov_keypairs = vec![gov_keypair_1, gov_keypair_2, gov_keypair_3];
  347. // We don't use this because money-transfer expects a cashier.
  348. // let signature_secret = SecretKey::random(&mut OsRng);
  349. // Spend hook and user data disabled
  350. let spend_hook = DrkSpendHook::from(0);
  351. let user_data = DrkUserData::from(0);
  352. let output1 = money_contract::transfer::wallet::BuilderOutputInfo {
  353. value: 400000,
  354. token_id: gdrk_token_id,
  355. public: gov_keypair_1.public,
  356. spend_hook,
  357. user_data,
  358. };
  359. let output2 = money_contract::transfer::wallet::BuilderOutputInfo {
  360. value: 400000,
  361. token_id: gdrk_token_id,
  362. public: gov_keypair_2.public,
  363. spend_hook,
  364. user_data,
  365. };
  366. let output3 = money_contract::transfer::wallet::BuilderOutputInfo {
  367. value: 200000,
  368. token_id: gdrk_token_id,
  369. public: gov_keypair_3.public,
  370. spend_hook,
  371. user_data,
  372. };
  373. assert!(2 * 400000 + 200000 == gdrk_supply);
  374. let builder = money_contract::transfer::wallet::Builder {
  375. clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
  376. value: gdrk_supply,
  377. token_id: gdrk_token_id,
  378. signature_secret: cashier_signature_secret,
  379. }],
  380. inputs: vec![],
  381. outputs: vec![output1, output2, output3],
  382. };
  383. let func_call = builder.build(&zk_bins)?;
  384. let tx = Transaction { func_calls: vec![func_call] };
  385. //// Validator
  386. for (idx, func_call) in tx.func_calls.iter().enumerate() {
  387. // So then the verifier will lookup the corresponding state_transition and apply
  388. // functions based off the func_id
  389. if func_call.func_id == "Money::transfer()" {
  390. debug!("money_contract::transfer::state_transition()");
  391. let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
  392. .expect("money_contract::state_transition() failed!");
  393. money_contract::transfer::validate::apply(&mut states, update);
  394. }
  395. }
  396. tx.zk_verify(&zk_bins);
  397. //// Wallet
  398. let mut gov_recv = vec![None, None, None];
  399. // Check that each person received one coin
  400. for (i, key) in gov_keypairs.iter().enumerate() {
  401. let gov_recv_coin = {
  402. let state = states.lookup_mut::<money_contract::State>(&"Money".to_string()).unwrap();
  403. let mut recv_coins = state.wallet_cache.get_received(&key.secret);
  404. assert_eq!(recv_coins.len(), 1);
  405. let recv_coin = recv_coins.pop().unwrap();
  406. let note = &recv_coin.note;
  407. assert_eq!(note.token_id, gdrk_token_id);
  408. // Normal payment
  409. assert_eq!(note.spend_hook, pallas::Base::from(0));
  410. assert_eq!(note.user_data, pallas::Base::from(0));
  411. let coords = key.public.0.to_affine().coordinates().unwrap();
  412. let coin = poseidon_hash::<8>([
  413. *coords.x(),
  414. *coords.y(),
  415. DrkValue::from(note.value),
  416. note.token_id,
  417. note.serial,
  418. note.spend_hook,
  419. note.user_data,
  420. note.coin_blind,
  421. ]);
  422. assert_eq!(coin, recv_coin.coin.0);
  423. debug!("Holder{} received a coin worth {} gDRK", i, note.value);
  424. recv_coin
  425. };
  426. gov_recv[i] = Some(gov_recv_coin);
  427. }
  428. // unwrap them for this demo
  429. let gov_recv: Vec<_> = gov_recv.into_iter().map(|r| r.unwrap()).collect();
  430. ///////////////////////////////////////////////////
  431. // DAO rules:
  432. // 1. gov token IDs must match on all inputs
  433. // 2. proposals must be submitted by minimum amount
  434. // 3. all votes >= quorum
  435. // 4. outcome > approval_ratio
  436. // 5. structure of outputs
  437. // output 0: value and address
  438. // output 1: change address
  439. ///////////////////////////////////////////////////
  440. ///////////////////////////////////////////////////
  441. // Propose the vote
  442. // In order to make a valid vote, first the proposer must
  443. // meet a criteria for a minimum number of gov tokens
  444. ///////////////////////////////////////////////////
  445. //// Wallet
  446. // TODO: look into proposal expiry once time for voting has finished
  447. let user_keypair = Keypair::random(&mut OsRng);
  448. // TODO: is it possible for an invalid transfer() to be constructed on exec()?
  449. // need to look into this
  450. let input = dao_contract::propose::wallet::Input {
  451. secret: gov_keypair_1.secret,
  452. note: gov_recv[0].note.clone(),
  453. };
  454. let builder = dao_contract::propose::wallet::Builder {
  455. inputs: vec![input],
  456. proposal: dao_contract::propose::wallet::Proposal {
  457. dest: user_keypair.public,
  458. amount: 1000,
  459. serial: pallas::Base::random(&mut OsRng),
  460. token_id: xdrk_token_id,
  461. blind: pallas::Base::random(&mut OsRng),
  462. },
  463. dao: dao_contract::propose::wallet::DaoParams {
  464. dao_proposer_limit,
  465. dao_quorum,
  466. dao_approval_ratio,
  467. gov_token_id: gdrk_token_id,
  468. dao_public_key: dao_keypair.public,
  469. dao_bulla_blind,
  470. },
  471. };
  472. let func_call = builder.build(&zk_bins);
  473. Ok(())
  474. }
  475. fn poseidon_hash<const N: usize>(messages: [pallas::Base; N]) -> pallas::Base {
  476. poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<N>, 3, 2>::init()
  477. .hash(messages)
  478. }