NAC_Blockchain/nac-cli/src/commands/transaction.rs

311 lines
9.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::cli::*;
use crate::client::nac_lens::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<u64>,
gas_price: Option<u64>,
) -> 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 <signed_tx>' 广播交易");
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<String> {
// 解析私钥
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"));
}
}