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

This commit is contained in:
parazyd
2023-01-10 19:16:18 +01:00
parent f83270f175
commit 17170e4f2e
3 changed files with 142 additions and 112 deletions

View File

@@ -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,
]))
}
}

View File

@@ -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 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<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(())

View File

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