Explorar o código

drk: add more info in inspect output

x hai 4 días
pai
achega
968225cd1e
Modificáronse 4 ficheiros con 210 adicións e 10 borrados
  1. 169 6
      bin/drk/src/common.rs
  2. 6 3
      bin/drk/src/interactive.rs
  3. 12 1
      bin/drk/src/main.rs
  4. 23 0
      bin/drk/src/money.rs

+ 169 - 6
bin/drk/src/common.rs

@@ -19,7 +19,15 @@
 use std::collections::HashMap;
 
 use darkfi::{tx::Transaction, util::parse::encode_base10, zk::halo2::Field};
-use darkfi_money_contract::{client::OwnCoin, model::TokenId};
+use darkfi_money_contract::{
+    client::OwnCoin,
+    model::{
+        Coin, MoneyAuthTokenFreezeParamsV1, MoneyAuthTokenMintParamsV1, MoneyBurnParamsV1,
+        MoneyFeeParamsV1, MoneyGenesisMintParamsV1, MoneyPoWRewardParamsV1, MoneyTokenMintParamsV1,
+        MoneyTransferParamsV1, TokenId,
+    },
+    MoneyFunction,
+};
 use darkfi_sdk::{
     crypto::{
         keypair::{Address, Network, PublicKey, SecretKey, StandardAddress},
@@ -30,7 +38,7 @@ use darkfi_sdk::{
 use darkfi_serial::{deserialize, serialize};
 use prettytable::{format, row, Table};
 
-use crate::money::BALANCE_BASE10_DECIMALS;
+use crate::{money::BALANCE_BASE10_DECIMALS, Drk};
 
 pub fn prettytable_addrs(
     network: Network,
@@ -219,7 +227,129 @@ pub fn prettytable_scanned_blocks(scanned_blocks: &[(u32, String, String)]) -> T
     table
 }
 
-pub fn pretty_tx(tx: &Transaction) -> String {
+/// Parse money call data and return formatted inputs/outputs string
+async fn format_money_call_data(calldata: &[u8], drk: &Drk) -> String {
+    if calldata.is_empty() {
+        return "-".to_string();
+    }
+
+    let nullifier_map = drk.get_coins_by_nullifier().await.unwrap_or_default();
+
+    let coin_display = |tx_local: bool, nullifier: &[u8; 32]| -> String {
+        if tx_local {
+            return "tx-local".to_string();
+        }
+        nullifier_map.get(nullifier).map(|c| c.to_string()).unwrap_or_else(|| "unknown".to_string())
+    };
+
+    let output_display = |tx_local: bool, coin: &Coin| -> String {
+        if tx_local {
+            "tx-local".to_string()
+        } else {
+            coin.to_string()
+        }
+    };
+
+    let func_code = calldata[0];
+    match MoneyFunction::try_from(func_code) {
+        Ok(MoneyFunction::TransferV1) => {
+            let Some(params) = deserialize::<MoneyTransferParamsV1>(&calldata[1..]).ok() else {
+                return "Failed to parse TransferV1 data".to_string()
+            };
+            let mut parts = vec![];
+            if !params.inputs.is_empty() {
+                parts.push("Inputs:".to_string());
+                parts.extend(params.inputs.iter().enumerate().map(|(i, input)| {
+                    format!(
+                        "  [{}] {}",
+                        i,
+                        coin_display(input.tx_local, &input.nullifier.to_bytes())
+                    )
+                }));
+            }
+            if !params.outputs.is_empty() {
+                parts.push("Outputs:".to_string());
+                parts.extend(params.outputs.iter().enumerate().map(|(i, output)| {
+                    format!("  [{}] {}", i, output_display(output.tx_local, &output.coin))
+                }));
+            }
+            parts.join("\n")
+        }
+        Ok(MoneyFunction::FeeV1) => {
+            if calldata.len() <= 9 {
+                return "Invalid FeeV1 data length".to_string()
+            }
+            let Some(params) = deserialize::<MoneyFeeParamsV1>(&calldata[9..]).ok() else {
+                return "Failed to parse FeeV1 data".to_string()
+            };
+            format!(
+                "Input: {}\nOutput: {}",
+                coin_display(params.input.tx_local, &params.input.nullifier.to_bytes()),
+                output_display(params.output.tx_local, &params.output.coin)
+            )
+        }
+        Ok(MoneyFunction::BurnV1) => {
+            let Some(params) = deserialize::<MoneyBurnParamsV1>(&calldata[1..]).ok() else {
+                return "Failed to parse BurnV1 data".to_string()
+            };
+            let mut parts = vec!["Inputs:".to_string()];
+            parts.extend(params.inputs.iter().enumerate().map(|(i, input)| {
+                format!("  [{}] {}", i, coin_display(input.tx_local, &input.nullifier.to_bytes()))
+            }));
+            parts.join("\n")
+        }
+        Ok(MoneyFunction::GenesisMintV1) => {
+            let Some(params) = deserialize::<MoneyGenesisMintParamsV1>(&calldata[1..]).ok() else {
+                return "Failed to parse GenesisMintV1 data".to_string()
+            };
+            let mut parts = vec![format!(
+                "Input: Value: {}, Token: {}",
+                params.input.value, params.input.token_id
+            )];
+            if !params.outputs.is_empty() {
+                parts.push("Outputs:".to_string());
+                parts.extend(params.outputs.iter().enumerate().map(|(i, output)| {
+                    format!("  [{}] Coin: {}", i, output_display(output.tx_local, &output.coin))
+                }));
+            }
+            parts.join("\n")
+        }
+        Ok(MoneyFunction::PoWRewardV1) => {
+            let Some(params) = deserialize::<MoneyPoWRewardParamsV1>(&calldata[1..]).ok() else {
+                return "Failed to parse PoWRewardV1 data".to_string()
+            };
+            format!(
+                "Input: Value: {}, Token: {}\nOutput: Coin: {}",
+                params.input.value,
+                params.input.token_id,
+                output_display(params.output.tx_local, &params.output.coin)
+            )
+        }
+        Ok(MoneyFunction::TokenMintV1) => {
+            let Some(params) = deserialize::<MoneyTokenMintParamsV1>(&calldata[1..]).ok() else {
+                return "Failed to parse TokenMintV1 data".to_string()
+            };
+            format!("Coin: {}", params.coin)
+        }
+        Ok(MoneyFunction::AuthTokenMintV1) => {
+            let Some(params) = deserialize::<MoneyAuthTokenMintParamsV1>(&calldata[1..]).ok()
+            else {
+                return "Failed to parse AuthTokenMintV1 data".to_string()
+            };
+            format!("Token ID: {}, Mint Pubkey: {}", params.token_id, params.mint_pubkey)
+        }
+        Ok(MoneyFunction::AuthTokenFreezeV1) => {
+            let Some(params) = deserialize::<MoneyAuthTokenFreezeParamsV1>(&calldata[1..]).ok()
+            else {
+                return "Failed to parse AuthTokenFreezeV1 data".to_string()
+            };
+            format!("Token ID: {}, Mint Public: {}", params.token_id, params.mint_public)
+        }
+        Err(_) => format!("Invalid function code: {func_code}"),
+    }
+}
+
+pub async fn pretty_tx(tx: &Transaction, drk: &Drk) -> String {
     let hash = tx.hash().to_string();
 
     let mut fees: Vec<String> = vec![];
@@ -228,7 +358,7 @@ pub fn pretty_tx(tx: &Transaction) -> String {
 
     let mut table = Table::new();
     table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-    table.add_row(row!["", "Contract", "Function"]);
+    table.set_titles(row!["", "Contract", "Function", "Details"]);
 
     for (i, call) in tx.calls.iter().enumerate() {
         if call.data.is_money_fee() {
@@ -251,11 +381,44 @@ pub fn pretty_tx(tx: &Transaction) -> String {
         };
 
         let calldata = &call.data.data;
+
+        let fn_name = match (call.data.contract_id, calldata[0]) {
+            (id, 0x00) if id == *MONEY_CONTRACT_ID => "Fee",
+            (id, 0x01) if id == *MONEY_CONTRACT_ID => "GenesisMint",
+            (id, 0x02) if id == *MONEY_CONTRACT_ID => "PoWReward",
+            (id, 0x03) if id == *MONEY_CONTRACT_ID => "Transfer",
+            (id, 0x04) if id == *MONEY_CONTRACT_ID => "AuthTokenMint",
+            (id, 0x05) if id == *MONEY_CONTRACT_ID => "AuthTokenFreeze",
+            (id, 0x06) if id == *MONEY_CONTRACT_ID => "TokenMint",
+            (id, 0x07) if id == *MONEY_CONTRACT_ID => "Burn",
+            (id, 0x00) if id == *DAO_CONTRACT_ID => "Mint",
+            (id, 0x01) if id == *DAO_CONTRACT_ID => "Propose",
+            (id, 0x02) if id == *DAO_CONTRACT_ID => "Vote",
+            (id, 0x03) if id == *DAO_CONTRACT_ID => "Exec",
+            (id, 0x04) if id == *DAO_CONTRACT_ID => "AuthMoneyTransfer",
+            (id, 0x00) if id == *DEPLOYOOOR_CONTRACT_ID => "Deploy",
+            (id, 0x01) if id == *DEPLOYOOOR_CONTRACT_ID => "Lock",
+            _ => "",
+        };
+        let fn_name = Some(fn_name).filter(|x| !x.is_empty());
+
+        let details = if call.data.contract_id == *MONEY_CONTRACT_ID {
+            format_money_call_data(calldata, drk).await
+        } else {
+            "-".to_string()
+        };
+
         table.add_row(row![
             i.to_string(),
             format!("{} [{}]", call.data.contract_id.to_string(), contract_name),
-            // Function code
-            if !calldata.is_empty() { calldata[0].to_string() } else { "-".to_string() },
+            if !calldata.is_empty() {
+                fn_name
+                    .map(|n| format!("{} [{}]", calldata[0], n))
+                    .unwrap_or_else(|| calldata[0].to_string())
+            } else {
+                "-".to_string()
+            },
+            details,
         ]);
     }
 

+ 6 - 3
bin/drk/src/interactive.rs

@@ -548,7 +548,7 @@ pub async fn interactive(
                 "dao" => handle_dao(drk, &parts, &input, &mut output).await,
                 "attach-fee" => handle_attach_fee(drk, &input, &mut output).await,
                 "tx-from-calls" => handle_tx_from_calls(drk, &parts, &input, &mut output).await,
-                "inspect" => handle_inspect(&input, &mut output).await,
+                "inspect" => handle_inspect(drk, &input, &mut output).await,
                 "broadcast" => handle_broadcast(drk, &input, &mut output).await,
                 "subscribe" => {
                     handle_subscribe(
@@ -2445,9 +2445,12 @@ async fn handle_tx_from_calls(
 }
 
 /// Auxiliary function to define the inspect command handling.
-async fn handle_inspect(input: &[String], output: &mut Vec<String>) {
+async fn handle_inspect(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
     match parse_tx_from_input(input).await {
-        Ok(tx) => output.push(pretty_tx(&tx)),
+        Ok(tx) => {
+            let lock = drk.read().await;
+            output.push(pretty_tx(&tx, &lock).await);
+        }
         Err(e) => output.push(format!("Error while parsing transaction: {e}")),
     }
 }

+ 12 - 1
bin/drk/src/main.rs

@@ -2045,7 +2045,18 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
         Subcmd::Inspect => {
             let tx = parse_tx_from_stdin().await?;
 
-            println!("{}", pretty_tx(&tx));
+            let drk = new_wallet(
+                network,
+                blockchain_config.cache_path,
+                blockchain_config.wallet_path,
+                blockchain_config.wallet_pass,
+                None,
+                &ex,
+                args.fun,
+            )
+            .await;
+
+            println!("{}", pretty_tx(&tx, &drk).await);
 
             Ok(())
         }

+ 23 - 0
bin/drk/src/money.rs

@@ -521,6 +521,29 @@ impl Drk {
         Ok(owncoins)
     }
 
+    /// Get a mapping of nullifiers to coins for all coins in the wallet.
+    pub async fn get_coins_by_nullifier(&self) -> Result<HashMap<[u8; 32], Coin>> {
+        let query = self.wallet.query_multiple(&MONEY_COINS_TABLE, &[], vec![]).await;
+
+        let rows = match query {
+            Ok(r) => r,
+            Err(e) => {
+                return Err(Error::DatabaseError(format!(
+                    "[get_coins_by_nullifier] Coins retrieval failed: {e}"
+                )))
+            }
+        };
+
+        let mut nullifier_map = HashMap::new();
+        for row in rows {
+            let (owncoin, _, _, _, _) = self.parse_coin_record(&row).await?;
+            let nullifier = owncoin.nullifier().to_bytes();
+            nullifier_map.insert(nullifier, owncoin.coin);
+        }
+
+        Ok(nullifier_map)
+    }
+
     /// Auxiliary function to parse a `MONEY_COINS_TABLE` record.
     /// The boolean in the returned tuple notes if the coin was marked
     /// as spent, along with the height and tx it was spent in.