Bläddra i källkod

drk: Add API for fetching all DAOs from the wallet and prefer that.

parazyd 3 år sedan
förälder
incheckning
17170e4f2e
3 ändrade filer med 142 tillägg och 112 borttagningar
  1. 21 1
      bin/drk/src/dao.rs
  2. 9 104
      bin/drk/src/rpc_dao.rs
  3. 112 7
      bin/drk/src/rpc_wallet.rs

+ 21 - 1
bin/drk/src/dao.rs

@@ -16,8 +16,9 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use darkfi_dao_contract::dao_model::DaoBulla;
 use darkfi_sdk::{
-    crypto::{SecretKey, TokenId},
+    crypto::{poseidon_hash, PublicKey, SecretKey, TokenId},
     incrementalmerkletree::Position,
     pasta::pallas,
 };
@@ -44,6 +45,8 @@ pub struct DaoParams {
 #[derive(Debug, Clone)]
 /// Parameters representing an intialized DAO, optionally deployed on-chain
 pub struct Dao {
+    /// Numeric identifier for the DAO
+    pub id: u64,
     /// Named identifier for the DAO
     pub name: String,
     /// The minimum amount of governance tokens needed to open a proposal
@@ -66,3 +69,20 @@ pub struct Dao {
     /// The call index in the transaction where the DAO was deployed
     pub call_index: Option<u32>,
 }
+
+impl Dao {
+    pub fn bulla(&self) -> DaoBulla {
+        let (x, y) = PublicKey::from_secret(self.secret_key).xy();
+
+        DaoBulla::from(poseidon_hash([
+            pallas::Base::from(self.proposer_limit),
+            pallas::Base::from(self.quorum),
+            pallas::Base::from(self.approval_ratio_base),
+            pallas::Base::from(self.approval_ratio_quot),
+            self.gov_token_id.inner(),
+            x,
+            y,
+            self.bulla_blind,
+        ]))
+    }
+}

+ 9 - 104
bin/drk/src/rpc_dao.rs

@@ -28,10 +28,8 @@ use darkfi_dao_contract::{
     dao_client,
     dao_client::{
         DaoInfo, DAO_DAOS_COL_APPROVAL_RATIO_BASE, DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
-        DAO_DAOS_COL_BULLA_BLIND, DAO_DAOS_COL_CALL_INDEX, DAO_DAOS_COL_DAO_ID,
-        DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_LEAF_POSITION, DAO_DAOS_COL_NAME,
-        DAO_DAOS_COL_PROPOSER_LIMIT, DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET,
-        DAO_DAOS_COL_TX_HASH, DAO_DAOS_TABLE,
+        DAO_DAOS_COL_BULLA_BLIND, DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_NAME,
+        DAO_DAOS_COL_PROPOSER_LIMIT, DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET, DAO_DAOS_TABLE,
     },
     DaoFunction, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
 };
@@ -107,85 +105,13 @@ impl Drk {
     }
 
     async fn dao_get_by_id(&self, dao_id: u64) -> Result<Dao> {
-        let query =
-            format!("SELECT * FROM {} WHERE {} = {}", DAO_DAOS_TABLE, DAO_DAOS_COL_DAO_ID, dao_id);
+        let daos = self.wallet_get_daos().await?;
 
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_DAO_ID,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_NAME,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_PROPOSER_LIMIT,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_QUORUM,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_APPROVAL_RATIO_BASE,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_GOV_TOKEN_ID,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_SECRET,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_BULLA_BLIND,
-            QueryType::OptionBlob as u8,
-            DAO_DAOS_COL_LEAF_POSITION,
-            QueryType::OptionBlob as u8,
-            DAO_DAOS_COL_TX_HASH,
-            QueryType::OptionInteger as u8,
-            DAO_DAOS_COL_CALL_INDEX,
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_single", params);
-        let rep = self.rpc_client.request(req).await?;
-
-        let Some(row) = rep.as_array() else {
-            return Err(anyhow!("Unexpected response from darkfid: {}", rep));
+        let Some(dao) = daos.iter().find(|x| x.id == dao_id) else {
+            return Err(anyhow!("DAO not found in wallet"))
         };
 
-        let dao_id: u64 = serde_json::from_value(row[0].clone())?;
-
-        let name_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
-        let name = deserialize(&name_bytes)?;
-        let proposer_limit = serde_json::from_value(row[2].clone())?;
-        let quorum = serde_json::from_value(row[3].clone())?;
-        let approval_ratio_base = serde_json::from_value(row[4].clone())?;
-        let approval_ratio_quot = serde_json::from_value(row[5].clone())?;
-        let gov_token_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
-        let gov_token_id = deserialize(&gov_token_bytes)?;
-        let secret_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
-        let secret_key = deserialize(&secret_bytes)?;
-        let bulla_blind_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
-        let bulla_blind = deserialize(&bulla_blind_bytes)?;
-
-        let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
-        let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
-        let call_index = serde_json::from_value(row[11].clone())?;
-
-        let leaf_position = if leaf_position_bytes.is_empty() {
-            None
-        } else {
-            Some(deserialize(&leaf_position_bytes)?)
-        };
-
-        let tx_hash =
-            if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
-
-        Ok(Dao {
-            name,
-            proposer_limit,
-            quorum,
-            approval_ratio_base,
-            approval_ratio_quot,
-            gov_token_id,
-            secret_key,
-            bulla_blind,
-            leaf_position,
-            tx_hash,
-            call_index,
-        })
+        Ok(dao.clone())
     }
 
     async fn dao_list_single(&self, dao_id: u64) -> Result<()> {
@@ -215,31 +141,10 @@ impl Drk {
             return self.dao_list_single(dao_id.unwrap()).await
         }
 
-        let query = format!(
-            "SELECT {}, {} FROM {}",
-            DAO_DAOS_COL_DAO_ID, DAO_DAOS_COL_NAME, DAO_DAOS_TABLE
-        );
-
-        let params = json!([
-            query,
-            QueryType::Integer as u8,
-            DAO_DAOS_COL_DAO_ID,
-            QueryType::Blob as u8,
-            DAO_DAOS_COL_NAME
-        ]);
-
-        let req = JsonRequest::new("wallet.query_row_multi", params);
-        let rep = self.rpc_client.request(req).await?;
+        let daos = self.wallet_get_daos().await?;
 
-        let Some(rows) = rep.as_array() else {
-            return Err(anyhow!("Unexpected response from darkfid: {}", rep))
-        };
-
-        for row in rows {
-            let dao_id: u64 = serde_json::from_value(row[0].clone())?;
-            let dao_name_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
-            let dao_name: String = deserialize(&dao_name_bytes)?;
-            println!("[{}] {}", dao_id, dao_name);
+        for dao in daos {
+            println!("[{}] {}", dao.id, dao.name);
         }
 
         Ok(())

+ 112 - 7
bin/drk/src/rpc_wallet.rs

@@ -21,6 +21,10 @@ use std::collections::HashMap;
 use anyhow::{anyhow, Result};
 use darkfi::{rpc::jsonrpc::JsonRequest, util::parse::encode_base10, wallet::walletdb::QueryType};
 use darkfi_dao_contract::dao_client::{
+    DAO_DAOS_COL_APPROVAL_RATIO_BASE, DAO_DAOS_COL_APPROVAL_RATIO_QUOT, DAO_DAOS_COL_BULLA_BLIND,
+    DAO_DAOS_COL_CALL_INDEX, DAO_DAOS_COL_DAO_ID, DAO_DAOS_COL_GOV_TOKEN_ID,
+    DAO_DAOS_COL_LEAF_POSITION, DAO_DAOS_COL_NAME, DAO_DAOS_COL_PROPOSER_LIMIT,
+    DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET, DAO_DAOS_COL_TX_HASH, DAO_DAOS_TABLE,
     DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE, DAO_TREES_TABLE,
 };
 use darkfi_money_contract::client::{
@@ -48,6 +52,7 @@ use rand::rngs::OsRng;
 use serde_json::json;
 
 use super::Drk;
+use crate::dao::Dao;
 
 impl Drk {
     /// Initialize wallet with tables for the Money Contract.
@@ -381,7 +386,7 @@ impl Drk {
         }
 
         if table.is_empty() {
-            println!("No unspent balances found");
+            eprintln!("No unspent balances found");
         } else {
             println!("{}", table);
         }
@@ -594,17 +599,17 @@ impl Drk {
 
     /// Reset the Money Contract Merkle tree and coins in the wallet
     pub async fn reset_money_tree(&self) -> Result<()> {
-        println!("Resetting Money Merkle tree");
+        eprintln!("Resetting Money Merkle tree");
         let tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
         self.put_money_tree(&tree).await?;
-        println!("Successfully reset Money Merkle tree");
+        eprintln!("Successfully reset Money Merkle tree");
 
-        println!("Resetting coins");
+        eprintln!("Resetting coins");
         let query = format!("DELETE FROM {};", MONEY_COINS_TABLE);
         let params = json!([query]);
         let req = JsonRequest::new("wallet.exec_sql", params);
         let _ = self.rpc_client.request(req).await?;
-        println!("Successfully reset coins");
+        eprintln!("Successfully reset coins");
 
         Ok(())
     }
@@ -637,12 +642,112 @@ impl Drk {
 
     /// Reset the DAO Contract Merkle trees in the wallet
     pub async fn reset_dao_trees(&self) -> Result<()> {
-        println!("Resetting DAO Merkle trees");
+        eprintln!("Resetting DAO Merkle trees");
         let tree0 = MerkleTree::new(100);
         let tree1 = MerkleTree::new(100);
         self.put_dao_trees(&tree0, &tree1).await?;
-        println!("Successfully reset DAO Merkle trees");
+        eprintln!("Successfully reset DAO Merkle trees");
 
         Ok(())
     }
+
+    /// Fetch all DAOs from the wallet
+    /// We use this a lot because we don't worry too much about performance in this
+    /// tool, and also in practice probably not a lot of DAOs will be in a single
+    /// wallet.
+    pub async fn wallet_get_daos(&self) -> Result<Vec<Dao>> {
+        let query = format!("SELECT * FROM {}", DAO_DAOS_TABLE);
+
+        let params = json!([
+            query,
+            QueryType::Integer as u8,
+            DAO_DAOS_COL_DAO_ID,
+            QueryType::Blob as u8,
+            DAO_DAOS_COL_NAME,
+            QueryType::Integer as u8,
+            DAO_DAOS_COL_PROPOSER_LIMIT,
+            QueryType::Integer as u8,
+            DAO_DAOS_COL_QUORUM,
+            QueryType::Integer as u8,
+            DAO_DAOS_COL_APPROVAL_RATIO_BASE,
+            QueryType::Integer as u8,
+            DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
+            QueryType::Blob as u8,
+            DAO_DAOS_COL_GOV_TOKEN_ID,
+            QueryType::Blob as u8,
+            DAO_DAOS_COL_SECRET,
+            QueryType::Blob as u8,
+            DAO_DAOS_COL_BULLA_BLIND,
+            QueryType::OptionBlob as u8,
+            DAO_DAOS_COL_LEAF_POSITION,
+            QueryType::OptionBlob as u8,
+            DAO_DAOS_COL_TX_HASH,
+            QueryType::OptionInteger as u8,
+            DAO_DAOS_COL_CALL_INDEX,
+        ]);
+
+        let req = JsonRequest::new("wallet.query_row_multi", params);
+        let rep = self.rpc_client.request(req).await?;
+
+        let Some(rows) = rep.as_array() else {
+            return Err(anyhow!("Unexpected response from darkfid: {}", rep));
+        };
+
+        let mut daos = Vec::with_capacity(rows.len());
+
+        for row in rows {
+            let id: u64 = serde_json::from_value(row[0].clone())?;
+
+            let name_bytes: Vec<u8> = serde_json::from_value(row[1].clone())?;
+            let name = deserialize(&name_bytes)?;
+
+            let proposer_limit = serde_json::from_value(row[2].clone())?;
+            let quorum = serde_json::from_value(row[3].clone())?;
+            let approval_ratio_base = serde_json::from_value(row[4].clone())?;
+            let approval_ratio_quot = serde_json::from_value(row[5].clone())?;
+
+            let gov_token_bytes: Vec<u8> = serde_json::from_value(row[6].clone())?;
+            let gov_token_id = deserialize(&gov_token_bytes)?;
+
+            let secret_bytes: Vec<u8> = serde_json::from_value(row[7].clone())?;
+            let secret_key = deserialize(&secret_bytes)?;
+
+            let bulla_blind_bytes: Vec<u8> = serde_json::from_value(row[8].clone())?;
+            let bulla_blind = deserialize(&bulla_blind_bytes)?;
+
+            let leaf_position_bytes: Vec<u8> = serde_json::from_value(row[9].clone())?;
+            let tx_hash_bytes: Vec<u8> = serde_json::from_value(row[10].clone())?;
+            let call_index = serde_json::from_value(row[11].clone())?;
+
+            let leaf_position = if leaf_position_bytes.is_empty() {
+                None
+            } else {
+                Some(deserialize(&leaf_position_bytes)?)
+            };
+
+            let tx_hash =
+                if tx_hash_bytes.is_empty() { None } else { Some(deserialize(&tx_hash_bytes)?) };
+
+            let dao = Dao {
+                id,
+                name,
+                proposer_limit,
+                quorum,
+                approval_ratio_base,
+                approval_ratio_quot,
+                gov_token_id,
+                secret_key,
+                bulla_blind,
+                leaf_position,
+                tx_hash,
+                call_index,
+            };
+
+            daos.push(dao);
+        }
+
+        // Sort by ID in SQL. The SELECT statement does not guarantee this.
+        daos.sort_by(|a, b| a.id.cmp(&b.id));
+        Ok(daos)
+    }
 }