Skip to content

Commit

Permalink
pcli: import seed phrase from stdin
Browse files Browse the repository at this point in the history
Changes the behavior for `pcli keys import phrase` to prompt for a
masked password input interactively. When run via a pipe, reads from
stdin instead, which preserves the ability to import seed phrases from
other tools.
  • Loading branch information
conorsch committed Jun 26, 2023
1 parent d0764d3 commit 5722c20
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 9 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions crates/bin/pcli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ibc-types = { git = "https://github.com/penumbra-zone/ibc-types", branch = "main

ibc-proto = { version = "0.31.0" }
ark-ff = { version = "0.4", default-features = false }
atty = "0.2"
ed25519-consensus = "2"
futures = "0.3"
async-stream = "0.2"
Expand All @@ -73,6 +74,7 @@ hex = "0.4"
rand = "0.8"
rand_chacha = "0.3.1"
rand_core = { version = "0.6.3", features = ["getrandom"] }
rpassword = "7"
indicatif = "0.16"
http-body = "0.4.5"
clap = { version = "3", features = ["derive", "env"] }
Expand Down
28 changes: 21 additions & 7 deletions crates/bin/pcli/src/command/keys.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::io::Read;
use std::str::FromStr;

use anyhow::{anyhow, Result};
Expand All @@ -24,11 +25,9 @@ pub enum KeysCmd {

#[derive(Debug, clap::Subcommand)]
pub enum ImportCmd {
/// Import from an existing seed phrase.
Phrase {
/// A 24 word phrase in quotes.
seed_phrase: String,
},
/// Import wallet from an existing 24-word seed phrase. Will prompt for input interactively.
/// Also accepts input from stdin, for use with pipes.
Phrase,
}

#[derive(Debug, clap::Subcommand)]
Expand Down Expand Up @@ -79,8 +78,23 @@ impl KeysCmd {
wallet.save(data_dir.join(crate::CUSTODY_FILE_NAME))?;
self.archive_wallet(&wallet)?;
}
KeysCmd::Import(ImportCmd::Phrase { seed_phrase }) => {
let wallet = KeyStore::from_seed_phrase(SeedPhrase::from_str(seed_phrase)?);
KeysCmd::Import(ImportCmd::Phrase) => {
let mut seed_phrase = String::new();
// The `rpassword` crate doesn't support reading from stdin, so we check
// for an interactive session. We must support non-interactive use cases,
// for integration with other tooling.
if atty::is(atty::Stream::Stdin) {
seed_phrase = rpassword::prompt_password("Enter seed phrase: ")?;
} else {
while let Ok(n_bytes) = std::io::stdin().lock().read_to_string(&mut seed_phrase)
{
if n_bytes == 0 {
break;
}
seed_phrase = seed_phrase.trim().to_string();
}
}
let wallet = KeyStore::from_seed_phrase(SeedPhrase::from_str(&seed_phrase)?);
wallet.save(data_dir.join(crate::CUSTODY_FILE_NAME))?;
self.archive_wallet(&wallet)?;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bin/pcli/tests/network_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ fn load_wallet_into_tmpdir() -> TempDir {
"keys",
"import",
"phrase",
SEED_PHRASE,
])
.write_stdin(SEED_PHRASE)
.timeout(std::time::Duration::from_secs(TIMEOUT_COMMAND_SECONDS));
setup_cmd
.assert()
Expand Down
4 changes: 3 additions & 1 deletion crates/wallet/src/key_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ impl KeyStore {
/// Write the wallet data to the provided path.
pub fn save(&self, path: impl AsRef<std::path::Path>) -> anyhow::Result<()> {
if path.as_ref().exists() {
let p = path.as_ref().to_string_lossy();
return Err(anyhow::anyhow!(
"Wallet file already exists, refusing to overwrite it"
"Wallet file already exists, refusing to overwrite it: {}",
&p
));
}
use std::io::Write;
Expand Down

0 comments on commit 5722c20

Please sign in to comment.