2018-08-06 20:51:12 -07:00
|
|
|
#[macro_use]
|
2018-06-29 18:33:20 -07:00
|
|
|
extern crate clap;
|
2018-07-12 17:16:30 -07:00
|
|
|
extern crate dirs;
|
2018-06-26 22:52:34 -07:00
|
|
|
extern crate solana;
|
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
2018-07-31 22:07:53 -07:00
|
|
|
use solana::client::mk_client;
|
2018-08-20 13:03:36 -07:00
|
|
|
use solana::crdt::NodeInfo;
|
2018-07-31 22:07:53 -07:00
|
|
|
use solana::drone::DRONE_PORT;
|
2018-07-27 21:37:53 -07:00
|
|
|
use solana::logger;
|
2018-09-14 01:58:39 -07:00
|
|
|
use solana::signature::{read_keypair, KeypairUtil};
|
|
|
|
use solana::thin_client::poll_gossip_for_leader;
|
|
|
|
use solana::wallet::{parse_command, process_command, read_leader, WalletConfig, WalletError};
|
2018-06-29 10:38:00 -07:00
|
|
|
use std::error;
|
2018-07-31 22:07:53 -07:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
2018-06-29 10:38:00 -07:00
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
pub fn parse_args(matches: &ArgMatches) -> Result<WalletConfig, Box<error::Error>> {
|
|
|
|
let leader: NodeInfo;
|
|
|
|
if let Some(l) = matches.value_of("leader") {
|
|
|
|
leader = read_leader(l)?.node_info;
|
|
|
|
} else {
|
|
|
|
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
|
|
|
|
leader = NodeInfo::new_with_socketaddr(&server_addr);
|
|
|
|
};
|
|
|
|
let timeout: Option<u64>;
|
|
|
|
if let Some(secs) = matches.value_of("timeout") {
|
|
|
|
timeout = Some(secs.to_string().parse().expect("integer"));
|
|
|
|
} else {
|
|
|
|
timeout = None;
|
2018-06-29 10:38:00 -07:00
|
|
|
}
|
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
let mut path = dirs::home_dir().expect("home directory");
|
|
|
|
let id_path = if matches.is_present("keypair") {
|
|
|
|
matches.value_of("keypair").unwrap()
|
|
|
|
} else {
|
|
|
|
path.extend(&[".config", "solana", "id.json"]);
|
|
|
|
path.to_str().unwrap()
|
|
|
|
};
|
|
|
|
let id = read_keypair(id_path).or_else(|err| {
|
|
|
|
Err(WalletError::BadParameter(format!(
|
|
|
|
"{}: Unable to open keypair file: {}",
|
|
|
|
err, id_path
|
|
|
|
)))
|
|
|
|
})?;
|
2018-06-29 10:38:00 -07:00
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
let leader = poll_gossip_for_leader(leader.contact_info.ncp, timeout)?;
|
2018-06-29 10:38:00 -07:00
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
let mut drone_addr = leader.contact_info.tpu;
|
|
|
|
drone_addr.set_port(DRONE_PORT);
|
2018-06-29 10:38:00 -07:00
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
let command = parse_command(id.pubkey(), &matches)?;
|
|
|
|
|
|
|
|
Ok(WalletConfig {
|
|
|
|
leader,
|
|
|
|
id,
|
|
|
|
drone_addr, // TODO: Add an option for this.
|
|
|
|
command,
|
|
|
|
})
|
2018-06-29 10:38:00 -07:00
|
|
|
}
|
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
fn main() -> Result<(), Box<error::Error>> {
|
|
|
|
logger::setup();
|
2018-06-29 18:33:20 -07:00
|
|
|
let matches = App::new("solana-wallet")
|
2018-08-06 20:51:12 -07:00
|
|
|
.version(crate_version!())
|
2018-06-29 18:33:20 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("leader")
|
|
|
|
.short("l")
|
|
|
|
.long("leader")
|
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("/path/to/leader.json"),
|
2018-09-14 16:25:14 -07:00
|
|
|
).arg(
|
2018-07-12 15:02:14 -07:00
|
|
|
Arg::with_name("keypair")
|
|
|
|
.short("k")
|
|
|
|
.long("keypair")
|
2018-06-29 18:33:20 -07:00
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
2018-07-12 15:02:14 -07:00
|
|
|
.help("/path/to/id.json"),
|
2018-09-14 16:25:14 -07:00
|
|
|
).arg(
|
2018-08-20 13:03:36 -07:00
|
|
|
Arg::with_name("timeout")
|
|
|
|
.long("timeout")
|
2018-09-14 15:32:57 -07:00
|
|
|
.value_name("SECS")
|
2018-08-20 13:03:36 -07:00
|
|
|
.takes_value(true)
|
2018-09-14 15:32:57 -07:00
|
|
|
.help("Max seconds to wait to get necessary gossip from the network"),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(
|
2018-06-29 18:33:20 -07:00
|
|
|
SubCommand::with_name("airdrop")
|
|
|
|
.about("Request a batch of tokens")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("tokens")
|
|
|
|
.long("tokens")
|
2018-09-14 15:32:57 -07:00
|
|
|
.value_name("NUM")
|
2018-06-29 18:33:20 -07:00
|
|
|
.takes_value(true)
|
2018-07-12 15:32:53 -07:00
|
|
|
.required(true)
|
2018-06-29 18:33:20 -07:00
|
|
|
.help("The number of tokens to request"),
|
|
|
|
),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(
|
2018-06-29 18:33:20 -07:00
|
|
|
SubCommand::with_name("pay")
|
|
|
|
.about("Send a payment")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("tokens")
|
|
|
|
.long("tokens")
|
2018-09-14 15:32:57 -07:00
|
|
|
.value_name("NUM")
|
2018-06-29 18:33:20 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
2018-08-20 13:03:36 -07:00
|
|
|
.help("The number of tokens to send"),
|
2018-09-14 16:25:14 -07:00
|
|
|
).arg(
|
2018-06-29 18:33:20 -07:00
|
|
|
Arg::with_name("to")
|
|
|
|
.long("to")
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("The pubkey of recipient"),
|
|
|
|
),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(
|
2018-06-29 18:33:20 -07:00
|
|
|
SubCommand::with_name("confirm")
|
|
|
|
.about("Confirm your payment by signature")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("signature")
|
|
|
|
.index(1)
|
|
|
|
.value_name("SIGNATURE")
|
|
|
|
.required(true)
|
|
|
|
.help("The transaction signature to confirm"),
|
|
|
|
),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(SubCommand::with_name("balance").about("Get your balance"))
|
2018-06-29 18:33:20 -07:00
|
|
|
.subcommand(SubCommand::with_name("address").about("Get your public key"))
|
|
|
|
.get_matches();
|
2018-06-26 22:52:34 -07:00
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
let config = parse_args(&matches)?;
|
2018-07-31 22:07:53 -07:00
|
|
|
let mut client = mk_client(&config.leader);
|
2018-06-29 10:38:00 -07:00
|
|
|
process_command(&config, &mut client)
|
|
|
|
}
|