Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Init
uses: carteramesh/ci/.github/actions/rust-init@main
with:
ubuntu_packages: "libhidapi-dev libudev-dev libgpgme-dev"
ubuntu_packages: "libudev-dev libgpgme-dev"
- uses: CarteraMesh/fireblocks-config-action@main
id: config
with:
Expand Down
3 changes: 2 additions & 1 deletion crates/clap-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ path = "src/lib.rs"
default = ["fireblocks"]
fireblocks = ["log"]
gpg = ["fireblocks-solana-signer/gpg"]
remote-wallet = []

[dependencies]
chrono = { workspace = true, features = ["default"] }
Expand All @@ -33,7 +34,7 @@ rpassword = { workspace = true }
solana-cluster-type = { workspace = true }
solana-commitment-config = { workspace = true }
solana-derivation-path = { workspace = true }
solana-remote-wallet = { workspace = true, features = ["default"] }
solana-remote-wallet = { workspace = true }
solana-sdk = { workspace = true }
thiserror = { workspace = true }
tiny-bip39 = { workspace = true }
Expand Down
82 changes: 51 additions & 31 deletions crates/clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ use {
keypair_from_seed_phrase_and_passphrase, null_signer::NullSigner, read_keypair,
read_keypair_file, Keypair, Signature, Signer,
},
signer::presigner::Presigner,
signer::{keypair::generate_seed_from_seed_phrase_and_passphrase, presigner::Presigner},
},
solana_sdk::signer::keypair::generate_seed_from_seed_phrase_and_passphrase,
std::{
cell::RefCell,
convert::TryFrom,
Expand Down Expand Up @@ -177,7 +176,8 @@ impl DefaultSigner {
})
.map_err(|_| {
std::io::Error::other(format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
"No default signer found, run \"solana-keygen new -o {}\" to create a new \
one",
self.path
))
})?;
Expand Down Expand Up @@ -764,6 +764,7 @@ pub fn signer_from_path(
/// )?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[allow(unused_variables)]
pub fn signer_from_path_with_config(
matches: &ArgMatches,
path: &str,
Expand All @@ -789,7 +790,8 @@ pub fn signer_from_path_with_config(
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a keypair file: {e}"
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(file) => Ok(Box::new(file)),
Expand All @@ -798,6 +800,12 @@ pub fn signer_from_path_with_config(
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
#[cfg(not(feature = "remote-wallet"))]
SignerSourceKind::Usb(locator) => Err(std::io::Error::other(format!(
"USB wallet support is not enabled for this platform {locator}."
))
.into()),
#[cfg(feature = "remote-wallet")]
SignerSourceKind::Usb(locator) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
Expand All @@ -823,16 +831,18 @@ pub fn signer_from_path_with_config(
} else if config.allow_null_signer || matches.is_present(SIGN_ONLY_ARG.name) {
Ok(Box::new(NullSigner::new(&pubkey)))
} else {
Err(std::io::Error::other(
format!("missing signature for supplied pubkey: {pubkey}"),
)
Err(std::io::Error::other(format!(
"missing signature for supplied pubkey: {pubkey}"
))
.into())
}
},
}
#[cfg(feature = "fireblocks")]
SignerSourceKind::Fireblocks(profile) => {
Ok(Box::new(fireblocks_solana_signer::FireblocksSigner::try_from_config(&[profile], |tx| log::info!("{tx}"))?))
},
SignerSourceKind::Fireblocks(profile) => Ok(Box::new(
fireblocks_solana_signer::FireblocksSigner::try_from_config(&[profile], |tx| {
log::info!("{tx}")
})?,
)),
}
}

Expand Down Expand Up @@ -913,8 +923,8 @@ pub fn resolve_signer_from_path(
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". \
Run \"solana-keygen new\" to create a keypair file: {e}"
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(_) => Ok(Some(path.to_string())),
Expand Down Expand Up @@ -953,7 +963,8 @@ pub const ASK_KEYWORD: &str = "ASK";
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
long: "skip-seed-phrase-validation",
name: "skip_seed_phrase_validation",
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 official English word list",
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 \
official English word list",
};

/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
Expand Down Expand Up @@ -1032,8 +1043,8 @@ pub fn keypair_from_path(
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". \
Run \"solana-keygen new\" to create a keypair file: {e}"
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(file) => Ok(file),
Expand Down Expand Up @@ -1063,7 +1074,8 @@ pub fn keypair_from_seed_phrase(
let seed_phrase = prompt_password(format!("[{keypair_name}] seed phrase: "))?;
let seed_phrase = seed_phrase.trim();
let passphrase_prompt = format!(
"[{keypair_name}] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: ",
"[{keypair_name}] If this seed phrase has an associated passphrase, enter it now. \
Otherwise, press ENTER to continue: ",
);

let keypair = if skip_validation {
Expand Down Expand Up @@ -1127,12 +1139,13 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {

#[cfg(test)]
mod tests {
#[cfg(feature = "remote-wallet")]
use solana_remote_wallet::{locator::Manufacturer, remote_wallet::initialize_wallet_manager};
use {
super::*,
crate::offline::OfflineArgs,
assert_matches::assert_matches,
clap::{value_t_or_exit, App, Arg},
solana_remote_wallet::{locator::Manufacturer, remote_wallet::initialize_wallet_manager},
solana_sdk::signature::write_keypair_file,
solana_system_interface::instruction::transfer,
tempfile::{NamedTempFile, TempDir},
Expand Down Expand Up @@ -1238,27 +1251,30 @@ mod tests {
} if p == relative_path_str)
);

let usb = "usb://ledger".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
assert_matches!(parse_signer_source(usb).unwrap(), SignerSource {
#[cfg(feature = "remote-wallet")]
{
let usb = "usb://ledger".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
assert_matches!(parse_signer_source(usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: None,
legacy: false,
} if u == expected_locator);
let usb = "usb://ledger?key=0/0".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
assert_matches!(parse_signer_source(usb).unwrap(), SignerSource {
let usb = "usb://ledger?key=0/0".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
assert_matches!(parse_signer_source(usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: d,
legacy: false,
} if u == expected_locator && d == expected_derivation_path);
}
// Catchall into SignerSource::Filepath fails
let junk = "sometextthatisnotapubkeyorfile".to_string();
assert!(Pubkey::from_str(&junk).is_err());
Expand Down Expand Up @@ -1332,12 +1348,16 @@ mod tests {
let clap_matches = clap_app.get_matches_from(args);
let keypair_str = value_t_or_exit!(clap_matches, "keypair", String);

#[cfg(feature = "remote-wallet")]
let wallet_manager = initialize_wallet_manager()?;

let signer = signer_from_path(
&clap_matches,
&keypair_str,
"signer",
#[cfg(not(feature = "remote-wallet"))]
&mut None,
#[cfg(feature = "remote-wallet")]
&mut Some(wallet_manager),
)?;

Expand Down
3 changes: 2 additions & 1 deletion crates/clap-v3-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ default = ["fireblocks"]
elgamal = ["dep:solana-zk-sdk"]
fireblocks = ["fireblocks-solana-signer"]
gpg = ["fireblocks-solana-signer/gpg"]
remote-wallet = []

[dependencies]
chrono = { workspace = true, features = ["default"] }
Expand All @@ -29,7 +30,7 @@ rpassword = { workspace = true }
solana-cluster-type = { workspace = true }
solana-commitment-config = { workspace = true }
solana-derivation-path = { workspace = true }
solana-remote-wallet = { workspace = true, features = ["default"] }
solana-remote-wallet = { workspace = true }
solana-sdk = { workspace = true }
solana-zk-sdk = { workspace = true, optional = true }
thiserror = { workspace = true }
Expand Down
67 changes: 41 additions & 26 deletions crates/clap-v3-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ use {
null_signer::NullSigner, read_keypair, read_keypair_file, EncodableKey,
EncodableKeypair, Keypair, Signature, Signer,
},
signer::{presigner::Presigner, SeedDerivable},
signer::{
keypair::generate_seed_from_seed_phrase_and_passphrase, presigner::Presigner,
SeedDerivable,
},
},
solana_sdk::signer::keypair::generate_seed_from_seed_phrase_and_passphrase,
std::{
cell::RefCell,
error,
Expand Down Expand Up @@ -173,12 +175,11 @@ impl DefaultSigner {
}
})
.map_err(|_| {
std::io::Error::other(
format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
std::io::Error::other(format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new \
one",
self.path
),
)
))
})?;
*self.is_path_checked.borrow_mut() = true;
}
Expand Down Expand Up @@ -654,9 +655,10 @@ pub fn signer_from_source_with_config(
)?))
}
SignerSourceKind::Filepath(path) => match read_keypair_file(path) {
Err(e) => Err(std::io::Error::other(
format!("could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a keypair file: {e}"),
)
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(file) => Ok(Box::new(file)),
},
Expand All @@ -682,24 +684,30 @@ pub fn signer_from_source_with_config(
}
}
SignerSourceKind::Pubkey(pubkey) => {
let presigner = try_pubkeys_sigs_of(matches, SIGNER_ARG.name).ok().flatten()
let presigner = try_pubkeys_sigs_of(matches, SIGNER_ARG.name)
.ok()
.flatten()
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(pubkey, presigners));
if let Some(presigner) = presigner {
Ok(Box::new(presigner))
} else if config.allow_null_signer || matches.try_contains_id(SIGN_ONLY_ARG.name).unwrap_or(false) {
} else if config.allow_null_signer
|| matches.try_contains_id(SIGN_ONLY_ARG.name).unwrap_or(false)
{
Ok(Box::new(NullSigner::new(pubkey)))
} else {
Err(std::io::Error::other(
format!("missing signature for supplied pubkey: {pubkey}"),
)
Err(std::io::Error::other(format!(
"missing signature for supplied pubkey: {pubkey}"
))
.into())
}
},
}
#[cfg(feature = "fireblocks")]
SignerSourceKind::Fireblocks(profile) => {
Ok(Box::new(fireblocks_solana_signer::FireblocksSigner::try_from_config(&[profile], |tx| tracing::info!("{tx}"))?))
},
SignerSourceKind::Fireblocks(profile) => Ok(Box::new(
fireblocks_solana_signer::FireblocksSigner::try_from_config(&[profile], |tx| {
tracing::info!("{tx}")
})?,
)),
}
}

Expand Down Expand Up @@ -799,8 +807,8 @@ pub fn resolve_signer_from_source(
}
SignerSourceKind::Filepath(path) => match read_keypair_file(path) {
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". \
Run \"solana-keygen new\" to create a keypair file: {e}"
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(_) => Ok(Some(path.to_string())),
Expand Down Expand Up @@ -848,7 +856,8 @@ pub const ASK_KEYWORD: &str = "ASK";
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
long: "skip-seed-phrase-validation",
name: "skip_seed_phrase_validation",
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 official English word list",
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 \
official English word list",
};

/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
Expand Down Expand Up @@ -1085,8 +1094,8 @@ fn encodable_key_from_source<K: EncodableKey + SeedDerivable>(
)?),
SignerSourceKind::Filepath(path) => match K::read_from_file(path) {
Err(e) => Err(std::io::Error::other(format!(
"could not read keypair file \"{path}\". \
Run \"solana-keygen new\" to create a keypair file: {e}"
"could not read keypair file \"{path}\". Run \"solana-keygen new\" to create a \
keypair file: {e}"
))
.into()),
Ok(file) => Ok(file),
Expand Down Expand Up @@ -1166,7 +1175,8 @@ fn encodable_key_from_seed_phrase<K: EncodableKey + SeedDerivable>(
let seed_phrase = prompt_password(format!("[{key_name}] seed phrase: "))?;
let seed_phrase = seed_phrase.trim();
let passphrase_prompt = format!(
"[{key_name}] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: ",
"[{key_name}] If this seed phrase has an associated passphrase, enter it now. Otherwise, \
press ENTER to continue: ",
);

let key = if skip_validation {
Expand Down Expand Up @@ -1217,11 +1227,12 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {

#[cfg(test)]
mod tests {
#[cfg(feature = "remote-wallet")]
use solana_remote_wallet::remote_wallet::initialize_wallet_manager;
use {
super::*,
crate::offline::OfflineArgs,
clap::{Arg, Command},
solana_remote_wallet::remote_wallet::initialize_wallet_manager,
solana_sdk::signature::write_keypair_file,
solana_system_interface::instruction::transfer,
tempfile::TempDir,
Expand Down Expand Up @@ -1287,12 +1298,16 @@ mod tests {
let clap_matches = clap_app.get_matches_from(args);
let keypair_str: String = clap_matches.value_of_t_or_exit("keypair");

#[cfg(feature = "remote-wallet")]
let wallet_manager = initialize_wallet_manager()?;

let signer = signer_from_path(
&clap_matches,
&keypair_str,
"signer",
#[cfg(not(feature = "remote-wallet"))]
&mut None,
#[cfg(feature = "remote-wallet")]
&mut Some(wallet_manager),
)?;

Expand Down
Loading
Loading