Overhaul wallet rpc/drone command-line arguments

This commit is contained in:
Michael Vines
2019-01-16 20:43:00 -08:00
parent 00c4c30d72
commit a84b6bc7e4
10 changed files with 220 additions and 126 deletions

View File

@@ -1,22 +1,46 @@
use clap::{crate_version, App, Arg, ArgMatches, SubCommand};
use solana::socketaddr;
use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil};
use solana_wallet::wallet::{parse_command, process_command, WalletConfig, WalletError};
use std::error;
use std::net::SocketAddr;
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn error::Error>> {
let network = if let Some(addr) = matches.value_of("network") {
addr.parse().or_else(|_| {
Err(WalletError::BadParameter(
"Invalid network location".to_string(),
))
})?
let host = matches
.value_of("host")
.unwrap()
.parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid host".to_string())))?;
let drone_host = if let Some(drone_host) = matches.value_of("drone_host") {
Some(
drone_host
.parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid drone host".to_string())))?,
)
} else {
socketaddr!("127.0.0.1:8001")
None
};
let proxy = matches.value_of("proxy").map(|proxy| proxy.to_string());
let rpc_host = if let Some(rpc_host) = matches.value_of("rpc_host") {
Some(
rpc_host
.parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid rpc host".to_string())))?,
)
} else {
None
};
let drone_port = matches
.value_of("drone_port")
.unwrap()
.parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid drone port".to_string())))?;
let rpc_port = matches
.value_of("rpc_port")
.unwrap()
.parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid rpc port".to_string())))?;
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
@@ -42,40 +66,83 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn erro
Ok(WalletConfig {
id,
command,
network,
proxy,
drone_port: None,
drone_host,
drone_port,
host,
rpc_client: None,
rpc_port: None,
rpc_host,
rpc_port,
rpc_tls: matches.is_present("rpc_tls"),
})
}
fn main() -> Result<(), Box<dyn error::Error>> {
solana_logger::setup();
let (default_host, default_rpc_port, default_drone_port) = {
let defaults = WalletConfig::default();
(
defaults.host.to_string(),
defaults.rpc_port.to_string(),
defaults.drone_port.to_string(),
)
};
let matches = App::new("solana-wallet")
.version(crate_version!())
.arg(
Arg::with_name("network")
Arg::with_name("host")
.short("n")
.long("network")
.value_name("HOST:PORT")
.long("host")
.value_name("IP ADDRESS")
.takes_value(true)
.help("Rendezvous with the network at this gossip entry point; defaults to 127.0.0.1:8001"),
).arg(
.default_value(&default_host)
.help("Host to use for both RPC and drone"),
)
.arg(
Arg::with_name("rpc_host")
.long("rpc-host")
.value_name("IP ADDRESS")
.takes_value(true)
.help("RPC host to use [default: same as --host]"),
)
.arg(
Arg::with_name("rpc_port")
.long("rpc-port")
.value_name("PORT")
.takes_value(true)
.default_value(&default_rpc_port)
.help("RPC port to use"),
)
.arg(
Arg::with_name("rpc_tps")
.long("rpc-tls")
.help("Enable TLS for the RPC endpoint"),
)
.arg(
Arg::with_name("drone_host")
.long("drone-host")
.value_name("IP ADDRESS")
.takes_value(true)
.help("Drone host to use [default: same as --host]"),
)
.arg(
Arg::with_name("drone_port")
.long("drone-port")
.value_name("PORT")
.takes_value(true)
.default_value(&default_drone_port)
.help("Drone port to use"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
).arg(
Arg::with_name("proxy")
.long("proxy")
.takes_value(true)
.value_name("URL")
.help("Address of TLS proxy")
.conflicts_with("rpc-port")
).subcommand(SubCommand::with_name("address").about("Get your public key"))
)
.subcommand(SubCommand::with_name("address").about("Get your public key"))
.subcommand(
SubCommand::with_name("airdrop")
.about("Request a batch of tokens")
@@ -87,7 +154,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.required(true)
.help("The number of tokens to request"),
),
).subcommand(SubCommand::with_name("balance").about("Get your balance"))
)
.subcommand(SubCommand::with_name("balance").about("Get your balance"))
.subcommand(
SubCommand::with_name("cancel")
.about("Cancel a transfer")
@@ -99,7 +167,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.required(true)
.help("The process id of the transfer to cancel"),
),
).subcommand(
)
.subcommand(
SubCommand::with_name("confirm")
.about("Confirm transaction by signature")
.arg(
@@ -110,7 +179,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.required(true)
.help("The transaction signature to confirm"),
),
).subcommand(
)
.subcommand(
SubCommand::with_name("deploy")
.about("Deploy a program")
.arg(
@@ -120,12 +190,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.required(true)
.help("/path/to/program.o"),
)
// TODO: Add "loader" argument; current default is bpf_loader
).subcommand(
SubCommand::with_name("get-transaction-count")
.about("Get current transaction count")
).subcommand(
), // TODO: Add "loader" argument; current default is bpf_loader
)
.subcommand(
SubCommand::with_name("get-transaction-count").about("Get current transaction count"),
)
.subcommand(
SubCommand::with_name("pay")
.about("Send a payment")
.arg(
@@ -135,27 +205,31 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.required(true)
.help("The pubkey of recipient"),
).arg(
)
.arg(
Arg::with_name("tokens")
.index(2)
.value_name("NUM")
.takes_value(true)
.required(true)
.help("The number of tokens to send"),
).arg(
)
.arg(
Arg::with_name("timestamp")
.long("after")
.value_name("DATETIME")
.takes_value(true)
.help("A timestamp after which transaction will execute"),
).arg(
)
.arg(
Arg::with_name("timestamp-pubkey")
.long("require-timestamp-from")
.value_name("PUBKEY")
.takes_value(true)
.requires("timestamp")
.help("Require timestamp from this third party"),
).arg(
)
.arg(
Arg::with_name("witness")
.long("require-signature-from")
.value_name("PUBKEY")
@@ -163,12 +237,14 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.multiple(true)
.use_delimiter(true)
.help("Any third party signatures required to unlock the tokens"),
).arg(
)
.arg(
Arg::with_name("cancelable")
.long("cancelable")
.takes_value(false),
),
).subcommand(
)
.subcommand(
SubCommand::with_name("send-signature")
.about("Send a signature to authorize a transfer")
.arg(
@@ -178,15 +254,17 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.required(true)
.help("The pubkey of recipient"),
).arg(
)
.arg(
Arg::with_name("process-id")
.index(2)
.value_name("PROCESS_ID")
.takes_value(true)
.required(true)
.help("The process id of the transfer to authorize")
)
).subcommand(
.help("The process id of the transfer to authorize"),
),
)
.subcommand(
SubCommand::with_name("send-timestamp")
.about("Send a timestamp to unlock a transfer")
.arg(
@@ -196,21 +274,24 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true)
.required(true)
.help("The pubkey of recipient"),
).arg(
)
.arg(
Arg::with_name("process-id")
.index(2)
.value_name("PROCESS_ID")
.takes_value(true)
.required(true)
.help("The process id of the transfer to unlock")
).arg(
.help("The process id of the transfer to unlock"),
)
.arg(
Arg::with_name("datetime")
.long("date")
.value_name("DATETIME")
.takes_value(true)
.help("Optional arbitrary timestamp to apply")
)
).get_matches();
.help("Optional arbitrary timestamp to apply"),
),
)
.get_matches();
let config = parse_args(&matches)?;
let result = process_command(&config)?;

View File

@@ -10,7 +10,6 @@ use solana::rpc_mock::{request_airdrop_transaction, MockRpcClient as RpcClient};
#[cfg(not(test))]
use solana::rpc_request::RpcClient;
use solana::rpc_request::{get_rpc_request_str, RpcRequest};
use solana::socketaddr;
#[cfg(not(test))]
use solana_drone::drone::request_airdrop_transaction;
use solana_drone::drone::DRONE_PORT;
@@ -25,7 +24,7 @@ use solana_sdk::system_transaction::SystemTransaction;
use solana_sdk::transaction::Transaction;
use std::fs::File;
use std::io::Read;
use std::net::{Ipv4Addr, SocketAddr};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
@@ -85,41 +84,41 @@ impl error::Error for WalletError {
pub struct WalletConfig {
pub id: Keypair,
pub command: WalletCommand,
pub network: SocketAddr,
pub proxy: Option<String>,
pub drone_port: Option<u16>,
pub drone_host: Option<IpAddr>,
pub drone_port: u16,
pub host: IpAddr,
pub rpc_client: Option<RpcClient>,
pub rpc_port: Option<u16>,
pub rpc_host: Option<IpAddr>,
pub rpc_port: u16,
pub rpc_tls: bool,
}
impl Default for WalletConfig {
fn default() -> WalletConfig {
let default_addr = socketaddr!(0, 8000);
WalletConfig {
id: Keypair::new(),
command: WalletCommand::Balance,
network: default_addr,
proxy: None,
drone_port: None,
drone_host: None,
drone_port: DRONE_PORT,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
id: Keypair::new(),
rpc_client: None,
rpc_port: None,
rpc_host: None,
rpc_port: RPC_PORT,
rpc_tls: false,
}
}
}
impl WalletConfig {
pub fn drone_addr(&self) -> SocketAddr {
// Assume drone is running on the provided network entrypoint
let mut drone_addr = self.network;
drone_addr.set_port(self.drone_port.unwrap_or(DRONE_PORT));
drone_addr
SocketAddr::new(self.drone_host.unwrap_or(self.host), self.drone_port)
}
pub fn rpc_addr(&self) -> String {
let mut rpc_addr = self.network;
rpc_addr.set_port(self.rpc_port.unwrap_or(RPC_PORT));
let rpc_addr_str = get_rpc_request_str(rpc_addr);
self.proxy.clone().unwrap_or(rpc_addr_str)
get_rpc_request_str(
SocketAddr::new(self.rpc_host.unwrap_or(self.host), self.rpc_port),
self.rpc_tls,
)
}
}
@@ -794,6 +793,7 @@ mod tests {
use clap::{App, Arg, SubCommand};
use serde_json::Value;
use solana::rpc_mock::{PUBKEY, SIGNATURE};
use solana::socketaddr;
use solana_sdk::signature::{gen_keypair_file, read_keypair, read_pkcs8, Keypair, KeypairUtil};
use std::fs;
use std::net::{Ipv4Addr, SocketAddr};
@@ -802,15 +802,29 @@ mod tests {
#[test]
fn test_wallet_config_drone_addr() {
let mut config = WalletConfig::default();
assert_eq!(config.drone_addr(), socketaddr!(0, DRONE_PORT));
assert_eq!(
config.drone_addr(),
SocketAddr::new(config.host, config.drone_port)
);
config.drone_port = Some(1234);
assert_eq!(config.drone_addr(), socketaddr!(0, 1234));
config.drone_port = 1234;
assert_eq!(config.drone_addr(), SocketAddr::new(config.host, 1234));
assert_eq!(config.rpc_addr(), "http://0.0.0.0:8899");
config.drone_host = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)));
assert_eq!(
config.drone_addr(),
SocketAddr::new(config.drone_host.unwrap(), 1234)
);
}
config.rpc_port = Some(1234);
assert_eq!(config.rpc_addr(), "http://0.0.0.0:1234");
#[test]
fn test_wallet_config_rpc_addr() {
let mut config = WalletConfig::default();
assert_eq!(config.rpc_addr(), "http://127.0.0.1:8899");
config.rpc_port = 1234;
assert_eq!(config.rpc_addr(), "http://127.0.0.1:1234");
config.rpc_host = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)));
assert_eq!(config.rpc_addr(), "http://127.0.0.2:1234");
}
#[test]