main.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi::{
  19. blockchain::Blockchain,
  20. consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
  21. crypto::{
  22. coin::Coin,
  23. proof::{ProvingKey, VerifyingKey},
  24. types::{DrkSpendHook, DrkUserData, DrkValue},
  25. },
  26. runtime::vm_runtime::Runtime,
  27. zk::circuit::{BurnContract, MintContract},
  28. zkas::decoder::ZkBinary,
  29. Result,
  30. };
  31. use darkfi_sdk::{
  32. crypto::{
  33. constants::MERKLE_DEPTH, pedersen::pedersen_commitment_u64, poseidon_hash,
  34. schnorr::SchnorrSecret, ContractId, Keypair, MerkleNode, MerkleTree, PublicKey, SecretKey,
  35. TokenId,
  36. },
  37. tx::ContractCall,
  38. };
  39. use darkfi_serial::{deserialize, serialize, Decodable, Encodable, WriteExt};
  40. use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
  41. use log::{debug, error};
  42. use pasta_curves::{
  43. arithmetic::CurveAffine,
  44. group::{ff::Field, Curve},
  45. pallas,
  46. };
  47. use rand::rngs::OsRng;
  48. use std::{
  49. any::{Any, TypeId},
  50. io::Cursor,
  51. time::Instant,
  52. };
  53. use dao_contract::{DaoFunction, DaoMintParams};
  54. use money_contract::{MoneyFunction, MoneyTransferParams};
  55. use crate::{
  56. contract::{dao, example, money},
  57. note::EncryptedNote2,
  58. schema::WalletCache,
  59. tx::Transaction,
  60. util::{StateRegistry, ZkContractTable},
  61. };
  62. mod contract;
  63. mod error;
  64. mod note;
  65. mod schema;
  66. mod tx;
  67. mod util;
  68. fn show_dao_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
  69. let db_info = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
  70. let value = db_info.get(&serialize(&"dao_tree".to_string())).expect("dao_tree").unwrap();
  71. let mut decoder = Cursor::new(&value);
  72. let set_size: u32 = Decodable::decode(&mut decoder)?;
  73. let tree: MerkleTree = Decodable::decode(decoder)?;
  74. debug!(target: "demo", "DAO state:");
  75. debug!(target: "demo", " tree: {} bytes", value.len());
  76. debug!(target: "demo", " set size: {}", set_size);
  77. let db_roots = chain.contracts.lookup(&chain.sled_db, contract_id, "dao_roots")?;
  78. for i in 0..set_size {
  79. let root = db_roots.get(&serialize(&i)).expect("dao_roots").unwrap();
  80. let root: MerkleNode = deserialize(&root)?;
  81. debug!(target: "demo", " root {}: {:?}", i, root);
  82. }
  83. Ok(())
  84. }
  85. fn show_money_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
  86. let db_info = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
  87. let value = db_info.get(&serialize(&"coin_tree".to_string())).expect("coin_tree").unwrap();
  88. let mut decoder = Cursor::new(&value);
  89. let set_size: u32 = Decodable::decode(&mut decoder)?;
  90. let tree: MerkleTree = Decodable::decode(decoder)?;
  91. debug!(target: "demo", "Money state:");
  92. debug!(target: "demo", " tree: {} bytes", value.len());
  93. debug!(target: "demo", " set size: {}", set_size);
  94. let db_roots = chain.contracts.lookup(&chain.sled_db, contract_id, "coin_roots")?;
  95. for i in 0..set_size {
  96. let root = db_roots.get(&serialize(&i)).expect("coin_roots").unwrap();
  97. let root: MerkleNode = deserialize(&root)?;
  98. debug!(target: "demo", " root {}: {:?}", i, root);
  99. }
  100. let db_nulls = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
  101. debug!(target: "demo", " nullifiers:");
  102. for obj in db_nulls.iter() {
  103. let (key, value) = obj.unwrap();
  104. debug!(target: "demo", " {:02x?}", &key[..]);
  105. }
  106. Ok(())
  107. }
  108. type BoxResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
  109. fn validate(
  110. tx: &Transaction,
  111. dao_wasm_bytes: &[u8],
  112. dao_contract_id: ContractId,
  113. money_wasm_bytes: &[u8],
  114. money_contract_id: ContractId,
  115. blockchain: &Blockchain,
  116. zk_bins: &ZkContractTable,
  117. ) -> Result<()> {
  118. // ContractId is not Hashable so put them in a Vec and do linear scan
  119. let wasm_bytes_lookup = vec![
  120. (dao_contract_id, "DAO", dao_wasm_bytes),
  121. (money_contract_id, "Money", money_wasm_bytes),
  122. ];
  123. // We can do all exec(), zk proof checks and signature verifies in parallel.
  124. let mut updates = vec![];
  125. let mut zkpublic_table = vec![];
  126. let mut sigpub_table = vec![];
  127. // Validate all function calls in the tx
  128. for (idx, call) in tx.calls.iter().enumerate() {
  129. // So then the verifier will lookup the corresponding state_transition and apply
  130. // functions based off the func_id
  131. // Write the actual payload data
  132. let mut payload = Vec::new();
  133. // Call index
  134. payload.write_u32(idx as u32)?;
  135. // Actuall calldata
  136. tx.calls.encode(&mut payload)?;
  137. // Lookup the wasm bytes
  138. let (_, contract_name, wasm_bytes) =
  139. wasm_bytes_lookup.iter().find(|(id, _name, _bytes)| *id == call.contract_id).unwrap();
  140. debug!(target: "demo", "{}::exec() contract called", contract_name);
  141. let mut runtime = Runtime::new(wasm_bytes, blockchain.clone(), call.contract_id)?;
  142. let update = runtime.exec(&payload)?;
  143. updates.push(update);
  144. let metadata = runtime.metadata(&payload)?;
  145. let mut decoder = Cursor::new(&metadata);
  146. let zk_public_values: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
  147. let signature_public_keys: Vec<pallas::Point> = Decodable::decode(&mut decoder)?;
  148. zkpublic_table.push(zk_public_values);
  149. sigpub_table.push(signature_public_keys);
  150. }
  151. tx.zk_verify(&zk_bins, &zkpublic_table)?;
  152. tx.verify_sigs(&sigpub_table)?;
  153. // Now we finished verification stage, just apply all changes
  154. assert_eq!(tx.calls.len(), updates.len());
  155. for (call, update) in tx.calls.iter().zip(updates.iter()) {
  156. // Lookup the wasm bytes
  157. let (_, contract_name, wasm_bytes) =
  158. wasm_bytes_lookup.iter().find(|(id, _name, _bytes)| *id == call.contract_id).unwrap();
  159. debug!(target: "demo", "{}::apply() contract called", contract_name);
  160. let mut runtime = Runtime::new(wasm_bytes, blockchain.clone(), call.contract_id)?;
  161. runtime.apply(&update)?;
  162. }
  163. Ok(())
  164. }
  165. #[async_std::main]
  166. async fn main() -> BoxResult<()> {
  167. // Debug log configuration
  168. let mut cfg = simplelog::ConfigBuilder::new();
  169. cfg.add_filter_ignore("sled".to_string());
  170. simplelog::TermLogger::init(
  171. simplelog::LevelFilter::Debug,
  172. cfg.build(),
  173. simplelog::TerminalMode::Mixed,
  174. simplelog::ColorChoice::Auto,
  175. )?;
  176. println!("wakie wakie young wagie");
  177. //return Ok(());
  178. //schema::schema().await?;
  179. //return Ok(());
  180. // =============================
  181. // Setup initial program parameters
  182. // =============================
  183. // Money parameters
  184. let xdrk_supply = 1_000_000;
  185. let xdrk_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
  186. // Governance token parameters
  187. let gdrk_supply = 1_000_000;
  188. let gdrk_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
  189. // DAO parameters
  190. let dao_proposer_limit = 110;
  191. let dao_quorum = 110;
  192. let dao_approval_ratio_quot = 1;
  193. let dao_approval_ratio_base = 2;
  194. // Initialize ZK binary table
  195. let mut zk_bins = ZkContractTable::new();
  196. debug!(target: "demo", "Loading dao-mint.zk");
  197. let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
  198. let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
  199. zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
  200. debug!(target: "demo", "Loading money-transfer contracts");
  201. {
  202. let start = Instant::now();
  203. let mint_pk = ProvingKey::build(11, &MintContract::default());
  204. debug!("Mint PK: [{:?}]", start.elapsed());
  205. let start = Instant::now();
  206. let burn_pk = ProvingKey::build(11, &BurnContract::default());
  207. debug!("Burn PK: [{:?}]", start.elapsed());
  208. let start = Instant::now();
  209. let mint_vk = VerifyingKey::build(11, &MintContract::default());
  210. debug!("Mint VK: [{:?}]", start.elapsed());
  211. let start = Instant::now();
  212. let burn_vk = VerifyingKey::build(11, &BurnContract::default());
  213. debug!("Burn VK: [{:?}]", start.elapsed());
  214. zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
  215. zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
  216. }
  217. /*
  218. debug!(target: "demo", "Loading dao-propose-main.zk");
  219. let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
  220. let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
  221. zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
  222. debug!(target: "demo", "Loading dao-propose-burn.zk");
  223. let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
  224. let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
  225. zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
  226. debug!(target: "demo", "Loading dao-vote-main.zk");
  227. let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
  228. let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
  229. zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
  230. debug!(target: "demo", "Loading dao-vote-burn.zk");
  231. let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
  232. let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
  233. zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
  234. let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
  235. let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
  236. zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
  237. */
  238. // State for money contracts
  239. let cashier_signature_secret = SecretKey::random(&mut OsRng);
  240. let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
  241. let faucet_signature_secret = SecretKey::random(&mut OsRng);
  242. let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
  243. // We use this to receive coins
  244. let mut cache = WalletCache::new();
  245. // Initialize a dummy blockchain
  246. // TODO: This blockchain interface should perhaps be ValidatorState and Mutex/RwLock.
  247. let db = sled::Config::new().temporary(true).open()?;
  248. let blockchain = Blockchain::new(&db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
  249. // ================================================================
  250. // Deploy the wasm contracts
  251. // ================================================================
  252. let dao_wasm_bytes = std::fs::read("dao_contract.wasm")?;
  253. let dao_contract_id = ContractId::from(pallas::Base::from(1));
  254. let money_wasm_bytes = std::fs::read("money_contract.wasm")?;
  255. let money_contract_id = ContractId::from(pallas::Base::from(2));
  256. // Block 1
  257. // This has 2 transaction deploying the DAO and Money wasm contracts
  258. // together with their ZK proofs.
  259. {
  260. let mut dao_runtime = Runtime::new(&dao_wasm_bytes, blockchain.clone(), dao_contract_id)?;
  261. let mut money_runtime =
  262. Runtime::new(&money_wasm_bytes, blockchain.clone(), money_contract_id)?;
  263. // 1. exec() - zk and sig verify also
  264. // ... none in this block
  265. // 2. commit() - all apply() and deploy()
  266. // Deploy function to initialize the smart contract state.
  267. // Here we pass an empty payload, but it's possible to feed in arbitrary data.
  268. dao_runtime.deploy(&[])?;
  269. money_runtime.deploy(&[])?;
  270. debug!(target: "demo", "Deployed DAO and money contracts");
  271. }
  272. // ================================================================
  273. // DAO::mint()
  274. // ================================================================
  275. // Wallet
  276. let dao_keypair = Keypair::random(&mut OsRng);
  277. let dao_bulla_blind = pallas::Base::random(&mut OsRng);
  278. let tx = {
  279. let signature_secret = SecretKey::random(&mut OsRng);
  280. // Create DAO mint tx
  281. let builder = dao::mint::wallet::Builder {
  282. dao_proposer_limit,
  283. dao_quorum,
  284. dao_approval_ratio_quot,
  285. dao_approval_ratio_base,
  286. gov_token_id: gdrk_token_id,
  287. dao_pubkey: dao_keypair.public,
  288. dao_bulla_blind,
  289. signature_secret,
  290. };
  291. let (params, dao_mint_proofs) = builder.build(&zk_bins);
  292. // Write the actual call data
  293. let mut calldata = Vec::new();
  294. // Selects which path executes in the contract.
  295. calldata.write_u8(DaoFunction::Mint as u8)?;
  296. params.encode(&mut calldata)?;
  297. let calls = vec![ContractCall { contract_id: dao_contract_id, data: calldata }];
  298. let signatures = vec![];
  299. //for func_call in &func_calls {
  300. // let sign = sign([signature_secret].to_vec(), func_call);
  301. // signatures.push(sign);
  302. //}
  303. let proofs = vec![dao_mint_proofs];
  304. Transaction { calls, proofs, signatures }
  305. };
  306. //// Validator
  307. validate(
  308. &tx,
  309. &dao_wasm_bytes,
  310. dao_contract_id,
  311. &money_wasm_bytes,
  312. money_contract_id,
  313. &blockchain,
  314. &zk_bins,
  315. )
  316. .expect("validate failed");
  317. // Wallet stuff
  318. // In your wallet, wait until you see the tx confirmed before doing anything below
  319. // So for example keep track of tx hash
  320. //
  321. // We also need to loop through all newly added items to the validator node
  322. // and repeat the same for our local merkle tree. The order of added items
  323. // to local merkle trees must be the same.
  324. //
  325. // One way to do this would be that .apply() keeps an in-memory per block
  326. // list of the order txs were applied. So then we can repeat the same order
  327. // for our local wallet trees.
  328. //
  329. // [ tx1, tx2, ... ]
  330. //
  331. // So the wallets know these are the new txs and this was the order they
  332. // were applied to the state in.
  333. // State updates are atomic so this will always be linear.
  334. //
  335. // When we see our DAO bulla, we call .witness()
  336. // We need to witness() the value in our local merkle tree
  337. let dao_bulla = {
  338. assert_eq!(tx.calls.len(), 1);
  339. let calldata = &tx.calls[0].data;
  340. let params_data = &calldata[1..];
  341. let params: DaoMintParams = Decodable::decode(params_data)?;
  342. params.dao_bulla.clone()
  343. };
  344. let mut dao_tree = MerkleTree::new(100);
  345. let dao_leaf_position = {
  346. let node = MerkleNode::from(dao_bulla.0);
  347. dao_tree.append(&node);
  348. dao_tree.witness().unwrap()
  349. };
  350. debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
  351. ///////////////////////////////////////////////////
  352. //// Mint the initial supply of treasury token
  353. //// and send it all to the DAO directly
  354. ///////////////////////////////////////////////////
  355. debug!(target: "demo", "Stage 2. Minting treasury token");
  356. cache.track(dao_keypair.secret);
  357. //// Wallet
  358. // Address of deployed contract in our example is dao::exec::FUNC_ID
  359. // This field is public, you can see it's being sent to a DAO
  360. // but nothing else is visible.
  361. //
  362. // In the python code we wrote:
  363. //
  364. // spend_hook = b"0xdao_ruleset"
  365. //
  366. let spend_hook = *dao::exec::FUNC_ID;
  367. let tx = {
  368. // The user_data can be a simple hash of the items passed into the ZK proof
  369. // up to corresponding linked ZK proof to interpret however they need.
  370. // In out case, it's the bulla for the DAO
  371. let user_data = dao_bulla.0;
  372. let builder = money::transfer::wallet::Builder {
  373. clear_inputs: vec![money::transfer::wallet::BuilderClearInputInfo {
  374. value: xdrk_supply,
  375. token_id: xdrk_token_id,
  376. signature_secret: cashier_signature_secret,
  377. }],
  378. inputs: vec![],
  379. outputs: vec![money::transfer::wallet::BuilderOutputInfo {
  380. value: xdrk_supply,
  381. token_id: xdrk_token_id,
  382. public: dao_keypair.public,
  383. serial: pallas::Base::random(&mut OsRng),
  384. coin_blind: pallas::Base::random(&mut OsRng),
  385. spend_hook,
  386. user_data,
  387. }],
  388. };
  389. let (params, proofs) = builder.build(&zk_bins)?;
  390. // Write the actual call data
  391. let mut calldata = Vec::new();
  392. // Selects which path executes in the contract.
  393. calldata.write_u8(MoneyFunction::Transfer as u8)?;
  394. params.encode(&mut calldata)?;
  395. let calls = vec![ContractCall { contract_id: money_contract_id, data: calldata }];
  396. let proofs = vec![proofs];
  397. // We sign everything
  398. let mut unsigned_tx_data = vec![];
  399. calls.encode(&mut unsigned_tx_data)?;
  400. proofs.encode(&mut unsigned_tx_data)?;
  401. let signature = cashier_signature_secret.sign(&mut OsRng, &unsigned_tx_data[..]);
  402. // Our tx has a single contract call which itself has a single input
  403. let signatures = vec![vec![signature]];
  404. Transaction { calls, proofs, signatures }
  405. };
  406. //// Validator
  407. validate(
  408. &tx,
  409. &dao_wasm_bytes,
  410. dao_contract_id,
  411. &money_wasm_bytes,
  412. money_contract_id,
  413. &blockchain,
  414. &zk_bins,
  415. )
  416. .expect("validate failed");
  417. // Wallet stuff
  418. // DAO reads the money received from the encrypted note
  419. {
  420. assert_eq!(tx.calls.len(), 1);
  421. let calldata = &tx.calls[0].data;
  422. let params_data = &calldata[1..];
  423. let params: MoneyTransferParams = Decodable::decode(params_data)?;
  424. for output in params.outputs {
  425. let coin = output.coin;
  426. let enc_note = note::EncryptedNote2 {
  427. ciphertext: output.ciphertext,
  428. ephem_public: output.ephem_public,
  429. };
  430. let coin = Coin(coin);
  431. cache.try_decrypt_note(coin, &enc_note);
  432. }
  433. }
  434. let mut recv_coins = cache.get_received(&dao_keypair.secret);
  435. assert_eq!(recv_coins.len(), 1);
  436. let dao_recv_coin = recv_coins.pop().unwrap();
  437. let treasury_note = dao_recv_coin.note;
  438. // Check the actual coin received is valid before accepting it
  439. let coords = dao_keypair.public.inner().to_affine().coordinates().unwrap();
  440. let coin = poseidon_hash::<8>([
  441. *coords.x(),
  442. *coords.y(),
  443. DrkValue::from(treasury_note.value),
  444. treasury_note.token_id.inner(),
  445. treasury_note.serial,
  446. treasury_note.spend_hook,
  447. treasury_note.user_data,
  448. treasury_note.coin_blind,
  449. ]);
  450. assert_eq!(coin, dao_recv_coin.coin.0);
  451. assert_eq!(treasury_note.spend_hook, *dao::exec::FUNC_ID);
  452. assert_eq!(treasury_note.user_data, dao_bulla.0);
  453. debug!("DAO received a coin worth {} xDRK", treasury_note.value);
  454. ///////////////////////////////////////////////////
  455. show_dao_state(&blockchain, &dao_contract_id)?;
  456. show_money_state(&blockchain, &money_contract_id)?;
  457. Ok(())
  458. }