Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
16bb8e9
Add sdk helpers for dynamic signing
Feb 19, 2020
9d1fdcf
Add client helpers for dynamic signing
Feb 19, 2020
2ca8917
Add keypair_util_from_path helper
Feb 14, 2020
bc58805
Cli: impl config.keypair as a trait object
Feb 15, 2020
75b2863
SDK: Add Debug and PartialEq for dyn KeypairUtil
t-nelson Feb 15, 2020
2d99638
ClapUtils: Arg parsing from pubkey+signers to Presigner
t-nelson Feb 15, 2020
c8a62ba
CLI: Add helper for getting signers from args
t-nelson Feb 15, 2020
0237fc2
CLI: Replace SigningAuthority with KeypairUtil trait-objs
t-nelson Feb 15, 2020
0f6dc7e
CLI: Drop disused signers command field
t-nelson Feb 15, 2020
f1ba25f
CLI: Drop redundant tests
t-nelson Feb 15, 2020
1703b82
Add clap validator that handles all current signer types
Feb 17, 2020
8a71eeb
clap_utils: Factor Presigner resolution to helper
t-nelson Feb 17, 2020
f39e883
SDK: `From` for boxing KeypairUtil implementors to trait objects
t-nelson Feb 17, 2020
2ed37bd
SDK: Derive `Clone` for `Presigner`
t-nelson Feb 17, 2020
f9d89ec
Don't panic
Feb 17, 2020
aa6c116
Cli: dedup signers in transfer for remote-wallet ergonomics
Feb 17, 2020
ffc8b0f
Update docs vis-a-vis ASK changes
Feb 17, 2020
4e1b2ad
Cli: update transaction types to use new dynamic-signer methods
Feb 19, 2020
fd298d6
CLI: Fix tests No. 1
t-nelson Feb 17, 2020
38237c1
Work around `CliConfig`'s signer not necessarily being a `Keypair`
t-nelson Feb 17, 2020
97e7919
CLI: Fix tests No. 2
t-nelson Feb 17, 2020
8f89f2d
Remove unused arg
Feb 19, 2020
2e5a9ca
Remove unused methods
Feb 19, 2020
09fc244
Move offline arg constants upstream
Feb 19, 2020
0794477
Make dynamic signing fallible
Feb 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions book/src/paper-wallet/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,22 +166,22 @@ Refer to the following page for a comprehensive guide on running a validator:
Solana CLI tooling supports secure keypair input for stake delegation. To do so,
first create a stake account with some SOL. Use the special `ASK` keyword to
trigger a seed phrase input prompt for the stake account and use
`--ask-seed-phrase keypair` to securely input the funding keypair.
`--keypair ASK` to securely input the funding keypair.

```bash
solana create-stake-account ASK 1 --ask-seed-phrase keypair
solana create-stake-account ASK 1 --keypair ASK

[stake_account] seed phrase: 🔒
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
[keypair] seed phrase: 🔒
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
```

Then, to delegate that stake to a validator, use `--ask-seed-phrase keypair` to
Then, to delegate that stake to a validator, use `--keypair ASK` to
securely input the funding keypair.

```bash
solana delegate-stake --ask-seed-phrase keypair <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
solana delegate-stake --keypair ASK <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>

[keypair] seed phrase: 🔒
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
Expand Down
16 changes: 15 additions & 1 deletion clap-utils/src/input_parsers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::keypair::{keypair_from_seed_phrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG};
use crate::keypair::{
keypair_from_seed_phrase, keypair_util_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
};
use chrono::DateTime;
use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::DerivationPath;
Expand Down Expand Up @@ -93,6 +95,18 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
})
}

// Return a signer from matches at `name`
pub fn signer_of(
name: &str,
matches: &ArgMatches<'_>,
) -> Result<Option<Box<dyn KeypairUtil>>, Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) {
keypair_util_from_path(matches, location, name).map(Some)
} else {
Ok(None)
}
}

pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
value_of(matches, name).map(sol_to_lamports)
}
Expand Down
13 changes: 12 additions & 1 deletion clap-utils/src/input_validators.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::keypair::ASK_KEYWORD;
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use chrono::DateTime;
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
Expand Down Expand Up @@ -50,6 +51,16 @@ pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String>
is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string))
}

pub fn is_valid_signer(string: String) -> Result<(), String> {
match parse_keypair_path(&string) {
KeypairUrl::Usb(path) => generate_remote_keypair(path, None)
.map(|_| ())
.map_err(|err| format!("{:?}", err)),
KeypairUrl::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
}

// Return an error if string cannot be parsed as pubkey=signature string
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
let mut signer = string.split('=');
Expand Down
75 changes: 70 additions & 5 deletions clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
use crate::ArgConstant;
use crate::{
input_parsers::{derivation_of, pubkeys_sigs_of},
offline::SIGNER_ARG,
ArgConstant,
};
use bip39::{Language, Mnemonic, Seed};
use clap::values_t;
use clap::{values_t, ArgMatches, Error, ErrorKind};
use rpassword::prompt_password_stderr;
use solana_sdk::signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
KeypairUtil,
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
use solana_sdk::{
pubkey::Pubkey,
signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
read_keypair_file, Keypair, KeypairUtil, Presigner, Signature,
},
};
use std::{
error,
io::{stdin, stdout, Write},
process::exit,
str::FromStr,
};

pub enum KeypairUrl {
Ask,
Filepath(String),
Usb(String),
Stdin,
Pubkey(Pubkey),
}

pub fn parse_keypair_path(path: &str) -> KeypairUrl {
Expand All @@ -26,11 +36,66 @@ pub fn parse_keypair_path(path: &str) -> KeypairUrl {
KeypairUrl::Ask
} else if path.starts_with("usb://") {
KeypairUrl::Usb(path.split_at(6).1.to_string())
} else if let Ok(pubkey) = Pubkey::from_str(path) {
KeypairUrl::Pubkey(pubkey)
} else {
KeypairUrl::Filepath(path.to_string())
}
}

pub fn presigner_from_pubkey_sigs(
pubkey: &Pubkey,
signers: &[(Pubkey, Signature)],
) -> Option<Presigner> {
signers.iter().find_map(|(signer, sig)| {
if *signer == *pubkey {
Some(Presigner::new(signer, sig))
} else {
None
}
})
}

pub fn keypair_util_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
) -> Result<Box<dyn KeypairUtil>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
Ok(Box::new(keypair_from_seed_phrase(
keypair_name,
skip_validation,
false,
)?))
}
KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)),
KeypairUrl::Stdin => {
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
KeypairUrl::Usb(path) => Ok(Box::new(generate_remote_keypair(
path,
derivation_of(matches, "derivation_path"),
)?)),
KeypairUrl::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
if let Some(presigner) = presigner {
Ok(Box::new(presigner))
} else {
Err(Error::with_description(
"Missing signature for supplied pubkey",
ErrorKind::MissingRequiredArgument,
)
.into())
}
}
}
}

// Keyword used to indicate that the user should be asked for a keypair seed phrase
pub const ASK_KEYWORD: &str = "ASK";

Expand Down
1 change: 1 addition & 0 deletions clap-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ pub struct ArgConstant<'a> {
pub mod input_parsers;
pub mod input_validators;
pub mod keypair;
pub mod offline;
19 changes: 19 additions & 0 deletions clap-utils/src/offline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::ArgConstant;

pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash",
long: "blockhash",
help: "Use the supplied blockhash",
};

pub const SIGN_ONLY_ARG: ArgConstant<'static> = ArgConstant {
name: "sign_only",
long: "sign-only",
help: "Sign the transaction offline",
};

pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
name: "signer",
long: "signer",
help: "Provide a public-key/signature pair for the transaction",
};
Loading