Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created September 27, 2025 16:38
Show Gist options
  • Select an option

  • Save rust-play/b46314629dd599fadc5fbd7b26fbbcef to your computer and use it in GitHub Desktop.

Select an option

Save rust-play/b46314629dd599fadc5fbd7b26fbbcef to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
use clap::{Parser, Subcommand};
use std::path::PathBuf;
// --- Constants and Types ---
const VERSION: &str = "0.4.1";
// Placeholder structs for complex types like Wallet and GlobalConfig
// In a full implementation, these would be defined in separate modules (e.g., `config.rs`, `wallet.rs`)
// and would likely derive traits like `serde::Deserialize` and `serde::Serialize`.
struct Wallet;
struct GlobalConfig;
// --- Command Line Interface (CLI) Implementation using Clap ---
/// A trust-minimized wallet script.
///
/// You can think of this program as a small shim between your
/// air-gapped hardware wallet and Bitcoin Core.
#[derive(Parser, Debug)]
#[clap(author, version = VERSION, about, long_about = None)]
#[clap(propagate_version = true)]
struct Cli {
#[clap(short, long, help = "Enable verbose logging")]
verbose: bool,
#[clap(
short,
long,
help = "Path to config file. E.g., ~/.config/coldcore/config.ini"
)]
config: Option<PathBuf>,
#[clap(short, long, help = "Enable debug logging")]
debug: bool,
#[clap(
long,
help = "Try to connect on the testnet network initially instead of mainnet."
)]
testnet: bool,
#[clap(short, long, help = "The specific wallet to open.")]
wallet: Option<String>,
#[clap(
short,
long,
help = "The Bitcoin Core RPC interface URL to use, e.g. 'http://user:pass@host:8332'"
)]
rpc: Option<String>,
#[clap(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Decode a PSBT file (Base64 or Hex).
Decodepsbt {
fname: String,
#[clap(long, default_value = "json")]
format: String,
},
/// Run initial setup for a wallet.
Setup,
/// Watch activity related to your wallets.
Watch,
/// Print the bitcoin-cli scanblocks command associated with this wallet.
Scanargs,
/// Check your wallet balances.
Balance {
#[clap(long, default_value = "plain")]
format: String,
},
/// Consolidate certain coins within your wallet to a single UTXO (interactive).
Consolidate,
/// Prepare a sending PSBT.
PrepareSend {
to_address: String,
amount: String,
#[clap(long, default_value = "")]
spend_from: String,
#[clap(long)]
fee_rate: Option<u32>,
},
/// Send all funds from the wallet.
Sendall {
to_address: String,
#[clap(long)]
fee_rate: Option<u32>,
},
/// Broadcast a signed PSBT.
Broadcast { signed_psbt_path: PathBuf },
/// Generate a new receiving address.
Newaddr {
#[clap(default_value = "1")]
num: u32,
#[clap(short = 'c', long)]
to_clipboard: bool,
},
/// Start the curses-based user interface.
Ui,
/// Reinitialize the wallet on a new instance of bitcoind.
ReinitWallet,
}
// --- Application Logic Placeholders ---
// The `main` function now acts as the entry point, handling CLI parsing and dispatch.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
// 1. Setup Logging (simplified)
if cli.debug {
eprintln!("Debug logging enabled.");
}
// 2. Load Configuration and Wallets (placeholder logic)
// In a real app, this would involve disk I/O, potential GPG/Pass decryption,
// and deserialization into GlobalConfig and Vec<Wallet>.
let (config, wallets) = load_config_and_wallets(&cli)?;
// 3. Command Dispatch
match cli.command {
Some(Commands::Setup) => handle_setup(config, wallets),
Some(Commands::Ui) => handle_ui(config, wallets),
Some(cmd) => handle_command(cmd, config, wallets),
None => handle_ui(config, wallets), // Default action in Python was `ui()`
}
Ok(())
}
fn load_config_and_wallets(
_cli: &Cli,
) -> Result<(GlobalConfig, Vec<Wallet>), Box<dyn std::error::Error>> {
// Implement full configuration loading here:
// - Check for --config or default paths
// - Handle GPG/Pass (via external commands or a dedicated library)
// - Parse INI file and create GlobalConfig and Wallet structs
// Placeholder return:
Ok((GlobalConfig, vec![]))
}
fn handle_setup(_config: GlobalConfig, _wallets: Vec<Wallet>) {
println!("Running initial wallet setup...");
// This would call a function equivalent to Python's `run_setup`,
// which handles RPC discovery, Coldcard export, and Core wallet creation.
}
fn handle_ui(_config: GlobalConfig, _wallets: Vec<Wallet>) {
println!("Starting the Curses-based UI...");
// This function is where the `ratatui`/`crossterm` logic for the TUI would live,
// mirroring the Python `start_ui` and `draw_menu` functions.
// It would need to manage state and threads for background RPC polling.
}
fn handle_command(cmd: Commands, _config: GlobalConfig, _wallets: Vec<Wallet>) {
println!("Executing command: {:?}", cmd);
// Each arm of this match statement would call the corresponding function
// (e.g., `decodepsbt`, `balance`, `prepare_send`), which would rely heavily
// on a full-featured Rust RPC client implementation.
}
// --- RPC Client Implementation (Stub) ---
// A full implementation would need a comprehensive RPC client.
// The `rpc` module would contain the `BitcoinRPC` struct and its methods.
mod rpc {
// This module would use `reqwest` for HTTP and `serde` for JSON.
// Error handling would involve custom error types (like the Python `JSONRPCError`).
pub struct BitcoinRPC;
impl BitcoinRPC {
// A full implementation would need methods like:
// pub fn new(...) -> Self { ... }
// pub fn getblockchaininfo(&self) -> Result<serde_json::Value, String> { ... }
// pub fn getnewaddress(&self) -> Result<String, String> { ... }
// pub fn walletcreatefundedpsbt(&self, ...) -> Result<String, String> { ... }
}
}
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment