From 17170e4f2e70c0556dfbaae358ff56fca3579cc8 Mon Sep 17 00:00:00 2001 From: parazyd Date: Tue, 10 Jan 2023 19:16:18 +0100 Subject: [PATCH] drk: Add API for fetching all DAOs from the wallet and prefer that. --- bin/drk/src/dao.rs | 22 ++++++- bin/drk/src/rpc_dao.rs | 113 +++--------------------------------- bin/drk/src/rpc_wallet.rs | 119 +++++++++++++++++++++++++++++++++++--- 3 files changed, 142 insertions(+), 112 deletions(-) diff --git a/bin/drk/src/dao.rs b/bin/drk/src/dao.rs index 2333d7188..f7aa36f77 100644 --- a/bin/drk/src/dao.rs +++ b/bin/drk/src/dao.rs @@ -16,8 +16,9 @@ * along with this program. If not, see . */ +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, } + +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, + ])) + } +} diff --git a/bin/drk/src/rpc_dao.rs b/bin/drk/src/rpc_dao.rs index 48bf70a4a..5f91e3126 100644 --- a/bin/drk/src/rpc_dao.rs +++ b/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 { - 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 = 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 = serde_json::from_value(row[6].clone())?; - let gov_token_id = deserialize(&gov_token_bytes)?; - let secret_bytes: Vec = serde_json::from_value(row[7].clone())?; - let secret_key = deserialize(&secret_bytes)?; - let bulla_blind_bytes: Vec = serde_json::from_value(row[8].clone())?; - let bulla_blind = deserialize(&bulla_blind_bytes)?; - - let leaf_position_bytes: Vec = serde_json::from_value(row[9].clone())?; - let tx_hash_bytes: Vec = 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 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 - ]); - - 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)) - }; - - for row in rows { - let dao_id: u64 = serde_json::from_value(row[0].clone())?; - let dao_name_bytes: Vec = 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(()) diff --git a/bin/drk/src/rpc_wallet.rs b/bin/drk/src/rpc_wallet.rs index e6504fba4..994789b27 100644 --- a/bin/drk/src/rpc_wallet.rs +++ b/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::::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> { + 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 = 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 = serde_json::from_value(row[6].clone())?; + let gov_token_id = deserialize(&gov_token_bytes)?; + + let secret_bytes: Vec = serde_json::from_value(row[7].clone())?; + let secret_key = deserialize(&secret_bytes)?; + + let bulla_blind_bytes: Vec = serde_json::from_value(row[8].clone())?; + let bulla_blind = deserialize(&bulla_blind_bytes)?; + + let leaf_position_bytes: Vec = serde_json::from_value(row[9].clone())?; + let tx_hash_bytes: Vec = 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) + } }