CLI: dynamic signing reboot (#8384)
* Add keypair_util_from_path helper * Cli: impl config.keypair as a trait object * SDK: Add Debug and PartialEq for dyn Signer * ClapUtils: Arg parsing from pubkey+signers to Presigner * Impl Signers for &dyn Signer collections * CLI: Add helper for getting signers from args * CLI: Replace SigningAuthority with Signer trait-objs * CLI: Drop disused signers command field * CLI: Drop redundant tests * Add clap validator that handles all current signer types * clap_utils: Factor Presigner resolution to helper * SDK: `From` for boxing Signer implementors to trait objects * SDK: Derive `Clone` for `Presigner` * Remove panic * Cli: dedup signers in transfer for remote-wallet ergonomics * Update docs vis-a-vis ASK changes * Cli: update transaction types to use new dynamic-signer methods * CLI: Fix tests No. 1 what to do about write_keypair outstanding * Work around `CliConfig`'s signer not necessarily being a `Keypair` * CLI: Fix tests No. 2 * Remove unused arg * Remove unused methods * Move offline arg constants upstream * Make cli signing fallible Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>
This commit is contained in:
		
							
								
								
									
										583
									
								
								cli/src/cli.rs
									
									
									
									
									
								
							
							
						
						
									
										583
									
								
								cli/src/cli.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -20,9 +20,11 @@ use solana_sdk::{
 | 
			
		||||
    commitment_config::CommitmentConfig,
 | 
			
		||||
    epoch_schedule::Epoch,
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    message::Message,
 | 
			
		||||
    pubkey::Pubkey,
 | 
			
		||||
    signature::{Keypair, Signer},
 | 
			
		||||
    system_transaction,
 | 
			
		||||
    system_instruction,
 | 
			
		||||
    transaction::Transaction,
 | 
			
		||||
};
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::{HashMap, VecDeque},
 | 
			
		||||
@@ -743,8 +745,10 @@ pub fn process_ping(
 | 
			
		||||
        let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
 | 
			
		||||
        last_blockhash = recent_blockhash;
 | 
			
		||||
 | 
			
		||||
        let transaction =
 | 
			
		||||
            system_transaction::transfer(&config.keypair, &to, lamports, recent_blockhash);
 | 
			
		||||
        let ix = system_instruction::transfer(&config.keypair.pubkey(), &to, lamports);
 | 
			
		||||
        let message = Message::new(vec![ix]);
 | 
			
		||||
        let mut transaction = Transaction::new_unsigned(message);
 | 
			
		||||
        transaction.try_sign(&[config.keypair.as_ref()], recent_blockhash)?;
 | 
			
		||||
        check_account_for_fee(
 | 
			
		||||
            rpc_client,
 | 
			
		||||
            &config.keypair.pubkey(),
 | 
			
		||||
 
 | 
			
		||||
@@ -4,17 +4,13 @@ use console::style;
 | 
			
		||||
use solana_clap_utils::{
 | 
			
		||||
    input_parsers::derivation_of,
 | 
			
		||||
    input_validators::{is_derivation, is_url},
 | 
			
		||||
    keypair::{
 | 
			
		||||
        self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
 | 
			
		||||
        SKIP_SEED_PHRASE_VALIDATION_ARG,
 | 
			
		||||
    },
 | 
			
		||||
    keypair::{keypair_util_from_path, SKIP_SEED_PHRASE_VALIDATION_ARG},
 | 
			
		||||
};
 | 
			
		||||
use solana_cli::{
 | 
			
		||||
    cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError},
 | 
			
		||||
    display::{println_name_value, println_name_value_or},
 | 
			
		||||
};
 | 
			
		||||
use solana_cli_config::config::{Config, CONFIG_FILE};
 | 
			
		||||
use solana_sdk::signature::read_keypair_file;
 | 
			
		||||
 | 
			
		||||
use std::error;
 | 
			
		||||
 | 
			
		||||
@@ -105,42 +101,24 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
 | 
			
		||||
    } = parse_command(&matches)?;
 | 
			
		||||
 | 
			
		||||
    let (keypair, keypair_path) = if require_keypair {
 | 
			
		||||
        let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
 | 
			
		||||
        match source {
 | 
			
		||||
            keypair::Source::Path => (
 | 
			
		||||
                keypair,
 | 
			
		||||
                Some(matches.value_of("keypair").unwrap().to_string()),
 | 
			
		||||
            ),
 | 
			
		||||
            keypair::Source::SeedPhrase => (keypair, None),
 | 
			
		||||
            keypair::Source::Generated => {
 | 
			
		||||
                let keypair_path = if config.keypair_path != "" {
 | 
			
		||||
                    config.keypair_path
 | 
			
		||||
                } else {
 | 
			
		||||
                    let default_keypair_path = CliConfig::default_keypair_path();
 | 
			
		||||
                    if !std::path::Path::new(&default_keypair_path).exists() {
 | 
			
		||||
                        return Err(CliError::KeypairFileNotFound(format!(
 | 
			
		||||
                            "Generate a new keypair at {} with `solana-keygen new`",
 | 
			
		||||
                            default_keypair_path
 | 
			
		||||
                        ))
 | 
			
		||||
                        .into());
 | 
			
		||||
                    }
 | 
			
		||||
        let path = if matches.is_present("keypair") {
 | 
			
		||||
            matches.value_of("keypair").unwrap().to_string()
 | 
			
		||||
        } else if config.keypair_path != "" {
 | 
			
		||||
            config.keypair_path
 | 
			
		||||
        } else {
 | 
			
		||||
            let default_keypair_path = CliConfig::default_keypair_path();
 | 
			
		||||
            if !std::path::Path::new(&default_keypair_path).exists() {
 | 
			
		||||
                return Err(CliError::KeypairFileNotFound(format!(
 | 
			
		||||
                    "Generate a new keypair at {} with `solana-keygen new`",
 | 
			
		||||
                    default_keypair_path
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let keypair = if keypair_path.starts_with("usb://") {
 | 
			
		||||
                    keypair
 | 
			
		||||
                } else {
 | 
			
		||||
                    read_keypair_file(&keypair_path).or_else(|err| {
 | 
			
		||||
                        Err(CliError::BadParameter(format!(
 | 
			
		||||
                            "{}: Unable to open keypair file: {}",
 | 
			
		||||
                            err, keypair_path
 | 
			
		||||
                        )))
 | 
			
		||||
                    })?
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                (keypair, Some(keypair_path))
 | 
			
		||||
                ))
 | 
			
		||||
                .into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            default_keypair_path
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let keypair = keypair_util_from_path(matches, &path, "keypair")?;
 | 
			
		||||
        (keypair, Some(path))
 | 
			
		||||
    } else {
 | 
			
		||||
        let default = CliConfig::default();
 | 
			
		||||
        (default.keypair, None)
 | 
			
		||||
@@ -201,6 +179,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
 | 
			
		||||
        Arg::with_name("derivation_path")
 | 
			
		||||
            .long("derivation-path")
 | 
			
		||||
            .value_name("ACCOUNT or ACCOUNT/CHANGE")
 | 
			
		||||
            .global(true)
 | 
			
		||||
            .takes_value(true)
 | 
			
		||||
            .validator(is_derivation)
 | 
			
		||||
            .help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'")
 | 
			
		||||
@@ -212,15 +191,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
 | 
			
		||||
            .global(true)
 | 
			
		||||
            .help("Show extra information header"),
 | 
			
		||||
    )
 | 
			
		||||
    .arg(
 | 
			
		||||
        Arg::with_name(ASK_SEED_PHRASE_ARG.name)
 | 
			
		||||
            .long(ASK_SEED_PHRASE_ARG.long)
 | 
			
		||||
            .value_name("KEYPAIR NAME")
 | 
			
		||||
            .global(true)
 | 
			
		||||
            .takes_value(true)
 | 
			
		||||
            .possible_values(&["keypair"])
 | 
			
		||||
            .help(ASK_SEED_PHRASE_ARG.help),
 | 
			
		||||
    )
 | 
			
		||||
    .arg(
 | 
			
		||||
        Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
 | 
			
		||||
            .long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										108
									
								
								cli/src/nonce.rs
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								cli/src/nonce.rs
									
									
									
									
									
								
							@@ -1,19 +1,20 @@
 | 
			
		||||
use crate::cli::{
 | 
			
		||||
    build_balance_message, check_account_for_fee, check_unique_pubkeys,
 | 
			
		||||
    log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
 | 
			
		||||
    SigningAuthority,
 | 
			
		||||
};
 | 
			
		||||
use crate::offline::BLOCKHASH_ARG;
 | 
			
		||||
use clap::{App, Arg, ArgMatches, SubCommand};
 | 
			
		||||
use solana_clap_utils::{input_parsers::*, input_validators::*, ArgConstant};
 | 
			
		||||
use solana_clap_utils::{
 | 
			
		||||
    input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
 | 
			
		||||
};
 | 
			
		||||
use solana_client::rpc_client::RpcClient;
 | 
			
		||||
use solana_sdk::{
 | 
			
		||||
    account::Account,
 | 
			
		||||
    account_utils::StateMut,
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    message::Message,
 | 
			
		||||
    nonce_state::{Meta, NonceState},
 | 
			
		||||
    pubkey::Pubkey,
 | 
			
		||||
    signature::{Keypair, Signer},
 | 
			
		||||
    signature::Signer,
 | 
			
		||||
    system_instruction::{
 | 
			
		||||
        advance_nonce_account, authorize_nonce_account, create_address_with_seed,
 | 
			
		||||
        create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
 | 
			
		||||
@@ -65,8 +66,8 @@ pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name(NONCE_AUTHORITY_ARG.name)
 | 
			
		||||
        .long(NONCE_AUTHORITY_ARG.long)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("KEYPAIR or PUBKEY")
 | 
			
		||||
        .validator(is_pubkey_or_keypair_or_ask_keyword)
 | 
			
		||||
        .value_name("KEYPAIR or PUBKEY or REMOTE WALLET PATH")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help(NONCE_AUTHORITY_ARG.help)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -218,8 +219,7 @@ impl NonceSubCommands for App<'_, '_> {
 | 
			
		||||
pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
 | 
			
		||||
    let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
 | 
			
		||||
    let new_authority = pubkey_of(matches, "new_authority").unwrap();
 | 
			
		||||
    let nonce_authority =
 | 
			
		||||
        SigningAuthority::new_from_matches(&matches, NONCE_AUTHORITY_ARG.name, None)?;
 | 
			
		||||
    let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?;
 | 
			
		||||
 | 
			
		||||
    Ok(CliCommandInfo {
 | 
			
		||||
        command: CliCommand::AuthorizeNonceAccount {
 | 
			
		||||
@@ -232,7 +232,7 @@ pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliComm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_nonce_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
 | 
			
		||||
    let nonce_account = keypair_of(matches, "nonce_account_keypair").unwrap();
 | 
			
		||||
    let nonce_account = signer_of("nonce_account_keypair", matches)?.unwrap();
 | 
			
		||||
    let seed = matches.value_of("seed").map(|s| s.to_string());
 | 
			
		||||
    let lamports = lamports_of_sol(matches, "amount").unwrap();
 | 
			
		||||
    let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
 | 
			
		||||
@@ -259,8 +259,7 @@ pub fn parse_get_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliEr
 | 
			
		||||
 | 
			
		||||
pub fn parse_new_nonce(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
 | 
			
		||||
    let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
 | 
			
		||||
    let nonce_authority =
 | 
			
		||||
        SigningAuthority::new_from_matches(&matches, NONCE_AUTHORITY_ARG.name, None)?;
 | 
			
		||||
    let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?;
 | 
			
		||||
 | 
			
		||||
    Ok(CliCommandInfo {
 | 
			
		||||
        command: CliCommand::NewNonce {
 | 
			
		||||
@@ -290,8 +289,7 @@ pub fn parse_withdraw_from_nonce_account(
 | 
			
		||||
    let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
 | 
			
		||||
    let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
 | 
			
		||||
    let lamports = lamports_of_sol(matches, "amount").unwrap();
 | 
			
		||||
    let nonce_authority =
 | 
			
		||||
        SigningAuthority::new_from_matches(&matches, NONCE_AUTHORITY_ARG.name, None)?;
 | 
			
		||||
    let nonce_authority = signer_of(NONCE_AUTHORITY_ARG.name, matches)?;
 | 
			
		||||
 | 
			
		||||
    Ok(CliCommandInfo {
 | 
			
		||||
        command: CliCommand::WithdrawFromNonceAccount {
 | 
			
		||||
@@ -336,36 +334,35 @@ pub fn process_authorize_nonce_account(
 | 
			
		||||
    rpc_client: &RpcClient,
 | 
			
		||||
    config: &CliConfig,
 | 
			
		||||
    nonce_account: &Pubkey,
 | 
			
		||||
    nonce_authority: Option<&SigningAuthority>,
 | 
			
		||||
    nonce_authority: Option<&dyn Signer>,
 | 
			
		||||
    new_authority: &Pubkey,
 | 
			
		||||
) -> ProcessResult {
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
 | 
			
		||||
    let nonce_authority = nonce_authority
 | 
			
		||||
        .map(|a| a.keypair())
 | 
			
		||||
        .unwrap_or(&config.keypair);
 | 
			
		||||
    let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref());
 | 
			
		||||
    let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        vec![ix],
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &[&config.keypair, nonce_authority],
 | 
			
		||||
    let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(
 | 
			
		||||
        &[config.keypair.as_ref(), nonce_authority],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    )?;
 | 
			
		||||
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result =
 | 
			
		||||
        rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
 | 
			
		||||
    let result = rpc_client
 | 
			
		||||
        .send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]);
 | 
			
		||||
    log_instruction_custom_error::<NonceError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn process_create_nonce_account(
 | 
			
		||||
    rpc_client: &RpcClient,
 | 
			
		||||
    config: &CliConfig,
 | 
			
		||||
    nonce_account: &Keypair,
 | 
			
		||||
    nonce_account: &dyn Signer,
 | 
			
		||||
    seed: Option<String>,
 | 
			
		||||
    nonce_authority: Option<Pubkey>,
 | 
			
		||||
    lamports: u64,
 | 
			
		||||
@@ -428,17 +425,15 @@ pub fn process_create_nonce_account(
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
 | 
			
		||||
    let signers = if nonce_account_pubkey != config.keypair.pubkey() {
 | 
			
		||||
        vec![&config.keypair, nonce_account] // both must sign if `from` and `to` differ
 | 
			
		||||
        vec![config.keypair.as_ref(), nonce_account] // both must sign if `from` and `to` differ
 | 
			
		||||
    } else {
 | 
			
		||||
        vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
 | 
			
		||||
        vec![config.keypair.as_ref()] // when stake_account == config.keypair and there's a seed, we only need one signature
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        ixs,
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &signers,
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(&signers, recent_blockhash)?;
 | 
			
		||||
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
@@ -473,7 +468,7 @@ pub fn process_new_nonce(
 | 
			
		||||
    rpc_client: &RpcClient,
 | 
			
		||||
    config: &CliConfig,
 | 
			
		||||
    nonce_account: &Pubkey,
 | 
			
		||||
    nonce_authority: Option<&SigningAuthority>,
 | 
			
		||||
    nonce_authority: Option<&dyn Signer>,
 | 
			
		||||
) -> ProcessResult {
 | 
			
		||||
    check_unique_pubkeys(
 | 
			
		||||
        (&config.keypair.pubkey(), "cli keypair".to_string()),
 | 
			
		||||
@@ -487,25 +482,23 @@ pub fn process_new_nonce(
 | 
			
		||||
        .into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let nonce_authority = nonce_authority
 | 
			
		||||
        .map(|a| a.keypair())
 | 
			
		||||
        .unwrap_or(&config.keypair);
 | 
			
		||||
    let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref());
 | 
			
		||||
    let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        vec![ix],
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &[&config.keypair, nonce_authority],
 | 
			
		||||
    let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(
 | 
			
		||||
        &[config.keypair.as_ref(), nonce_authority],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    )?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result =
 | 
			
		||||
        rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
 | 
			
		||||
    let result = rpc_client
 | 
			
		||||
        .send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]);
 | 
			
		||||
    log_instruction_custom_error::<SystemError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -562,35 +555,33 @@ pub fn process_withdraw_from_nonce_account(
 | 
			
		||||
    rpc_client: &RpcClient,
 | 
			
		||||
    config: &CliConfig,
 | 
			
		||||
    nonce_account: &Pubkey,
 | 
			
		||||
    nonce_authority: Option<&SigningAuthority>,
 | 
			
		||||
    nonce_authority: Option<&dyn Signer>,
 | 
			
		||||
    destination_account_pubkey: &Pubkey,
 | 
			
		||||
    lamports: u64,
 | 
			
		||||
) -> ProcessResult {
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
 | 
			
		||||
    let nonce_authority = nonce_authority
 | 
			
		||||
        .map(|a| a.keypair())
 | 
			
		||||
        .unwrap_or(&config.keypair);
 | 
			
		||||
    let nonce_authority = nonce_authority.unwrap_or_else(|| config.keypair.as_ref());
 | 
			
		||||
    let ix = withdraw_nonce_account(
 | 
			
		||||
        nonce_account,
 | 
			
		||||
        &nonce_authority.pubkey(),
 | 
			
		||||
        destination_account_pubkey,
 | 
			
		||||
        lamports,
 | 
			
		||||
    );
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        vec![ix],
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &[&config.keypair, nonce_authority],
 | 
			
		||||
    let message = Message::new_with_payer(vec![ix], Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(
 | 
			
		||||
        &[config.keypair.as_ref(), nonce_authority],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    )?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result =
 | 
			
		||||
        rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, nonce_authority]);
 | 
			
		||||
    let result = rpc_client
 | 
			
		||||
        .send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), nonce_authority]);
 | 
			
		||||
    log_instruction_custom_error::<NonceError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -602,9 +593,10 @@ mod tests {
 | 
			
		||||
        account::Account,
 | 
			
		||||
        hash::hash,
 | 
			
		||||
        nonce_state::{Meta as NonceMeta, NonceState},
 | 
			
		||||
        signature::{read_keypair_file, write_keypair},
 | 
			
		||||
        signature::{read_keypair_file, write_keypair, Keypair},
 | 
			
		||||
        system_program,
 | 
			
		||||
    };
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
    use tempfile::NamedTempFile;
 | 
			
		||||
 | 
			
		||||
    fn make_tmp_file() -> (String, NamedTempFile) {
 | 
			
		||||
@@ -678,7 +670,7 @@ mod tests {
 | 
			
		||||
            parse_command(&test_create_nonce_account).unwrap(),
 | 
			
		||||
            CliCommandInfo {
 | 
			
		||||
                command: CliCommand::CreateNonceAccount {
 | 
			
		||||
                    nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
 | 
			
		||||
                    nonce_account: Rc::new(read_keypair_file(&keypair_file).unwrap().into()),
 | 
			
		||||
                    seed: None,
 | 
			
		||||
                    nonce_authority: None,
 | 
			
		||||
                    lamports: 50_000_000_000,
 | 
			
		||||
@@ -700,7 +692,7 @@ mod tests {
 | 
			
		||||
            parse_command(&test_create_nonce_account).unwrap(),
 | 
			
		||||
            CliCommandInfo {
 | 
			
		||||
                command: CliCommand::CreateNonceAccount {
 | 
			
		||||
                    nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
 | 
			
		||||
                    nonce_account: Rc::new(read_keypair_file(&keypair_file).unwrap().into()),
 | 
			
		||||
                    seed: None,
 | 
			
		||||
                    nonce_authority: Some(
 | 
			
		||||
                        read_keypair_file(&authority_keypair_file).unwrap().pubkey()
 | 
			
		||||
 
 | 
			
		||||
@@ -3,30 +3,12 @@ use serde_json::Value;
 | 
			
		||||
use solana_clap_utils::{
 | 
			
		||||
    input_parsers::value_of,
 | 
			
		||||
    input_validators::{is_hash, is_pubkey_sig},
 | 
			
		||||
    ArgConstant,
 | 
			
		||||
    offline::{BLOCKHASH_ARG, SIGNER_ARG, SIGN_ONLY_ARG},
 | 
			
		||||
};
 | 
			
		||||
use solana_client::rpc_client::RpcClient;
 | 
			
		||||
use solana_sdk::{fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature};
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
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",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum BlockhashQuery {
 | 
			
		||||
    None(Hash, FeeCalculator),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										647
									
								
								cli/src/stake.rs
									
									
									
									
									
								
							
							
						
						
									
										647
									
								
								cli/src/stake.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -191,19 +191,20 @@ pub fn process_create_storage_account(
 | 
			
		||||
    );
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new_signed_instructions(
 | 
			
		||||
        &[&config.keypair, &storage_account],
 | 
			
		||||
        ixs,
 | 
			
		||||
    let message = Message::new(ixs);
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(
 | 
			
		||||
        &[config.keypair.as_ref(), storage_account],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    )?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result =
 | 
			
		||||
        rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &storage_account]);
 | 
			
		||||
    let result = rpc_client
 | 
			
		||||
        .send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref(), storage_account]);
 | 
			
		||||
    log_instruction_custom_error::<SystemError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -217,10 +218,10 @@ pub fn process_claim_storage_reward(
 | 
			
		||||
 | 
			
		||||
    let instruction =
 | 
			
		||||
        storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey);
 | 
			
		||||
    let signers = [&config.keypair];
 | 
			
		||||
    let signers = [config.keypair.as_ref()];
 | 
			
		||||
    let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new(&signers, message, recent_blockhash);
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(&signers, recent_blockhash)?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
 
 | 
			
		||||
@@ -301,7 +301,7 @@ pub fn process_set_validator_info(
 | 
			
		||||
        .unwrap_or(0);
 | 
			
		||||
 | 
			
		||||
    let keys = vec![(id(), false), (config.keypair.pubkey(), true)];
 | 
			
		||||
    let (message, signers): (Message, Vec<&Keypair>) = if balance == 0 {
 | 
			
		||||
    let (message, signers): (Message, Vec<&dyn Signer>) = if balance == 0 {
 | 
			
		||||
        if info_pubkey != info_keypair.pubkey() {
 | 
			
		||||
            println!(
 | 
			
		||||
                "Account {:?} does not exist. Generating new keypair...",
 | 
			
		||||
@@ -327,7 +327,7 @@ pub fn process_set_validator_info(
 | 
			
		||||
            keys,
 | 
			
		||||
            &validator_info,
 | 
			
		||||
        )]);
 | 
			
		||||
        let signers = vec![&config.keypair, &info_keypair];
 | 
			
		||||
        let signers = vec![config.keypair.as_ref(), &info_keypair];
 | 
			
		||||
        let message = Message::new(instructions);
 | 
			
		||||
        (message, signers)
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -343,13 +343,14 @@ pub fn process_set_validator_info(
 | 
			
		||||
            &validator_info,
 | 
			
		||||
        )];
 | 
			
		||||
        let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey()));
 | 
			
		||||
        let signers = vec![&config.keypair];
 | 
			
		||||
        let signers = vec![config.keypair.as_ref()];
 | 
			
		||||
        (message, signers)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Submit transaction
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
    let mut tx = Transaction::new(&signers, message, recent_blockhash);
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(&signers, recent_blockhash)?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ use solana_clap_utils::{input_parsers::*, input_validators::*};
 | 
			
		||||
use solana_client::rpc_client::RpcClient;
 | 
			
		||||
use solana_sdk::{
 | 
			
		||||
    account::Account,
 | 
			
		||||
    message::Message,
 | 
			
		||||
    pubkey::Pubkey,
 | 
			
		||||
    signature::Keypair,
 | 
			
		||||
    signature::Signer,
 | 
			
		||||
@@ -311,12 +312,14 @@ pub fn process_create_vote_account(
 | 
			
		||||
    let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
			
		||||
 | 
			
		||||
    let signers = if vote_account_pubkey != config.keypair.pubkey() {
 | 
			
		||||
        vec![&config.keypair, vote_account] // both must sign if `from` and `to` differ
 | 
			
		||||
        vec![config.keypair.as_ref(), vote_account] // both must sign if `from` and `to` differ
 | 
			
		||||
    } else {
 | 
			
		||||
        vec![&config.keypair] // when stake_account == config.keypair and there's a seed, we only need one signature
 | 
			
		||||
        vec![config.keypair.as_ref()] // when stake_account == config.keypair and there's a seed, we only need one signature
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new_signed_instructions(&signers, ixs, recent_blockhash);
 | 
			
		||||
    let message = Message::new(ixs);
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(&signers, recent_blockhash)?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
@@ -346,19 +349,16 @@ pub fn process_vote_authorize(
 | 
			
		||||
        vote_authorize,           // vote or withdraw
 | 
			
		||||
    )];
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        ixs,
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &[&config.keypair],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(&[config.keypair.as_ref()], recent_blockhash)?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
 | 
			
		||||
    let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref()]);
 | 
			
		||||
    log_instruction_custom_error::<VoteError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -380,19 +380,19 @@ pub fn process_vote_update_validator(
 | 
			
		||||
        new_identity_pubkey,
 | 
			
		||||
    )];
 | 
			
		||||
 | 
			
		||||
    let mut tx = Transaction::new_signed_with_payer(
 | 
			
		||||
        ixs,
 | 
			
		||||
        Some(&config.keypair.pubkey()),
 | 
			
		||||
        &[&config.keypair, authorized_voter],
 | 
			
		||||
    let message = Message::new_with_payer(ixs, Some(&config.keypair.pubkey()));
 | 
			
		||||
    let mut tx = Transaction::new_unsigned(message);
 | 
			
		||||
    tx.try_sign(
 | 
			
		||||
        &[config.keypair.as_ref(), authorized_voter],
 | 
			
		||||
        recent_blockhash,
 | 
			
		||||
    );
 | 
			
		||||
    )?;
 | 
			
		||||
    check_account_for_fee(
 | 
			
		||||
        rpc_client,
 | 
			
		||||
        &config.keypair.pubkey(),
 | 
			
		||||
        &fee_calculator,
 | 
			
		||||
        &tx.message,
 | 
			
		||||
    )?;
 | 
			
		||||
    let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
 | 
			
		||||
    let result = rpc_client.send_and_confirm_transaction(&mut tx, &[config.keypair.as_ref()]);
 | 
			
		||||
    log_instruction_custom_error::<VoteError>(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user