Kaynağa Gözat

drk: updated basic dao handling

skoupidi 2 yıl önce
ebeveyn
işleme
b89c4196a4
3 değiştirilmiş dosya ile 148 ekleme ve 130 silme
  1. 135 111
      bin/drk/src/dao.rs
  2. 2 8
      bin/drk/src/main.rs
  3. 11 11
      contrib/localnet/darkfid-single-node/README.md

+ 135 - 111
bin/drk/src/dao.rs

@@ -448,110 +448,122 @@ impl Drk {
         Ok(ret)
     }
 
-    /// 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 {
-            Ok(r) => r,
-            Err(e) => {
-                return Err(Error::RusqliteError(format!("[get_daos] DAOs retrieval failed: {e:?}")))
-            }
+    /// 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 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::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("[get_daos] Proposer limit bytes parsing failed"))
-            };
-            let proposer_limit = deserialize_async(proposer_limit_bytes).await?;
+        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("[get_daos] Quorum bytes parsing failed"))
-            };
-            let quorum = deserialize_async(quorum_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("[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_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("[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::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("[get_daos] Gov token bytes parsing failed"))
-            };
-            let gov_token_id = deserialize_async(gov_token_bytes).await?;
+        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("[get_daos] Secret key bytes parsing failed"))
-            };
-            let secret_key = deserialize_async(secret_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("[get_daos] Bulla blind bytes parsing failed"))
-            };
-            let bulla_blind = deserialize_async(bulla_blind_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 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 {
+        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 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 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 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 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,
-            };
+        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);
+        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 {
+            Ok(r) => r,
+            Err(e) => {
+                return Err(Error::RusqliteError(format!("[get_daos] DAOs retrieval failed: {e:?}")))
+            }
+        };
+
+        let mut daos = Vec::with_capacity(rows.len());
+        for row in rows {
+            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,

+ 2 - 8
bin/drk/src/main.rs

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

+ 11 - 11
contrib/localnet/darkfid-single-node/README.md

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