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)
+ }
}