Skip to content

Commit

Permalink
cli: program deploy with offline signing (--sign-only mode)
Browse files Browse the repository at this point in the history
  • Loading branch information
norwnd committed Oct 28, 2023
1 parent b0bf24b commit 0ac8a3d
Show file tree
Hide file tree
Showing 16 changed files with 1,455 additions and 314 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions clap-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ thiserror = { workspace = true }
tiny-bip39 = { workspace = true }
uriparse = { workspace = true }
url = { workspace = true }
itertools = { workspace = true }

[dev-dependencies]
assert_matches = { workspace = true }
Expand Down
26 changes: 24 additions & 2 deletions clap-utils/src/input_parsers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use {
crate::keypair::{
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
signer_from_path_with_config, SignerFromPathConfig, ASK_KEYWORD,
SKIP_SEED_PHRASE_VALIDATION_ARG,
},
chrono::DateTime,
clap::ArgMatches,
Expand Down Expand Up @@ -118,7 +119,7 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
})
}

// Return a signer from matches at `name`
// Return a signer from matches at `name` (returns error if signer is NullSigner).
#[allow(clippy::type_complexity)]
pub fn signer_of(
matches: &ArgMatches<'_>,
Expand All @@ -134,6 +135,27 @@ pub fn signer_of(
}
}

// Return a signer from matches at `name` (returns NullSigner and doesn't error).
#[allow(clippy::type_complexity)]
pub fn signer_of_allow_null_signer(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: &mut Option<Rc<RemoteWalletManager>>,
) -> Result<(Option<Box<dyn Signer>>, Option<Pubkey>), Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) {
// Allow pubkey signers without accompanying signatures
let config = SignerFromPathConfig {
allow_null_signer: true,
};
let signer =
signer_from_path_with_config(matches, location, name, wallet_manager, &config)?;
let signer_pubkey = signer.pubkey();
Ok((Some(signer), Some(signer_pubkey)))
} else {
Ok((None, None))
}
}

pub fn pubkey_of_signer(
matches: &ArgMatches<'_>,
name: &str,
Expand Down
41 changes: 29 additions & 12 deletions clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use {
},
bip39::{Language, Mnemonic, Seed},
clap::ArgMatches,
itertools::Itertools,
rpassword::prompt_password,
solana_remote_wallet::{
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
Expand Down Expand Up @@ -66,6 +67,7 @@ impl SignOnly {
}
pub type CliSigners = Vec<Box<dyn Signer>>;
pub type SignerIndex = usize;
#[derive(Debug)]
pub struct CliSignerInfo {
pub signers: CliSigners,
}
Expand Down Expand Up @@ -196,10 +198,13 @@ impl DefaultSigner {
/// `bulk_signers` is a vector of signers, all of which are optional. If any
/// of those signers is `None`, then the default signer will be loaded; if
/// all of those signers are `Some`, then the default signer will not be
/// loaded.
/// loaded. If multiple equivalent (same pub key) signers are provided - only
/// one of those will be returned in the result, such that NullSigner(s)
/// always get lower priority.
///
/// The returned value includes all of the `bulk_signers` that were not
/// `None`, and maybe the default signer, if it was loaded.
/// `None`, and maybe the default signer (if it was loaded). There is no
/// guarantees on resulting signers ordering.
///
/// # Examples
///
Expand Down Expand Up @@ -245,17 +250,29 @@ impl DefaultSigner {
wallet_manager: &mut Option<Rc<RemoteWalletManager>>,
) -> Result<CliSignerInfo, Box<dyn error::Error>> {
let mut unique_signers = vec![];

// Determine if the default signer is needed
if bulk_signers.iter().any(|signer| signer.is_none()) {
let default_signer = self.signer_from_path(matches, wallet_manager)?;
unique_signers.push(default_signer);
}

for signer in bulk_signers.into_iter().flatten() {
if !unique_signers.iter().any(|s| s == &signer) {
unique_signers.push(signer);
// Group provided signers by pub key
for (_, mut signers) in &bulk_signers.into_iter().group_by(|signer| -> Pubkey {
if let Some(signer) = signer {
return signer.pubkey();
}
Pubkey::default()
}) {
let best_signer = signers.next().unwrap(); // must have at least 1 elem
if best_signer.is_none() {
// If there is a group of None signers, we need to add default one.
let default_signer = self.signer_from_path(matches, wallet_manager)?;
unique_signers.push(default_signer);
continue; // nothing else to do for this group
}
let mut best_signer = best_signer.unwrap(); // can't be None here
for signer in signers.skip(1) {
let signer = signer.unwrap(); // can't be None here
if !signer.is_null_signer() {
best_signer = signer;
break; // prefer any signer over null signer
}
}
unique_signers.push(best_signer);
}
Ok(CliSignerInfo {
signers: unique_signers,
Expand Down
15 changes: 14 additions & 1 deletion cli-output/src/cli_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2090,14 +2090,26 @@ impl fmt::Display for CliProgramId {
#[serde(rename_all = "camelCase")]
pub struct CliProgramBuffer {
pub buffer: String,
pub program_data_max_len: usize,
pub min_rent_exempt_program_balance: u64,
}

impl QuietDisplay for CliProgramBuffer {}
impl VerboseDisplay for CliProgramBuffer {}

impl fmt::Display for CliProgramBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Buffer:", &self.buffer)
writeln_name_value(f, "Buffer:", &self.buffer)?;
writeln_name_value(
f,
"Program data max length:",
&format!("{:?}", self.program_data_max_len),
)?;
writeln_name_value(
f,
"Min rent-exempt program balance:",
&format!("{:?}", self.min_rent_exempt_program_balance),
)
}
}

Expand Down Expand Up @@ -3134,6 +3146,7 @@ mod tests {

#[test]
fn test_return_signers() {
#[derive(Debug)]
struct BadSigner {
pubkey: Pubkey,
}
Expand Down
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ solana_rbpf = { workspace = true }
spl-memo = { workspace = true, features = ["no-entrypoint"] }
thiserror = { workspace = true }
tiny-bip39 = { workspace = true }
rand = { workspace = true }

[dev-dependencies]
assert_matches = { workspace = true }
Expand Down
Loading

0 comments on commit 0ac8a3d

Please sign in to comment.