SignerSource: rename input scheme to `prompt`, default to bip44 solana base key (#17154)
* Rename ask to prompt * Default to Solana bip44 base if no derivation-path * Add SignerSource legacy field, support legacy ASK * Update docs * Fix docs: validator current doesn't support uri SignerSources
This commit is contained in:
parent
8eb05d6ed4
commit
a5ec3a0547
|
@ -57,7 +57,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
|
||||||
if let Some(value) = matches.value_of(name) {
|
if let Some(value) = matches.value_of(name) {
|
||||||
if value == ASK_KEYWORD {
|
if value == ASK_KEYWORD {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
keypair_from_seed_phrase(name, skip_validation, true, None).ok()
|
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||||
} else {
|
} else {
|
||||||
read_keypair_file(value).ok()
|
read_keypair_file(value).ok()
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>>
|
||||||
.filter_map(|value| {
|
.filter_map(|value| {
|
||||||
if value == ASK_KEYWORD {
|
if value == ASK_KEYWORD {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
keypair_from_seed_phrase(name, skip_validation, true, None).ok()
|
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||||
} else {
|
} else {
|
||||||
read_keypair_file(value).ok()
|
read_keypair_file(value).ok()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ use {
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{
|
signature::{
|
||||||
generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed_and_derivation_path,
|
generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed,
|
||||||
|
keypair_from_seed_and_derivation_path, keypair_from_seed_phrase_and_passphrase,
|
||||||
read_keypair, read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
read_keypair, read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -140,6 +141,7 @@ impl DefaultSigner {
|
||||||
pub(crate) struct SignerSource {
|
pub(crate) struct SignerSource {
|
||||||
pub kind: SignerSourceKind,
|
pub kind: SignerSourceKind,
|
||||||
pub derivation_path: Option<DerivationPath>,
|
pub derivation_path: Option<DerivationPath>,
|
||||||
|
pub legacy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignerSource {
|
impl SignerSource {
|
||||||
|
@ -147,12 +149,21 @@ impl SignerSource {
|
||||||
Self {
|
Self {
|
||||||
kind,
|
kind,
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_legacy(kind: SignerSourceKind) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
derivation_path: None,
|
||||||
|
legacy: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum SignerSourceKind {
|
pub(crate) enum SignerSourceKind {
|
||||||
Ask,
|
Prompt,
|
||||||
Filepath(String),
|
Filepath(String),
|
||||||
Usb(RemoteWalletLocator),
|
Usb(RemoteWalletLocator),
|
||||||
Stdin,
|
Stdin,
|
||||||
|
@ -181,9 +192,10 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||||
if let Some(scheme) = uri.scheme() {
|
if let Some(scheme) = uri.scheme() {
|
||||||
let scheme = scheme.as_str().to_ascii_lowercase();
|
let scheme = scheme.as_str().to_ascii_lowercase();
|
||||||
match scheme.as_str() {
|
match scheme.as_str() {
|
||||||
"ask" => Ok(SignerSource {
|
"prompt" => Ok(SignerSource {
|
||||||
kind: SignerSourceKind::Ask,
|
kind: SignerSourceKind::Prompt,
|
||||||
derivation_path: DerivationPath::from_uri_any_query(&uri)?,
|
derivation_path: DerivationPath::from_uri_any_query(&uri)?,
|
||||||
|
legacy: false,
|
||||||
}),
|
}),
|
||||||
"file" => Ok(SignerSource::new(SignerSourceKind::Filepath(
|
"file" => Ok(SignerSource::new(SignerSourceKind::Filepath(
|
||||||
uri.path().to_string(),
|
uri.path().to_string(),
|
||||||
|
@ -192,13 +204,14 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||||
"usb" => Ok(SignerSource {
|
"usb" => Ok(SignerSource {
|
||||||
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
|
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
|
||||||
derivation_path: DerivationPath::from_uri_key_query(&uri)?,
|
derivation_path: DerivationPath::from_uri_key_query(&uri)?,
|
||||||
|
legacy: false,
|
||||||
}),
|
}),
|
||||||
_ => Err(SignerSourceError::UnrecognizedSource),
|
_ => Err(SignerSourceError::UnrecognizedSource),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match source {
|
match source {
|
||||||
"-" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
"-" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||||
ASK_KEYWORD => Ok(SignerSource::new(SignerSourceKind::Ask)),
|
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
|
||||||
_ => match Pubkey::from_str(source) {
|
_ => match Pubkey::from_str(source) {
|
||||||
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
||||||
Err(_) => std::fs::metadata(source)
|
Err(_) => std::fs::metadata(source)
|
||||||
|
@ -259,15 +272,17 @@ pub fn signer_from_path_with_config(
|
||||||
let SignerSource {
|
let SignerSource {
|
||||||
kind,
|
kind,
|
||||||
derivation_path,
|
derivation_path,
|
||||||
|
legacy,
|
||||||
} = parse_signer_source(path)?;
|
} = parse_signer_source(path)?;
|
||||||
match kind {
|
match kind {
|
||||||
SignerSourceKind::Ask => {
|
SignerSourceKind::Prompt => {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
Ok(Box::new(keypair_from_seed_phrase(
|
Ok(Box::new(keypair_from_seed_phrase(
|
||||||
keypair_name,
|
keypair_name,
|
||||||
skip_validation,
|
skip_validation,
|
||||||
false,
|
false,
|
||||||
derivation_path,
|
derivation_path,
|
||||||
|
legacy,
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||||
|
@ -339,18 +354,30 @@ pub fn resolve_signer_from_path(
|
||||||
let SignerSource {
|
let SignerSource {
|
||||||
kind,
|
kind,
|
||||||
derivation_path,
|
derivation_path,
|
||||||
|
legacy,
|
||||||
} = parse_signer_source(path)?;
|
} = parse_signer_source(path)?;
|
||||||
match kind {
|
match kind {
|
||||||
SignerSourceKind::Ask => {
|
SignerSourceKind::Prompt => {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
// This method validates the seed phrase, but returns `None` because there is no path
|
// This method validates the seed phrase, but returns `None` because there is no path
|
||||||
// on disk or to a device
|
// on disk or to a device
|
||||||
keypair_from_seed_phrase(keypair_name, skip_validation, false, derivation_path).map(|_| None)
|
keypair_from_seed_phrase(
|
||||||
|
keypair_name,
|
||||||
|
skip_validation,
|
||||||
|
false,
|
||||||
|
derivation_path,
|
||||||
|
legacy,
|
||||||
|
)
|
||||||
|
.map(|_| None)
|
||||||
}
|
}
|
||||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||||
Err(e) => Err(std::io::Error::new(
|
Err(e) => Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
|
format!(
|
||||||
|
"could not read keypair file \"{}\". \
|
||||||
|
Run \"solana-keygen new\" to create a keypair file: {}",
|
||||||
|
path, e
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.into()),
|
.into()),
|
||||||
Ok(_) => Ok(Some(path.to_string())),
|
Ok(_) => Ok(Some(path.to_string())),
|
||||||
|
@ -383,7 +410,7 @@ pub fn resolve_signer_from_path(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
// Keyword used to indicate that the user should be prompted for a keypair seed phrase
|
||||||
pub const ASK_KEYWORD: &str = "ASK";
|
pub const ASK_KEYWORD: &str = "ASK";
|
||||||
|
|
||||||
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
@ -412,6 +439,7 @@ pub fn keypair_from_seed_phrase(
|
||||||
skip_validation: bool,
|
skip_validation: bool,
|
||||||
confirm_pubkey: bool,
|
confirm_pubkey: bool,
|
||||||
derivation_path: Option<DerivationPath>,
|
derivation_path: Option<DerivationPath>,
|
||||||
|
legacy: bool,
|
||||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||||
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||||
let seed_phrase = seed_phrase.trim();
|
let seed_phrase = seed_phrase.trim();
|
||||||
|
@ -422,8 +450,12 @@ pub fn keypair_from_seed_phrase(
|
||||||
|
|
||||||
let keypair = if skip_validation {
|
let keypair = if skip_validation {
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
let seed = generate_seed_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase);
|
if legacy {
|
||||||
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
|
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||||
|
} else {
|
||||||
|
let seed = generate_seed_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase);
|
||||||
|
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||||
let parse_language_fn = || {
|
let parse_language_fn = || {
|
||||||
|
@ -446,7 +478,11 @@ pub fn keypair_from_seed_phrase(
|
||||||
let mnemonic = parse_language_fn()?;
|
let mnemonic = parse_language_fn()?;
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
let seed = Seed::new(&mnemonic, &passphrase);
|
let seed = Seed::new(&mnemonic, &passphrase);
|
||||||
keypair_from_seed_and_derivation_path(&seed.as_bytes(), derivation_path)?
|
if legacy {
|
||||||
|
keypair_from_seed(seed.as_bytes())?
|
||||||
|
} else {
|
||||||
|
keypair_from_seed_and_derivation_path(&seed.as_bytes(), derivation_path)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if confirm_pubkey {
|
if confirm_pubkey {
|
||||||
|
@ -525,21 +561,24 @@ mod tests {
|
||||||
SignerSource {
|
SignerSource {
|
||||||
kind: SignerSourceKind::Stdin,
|
kind: SignerSourceKind::Stdin,
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
let ask = "stdin:".to_string();
|
let stdin = "stdin:".to_string();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse_signer_source(&ask).unwrap(),
|
parse_signer_source(&stdin).unwrap(),
|
||||||
SignerSource {
|
SignerSource {
|
||||||
kind: SignerSourceKind::Stdin,
|
kind: SignerSourceKind::Stdin,
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse_signer_source(ASK_KEYWORD).unwrap(),
|
parse_signer_source(ASK_KEYWORD).unwrap(),
|
||||||
SignerSource {
|
SignerSource {
|
||||||
kind: SignerSourceKind::Ask,
|
kind: SignerSourceKind::Prompt,
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: true,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
let pubkey = Pubkey::new_unique();
|
let pubkey = Pubkey::new_unique();
|
||||||
|
@ -547,6 +586,7 @@ mod tests {
|
||||||
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
|
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Pubkey(p),
|
kind: SignerSourceKind::Pubkey(p),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
}
|
}
|
||||||
if p == pubkey)
|
if p == pubkey)
|
||||||
);
|
);
|
||||||
|
@ -567,12 +607,14 @@ mod tests {
|
||||||
matches!(parse_signer_source(absolute_path_str).unwrap(), SignerSource {
|
matches!(parse_signer_source(absolute_path_str).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Filepath(p),
|
kind: SignerSourceKind::Filepath(p),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
} if p == absolute_path_str)
|
} if p == absolute_path_str)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource {
|
matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Filepath(p),
|
kind: SignerSourceKind::Filepath(p),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
} if p == relative_path_str)
|
} if p == relative_path_str)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -584,6 +626,7 @@ mod tests {
|
||||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Usb(u),
|
kind: SignerSourceKind::Usb(u),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
} if u == expected_locator));
|
} if u == expected_locator));
|
||||||
let usb = "usb://ledger?key=0/0".to_string();
|
let usb = "usb://ledger?key=0/0".to_string();
|
||||||
let expected_locator = RemoteWalletLocator {
|
let expected_locator = RemoteWalletLocator {
|
||||||
|
@ -594,6 +637,7 @@ mod tests {
|
||||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Usb(u),
|
kind: SignerSourceKind::Usb(u),
|
||||||
derivation_path: d,
|
derivation_path: d,
|
||||||
|
legacy: false,
|
||||||
} if u == expected_locator && d == expected_derivation_path));
|
} if u == expected_locator && d == expected_derivation_path));
|
||||||
// Catchall into SignerSource::Filepath fails
|
// Catchall into SignerSource::Filepath fails
|
||||||
let junk = "sometextthatisnotapubkeyorfile".to_string();
|
let junk = "sometextthatisnotapubkeyorfile".to_string();
|
||||||
|
@ -603,24 +647,27 @@ mod tests {
|
||||||
Err(SignerSourceError::IoError(_))
|
Err(SignerSourceError::IoError(_))
|
||||||
));
|
));
|
||||||
|
|
||||||
let ask = "ask:".to_string();
|
let prompt = "prompt:".to_string();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse_signer_source(&ask).unwrap(),
|
parse_signer_source(&prompt).unwrap(),
|
||||||
SignerSource {
|
SignerSource {
|
||||||
kind: SignerSourceKind::Ask,
|
kind: SignerSourceKind::Prompt,
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert!(
|
assert!(
|
||||||
matches!(parse_signer_source(&format!("file:{}", absolute_path_str)).unwrap(), SignerSource {
|
matches!(parse_signer_source(&format!("file:{}", absolute_path_str)).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Filepath(p),
|
kind: SignerSourceKind::Filepath(p),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
} if p == absolute_path_str)
|
} if p == absolute_path_str)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(parse_signer_source(&format!("file:{}", relative_path_str)).unwrap(), SignerSource {
|
matches!(parse_signer_source(&format!("file:{}", relative_path_str)).unwrap(), SignerSource {
|
||||||
kind: SignerSourceKind::Filepath(p),
|
kind: SignerSourceKind::Filepath(p),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
|
legacy: false,
|
||||||
} if p == relative_path_str)
|
} if p == relative_path_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,13 +48,13 @@ on your wallet type.
|
||||||
In a paper wallet, the keypair is securely derived from the seed words and
|
In a paper wallet, the keypair is securely derived from the seed words and
|
||||||
optional passphrase you entered when the wallet was create. To use a paper
|
optional passphrase you entered when the wallet was create. To use a paper
|
||||||
wallet keypair anywhere the `<KEYPAIR>` text is shown in examples or help
|
wallet keypair anywhere the `<KEYPAIR>` text is shown in examples or help
|
||||||
documents, enter the uri scheme `ask://` and the program will prompt you to
|
documents, enter the uri scheme `prompt://` and the program will prompt you to
|
||||||
enter your seed words when you run the command.
|
enter your seed words when you run the command.
|
||||||
|
|
||||||
To display the wallet address of a Paper Wallet:
|
To display the wallet address of a Paper Wallet:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask://
|
solana-keygen pubkey prompt://
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File System Wallet
|
#### File System Wallet
|
||||||
|
|
|
@ -155,7 +155,7 @@ solana-keygen new --no-outfile
|
||||||
The corresponding identity public key can now be viewed by running:
|
The corresponding identity public key can now be viewed by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask://
|
solana-keygen pubkey ASK
|
||||||
```
|
```
|
||||||
|
|
||||||
and then entering your seed phrase.
|
and then entering your seed phrase.
|
||||||
|
@ -294,7 +294,7 @@ The ledger will be placed in the `ledger/` directory by default, use the
|
||||||
> [paper wallet seed phrase](../wallet-guide/paper-wallet.md)
|
> [paper wallet seed phrase](../wallet-guide/paper-wallet.md)
|
||||||
> for your `--identity` and/or
|
> for your `--identity` and/or
|
||||||
> `--authorized-voter` keypairs. To use these, pass the respective argument as
|
> `--authorized-voter` keypairs. To use these, pass the respective argument as
|
||||||
> `solana-validator --identity ask:// ... --authorized-voter ask:// ...`
|
> `solana-validator --identity ASK ... --authorized-voter ASK ...`
|
||||||
> and you will be prompted to enter your seed phrases and optional passphrase.
|
> and you will be prompted to enter your seed phrases and optional passphrase.
|
||||||
|
|
||||||
Confirm your validator connected to the network by opening a new terminal and
|
Confirm your validator connected to the network by opening a new terminal and
|
||||||
|
|
|
@ -91,7 +91,7 @@ to use your seed phrase (and a passphrase if you chose to use one) as a signer
|
||||||
with the solana command-line tools using the `ask` uri scheme.
|
with the solana command-line tools using the `ask` uri scheme.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask://
|
solana-keygen pubkey prompt://
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note that you could potentially use different passphrases for the same seed phrase. Each unique passphrase will yield a different keypair.
|
> Note that you could potentially use different passphrases for the same seed phrase. Each unique passphrase will yield a different keypair.
|
||||||
|
@ -103,10 +103,10 @@ will need to pass the `--skip-seed-phrase-validation` argument and forego this
|
||||||
validation.
|
validation.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask:// --skip-seed-phrase-validation
|
solana-keygen pubkey prompt:// --skip-seed-phrase-validation
|
||||||
```
|
```
|
||||||
|
|
||||||
After entering your seed phrase with `solana-keygen pubkey ask://` the console
|
After entering your seed phrase with `solana-keygen pubkey prompt://` the console
|
||||||
will display a string of base-58 character. This is the base _wallet address_
|
will display a string of base-58 character. This is the base _wallet address_
|
||||||
associated with your seed phrase.
|
associated with your seed phrase.
|
||||||
|
|
||||||
|
@ -128,17 +128,17 @@ The solana-cli supports
|
||||||
hierarchical derivation of private keys from your seed phrase and passphrase by
|
hierarchical derivation of private keys from your seed phrase and passphrase by
|
||||||
adding either the `?key=` query string or the `?full-path=` query string.
|
adding either the `?key=` query string or the `?full-path=` query string.
|
||||||
|
|
||||||
To use solana's BIP44 derivation path `m/44'/501'`, supply the `?key=m` query
|
By default, `prompt:` will derive solana's base derivation path `m/44'/501'`. To
|
||||||
string, or `?key=<ACCOUNT>/<CHANGE>`.
|
derive a child key, supply the `?key=<ACCOUNT>/<CHANGE>` query string.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask://?key=0/1
|
solana-keygen pubkey prompt://?key=0/1
|
||||||
```
|
```
|
||||||
|
|
||||||
To use a derivation path other than solana's standard BIP44, you can supply `?full-path=m/<PURPOSE>/<COIN_TYPE>/<ACCOUNT>/<CHANGE>`.
|
To use a derivation path other than solana's standard BIP44, you can supply `?full-path=m/<PURPOSE>/<COIN_TYPE>/<ACCOUNT>/<CHANGE>`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen pubkey ask://?full-path=m/44/2017/0/1
|
solana-keygen pubkey prompt://?full-path=m/44/2017/0/1
|
||||||
```
|
```
|
||||||
|
|
||||||
Because Solana uses Ed25519 keypairs, as per
|
Because Solana uses Ed25519 keypairs, as per
|
||||||
|
@ -153,10 +153,10 @@ To verify you control the private key of a paper wallet address, use
|
||||||
`solana-keygen verify`:
|
`solana-keygen verify`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-keygen verify <PUBKEY> ask://
|
solana-keygen verify <PUBKEY> prompt://
|
||||||
```
|
```
|
||||||
|
|
||||||
where `<PUBKEY>` is replaced with the wallet address and they keyword `ask://`
|
where `<PUBKEY>` is replaced with the wallet address and the keyword `prompt://`
|
||||||
tells the command to prompt you for the keypair's seed phrase; `key` and
|
tells the command to prompt you for the keypair's seed phrase; `key` and
|
||||||
`full-path` query-strings accepted. Note that for security reasons, your seed
|
`full-path` query-strings accepted. Note that for security reasons, your seed
|
||||||
phrase will not be displayed as you type. After entering your seed phrase, the
|
phrase will not be displayed as you type. After entering your seed phrase, the
|
||||||
|
|
|
@ -589,7 +589,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
let keypair = keypair_from_seed_phrase("recover", skip_validation, true, None)?;
|
let keypair = keypair_from_seed_phrase("recover", skip_validation, true, None, true)?;
|
||||||
output_keypair(&keypair, &outfile, "recovered")?;
|
output_keypair(&keypair, &outfile, "recovered")?;
|
||||||
}
|
}
|
||||||
("grind", Some(matches)) => {
|
("grind", Some(matches)) => {
|
||||||
|
|
|
@ -398,16 +398,13 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided;
|
/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided;
|
||||||
/// otherwise builds standard Keypair using the seed as SecretKey
|
/// otherwise generates the base Bip44 Solana keypair from the seed
|
||||||
pub fn keypair_from_seed_and_derivation_path(
|
pub fn keypair_from_seed_and_derivation_path(
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
derivation_path: Option<DerivationPath>,
|
derivation_path: Option<DerivationPath>,
|
||||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||||
if let Some(derivation_path) = derivation_path {
|
let derivation_path = derivation_path.unwrap_or_else(DerivationPath::default);
|
||||||
bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
|
bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
|
||||||
} else {
|
|
||||||
keypair_from_seed(seed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a Keypair using Bip32 Hierarchical Derivation
|
/// Generates a Keypair using Bip32 Hierarchical Derivation
|
||||||
|
|
Loading…
Reference in New Issue