Skip to content

Commit

Permalink
Implement remote-wallet in cli for read-only pubkey usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyera Eulberg committed Feb 4, 2020
1 parent 2c0ba1c commit d650cac
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {

#[derive(Debug, PartialEq)]
pub enum Source {
File,
Generated,
Path,
SeedPhrase,
}

Expand Down Expand Up @@ -131,7 +131,12 @@ pub fn keypair_input(
keypair_from_seed_phrase(keypair_name, skip_validation, true)
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
read_keypair_file(keypair_file).map(|keypair| KeypairWithSource::new(keypair, Source::File))
if keypair_file.starts_with("usb://") {
Ok(KeypairWithSource::new(Keypair::new(), Source::Path))
} else {
read_keypair_file(keypair_file)
.map(|keypair| KeypairWithSource::new(keypair, Source::Path))
}
} else {
Ok(KeypairWithSource::new(Keypair::new(), Source::Generated))
}
Expand Down
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ solana-config-program = { path = "../programs/config", version = "0.24.0" }
solana-faucet = { path = "../faucet", version = "0.24.0" }
solana-logger = { path = "../logger", version = "0.24.0" }
solana-net-utils = { path = "../net-utils", version = "0.24.0" }
solana-remote-wallet = { path = "../remote-wallet", version = "0.24.0" }
solana-runtime = { path = "../runtime", version = "0.24.0" }
solana-sdk = { path = "../sdk", version = "0.24.0" }
solana-stake-program = { path = "../programs/stake", version = "0.24.0" }
Expand Down
38 changes: 32 additions & 6 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ use solana_client::{client_error::ClientError, rpc_client::RpcClient};
use solana_faucet::faucet::request_airdrop_transaction;
#[cfg(test)]
use solana_faucet::faucet_mock::request_airdrop_transaction;
use solana_remote_wallet::{
ledger::get_ledger_from_info,
remote_wallet::{DerivationPath, RemoteWallet, RemoteWalletInfo},
};
use solana_sdk::{
bpf_loader,
clock::{Epoch, Slot},
Expand Down Expand Up @@ -416,6 +420,7 @@ pub struct CliConfig {
pub json_rpc_url: String,
pub keypair: Keypair,
pub keypair_path: Option<String>,
pub derivation_path: Option<DerivationPath>,
pub rpc_client: Option<RpcClient>,
pub verbose: bool,
}
Expand All @@ -430,6 +435,22 @@ impl CliConfig {
pub fn default_json_rpc_url() -> String {
"http://127.0.0.1:8899".to_string()
}

pub(crate) fn pubkey(&self) -> Result<Pubkey, Box<dyn std::error::Error>> {
if let Some(path) = &self.keypair_path {
if path.starts_with("usb://") {
let (remote_wallet_info, mut derivation_path) =
RemoteWalletInfo::parse_path(path.to_string())?;
if let Some(derivation) = &self.derivation_path {
let derivation = derivation.clone();
derivation_path = derivation;
}
let ledger = get_ledger_from_info(remote_wallet_info)?;
return Ok(ledger.get_pubkey(derivation_path)?);
}
}
Ok(self.keypair.pubkey())
}
}

impl Default for CliConfig {
Expand All @@ -442,6 +463,7 @@ impl Default for CliConfig {
json_rpc_url: Self::default_json_rpc_url(),
keypair: Keypair::new(),
keypair_path: Some(Self::default_keypair_path()),
derivation_path: None,
rpc_client: None,
verbose: false,
}
Expand Down Expand Up @@ -821,7 +843,7 @@ fn process_create_address_with_seed(
seed: &str,
program_id: &Pubkey,
) -> ProcessResult {
let config_pubkey = config.keypair.pubkey();
let config_pubkey = config.pubkey()?;
let from_pubkey = from_pubkey.unwrap_or(&config_pubkey);
let address = create_address_with_seed(from_pubkey, seed, program_id)?;
Ok(address.to_string())
Expand All @@ -834,12 +856,13 @@ fn process_airdrop(
lamports: u64,
use_lamports_unit: bool,
) -> ProcessResult {
let pubkey = config.pubkey()?;
println!(
"Requesting airdrop of {} from {}",
build_balance_message(lamports, use_lamports_unit, true),
faucet_addr
);
let previous_balance = match rpc_client.retry_get_balance(&config.keypair.pubkey(), 5)? {
let previous_balance = match rpc_client.retry_get_balance(&pubkey, 5)? {
Some(lamports) => lamports,
None => {
return Err(CliError::RpcRequestError(
Expand All @@ -849,10 +872,10 @@ fn process_airdrop(
}
};

request_and_confirm_airdrop(&rpc_client, faucet_addr, &config.keypair.pubkey(), lamports)?;
request_and_confirm_airdrop(&rpc_client, faucet_addr, &pubkey, lamports)?;

let current_balance = rpc_client
.retry_get_balance(&config.keypair.pubkey(), 5)?
.retry_get_balance(&pubkey, 5)?
.unwrap_or(previous_balance);

Ok(build_balance_message(
Expand All @@ -868,7 +891,7 @@ fn process_balance(
pubkey: &Option<Pubkey>,
use_lamports_unit: bool,
) -> ProcessResult {
let pubkey = pubkey.unwrap_or(config.keypair.pubkey());
let pubkey = pubkey.unwrap_or(config.pubkey()?);
let balance = rpc_client.retry_get_balance(&pubkey, 5)?;
match balance {
Some(lamports) => Ok(build_balance_message(lamports, use_lamports_unit, true)),
Expand Down Expand Up @@ -1235,6 +1258,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
if config.verbose {
if let Some(keypair_path) = &config.keypair_path {
println_name_value("Keypair:", keypair_path);
if keypair_path.starts_with("usb://") {
println_name_value("Pubkey:", &format!("{:?}", config.pubkey()?));
}
}
println_name_value("RPC Endpoint:", &config.json_rpc_url);
}
Expand All @@ -1251,7 +1277,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
match &config.command {
// Cluster Query Commands
// Get address of this client
CliCommand::Address => Ok(format!("{}", config.keypair.pubkey())),
CliCommand::Address => Ok(format!("{}", config.pubkey()?)),

// Return software version of solana-cli and cluster entrypoint node
CliCommand::Catchup { node_pubkey } => process_catchup(&rpc_client, node_pubkey),
Expand Down
32 changes: 23 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
use console::style;

use solana_clap_utils::{
input_validators::is_url,
input_parsers::derivation_of,
input_validators::{is_derivation, is_url},
keypair::{
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
SKIP_SEED_PHRASE_VALIDATION_ARG,
Expand Down Expand Up @@ -102,7 +103,7 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
let (keypair, keypair_path) = if require_keypair {
let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
match source {
keypair::Source::File => (
keypair::Source::Path => (
keypair,
Some(matches.value_of("keypair").unwrap().to_string()),
),
Expand All @@ -122,12 +123,16 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
default_keypair_path
};

let keypair = read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path
)))
})?;
let keypair = if keypair_path.starts_with("usb://") {
keypair
} else {
read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path
)))
})?
};

(keypair, Some(keypair_path))
}
Expand All @@ -142,6 +147,7 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
json_rpc_url,
keypair,
keypair_path,
derivation_path: derivation_of(matches, "derivation_path"),
rpc_client: None,
verbose: matches.is_present("verbose"),
})
Expand Down Expand Up @@ -185,7 +191,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.value_name("PATH")
.global(true)
.takes_value(true)
.help("/path/to/id.json"),
.help("/path/to/id.json or usb://remote/wallet/path"),
)
.arg(
Arg::with_name("derivation_path")
.long("derivation-path")
.value_name("ACCOUNT or ACCOUNT/CHANGE")
.takes_value(true)
.validator(is_derivation)
.help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'")
)
.arg(
Arg::with_name("verbose")
Expand Down

0 comments on commit d650cac

Please sign in to comment.