drk: updated basic dao handling

This commit is contained in:
skoupidi
2024-05-22 13:44:28 +03:00
parent 5f9b862e9a
commit b89c4196a4
3 changed files with 152 additions and 134 deletions

View File

@@ -448,6 +448,110 @@ impl Drk {
Ok(ret)
}
/// Auxiliary function to parse a `DAO_DAOS_TABLE` record.
async fn parse_dao_record(&self, row: &[Value]) -> Result<Dao> {
let Value::Integer(id) = row[0] else {
return Err(Error::ParseFailed("[parse_dao_record] ID parsing failed"))
};
let Ok(id) = u64::try_from(id) else {
return Err(Error::ParseFailed("[parse_dao_record] ID parsing failed"))
};
let Value::Text(ref name) = row[1] else {
return Err(Error::ParseFailed("[parse_dao_record] Name parsing failed"))
};
let name = name.clone();
let Value::Blob(ref proposer_limit_bytes) = row[2] else {
return Err(Error::ParseFailed("[parse_dao_record] Proposer limit bytes parsing failed"))
};
let proposer_limit = deserialize_async(proposer_limit_bytes).await?;
let Value::Blob(ref quorum_bytes) = row[3] else {
return Err(Error::ParseFailed("[parse_dao_record] Quorum bytes parsing failed"))
};
let quorum = deserialize_async(quorum_bytes).await?;
let Value::Integer(approval_ratio_base) = row[4] else {
return Err(Error::ParseFailed("[parse_dao_record] Approval ratio base parsing failed"))
};
let Ok(approval_ratio_base) = u64::try_from(approval_ratio_base) else {
return Err(Error::ParseFailed("[parse_dao_record] Approval ratio base parsing failed"))
};
let Value::Integer(approval_ratio_quot) = row[5] else {
return Err(Error::ParseFailed("[parse_dao_record] Approval ratio quot parsing failed"))
};
let Ok(approval_ratio_quot) = u64::try_from(approval_ratio_quot) else {
return Err(Error::ParseFailed("[parse_dao_record] Approval ratio quot parsing failed"))
};
let Value::Blob(ref gov_token_bytes) = row[6] else {
return Err(Error::ParseFailed("[parse_dao_record] Gov token bytes parsing failed"))
};
let gov_token_id = deserialize_async(gov_token_bytes).await?;
let Value::Blob(ref secret_bytes) = row[7] else {
return Err(Error::ParseFailed("[parse_dao_record] Secret key bytes parsing failed"))
};
let secret_key = deserialize_async(secret_bytes).await?;
let Value::Blob(ref bulla_blind_bytes) = row[8] else {
return Err(Error::ParseFailed("[parse_dao_record] Bulla blind bytes parsing failed"))
};
let bulla_blind = deserialize_async(bulla_blind_bytes).await?;
let leaf_position = match row[9] {
Value::Blob(ref leaf_position_bytes) => {
Some(deserialize_async(leaf_position_bytes).await?)
}
Value::Null => None,
_ => {
return Err(Error::ParseFailed(
"[parse_dao_record] Leaf position bytes parsing failed",
))
}
};
let tx_hash = match row[10] {
Value::Blob(ref tx_hash_bytes) => Some(deserialize_async(tx_hash_bytes).await?),
Value::Null => None,
_ => {
return Err(Error::ParseFailed(
"[parse_dao_record] Transaction hash bytes parsing failed",
))
}
};
let call_index = match row[11] {
Value::Integer(call_index) => {
let Ok(call_index) = u8::try_from(call_index) else {
return Err(Error::ParseFailed("[parse_dao_record] Call index parsing failed"))
};
Some(call_index)
}
Value::Null => None,
_ => return Err(Error::ParseFailed("[parse_dao_record] Call index parsing failed")),
};
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,
};
Ok(dao)
}
/// Fetch all known DAOs from the wallet.
pub async fn get_daos(&self) -> Result<Vec<Dao>> {
let rows = match self.wallet.query_multiple(&DAO_DAOS_TABLE, &[], &[]).await {
@@ -459,99 +563,7 @@ impl Drk {
let mut daos = Vec::with_capacity(rows.len());
for row in rows {
let Value::Integer(id) = row[0] else {
return Err(Error::ParseFailed("[get_daos] ID parsing failed"))
};
let Ok(id) = u64::try_from(id) else {
return Err(Error::ParseFailed("[get_daos] ID parsing failed"))
};
let Value::Text(ref name) = row[1] else {
return Err(Error::ParseFailed("[get_daos] Name parsing failed"))
};
let name = name.clone();
let Value::Blob(ref proposer_limit_bytes) = row[2] else {
return Err(Error::ParseFailed("[get_daos] Proposer limit bytes parsing failed"))
};
let proposer_limit = deserialize_async(proposer_limit_bytes).await?;
let Value::Blob(ref quorum_bytes) = row[3] else {
return Err(Error::ParseFailed("[get_daos] Quorum bytes parsing failed"))
};
let quorum = deserialize_async(quorum_bytes).await?;
let Value::Integer(approval_ratio_base) = row[4] else {
return Err(Error::ParseFailed("[get_daos] Approval ratio base parsing failed"))
};
let Ok(approval_ratio_base) = u64::try_from(approval_ratio_base) else {
return Err(Error::ParseFailed("[get_daos] Approval ratio base parsing failed"))
};
let Value::Integer(approval_ratio_quot) = row[5] else {
return Err(Error::ParseFailed("[get_daos] Approval ratio quot parsing failed"))
};
let Ok(approval_ratio_quot) = u64::try_from(approval_ratio_quot) else {
return Err(Error::ParseFailed("[get_daos] Approval ratio quot parsing failed"))
};
let Value::Blob(ref gov_token_bytes) = row[6] else {
return Err(Error::ParseFailed("[get_daos] Gov token bytes parsing failed"))
};
let gov_token_id = deserialize_async(gov_token_bytes).await?;
let Value::Blob(ref secret_bytes) = row[7] else {
return Err(Error::ParseFailed("[get_daos] Secret key bytes parsing failed"))
};
let secret_key = deserialize_async(secret_bytes).await?;
let Value::Blob(ref bulla_blind_bytes) = row[8] else {
return Err(Error::ParseFailed("[get_daos] Bulla blind bytes parsing failed"))
};
let bulla_blind = deserialize_async(bulla_blind_bytes).await?;
let Value::Blob(ref leaf_position_bytes) = row[9] else {
return Err(Error::ParseFailed("[get_daos] Leaf position bytes parsing failed"))
};
let leaf_position = if leaf_position_bytes.is_empty() {
None
} else {
Some(deserialize_async(leaf_position_bytes).await?)
};
let Value::Blob(ref tx_hash_bytes) = row[10] else {
return Err(Error::ParseFailed("[get_daos] Transaction hash bytes parsing failed"))
};
let tx_hash = if tx_hash_bytes.is_empty() {
None
} else {
Some(deserialize_async(tx_hash_bytes).await?)
};
let Value::Integer(call_index) = row[11] else {
return Err(Error::ParseFailed("[get_daos] Call index parsing failed"))
};
let Ok(call_index) = u8::try_from(call_index) else {
return Err(Error::ParseFailed("[get_daos] Call index parsing failed"))
};
let call_index = Some(call_index);
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);
daos.push(self.parse_dao_record(&row).await?);
}
// Here we sort the vec by ID. The SQL SELECT statement does not guarantee
@@ -1056,16 +1068,13 @@ impl Drk {
}
/// Import given DAO params into the wallet with a given name.
pub async fn import_dao(&self, dao_name: String, dao_params: DaoParams) -> Result<()> {
pub async fn import_dao(&self, dao_name: &str, dao_params: DaoParams) -> Result<()> {
// First let's check if we've imported this DAO with the given name before.
// TODO: instead of getting all DAOs and filtering in rust,
// we can use the DB api directly to query for the record
// and return the error if it exists
let daos = self.get_daos().await?;
if daos.iter().any(|x| x.name == dao_name) {
return Err(Error::RusqliteError(
"[import_dao] This DAO has already been imported".to_string(),
))
if let Ok(dao) = self.get_dao_by_alias(dao_name).await {
return Err(Error::RusqliteError(format!(
"[import_dao] This DAO has already been imported with ID {}",
dao.id
)))
}
println!("Importing \"{dao_name}\" DAO into the wallet");
@@ -1156,11 +1165,35 @@ impl Drk {
Ok(dao.clone())
}
/// List DAO(s) imported in the wallet. If an ID is given, just print the
/// Fetch a DAO given its name alias.
pub async fn get_dao_by_alias(&self, alias_filter: &str) -> Result<Dao> {
let row = match self
.wallet
.query_single(
&DAO_DAOS_TABLE,
&[],
convert_named_params! {(DAO_DAOS_COL_NAME, alias_filter)},
)
.await
{
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
"[get_dao_by_alias] DAO retrieval failed: {e:?}"
)))
}
};
self.parse_dao_record(&row).await
}
/// List DAO(s) imported in the wallet. If a name aliasis given, just print the
/// metadata for that specific one, if found.
pub async fn dao_list(&self, dao_id: Option<u64>) -> Result<()> {
if let Some(dao_id) = dao_id {
return self.dao_list_single(dao_id).await
pub async fn dao_list(&self, alias_filter: &Option<String>) -> Result<()> {
if let Some(alias) = alias_filter {
let dao = self.get_dao_by_alias(alias).await?;
println!("{dao}");
return Ok(());
}
let daos = self.get_daos().await?;
@@ -1171,15 +1204,6 @@ impl Drk {
Ok(())
}
/// Retrieve DAO for provided ID and print its metadata.
async fn dao_list_single(&self, dao_id: u64) -> Result<()> {
let dao = self.get_dao_by_id(dao_id).await?;
println!("{dao}");
Ok(())
}
/// Fetch known unspent balances from the wallet for the given DAO ID
pub async fn dao_balance(&self, dao_id: u64) -> Result<HashMap<String, u64>> {
// TODO: instead of getting all DAOs and filtering in rust,

View File

@@ -1075,7 +1075,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
if let Err(e) = drk.import_dao(dao_name, dao_params).await {
if let Err(e) = drk.import_dao(&dao_name, dao_params).await {
eprintln!("Failed to import DAO: {e:?}");
exit(2);
}
@@ -1085,13 +1085,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
DaoSubcmd::List { dao_alias } => {
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
// We cannot use .map() since get_dao_id() uses ?
let dao_id = match dao_alias {
Some(alias) => Some(drk.get_dao_id(&alias).await?),
None => None,
};
if let Err(e) = drk.dao_list(dao_id).await {
if let Err(e) = drk.dao_list(&dao_alias).await {
eprintln!("Failed to list DAO: {e:?}");
exit(2);
}

View File

@@ -61,17 +61,17 @@ of the guide can be added for future regressions.
| 16 | OTC initialization | otc init -v {AMOUNT}:{AMOUNT} -t {ALIAS}:{ALIAS} | Pass |
| 17 | OTC join | otc join | Pass |
| 18 | OTC sign | otc sign | Pass |
| 19 | DAO create | dao create {LIMIT} {QUORUM} {RATIO} {TOKEN} | Failure: needs #12 |
| 20 | DAO view | dao view | Failure: needs #18 |
| 21 | DAO import | dao import | Failure: needs #18 |
| 22 | DAO list | dao sign | Failure: needs #18 |
| 23 | DAO mint | dao mint {DAO} | Failure: needs #18 |
| 24 | DAO balance | dao balance {DAO} | Failure: needs #18 |
| 25 | DAO propose | dao propose {DAO} {ADDR} {AMOUNT} {TOKEN} | Failure: needs #18 |
| 26 | DAO proposals retrieval | dao proposals {DAO} | Failure: needs #24 |
| 27 | DAO proposal retrieval | dao proposal {DAO} {PROPOSAL_ID} | Failure: needs #24 |
| 28 | DAO vote | dao vote {DAO} {PROPOSAL_ID} {VOTE} {WEIGHT} | Failure: needs #24 |
| 29 | DAO proposal execution | dao exec {DAO} {PROPOSAL_ID} | Failure: needs #27 |
| 19 | DAO create | dao create {LIMIT} {QUORUM} {RATIO} {TOKEN} | Pass |
| 20 | DAO view | dao view | Pass |
| 21 | DAO import | dao import | Pass |
| 22 | DAO list | dao list | Pass |
| 23 | DAO mint | dao mint {DAO} | Failure: needs #19 |
| 24 | DAO balance | dao balance {DAO} | Failure: needs #19 |
| 25 | DAO propose | dao propose {DAO} {ADDR} {AMOUNT} {TOKEN} | Failure: needs #19 |
| 26 | DAO proposals retrieval | dao proposals {DAO} | Failure: needs #25 |
| 27 | DAO proposal retrieval | dao proposal {DAO} {PROPOSAL_ID} | Failure: needs #25 |
| 28 | DAO vote | dao vote {DAO} {PROPOSAL_ID} {VOTE} {WEIGHT} | Failure: needs #25 |
| 29 | DAO proposal execution | dao exec {DAO} {PROPOSAL_ID} | Failure: needs #28 |
| 30 | Coins unspend | unspend {COIN} | Pass |
| 31 | Transaction inspect | inspect | Pass |
| 32 | Transaction simulate | explorer simulate-tx | Pass |