- Add CLI commands for DEX, IBC, ZK, and Compiler services - DEX: markets, orderbook, orders, liquidity pools - IBC: chains, transfers, packets, relayers - ZK: circuit compilation, proof generation (Groth16/PLONK/STARK) - Compiler: WASM compilation, ABI extraction, security scan - Add WebSocket module for real-time event streaming - Block, transaction, address, contract event channels - Market and mining event streams - Subscription management with broadcast channels - Implement API versioning strategy - URL path, header, and query parameter versioning - Version registry with deprecation support - Deprecation and sunset headers
633 lines
16 KiB
Rust
633 lines
16 KiB
Rust
//! Synor blockchain CLI.
|
|
//!
|
|
//! Command-line interface for interacting with the Synor blockchain.
|
|
|
|
#![allow(dead_code)]
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
mod client;
|
|
mod commands;
|
|
mod config;
|
|
mod output;
|
|
mod wallet;
|
|
|
|
use crate::client::RpcClient;
|
|
use crate::config::CliConfig;
|
|
|
|
/// Synor blockchain CLI.
|
|
#[derive(Parser)]
|
|
#[command(name = "synor")]
|
|
#[command(version, about = "Synor blockchain CLI", long_about = None)]
|
|
struct Cli {
|
|
/// RPC server URL
|
|
#[arg(
|
|
short,
|
|
long,
|
|
env = "SYNOR_RPC_URL",
|
|
default_value = "http://127.0.0.1:16110"
|
|
)]
|
|
rpc: String,
|
|
|
|
/// Configuration file path
|
|
#[arg(short, long)]
|
|
config: Option<PathBuf>,
|
|
|
|
/// Output format (text, json)
|
|
#[arg(short, long, default_value = "text")]
|
|
output: String,
|
|
|
|
/// Enable verbose output
|
|
#[arg(short, long)]
|
|
verbose: bool,
|
|
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
// ==================== Node Commands ====================
|
|
/// Get node information
|
|
Info,
|
|
|
|
/// Get node version
|
|
Version,
|
|
|
|
/// Get sync status
|
|
SyncStatus,
|
|
|
|
/// Get peer information
|
|
Peers,
|
|
|
|
// ==================== Block Commands ====================
|
|
/// Get block information
|
|
Block {
|
|
/// Block hash or height
|
|
id: String,
|
|
},
|
|
|
|
/// Get latest blocks
|
|
Blocks {
|
|
/// Number of blocks
|
|
#[arg(short, long, default_value = "10")]
|
|
count: usize,
|
|
},
|
|
|
|
/// Get current tips
|
|
Tips,
|
|
|
|
/// Get block count
|
|
BlockCount,
|
|
|
|
// ==================== Transaction Commands ====================
|
|
/// Get transaction information
|
|
Tx {
|
|
/// Transaction hash
|
|
hash: String,
|
|
},
|
|
|
|
/// Send transaction
|
|
Send {
|
|
/// Recipient address
|
|
to: String,
|
|
|
|
/// Amount in SYNOR
|
|
amount: String,
|
|
|
|
/// Fee in SYNOR (optional)
|
|
#[arg(short, long)]
|
|
fee: Option<String>,
|
|
},
|
|
|
|
/// Get mempool entries
|
|
Mempool {
|
|
/// Include transaction details
|
|
#[arg(short, long)]
|
|
verbose: bool,
|
|
},
|
|
|
|
// ==================== Wallet Commands ====================
|
|
/// Wallet operations
|
|
#[command(subcommand)]
|
|
Wallet(WalletCommands),
|
|
|
|
/// Get balance
|
|
Balance {
|
|
/// Address (uses wallet default if not specified)
|
|
address: Option<String>,
|
|
},
|
|
|
|
/// Get UTXOs
|
|
Utxos {
|
|
/// Address
|
|
address: String,
|
|
},
|
|
|
|
// ==================== Address Commands ====================
|
|
/// Validate an address
|
|
ValidateAddress {
|
|
/// Address to validate
|
|
address: String,
|
|
},
|
|
|
|
/// Decode an address
|
|
DecodeAddress {
|
|
/// Address to decode
|
|
address: String,
|
|
},
|
|
|
|
// ==================== Mining Commands ====================
|
|
/// Mining operations
|
|
#[command(subcommand)]
|
|
Mining(MiningCommands),
|
|
|
|
// ==================== Contract Commands ====================
|
|
/// Contract operations
|
|
#[command(subcommand)]
|
|
Contract(ContractCommands),
|
|
|
|
// ==================== Governance Commands ====================
|
|
/// Governance operations (DAO voting, treasury)
|
|
#[command(subcommand)]
|
|
Governance(GovernanceCommands),
|
|
|
|
// ==================== Hosting Commands ====================
|
|
/// Deploy web applications to Synor Hosting
|
|
#[command(subcommand)]
|
|
Deploy(DeployCommands),
|
|
|
|
// ==================== Network Commands ====================
|
|
/// Add a peer
|
|
AddPeer {
|
|
/// Peer address (host:port)
|
|
address: String,
|
|
},
|
|
|
|
/// Ban a peer
|
|
BanPeer {
|
|
/// Peer address or ID
|
|
peer: String,
|
|
},
|
|
|
|
/// Unban a peer
|
|
UnbanPeer {
|
|
/// Peer address or ID
|
|
peer: String,
|
|
},
|
|
|
|
// ==================== DEX Commands ====================
|
|
/// Decentralized exchange operations (trading, liquidity)
|
|
#[command(subcommand)]
|
|
Dex(commands::dex::DexCommands),
|
|
|
|
// ==================== IBC Commands ====================
|
|
/// Inter-Blockchain Communication (cross-chain transfers)
|
|
#[command(subcommand)]
|
|
Ibc(commands::ibc::IbcCommands),
|
|
|
|
// ==================== ZK Commands ====================
|
|
/// Zero-knowledge proof operations
|
|
#[command(subcommand)]
|
|
Zk(commands::zk::ZkCommands),
|
|
|
|
// ==================== Compiler Commands ====================
|
|
/// Smart contract compiler tools
|
|
#[command(subcommand)]
|
|
Compiler(commands::compiler::CompilerCommands),
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum WalletCommands {
|
|
/// Create a new wallet (uses Hybrid keys: Ed25519 + Dilithium)
|
|
Create {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
|
|
/// Import wallet from seed phrase
|
|
Import {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
|
|
/// Export wallet
|
|
Export {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
|
|
/// List wallets
|
|
List,
|
|
|
|
/// Get wallet info
|
|
Info {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
|
|
/// Generate new address
|
|
NewAddress {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
|
|
/// List addresses
|
|
Addresses {
|
|
/// Wallet name
|
|
#[arg(short, long, default_value = "default")]
|
|
name: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum MiningCommands {
|
|
/// Get mining info
|
|
Info,
|
|
|
|
/// Get block template
|
|
Template {
|
|
/// Coinbase address
|
|
address: String,
|
|
},
|
|
|
|
/// Submit a mined block
|
|
Submit {
|
|
/// Block hex
|
|
block: String,
|
|
},
|
|
|
|
/// Estimate network hashrate
|
|
Hashrate,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum ContractCommands {
|
|
/// Deploy a contract
|
|
Deploy {
|
|
/// Path to WASM file
|
|
wasm: PathBuf,
|
|
|
|
/// Deployer address (bech32)
|
|
#[arg(short, long)]
|
|
deployer: String,
|
|
|
|
/// Constructor arguments (hex)
|
|
#[arg(short, long)]
|
|
args: Option<String>,
|
|
|
|
/// Gas limit
|
|
#[arg(short, long, default_value = "1000000")]
|
|
gas: u64,
|
|
},
|
|
|
|
/// Call a contract method
|
|
Call {
|
|
/// Contract ID (hex)
|
|
contract_id: String,
|
|
|
|
/// Method name
|
|
method: String,
|
|
|
|
/// Caller address (bech32)
|
|
#[arg(short, long)]
|
|
caller: String,
|
|
|
|
/// Arguments (hex)
|
|
#[arg(short, long)]
|
|
args: Option<String>,
|
|
|
|
/// Value to send
|
|
#[arg(short, long, default_value = "0")]
|
|
value: u64,
|
|
|
|
/// Gas limit
|
|
#[arg(short, long, default_value = "1000000")]
|
|
gas: u64,
|
|
},
|
|
|
|
/// Get contract code
|
|
Code {
|
|
/// Contract ID (hex)
|
|
contract_id: String,
|
|
},
|
|
|
|
/// Get contract storage
|
|
Storage {
|
|
/// Contract ID (hex)
|
|
contract_id: String,
|
|
|
|
/// Storage key (hex)
|
|
key: String,
|
|
},
|
|
|
|
/// Estimate gas for a call
|
|
EstimateGas {
|
|
/// Contract ID (hex)
|
|
contract_id: String,
|
|
|
|
/// Method name
|
|
method: String,
|
|
|
|
/// Caller address (bech32)
|
|
#[arg(short, long)]
|
|
caller: String,
|
|
|
|
/// Arguments (hex)
|
|
#[arg(short, long)]
|
|
args: Option<String>,
|
|
|
|
/// Value to send
|
|
#[arg(short, long, default_value = "0")]
|
|
value: u64,
|
|
},
|
|
|
|
/// Get contract metadata
|
|
Info {
|
|
/// Contract ID (hex)
|
|
contract_id: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum GovernanceCommands {
|
|
/// Get governance info
|
|
Info,
|
|
|
|
/// Get DAO statistics
|
|
Stats,
|
|
|
|
/// List proposals
|
|
Proposals {
|
|
/// Filter by state (active, pending, passed, defeated, executed)
|
|
#[arg(short, long)]
|
|
state: Option<String>,
|
|
},
|
|
|
|
/// Get proposal details
|
|
Proposal {
|
|
/// Proposal ID (hex)
|
|
id: String,
|
|
},
|
|
|
|
/// Create a proposal
|
|
CreateProposal {
|
|
/// Proposer address (bech32)
|
|
#[arg(short, long)]
|
|
proposer: String,
|
|
|
|
/// Proposal type (treasury_spend, ecosystem_grant, parameter_change, signaling)
|
|
#[arg(short = 't', long)]
|
|
proposal_type: String,
|
|
|
|
/// Proposal title
|
|
#[arg(long)]
|
|
title: String,
|
|
|
|
/// Proposal description
|
|
#[arg(short, long)]
|
|
description: String,
|
|
|
|
/// Recipient address (for treasury/grant proposals)
|
|
#[arg(long)]
|
|
recipient: Option<String>,
|
|
|
|
/// Amount in SYNOR (for treasury/grant proposals)
|
|
#[arg(long)]
|
|
amount: Option<u64>,
|
|
|
|
/// Parameter name (for parameter_change proposals)
|
|
#[arg(long)]
|
|
parameter: Option<String>,
|
|
|
|
/// Old value (for parameter_change proposals)
|
|
#[arg(long)]
|
|
old_value: Option<String>,
|
|
|
|
/// New value (for parameter_change proposals)
|
|
#[arg(long)]
|
|
new_value: Option<String>,
|
|
},
|
|
|
|
/// Vote on a proposal
|
|
Vote {
|
|
/// Proposal ID (hex)
|
|
#[arg(short, long)]
|
|
proposal_id: String,
|
|
|
|
/// Voter address (bech32)
|
|
#[arg(short, long)]
|
|
voter: String,
|
|
|
|
/// Vote choice (yes, no, abstain)
|
|
#[arg(short, long)]
|
|
choice: String,
|
|
|
|
/// Optional reason for the vote
|
|
#[arg(short, long)]
|
|
reason: Option<String>,
|
|
},
|
|
|
|
/// Execute a passed proposal
|
|
Execute {
|
|
/// Proposal ID (hex)
|
|
#[arg(short, long)]
|
|
proposal_id: String,
|
|
|
|
/// Executor address (bech32)
|
|
#[arg(short, long)]
|
|
executor: String,
|
|
},
|
|
|
|
/// Get treasury overview
|
|
Treasury,
|
|
|
|
/// Get treasury pool details
|
|
TreasuryPool {
|
|
/// Pool ID (hex)
|
|
id: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum DeployCommands {
|
|
/// Deploy the current project to Synor Hosting
|
|
Push {
|
|
/// Deployment name (defaults to directory name)
|
|
#[arg(short, long)]
|
|
name: Option<String>,
|
|
|
|
/// Output directory (defaults to synor.json build.output or "dist")
|
|
#[arg(short, long)]
|
|
output: Option<PathBuf>,
|
|
|
|
/// Hosting gateway URL
|
|
#[arg(long, env = "SYNOR_HOSTING_URL", default_value = "http://127.0.0.1:8280")]
|
|
gateway: String,
|
|
|
|
/// Skip running the build command
|
|
#[arg(long)]
|
|
skip_build: bool,
|
|
},
|
|
|
|
/// Initialize synor.json in the current directory
|
|
Init {
|
|
/// Deployment name
|
|
#[arg(short, long)]
|
|
name: Option<String>,
|
|
|
|
/// Enable SPA mode (fallback to index.html)
|
|
#[arg(long)]
|
|
spa: bool,
|
|
|
|
/// Output directory
|
|
#[arg(short, long)]
|
|
output: Option<String>,
|
|
},
|
|
|
|
/// List deployments
|
|
List {
|
|
/// Hosting gateway URL
|
|
#[arg(long, env = "SYNOR_HOSTING_URL", default_value = "http://127.0.0.1:8280")]
|
|
gateway: String,
|
|
},
|
|
|
|
/// Delete a deployment
|
|
Delete {
|
|
/// Deployment name
|
|
name: String,
|
|
|
|
/// Hosting gateway URL
|
|
#[arg(long, env = "SYNOR_HOSTING_URL", default_value = "http://127.0.0.1:8280")]
|
|
gateway: String,
|
|
},
|
|
|
|
/// Show deployment info
|
|
Info {
|
|
/// Deployment name
|
|
name: String,
|
|
|
|
/// Hosting gateway URL
|
|
#[arg(long, env = "SYNOR_HOSTING_URL", default_value = "http://127.0.0.1:8280")]
|
|
gateway: String,
|
|
},
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let cli = Cli::parse();
|
|
|
|
// Initialize logging
|
|
if cli.verbose {
|
|
tracing_subscriber::fmt()
|
|
.with_max_level(tracing::Level::DEBUG)
|
|
.init();
|
|
}
|
|
|
|
// Load config
|
|
let config = CliConfig::load_or_default(cli.config.as_deref());
|
|
|
|
// Create RPC client
|
|
let client = RpcClient::new(&cli.rpc);
|
|
|
|
// Set output format
|
|
let output = output::OutputFormat::from_str(&cli.output);
|
|
|
|
// Execute command
|
|
let result = match cli.command {
|
|
// Node commands
|
|
Commands::Info => commands::node::info(&client, output).await,
|
|
Commands::Version => commands::node::version(&client, output).await,
|
|
Commands::SyncStatus => commands::node::sync_status(&client, output).await,
|
|
Commands::Peers => commands::node::peers(&client, output).await,
|
|
|
|
// Block commands
|
|
Commands::Block { id } => commands::block::get_block(&client, &id, output).await,
|
|
Commands::Blocks { count } => commands::block::get_blocks(&client, count, output).await,
|
|
Commands::Tips => commands::block::get_tips(&client, output).await,
|
|
Commands::BlockCount => commands::block::get_block_count(&client, output).await,
|
|
|
|
// Transaction commands
|
|
Commands::Tx { hash } => commands::tx::get_tx(&client, &hash, output).await,
|
|
Commands::Send { to, amount, fee } => {
|
|
commands::tx::send(&client, &config, &to, &amount, fee.as_deref(), output).await
|
|
}
|
|
Commands::Mempool { verbose } => commands::tx::mempool(&client, verbose, output).await,
|
|
|
|
// Wallet commands
|
|
Commands::Wallet(cmd) => commands::wallet::handle(&config, cmd, output).await,
|
|
Commands::Balance { address } => {
|
|
commands::wallet::balance(&client, &config, address.as_deref(), output).await
|
|
}
|
|
Commands::Utxos { address } => commands::wallet::utxos(&client, &address, output).await,
|
|
|
|
// Address commands
|
|
Commands::ValidateAddress { address } => {
|
|
commands::address::validate(&address, output).await
|
|
}
|
|
Commands::DecodeAddress { address } => commands::address::decode(&address, output).await,
|
|
|
|
// Mining commands
|
|
Commands::Mining(cmd) => commands::mining::handle(&client, cmd, output).await,
|
|
|
|
// Contract commands
|
|
Commands::Contract(cmd) => commands::contract::handle(&client, &config, cmd, output).await,
|
|
|
|
// Governance commands
|
|
Commands::Governance(cmd) => commands::governance::handle(&client, cmd, output).await,
|
|
|
|
// Deploy commands
|
|
Commands::Deploy(cmd) => match cmd {
|
|
DeployCommands::Push {
|
|
name,
|
|
output: out_dir,
|
|
gateway,
|
|
skip_build,
|
|
} => commands::deploy::deploy(name, out_dir, &gateway, skip_build, output).await,
|
|
DeployCommands::Init { name, spa, output: out_dir } => {
|
|
commands::deploy::init(name, spa, out_dir, output)
|
|
}
|
|
DeployCommands::List { gateway } => commands::deploy::list(&gateway, output).await,
|
|
DeployCommands::Delete { name, gateway } => {
|
|
commands::deploy::delete(&name, &gateway, output).await
|
|
}
|
|
DeployCommands::Info { name, gateway } => {
|
|
// TODO: Implement info command
|
|
output::print_info(&format!("Deployment info for: {}", name));
|
|
output::print_kv("Gateway", &gateway);
|
|
Ok(())
|
|
}
|
|
},
|
|
|
|
// Network commands
|
|
Commands::AddPeer { address } => {
|
|
commands::network::add_peer(&client, &address, output).await
|
|
}
|
|
Commands::BanPeer { peer } => commands::network::ban_peer(&client, &peer, output).await,
|
|
Commands::UnbanPeer { peer } => commands::network::unban_peer(&client, &peer, output).await,
|
|
|
|
// DEX commands
|
|
Commands::Dex(cmd) => commands::dex::handle(&client, cmd, output).await,
|
|
|
|
// IBC commands
|
|
Commands::Ibc(cmd) => commands::ibc::handle(&client, cmd, output).await,
|
|
|
|
// ZK commands
|
|
Commands::Zk(cmd) => commands::zk::handle(&client, cmd, output).await,
|
|
|
|
// Compiler commands
|
|
Commands::Compiler(cmd) => commands::compiler::handle(&client, cmd, output).await,
|
|
};
|
|
|
|
if let Err(e) = result {
|
|
eprintln!("Error: {}", e);
|
|
std::process::exit(1);
|
|
}
|
|
}
|