Эх сурвалжийг харах

drk/interactive: alias commands added

skoupidi 1 жил өмнө
parent
commit
9efb86333c

+ 180 - 35
bin/drk/src/interactive.rs

@@ -45,7 +45,7 @@ use darkfi::{
     Error,
 };
 use darkfi_dao_contract::{blockwindow, model::DaoProposalBulla, DaoFunction};
-use darkfi_money_contract::model::{Coin, CoinAttributes};
+use darkfi_money_contract::model::{Coin, CoinAttributes, TokenId};
 use darkfi_sdk::{
     crypto::{
         note::AeadEncryptedNote, BaseBlind, FuncId, FuncRef, Keypair, PublicKey, DAO_CONTRACT_ID,
@@ -101,6 +101,7 @@ fn help(output: &mut Vec<String>) {
     output.push(String::from("\tunsnooze: Enables the background subscription messages printing"));
     output.push(String::from("\tscan: Scan the blockchain and parse relevant transactions"));
     output.push(String::from("\texplorer: Explorer related subcommands"));
+    output.push(String::from("\talias: Token alias"));
 }
 
 /// Auxiliary function to define the interactive shell completions.
@@ -195,7 +196,7 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
         return
     }
 
-    if last.starts_with("a") {
+    if last.starts_with("at") {
         lc.push(prefix + "attach-fee");
         return
     }
@@ -246,7 +247,24 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
         return
     }
 
+    if last.starts_with("al") {
+        lc.push(prefix.clone() + "alias");
+        lc.push(prefix.clone() + "alias add");
+        lc.push(prefix.clone() + "alias show");
+        lc.push(prefix + "alias remove");
+        return
+    }
+
     // Now the catch alls
+    if last.starts_with("a") {
+        lc.push(prefix.clone() + "attach-fee");
+        lc.push(prefix.clone() + "alias");
+        lc.push(prefix.clone() + "alias add");
+        lc.push(prefix.clone() + "alias show");
+        lc.push(prefix + "alias remove");
+        return
+    }
+
     if last.starts_with("s") {
         lc.push(prefix.clone() + "spend");
         lc.push(prefix.clone() + "subscribe");
@@ -268,32 +286,38 @@ fn hints(buffer: &str) -> Option<(String, i32, bool)> {
     // Split commands so we always process the last one
     let commands: Vec<&str> = buffer.split('|').collect();
     let last = commands.last().unwrap().trim_start();
+    let color = 35; // magenta
+    let bold = false;
     match last {
-        "completions " => Some(("<shell>".to_string(), 35, false)), // 35 = magenta
-        "wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
-        "wallet default-address " => Some(("<index>".to_string(), 35, false)),
-        "unspend " => Some(("<coin>".to_string(), 35, false)),
-        "transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), 35, false)),
-        "otc " => Some(("(init|join|inspect|sign)".to_string(), 35, false)),
-        "otc init " => Some(("<value_pair> <token_pair>".to_string(), 35, false)),
-        "dao " => Some(("(create|view|import|update-keys|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook)".to_string(), 35, false)),
-        "dao create " => Some(("<proposer-limit> <quorum> <early-exec-quorum> <approval-ratio> <gov-token-id>".to_string(), 35, false)),
-        "dao import " => Some(("<name>".to_string(), 35, false)),
-        "dao list " => Some(("[name]".to_string(), 35, false)),
-        "dao balance " => Some(("<name>".to_string(), 35, false)),
-        "dao mint " => Some(("<name>".to_string(), 35, false)),
-        "dao propose-transfer " => Some(("<name> <duration> <amount> <token> <recipient> [spend-hook] [user-data]".to_string(), 35, false)),
-        "dao propose-generic" => Some(("<name> <duration> [user-data]".to_string(), 35, false)),
-        "dao proposals " => Some(("<name>".to_string(), 35, false)),
-        "dao proposal " => Some(("[--(export|mint-proposal)] <bulla>".to_string(), 35, false)),
-        "dao vote " => Some(("<bulla> <vote> [vote-weight]".to_string(), 35, false)),
-        "dao exec " => Some(("[--early] <bulla>".to_string(), 35, false)),
-        "scan " => Some(("[--reset]".to_string(), 35, false)),
-        "scan --reset " => Some(("<height>".to_string(), 35, false)),
-        "explorer " => Some(("(fetch-tx|simulate-tx|txs-history|clear-reverted|scanned-blocks)".to_string(), 35, false)),
-        "explorer fetch-tx " => Some(("[--encode] <tx-hash>".to_string(), 35, false)),
-        "explorer txs-history " => Some(("[--encode] [tx-hash]".to_string(), 35, false)),
-        "explorer scanned-blocks " => Some(("[height]".to_string(), 35, false)),
+        "completions " => Some(("<shell>".to_string(), color, bold)),
+        "wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), color, bold)),
+        "wallet default-address " => Some(("<index>".to_string(), color, bold)),
+        "unspend " => Some(("<coin>".to_string(), color, bold)),
+        "transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), color, bold)),
+        "otc " => Some(("(init|join|inspect|sign)".to_string(), color, bold)),
+        "otc init " => Some(("<value_pair> <token_pair>".to_string(), color, bold)),
+        "dao " => Some(("(create|view|import|update-keys|list|balance|mint|propose-transfer|propose-generic|proposals|proposal|proposal-import|vote|exec|spend-hook)".to_string(), color, bold)),
+        "dao create " => Some(("<proposer-limit> <quorum> <early-exec-quorum> <approval-ratio> <gov-token-id>".to_string(), color, bold)),
+        "dao import " => Some(("<name>".to_string(), color, bold)),
+        "dao list " => Some(("[name]".to_string(), color, bold)),
+        "dao balance " => Some(("<name>".to_string(), color, bold)),
+        "dao mint " => Some(("<name>".to_string(), color, bold)),
+        "dao propose-transfer " => Some(("<name> <duration> <amount> <token> <recipient> [spend-hook] [user-data]".to_string(), color, bold)),
+        "dao propose-generic" => Some(("<name> <duration> [user-data]".to_string(), color, bold)),
+        "dao proposals " => Some(("<name>".to_string(), color, bold)),
+        "dao proposal " => Some(("[--(export|mint-proposal)] <bulla>".to_string(), color, bold)),
+        "dao vote " => Some(("<bulla> <vote> [vote-weight]".to_string(), color, bold)),
+        "dao exec " => Some(("[--early] <bulla>".to_string(), color, bold)),
+        "scan " => Some(("[--reset]".to_string(), color, bold)),
+        "scan --reset " => Some(("<height>".to_string(), color, bold)),
+        "explorer " => Some(("(fetch-tx|simulate-tx|txs-history|clear-reverted|scanned-blocks)".to_string(), color, bold)),
+        "explorer fetch-tx " => Some(("[--encode] <tx-hash>".to_string(), color, bold)),
+        "explorer txs-history " => Some(("[--encode] [tx-hash]".to_string(), color, bold)),
+        "explorer scanned-blocks " => Some(("[height]".to_string(), color, bold)),
+        "alias " => Some(("(add|show|remove)".to_string(), color, bold)),
+        "alias add " => Some(("<alias> <token>".to_string(), color, bold)),
+        "alias show " => Some(("[-a, --alias <alias>] [-t, --token <token>]".to_string(), color, bold)),
+        "alias remove " => Some(("<alias>".to_string(), color, bold)),
         _ => None,
     }
 }
@@ -480,6 +504,7 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
                     .await
                 }
                 "explorer" => handle_explorer(drk, &parts, &input, &mut output).await,
+                "alias" => handle_alias(drk, &parts, &mut output).await,
                 _ => output.push(format!("Unreconized command: {}", parts[0])),
             }
 
@@ -695,7 +720,7 @@ async fn handle_wallet_initialize(drk: &DrkPtr, output: &mut Vec<String>) {
         output.push(format!("Error initializing wallet: {e:?}"));
         return
     }
-    if let Err(e) = lock.initialize_money().await {
+    if let Err(e) = lock.initialize_money(output).await {
         output.push(format!("Failed to initialize Money: {e:?}"));
         return
     }
@@ -1528,7 +1553,7 @@ async fn handle_dao_update_keys(
 /// Auxiliary function to define the dao list subcommand handling.
 async fn handle_dao_list(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 2 || parts.len() != 3 {
+    if parts.len() != 2 && parts.len() != 3 {
         output.push(String::from("Malformed `dao list` subcommand"));
         output.push(String::from("Usage: dao list [name]"));
         return
@@ -1699,7 +1724,7 @@ async fn handle_dao_propose_transfer(drk: &DrkPtr, parts: &[&str], output: &mut
 /// Auxiliary function to define the dao propose generic subcommand handling.
 async fn handle_dao_propose_generic(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 4 || parts.len() != 5 {
+    if parts.len() != 4 && parts.len() != 5 {
         output.push(String::from("Malformed `dao proposal-generic` subcommand"));
         output.push(String::from("Usage: dao proposal-generic <name> <duration> [user-data]"));
         return
@@ -1771,7 +1796,7 @@ async fn handle_dao_proposals(drk: &DrkPtr, parts: &[&str], output: &mut Vec<Str
 /// Auxiliary function to define the dao proposal subcommand handling.
 async fn handle_dao_proposal(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 3 || parts.len() != 4 {
+    if parts.len() != 3 && parts.len() != 4 {
         output.push(String::from("Malformed `dao proposal` subcommand"));
         output.push(String::from("Usage: dao proposal [--(export|mint-proposal)] <bulla>"));
         return
@@ -2102,7 +2127,7 @@ async fn handle_dao_proposal_import(
 /// Auxiliary function to define the dao vote subcommand handling.
 async fn handle_dao_vote(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 4 || parts.len() != 5 {
+    if parts.len() != 4 && parts.len() != 5 {
         output.push(String::from("Malformed `dao vote` subcommand"));
         output.push(String::from("Usage: dao vote <bulla> <vote> [vote-weight]"));
         return
@@ -2155,7 +2180,7 @@ async fn handle_dao_vote(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>)
 /// Auxiliary function to define the dao exec subcommand handling.
 async fn handle_dao_exec(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 3 || parts.len() != 4 {
+    if parts.len() != 3 && parts.len() != 4 {
         output.push(String::from("Malformed `dao exec` subcommand"));
         output.push(String::from("Usage: dao exec [--early] <bulla>"));
         return
@@ -2415,7 +2440,7 @@ async fn handle_explorer(drk: &DrkPtr, parts: &[&str], input: &[String], output:
 /// Auxiliary function to define the explorer fetch transaction subcommand handling.
 async fn handle_explorer_fetch_tx(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 3 || parts.len() != 4 {
+    if parts.len() != 3 && parts.len() != 4 {
         output.push(String::from("Malformed `explorer fetch-tx` subcommand"));
         output.push(String::from("Usage: explorer fetch-tx [--encode] <tx-hash>"));
         return
@@ -2582,7 +2607,7 @@ async fn handle_explorer_clear_reverted(drk: &DrkPtr, parts: &[&str], output: &m
 /// Auxiliary function to define the explorer scanned blocks subcommand handling.
 async fn handle_explorer_scanned_blocks(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
     // Check correct subcommand structure
-    if parts.len() != 2 || parts.len() != 3 {
+    if parts.len() != 2 && parts.len() != 3 {
         output.push(String::from("Malformed `explorer scanned-blocks` subcommand"));
         output.push(String::from("Usage: explorer scanned-blocks [height]"));
         return
@@ -2630,3 +2655,123 @@ async fn handle_explorer_scanned_blocks(drk: &DrkPtr, parts: &[&str], output: &m
         output.push(format!("{table}"));
     }
 }
+
+/// Auxiliary function to define the alias command handling.
+async fn handle_alias(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct command structure
+    if parts.len() < 2 {
+        output.push(String::from("Malformed `alias` command"));
+        output.push(String::from("Usage: alias (add|show|remove)"));
+        return
+    }
+
+    // Handle subcommand
+    match parts[1] {
+        "add" => handle_alias_add(drk, parts, output).await,
+        "show" => handle_alias_show(drk, parts, output).await,
+        "remove" => handle_alias_remove(drk, parts, output).await,
+        _ => {
+            output.push(format!("Unreconized alias subcommand: {}", parts[1]));
+            output.push(String::from("Usage: alias (add|show|remove)"));
+        }
+    }
+}
+
+/// Auxiliary function to define the alias add subcommand handling.
+async fn handle_alias_add(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 4 {
+        output.push(String::from("Malformed `alias add` subcommand"));
+        output.push(String::from("Usage: alias add <alias> <token>"));
+        return
+    }
+
+    if parts[2].len() > 5 {
+        output.push(String::from("Error: Alias exceeds 5 characters"));
+        return
+    }
+
+    let token_id = match TokenId::from_str(parts[3]) {
+        Ok(t) => t,
+        Err(e) => {
+            output.push(format!("Invalid Token ID: {e:?}"));
+            return
+        }
+    };
+
+    if let Err(e) = drk.read().await.add_alias(String::from(parts[2]), token_id, output).await {
+        output.push(format!("Failed to add alias: {e}"));
+    }
+}
+
+/// Auxiliary function to define the alias show subcommand handling.
+async fn handle_alias_show(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct command structure
+    if parts.len() != 2 && parts.len() != 4 && parts.len() != 6 {
+        output.push(String::from("Malformed `alias show` command"));
+        output.push(String::from("Usage: alias show [-a, --alias <alias>] [-t, --token <token>]"));
+        return
+    }
+
+    let mut alias = None;
+    let mut token_id = None;
+    if parts.len() > 2 {
+        let mut index = 2;
+        if parts[index] == "-a" || parts[index] == "--alias" {
+            alias = Some(String::from(parts[index + 1]));
+            index += 2;
+        }
+
+        if index < parts.len() && (parts[index] == "-t" || parts[index] == "--token") {
+            match TokenId::from_str(parts[index + 1]) {
+                Ok(t) => token_id = Some(t),
+                Err(e) => {
+                    output.push(format!("Invalid Token ID: {e:?}"));
+                    return
+                }
+            };
+            index += 2;
+        }
+
+        // Check alias again in case it was after token
+        if index < parts.len() && (parts[index] == "-a" || parts[index] == "--alias") {
+            alias = Some(String::from(parts[index + 1]));
+        }
+    }
+
+    let map = match drk.read().await.get_aliases(alias, token_id).await {
+        Ok(m) => m,
+        Err(e) => {
+            output.push(format!("Failed to fetch aliases map: {e:?}"));
+            return
+        }
+    };
+
+    // Create a prettytable with the new data:
+    let mut table = Table::new();
+    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+    table.set_titles(row!["Alias", "Token ID"]);
+    for (alias, token_id) in map.iter() {
+        table.add_row(row![alias, token_id]);
+    }
+
+    if table.is_empty() {
+        output.push(String::from("No aliases found"));
+    } else {
+        output.push(format!("{table}"));
+    }
+}
+
+/// Auxiliary function to define the alias remove subcommand handling.
+async fn handle_alias_remove(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 3 {
+        output.push(String::from("Malformed `alias remove` subcommand"));
+        output.push(String::from("Usage: alias remove <alias>"));
+        return
+    }
+
+    if let Err(e) = drk.read().await.remove_alias(String::from(parts[2]), output).await {
+        output.push(format!("Failed to remove alias: {e}"));
+    }
+}

+ 14 - 5
bin/drk/src/main.rs

@@ -715,10 +715,13 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                         eprintln!("Error initializing wallet: {e:?}");
                         exit(2);
                     }
-                    if let Err(e) = drk.initialize_money().await {
+                    let mut output = vec![];
+                    if let Err(e) = drk.initialize_money(&mut output).await {
+                        print_output(&output);
                         eprintln!("Failed to initialize Money: {e:?}");
                         exit(2);
                     }
+                    print_output(&output);
                     if let Err(e) = drk.initialize_dao().await {
                         eprintln!("Failed to initialize DAO: {e:?}");
                         exit(2);
@@ -2258,10 +2261,13 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                     args.fun,
                 )
                 .await;
-                if let Err(e) = drk.add_alias(alias, token_id).await {
-                    eprintln!("Failed to add alias: {e:?}");
+                let mut output = vec![];
+                if let Err(e) = drk.add_alias(alias, token_id, &mut output).await {
+                    print_output(&output);
+                    eprintln!("Failed to add alias: {e}");
                     exit(2);
                 }
+                print_output(&output);
 
                 Ok(())
             }
@@ -2316,10 +2322,13 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                     args.fun,
                 )
                 .await;
-                if let Err(e) = drk.remove_alias(alias).await {
-                    eprintln!("Failed to remove alias: {e:?}");
+                let mut output = vec![];
+                if let Err(e) = drk.remove_alias(alias, &mut output).await {
+                    print_output(&output);
+                    eprintln!("Failed to remove alias: {e}");
                     exit(2);
                 }
+                print_output(&output);
 
                 Ok(())
             }

+ 15 - 6
bin/drk/src/money.rs

@@ -120,13 +120,13 @@ pub const BALANCE_BASE10_DECIMALS: usize = 8;
 
 impl Drk {
     /// Initialize wallet with tables for the Money contract.
-    pub async fn initialize_money(&self) -> WalletDbResult<()> {
+    pub async fn initialize_money(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
         // Initialize Money wallet schema
         let wallet_schema = include_str!("../money.sql");
         self.wallet.exec_batch_sql(wallet_schema)?;
 
         // Insert DRK alias
-        self.add_alias("DRK".to_string(), *DARK_TOKEN_ID).await?;
+        self.add_alias("DRK".to_string(), *DARK_TOKEN_ID, output).await?;
 
         Ok(())
     }
@@ -573,8 +573,13 @@ impl Drk {
     }
 
     /// Create an alias record for provided Token ID.
-    pub async fn add_alias(&self, alias: String, token_id: TokenId) -> WalletDbResult<()> {
-        println!("Generating alias {alias} for Token: {token_id}");
+    pub async fn add_alias(
+        &self,
+        alias: String,
+        token_id: TokenId,
+        output: &mut Vec<String>,
+    ) -> WalletDbResult<()> {
+        output.push(format!("Generating alias {alias} for Token: {token_id}"));
         let query = format!(
             "INSERT OR REPLACE INTO {} ({}, {}) VALUES (?1, ?2);",
             *MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
@@ -644,8 +649,12 @@ impl Drk {
     }
 
     /// Remove provided alias record from the wallet database.
-    pub async fn remove_alias(&self, alias: String) -> WalletDbResult<()> {
-        println!("Removing alias: {alias}");
+    pub async fn remove_alias(
+        &self,
+        alias: String,
+        output: &mut Vec<String>,
+    ) -> WalletDbResult<()> {
+        output.push(format!("Removing alias: {alias}"));
         let query = format!(
             "DELETE FROM {} WHERE {} = ?1;",
             *MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS,