rpc_dao.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 anyhow::{anyhow, Result};
  19. use darkfi::{
  20. rpc::jsonrpc::JsonRequest,
  21. tx::Transaction,
  22. wallet::walletdb::QueryType,
  23. zk::{empty_witnesses, ProvingKey, ZkCircuit},
  24. zkas::ZkBinary,
  25. };
  26. use darkfi_dao_contract::{
  27. dao_client,
  28. dao_client::{
  29. DaoInfo, DAO_DAOS_COL_APPROVAL_RATIO_BASE, DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
  30. DAO_DAOS_COL_BULLA_BLIND, DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_NAME,
  31. DAO_DAOS_COL_PROPOSER_LIMIT, DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET, DAO_DAOS_TABLE,
  32. },
  33. DaoFunction, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
  34. };
  35. use darkfi_sdk::{
  36. crypto::{PublicKey, DAO_CONTRACT_ID},
  37. ContractCall,
  38. };
  39. use darkfi_serial::{deserialize, serialize, Encodable};
  40. use rand::rngs::OsRng;
  41. use serde_json::json;
  42. use super::Drk;
  43. use crate::{dao::Dao, DaoParams};
  44. impl Drk {
  45. /// Import given DAO into the wallet
  46. pub async fn dao_import(&self, dao_name: String, dao_params: DaoParams) -> Result<()> {
  47. // First let's check if we've imported this DAO before. We use the name
  48. // as the identifier.
  49. let query = format!("SELECT {} FROM {}", DAO_DAOS_COL_NAME, DAO_DAOS_TABLE);
  50. let params = json!([query, QueryType::Blob as u8, DAO_DAOS_COL_NAME]);
  51. let req = JsonRequest::new("wallet.query_row_multi", params);
  52. let rep = self.rpc_client.request(req).await?;
  53. // The returned thing should be an array of found rows.
  54. let Some(rows) = rep.as_array() else {
  55. return Err(anyhow!("Unexpected response from darkfid: {}", rep))
  56. };
  57. for row in rows {
  58. let name_bytes: Vec<u8> = serde_json::from_value(row[0].clone())?;
  59. let name: String = deserialize(&name_bytes)?;
  60. if name == dao_name {
  61. return Err(anyhow!("DAO \"{}\" already imported in wallet", dao_name))
  62. }
  63. }
  64. eprintln!("Importing \"{}\" DAO into wallet", dao_name);
  65. let query = format!(
  66. "INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);",
  67. DAO_DAOS_TABLE, DAO_DAOS_COL_NAME, DAO_DAOS_COL_PROPOSER_LIMIT,
  68. DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_APPROVAL_RATIO_BASE, DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
  69. DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_SECRET, DAO_DAOS_COL_BULLA_BLIND,
  70. );
  71. let params = json!([
  72. query,
  73. QueryType::Blob as u8,
  74. serialize(&dao_name),
  75. QueryType::Integer as u8,
  76. dao_params.proposer_limit,
  77. QueryType::Integer as u8,
  78. dao_params.quorum,
  79. QueryType::Integer as u8,
  80. dao_params.approval_ratio_base,
  81. QueryType::Integer as u8,
  82. dao_params.approval_ratio_quot,
  83. QueryType::Blob as u8,
  84. serialize(&dao_params.gov_token_id),
  85. QueryType::Blob as u8,
  86. serialize(&dao_params.secret_key),
  87. QueryType::Blob as u8,
  88. serialize(&dao_params.bulla_blind),
  89. ]);
  90. eprintln!("Executing JSON-RPC request to add DAO to wallet");
  91. let req = JsonRequest::new("wallet.exec_sql", params);
  92. self.rpc_client.request(req).await?;
  93. eprintln!("DAO imported successfully");
  94. Ok(())
  95. }
  96. async fn dao_get_by_id(&self, dao_id: u64) -> Result<Dao> {
  97. let daos = self.wallet_get_daos().await?;
  98. let Some(dao) = daos.iter().find(|x| x.id == dao_id) else {
  99. return Err(anyhow!("DAO not found in wallet"))
  100. };
  101. Ok(dao.clone())
  102. }
  103. async fn dao_list_single(&self, dao_id: u64) -> Result<()> {
  104. let dao = self.dao_get_by_id(dao_id).await?;
  105. println!("DAO Parameters:");
  106. println!("Name: {}", dao.name);
  107. println!("Proposer limit: {}", dao.proposer_limit);
  108. println!("Quorum: {}", dao.quorum);
  109. println!(
  110. "Approval ratio: {}",
  111. dao.approval_ratio_base as f64 / dao.approval_ratio_quot as f64
  112. );
  113. println!("Governance token ID: {}", dao.gov_token_id);
  114. println!("Secret key: {}", dao.secret_key);
  115. println!("Bulla blind: {:?}", dao.bulla_blind);
  116. println!("Leaf position: {:?}", dao.leaf_position);
  117. println!("Tx hash: {:?}", dao.tx_hash);
  118. println!("Call idx: {:?}", dao.call_index);
  119. Ok(())
  120. }
  121. /// List DAO(s) imported in the wallet
  122. pub async fn dao_list(&self, dao_id: Option<u64>) -> Result<()> {
  123. if dao_id.is_some() {
  124. return self.dao_list_single(dao_id.unwrap()).await
  125. }
  126. let daos = self.wallet_get_daos().await?;
  127. for dao in daos {
  128. println!("[{}] {}", dao.id, dao.name);
  129. }
  130. Ok(())
  131. }
  132. /// Mint a DAO on-chain
  133. pub async fn dao_mint(&self, dao_id: u64) -> Result<Transaction> {
  134. let dao = self.dao_get_by_id(dao_id).await?;
  135. if dao.tx_hash.is_some() {
  136. return Err(anyhow!("This DAO seems to have already been minted on-chain"))
  137. }
  138. let dao_info = DaoInfo {
  139. proposer_limit: dao.proposer_limit,
  140. quorum: dao.quorum,
  141. approval_ratio_base: dao.approval_ratio_base,
  142. approval_ratio_quot: dao.approval_ratio_quot,
  143. gov_token_id: dao.gov_token_id,
  144. public_key: PublicKey::from_secret(dao.secret_key),
  145. bulla_blind: dao.bulla_blind,
  146. };
  147. let zkas_bins = self.lookup_zkas(&DAO_CONTRACT_ID).await?;
  148. let Some(dao_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == DAO_CONTRACT_ZKAS_DAO_MINT_NS) else {
  149. return Err(anyhow!("DAO Mint circuit not found"));
  150. };
  151. let dao_mint_zkbin = ZkBinary::decode(&dao_mint_zkbin.1)?;
  152. let k = 13;
  153. let dao_mint_circuit =
  154. ZkCircuit::new(empty_witnesses(&dao_mint_zkbin), dao_mint_zkbin.clone());
  155. eprintln!("Creating DAO Mint proving key");
  156. let dao_mint_pk = ProvingKey::build(k, &dao_mint_circuit);
  157. let (params, proofs) =
  158. dao_client::make_mint_call(&dao_info, &dao.secret_key, &dao_mint_zkbin, &dao_mint_pk)?;
  159. let mut data = vec![DaoFunction::Mint as u8];
  160. params.encode(&mut data)?;
  161. let calls = vec![ContractCall { contract_id: *DAO_CONTRACT_ID, data }];
  162. let proofs = vec![proofs];
  163. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  164. let sigs = tx.create_sigs(&mut OsRng, &[dao.secret_key])?;
  165. tx.signatures = vec![sigs];
  166. Ok(tx)
  167. }
  168. }