use crate::cli::*; use crate::client::nrpc::NrpcClient; use crate::config::Config; use crate::error::{CliError, Result}; use crate::utils::*; use colored::Colorize; use dialoguer::{Password, Confirm}; use serde_json::json; use secp256k1::{Secp256k1, Message, SecretKey}; use sha3::{Digest, Sha3_384}; use std::fs; pub async fn execute(cmd: &TransactionCommands, _cli: &Cli) -> Result<()> { match cmd { TransactionCommands::Send { from, to, amount, gas_limit, gas_price } => { send_transaction(from, to, amount, *gas_limit, *gas_price).await } TransactionCommands::Show { tx_hash } => { show_transaction(tx_hash).await } TransactionCommands::List { address, limit } => { list_transactions(address, *limit).await } TransactionCommands::Sign { tx_file, private_key } => { sign_transaction_file(tx_file, private_key).await } TransactionCommands::Broadcast { signed_tx } => { broadcast_transaction(signed_tx).await } } } /// 发送交易 async fn send_transaction( from: &str, to: &str, amount: &str, gas_limit: Option, gas_price: Option, ) -> Result<()> { // 验证地址格式 validate_address(from)?; validate_address(to)?; // 解析金额 let value: u128 = amount.parse() .map_err(|_| CliError::InvalidInput(format!("无效的金额: {}", amount)))?; print_info("准备发送交易..."); println!(); println!("{:12} {}", "从:".bold(), from.green()); println!("{:12} {}", "到:".bold(), to.green()); println!("{:12} {} NAC", "金额:".bold(), amount.cyan()); println!(); // 确认 let confirmed = Confirm::new() .with_prompt("确认发送交易?") .default(true) .interact() .map_err(|e| CliError::Io(format!("读取确认失败: {}", e)))?; if !confirmed { print_info("已取消交易"); return Ok(()); } // 获取密码 let password = Password::new() .with_prompt("请输入账户密码") .interact() .map_err(|e| CliError::Io(format!("读取密码失败: {}", e)))?; // 从keystore导出私钥 let manager = KeystoreManager::default()?; let private_key = manager.export(from, &password)?; // 加载配置 let config = Config::load() .map_err(|_| CliError::Config("未找到配置文件,请先运行 'nac config init'".to_string()))?; // 创建RPC客户端 let client = NrpcClient::new(config.get_current_rpc_url()); // 获取nonce print_info("获取账户nonce..."); let nonce = client.get_nonce(from).await?; // 构建交易 let gas_limit = gas_limit.unwrap_or(21000); let gas_price = gas_price.unwrap_or(1000000000); let tx = json!({ "from": from, "to": to, "value": value.to_string(), "data": "0x", "nonce": nonce, "gas_limit": gas_limit, "gas_price": gas_price, }); // 签名交易 print_info("签名交易..."); let signed_tx = sign_tx(&tx, &private_key)?; // 发送交易 print_info("发送交易到网络..."); let tx_hash = client.send_transaction(&signed_tx).await?; print_success("交易已发送!"); println!(); println!("{}", "交易哈希:".bold()); println!(" {}", tx_hash.cyan()); println!(); println!("提示: 使用 'nac tx show {}' 查询交易状态", tx_hash); println!(); Ok(()) } /// 显示交易详情 async fn show_transaction(tx_hash: &str) -> Result<()> { // 验证哈希格式 validate_hash(tx_hash)?; // 加载配置 let config = Config::load() .map_err(|_| CliError::Config("未找到配置文件,请先运行 'nac config init'".to_string()))?; // 创建RPC客户端 let client = NrpcClient::new(config.get_current_rpc_url()); print_info(&format!("正在查询交易 {}...", tx_hash)); // 查询交易 let tx = client.get_transaction(tx_hash).await?; println!(); println!("{}", "交易详情".bold()); println!("{}", "=".repeat(80)); println!(); println!("{}", serde_json::to_string_pretty(&tx).unwrap_or_default()); println!(); // 查询收据 print_info("查询交易收据..."); match client.get_transaction_receipt(tx_hash).await { Ok(receipt) => { println!(); println!("{}", "交易收据".bold()); println!("{}", "=".repeat(80)); println!(); println!("{}", serde_json::to_string_pretty(&receipt).unwrap_or_default()); println!(); // 如果有宪法收据,也显示 if let Some(cr_id) = receipt.get("constitutional_receipt_id") { if let Some(cr_id_str) = cr_id.as_str() { print_info("查询宪法收据..."); match client.get_constitutional_receipt(cr_id_str).await { Ok(cr) => { println!(); println!("{}", "宪法收据 (Constitutional Receipt)".bold()); println!("{}", "=".repeat(80)); println!(); println!("{}", serde_json::to_string_pretty(&cr).unwrap_or_default()); println!(); } Err(e) => { print_warning(&format!("查询宪法收据失败: {}", e)); } } } } } Err(e) => { print_warning(&format!("查询收据失败: {}", e)); } } Ok(()) } /// 列出交易历史 async fn list_transactions(address: &str, limit: usize) -> Result<()> { validate_address(address)?; print_info(&format!("查询地址 {} 的交易历史(最近{}笔)...", address, limit)); println!(); print_warning("此功能需要节点支持交易历史查询API"); println!(); // TODO: 实现交易历史查询 // 需要节点提供 nac_account_getTransactions 方法 Ok(()) } /// 签名交易文件 async fn sign_transaction_file(tx_file: &str, private_key: &str) -> Result<()> { // 读取交易文件 let tx_json = fs::read_to_string(tx_file) .map_err(|e| CliError::Io(format!("读取交易文件失败: {}", e)))?; let tx: serde_json::Value = serde_json::from_str(&tx_json) .map_err(|e| CliError::Json(e))?; // 签名 print_info("签名交易..."); let signed_tx = sign_tx(&tx, private_key)?; // 输出签名后的交易 println!(); println!("{}", "已签名交易:".bold()); println!("{}", signed_tx); println!(); println!("提示: 使用 'nac tx broadcast ' 广播交易"); println!(); Ok(()) } /// 广播已签名交易 async fn broadcast_transaction(signed_tx: &str) -> Result<()> { // 加载配置 let config = Config::load() .map_err(|_| CliError::Config("未找到配置文件,请先运行 'nac config init'".to_string()))?; // 创建RPC客户端 let client = NrpcClient::new(config.get_current_rpc_url()); print_info("广播交易到网络..."); let tx_hash = client.send_transaction(signed_tx).await?; print_success("交易已广播!"); println!(); println!("{}", "交易哈希:".bold()); println!(" {}", tx_hash.cyan()); println!(); Ok(()) } /// 签名交易 fn sign_tx(tx: &serde_json::Value, private_key: &str) -> Result { // 解析私钥 let secret_bytes = hex::decode(private_key) .map_err(|e| CliError::Crypto(format!("私钥格式错误: {}", e)))?; let secret_key = SecretKey::from_slice(&secret_bytes) .map_err(|e| CliError::Crypto(format!("无效的私钥: {}", e)))?; // 序列化交易数据 let tx_bytes = serde_json::to_vec(tx) .map_err(|e| CliError::Crypto(format!("序列化交易失败: {}", e)))?; // 计算交易哈希(SHA3-384) let mut hasher = Sha3_384::new(); hasher.update(&tx_bytes); let hash = hasher.finalize(); // 取前32字节用于签名(secp256k1需要32字节消息) let message_bytes = &hash[..32]; let message = Message::from_digest_slice(message_bytes) .map_err(|e| CliError::Crypto(format!("创建消息失败: {}", e)))?; // 签名 let secp = Secp256k1::new(); let signature = secp.sign_ecdsa(&message, &secret_key); // 构建签名后的交易 let mut signed_tx = tx.clone(); if let Some(obj) = signed_tx.as_object_mut() { obj.insert("signature".to_string(), json!(hex::encode(signature.serialize_compact()))); } // 返回签名后的交易(hex编码) let signed_bytes = serde_json::to_vec(&signed_tx) .map_err(|e| CliError::Crypto(format!("序列化签名交易失败: {}", e)))?; Ok(format!("0x{}", hex::encode(signed_bytes))) } #[cfg(test)] mod tests { use super::*; #[test] fn test_sign_transaction() { let tx = json!({ "from": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "to": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "value": "1000000000000000000", "data": "0x", "nonce": 0, "gas_limit": 21000, "gas_price": 1000000000, }); let private_key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; let result = sign_tx(&tx, private_key); assert!(result.is_ok()); let signed = result.unwrap(); assert!(signed.starts_with("0x")); } }