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:
parent
aa80f69171
commit
4ddbf8d509
|
@ -251,10 +251,10 @@ Refer to the following page for a comprehensive guide on running a validator:
|
|||
Solana CLI tooling supports secure keypair input for stake delegation. To do so,
|
||||
first create a stake account with some SOL. Use the special `ASK` keyword to
|
||||
trigger a seed phrase input prompt for the stake account and use
|
||||
`--ask-seed-phrase keypair` to securely input the funding keypair.
|
||||
`--keypair ASK` to securely input the funding keypair.
|
||||
|
||||
```bash
|
||||
solana create-stake-account ASK 1 --ask-seed-phrase keypair
|
||||
solana create-stake-account ASK 1 --keypair ASK
|
||||
|
||||
[stake_account] seed phrase: 🔒
|
||||
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||
|
@ -262,11 +262,11 @@ solana create-stake-account ASK 1 --ask-seed-phrase keypair
|
|||
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||
```
|
||||
|
||||
Then, to delegate that stake to a validator, use `--ask-seed-phrase keypair` to
|
||||
Then, to delegate that stake to a validator, use `--keypair ASK` to
|
||||
securely input the funding keypair.
|
||||
|
||||
```bash
|
||||
solana delegate-stake --ask-seed-phrase keypair <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
|
||||
solana delegate-stake --keypair ASK <STAKE_ACCOUNT_PUBKEY> <VOTE_ACCOUNT_PUBKEY>
|
||||
|
||||
[keypair] seed phrase: 🔒
|
||||
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::keypair::{keypair_from_seed_phrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG};
|
||||
use crate::keypair::{
|
||||
keypair_from_seed_phrase, keypair_util_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
};
|
||||
use chrono::DateTime;
|
||||
use clap::ArgMatches;
|
||||
use solana_remote_wallet::remote_wallet::DerivationPath;
|
||||
|
@ -93,6 +95,18 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
|
|||
})
|
||||
}
|
||||
|
||||
// Return a signer from matches at `name`
|
||||
pub fn signer_of(
|
||||
name: &str,
|
||||
matches: &ArgMatches<'_>,
|
||||
) -> Result<Option<Box<dyn Signer>>, Box<dyn std::error::Error>> {
|
||||
if let Some(location) = matches.value_of(name) {
|
||||
keypair_util_from_path(matches, location, name).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
||||
value_of(matches, name).map(sol_to_lamports)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::keypair::ASK_KEYWORD;
|
||||
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
||||
use chrono::DateTime;
|
||||
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
|
@ -50,6 +51,16 @@ pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String>
|
|||
is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string))
|
||||
}
|
||||
|
||||
pub fn is_valid_signer(string: String) -> Result<(), String> {
|
||||
match parse_keypair_path(&string) {
|
||||
KeypairUrl::Usb(path) => generate_remote_keypair(path, None)
|
||||
.map(|_| ())
|
||||
.map_err(|err| format!("{:?}", err)),
|
||||
KeypairUrl::Filepath(path) => is_keypair(path),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if string cannot be parsed as pubkey=signature string
|
||||
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
|
||||
let mut signer = string.split('=');
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
use crate::ArgConstant;
|
||||
use crate::{
|
||||
input_parsers::{derivation_of, pubkeys_sigs_of},
|
||||
offline::SIGNER_ARG,
|
||||
ArgConstant,
|
||||
};
|
||||
use bip39::{Language, Mnemonic, Seed};
|
||||
use clap::values_t;
|
||||
use clap::{values_t, ArgMatches, Error, ErrorKind};
|
||||
use rpassword::prompt_password_stderr;
|
||||
use solana_sdk::signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair, Signer,
|
||||
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||
read_keypair_file, Keypair, Presigner, Signature, Signer,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
pub enum KeypairUrl {
|
||||
|
@ -16,6 +26,7 @@ pub enum KeypairUrl {
|
|||
Filepath(String),
|
||||
Usb(String),
|
||||
Stdin,
|
||||
Pubkey(Pubkey),
|
||||
}
|
||||
|
||||
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
|
||||
|
@ -25,11 +36,66 @@ pub fn parse_keypair_path(path: &str) -> KeypairUrl {
|
|||
KeypairUrl::Ask
|
||||
} else if path.starts_with("usb://") {
|
||||
KeypairUrl::Usb(path.split_at(6).1.to_string())
|
||||
} else if let Ok(pubkey) = Pubkey::from_str(path) {
|
||||
KeypairUrl::Pubkey(pubkey)
|
||||
} else {
|
||||
KeypairUrl::Filepath(path.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn presigner_from_pubkey_sigs(
|
||||
pubkey: &Pubkey,
|
||||
signers: &[(Pubkey, Signature)],
|
||||
) -> Option<Presigner> {
|
||||
signers.iter().find_map(|(signer, sig)| {
|
||||
if *signer == *pubkey {
|
||||
Some(Presigner::new(signer, sig))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keypair_util_from_path(
|
||||
matches: &ArgMatches,
|
||||
path: &str,
|
||||
keypair_name: &str,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(Box::new(keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
false,
|
||||
)?))
|
||||
}
|
||||
KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)),
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(Box::new(read_keypair(&mut stdin)?))
|
||||
}
|
||||
KeypairUrl::Usb(path) => Ok(Box::new(generate_remote_keypair(
|
||||
path,
|
||||
derivation_of(matches, "derivation_path"),
|
||||
)?)),
|
||||
KeypairUrl::Pubkey(pubkey) => {
|
||||
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
|
||||
.as_ref()
|
||||
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
|
||||
if let Some(presigner) = presigner {
|
||||
Ok(Box::new(presigner))
|
||||
} else {
|
||||
Err(Error::with_description(
|
||||
"Missing signature for supplied pubkey",
|
||||
ErrorKind::MissingRequiredArgument,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||
pub const ASK_KEYWORD: &str = "ASK";
|
||||
|
||||
|
|
|
@ -26,3 +26,4 @@ pub struct ArgConstant<'a> {
|
|||
pub mod input_parsers;
|
||||
pub mod input_validators;
|
||||
pub mod keypair;
|
||||
pub mod offline;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
use crate::ArgConstant;
|
||||
|
||||
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",
|
||||
};
|
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use solana_cli::cli::{
|
||||
process_command, request_and_confirm_airdrop, CliCommand, CliConfig, SigningAuthority,
|
||||
};
|
||||
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, Keypair, Signer},
|
||||
system_instruction::create_address_with_seed,
|
||||
system_program,
|
||||
};
|
||||
|
@ -15,6 +13,7 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[cfg(test)]
|
||||
use solana_core::validator::new_validator_for_tests;
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -51,11 +50,13 @@ fn test_nonce() {
|
|||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_nonce = CliConfig::default();
|
||||
config_nonce.keypair = keypair.into();
|
||||
config_nonce.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
full_battery_tests(
|
||||
&rpc_client,
|
||||
|
@ -84,11 +85,13 @@ fn test_nonce_with_seed() {
|
|||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_nonce = CliConfig::default();
|
||||
config_nonce.keypair = keypair.into();
|
||||
config_nonce.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
full_battery_tests(
|
||||
&rpc_client,
|
||||
|
@ -117,11 +120,12 @@ fn test_nonce_with_authority() {
|
|||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_nonce = CliConfig::default();
|
||||
config_nonce.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_nonce.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
let nonce_authority = Keypair::new();
|
||||
let (authority_keypair_file, mut tmp_file2) = make_tmp_file();
|
||||
|
@ -141,7 +145,7 @@ fn test_nonce_with_authority() {
|
|||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
||||
|
||||
fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option<SigningAuthority> {
|
||||
fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option<Box<dyn Signer>> {
|
||||
keypair_file.map(|akf| read_keypair_file(&akf).unwrap().into())
|
||||
}
|
||||
|
||||
|
@ -167,15 +171,14 @@ fn full_battery_tests(
|
|||
create_address_with_seed(&config_nonce.keypair.pubkey(), seed, &system_program::id())
|
||||
.unwrap()
|
||||
} else {
|
||||
config_nonce.keypair.pubkey()
|
||||
read_keypair_file(&nonce_keypair_file).unwrap().pubkey()
|
||||
};
|
||||
|
||||
// Create nonce account
|
||||
config_payer.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed,
|
||||
nonce_authority: read_keypair_from_option(&authority_keypair_file)
|
||||
.map(|na: SigningAuthority| na.pubkey()),
|
||||
nonce_authority: read_keypair_from_option(&authority_keypair_file).map(|k| k.pubkey()),
|
||||
lamports: 1000,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use chrono::prelude::*;
|
||||
use serde_json::Value;
|
||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||
|
@ -18,6 +19,7 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[cfg(test)]
|
||||
use solana_core::validator::new_validator_for_tests;
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use tempfile::NamedTempFile;
|
||||
|
@ -304,17 +306,20 @@ fn test_offline_pay_tx() {
|
|||
check_balance(0, &rpc_client, &bob_pubkey);
|
||||
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner =
|
||||
presigner_from_pubkey_sigs(&config_offline.keypair.pubkey(), &signers).unwrap();
|
||||
let online_pubkey = config_online.keypair.pubkey();
|
||||
config_online.keypair = offline_presigner.into();
|
||||
config_online.command = CliCommand::Pay(PayCommand {
|
||||
lamports: 10,
|
||||
to: bob_pubkey,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
..PayCommand::default()
|
||||
});
|
||||
process_command(&config_online).unwrap();
|
||||
|
||||
check_balance(40, &rpc_client, &config_offline.keypair.pubkey());
|
||||
check_balance(50, &rpc_client, &config_online.keypair.pubkey());
|
||||
check_balance(50, &rpc_client, &online_pubkey);
|
||||
check_balance(10, &rpc_client, &bob_pubkey);
|
||||
|
||||
server.close().unwrap();
|
||||
|
@ -357,7 +362,7 @@ fn test_nonced_pay_tx() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(config.keypair.pubkey()),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
|
|
@ -2,7 +2,6 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::new_validator_for_tests;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::signature::Signer;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||
|
@ -20,6 +21,7 @@ use std::sync::mpsc::channel;
|
|||
use solana_core::validator::{
|
||||
new_validator_for_tests, new_validator_for_tests_ex, new_validator_for_tests_with_vote_pubkey,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -77,14 +79,13 @@ fn test_stake_delegation_force() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -100,7 +101,6 @@ fn test_stake_delegation_force() {
|
|||
stake_authority: None,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -115,7 +115,6 @@ fn test_stake_delegation_force() {
|
|||
stake_authority: None,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -139,13 +138,14 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
let validator_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (validator_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&validator_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_validator = CliConfig::default();
|
||||
config_validator.keypair = validator_keypair.into();
|
||||
config_validator.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let (validator_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_validator.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
let mut config_stake = CliConfig::default();
|
||||
config_stake.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
@ -169,14 +169,13 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||
// Create stake account with a seed, uses the validator config as the base,
|
||||
// which is nice ;)
|
||||
config_validator.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&validator_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&validator_keypair_file).unwrap().into()),
|
||||
seed: Some("hi there".to_string()),
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -192,7 +191,6 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||
stake_authority: None,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -205,7 +203,6 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||
stake_account_pubkey: stake_address,
|
||||
stake_authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -233,11 +230,13 @@ fn test_stake_delegation_and_deactivation() {
|
|||
config_validator.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_stake = CliConfig::default();
|
||||
config_stake.keypair = stake_keypair.into();
|
||||
config_stake.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_stake.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
|
@ -250,14 +249,13 @@ fn test_stake_delegation_and_deactivation() {
|
|||
|
||||
// Create stake account
|
||||
config_validator.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -273,7 +271,6 @@ fn test_stake_delegation_and_deactivation() {
|
|||
stake_authority: None,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -286,7 +283,6 @@ fn test_stake_delegation_and_deactivation() {
|
|||
stake_account_pubkey: config_stake.keypair.pubkey(),
|
||||
stake_authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -318,11 +314,13 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_stake = CliConfig::default();
|
||||
config_stake.keypair = stake_keypair.into();
|
||||
config_stake.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_stake.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
let mut config_offline = CliConfig::default();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
|
@ -350,14 +348,13 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
|
||||
// Create stake account
|
||||
config_validator.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: Some(config_offline.keypair.pubkey().into()),
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -374,7 +371,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
stake_authority: None,
|
||||
force: false,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -382,17 +378,18 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner =
|
||||
presigner_from_pubkey_sigs(&config_offline.keypair.pubkey(), &signers).unwrap();
|
||||
config_payer.command = CliCommand::DelegateStake {
|
||||
stake_account_pubkey: config_stake.keypair.pubkey(),
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: Some(config_offline.keypair.pubkey().into()),
|
||||
stake_authority: Some(offline_presigner.clone().into()),
|
||||
force: false,
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: Some(config_offline.keypair.pubkey().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
|
@ -402,7 +399,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
stake_account_pubkey: config_stake.keypair.pubkey(),
|
||||
stake_authority: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -410,15 +406,16 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner =
|
||||
presigner_from_pubkey_sigs(&config_offline.keypair.pubkey(), &signers).unwrap();
|
||||
config_payer.command = CliCommand::DeactivateStake {
|
||||
stake_account_pubkey: config_stake.keypair.pubkey(),
|
||||
stake_authority: Some(config_offline.keypair.pubkey().into()),
|
||||
stake_authority: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: Some(config_offline.keypair.pubkey().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
|
@ -438,7 +435,11 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
let config_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (config_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config = CliConfig::default();
|
||||
config.keypair = config_keypair.into();
|
||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let minimum_nonce_balance = rpc_client
|
||||
|
@ -453,14 +454,13 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -474,7 +474,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(config.keypair.pubkey()),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -496,7 +496,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
stake_authority: None,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: None,
|
||||
|
@ -513,15 +512,13 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
};
|
||||
|
||||
// Deactivate stake
|
||||
let config_keypair = Keypair::from_bytes(&config.keypair.to_bytes()).unwrap();
|
||||
config.command = CliCommand::DeactivateStake {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
stake_authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: Some(config_keypair.into()),
|
||||
nonce_authority: Some(read_keypair_file(&config_keypair_file).unwrap().into()),
|
||||
fee_payer: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
@ -547,8 +544,13 @@ fn test_stake_authorize() {
|
|||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &config.keypair.pubkey(), 100_000)
|
||||
.unwrap();
|
||||
|
||||
let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (offline_authority_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&offline_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_offline = CliConfig::default();
|
||||
config_offline.keypair = offline_keypair.into();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
let offline_authority_pubkey = config_offline.keypair.pubkey();
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
// Verfiy that we cannot reach the cluster
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
@ -567,14 +569,13 @@ fn test_stake_authorize() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -594,7 +595,6 @@ fn test_stake_authorize() {
|
|||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -610,16 +610,12 @@ fn test_stake_authorize() {
|
|||
assert_eq!(current_authority, online_authority_pubkey);
|
||||
|
||||
// Assign new offline stake authority
|
||||
let offline_authority_pubkey = config_offline.keypair.pubkey();
|
||||
let (offline_authority_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorized_pubkey: offline_authority_pubkey,
|
||||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(read_keypair_file(&online_authority_file).unwrap().into()),
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -646,7 +642,6 @@ fn test_stake_authorize() {
|
|||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(read_keypair_file(&offline_authority_file).unwrap().into()),
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -654,17 +649,18 @@ fn test_stake_authorize() {
|
|||
};
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
|
||||
let offline_presigner =
|
||||
presigner_from_pubkey_sigs(&offline_authority_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorized_pubkey: nonced_authority_pubkey,
|
||||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(offline_authority_pubkey.into()),
|
||||
authority: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: Some(offline_authority_pubkey.into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
|
@ -683,7 +679,7 @@ fn test_stake_authorize() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(config_offline.keypair.pubkey()),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -709,7 +705,6 @@ fn test_stake_authorize() {
|
|||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(read_keypair_file(&nonced_authority_file).unwrap().into()),
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: None,
|
||||
|
@ -718,17 +713,20 @@ fn test_stake_authorize() {
|
|||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
|
||||
assert_eq!(blockhash, nonce_hash);
|
||||
let offline_presigner =
|
||||
presigner_from_pubkey_sigs(&offline_authority_pubkey, &signers).unwrap();
|
||||
let nonced_authority_presigner =
|
||||
presigner_from_pubkey_sigs(&nonced_authority_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorized_pubkey: online_authority_pubkey,
|
||||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(nonced_authority_pubkey.into()),
|
||||
authority: Some(nonced_authority_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: Some(offline_authority_pubkey.into()),
|
||||
fee_payer: Some(offline_authority_pubkey.into()),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
|
@ -766,17 +764,17 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
let mut config = CliConfig::default();
|
||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let payer_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let (payer_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&payer_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let mut config_payer = CliConfig::default();
|
||||
config_payer.keypair = payer_keypair.into();
|
||||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let payer_pubkey = config_payer.keypair.pubkey();
|
||||
let (payer_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_payer.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
let mut config_offline = CliConfig::default();
|
||||
let offline_pubkey = config_offline.keypair.pubkey();
|
||||
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
// Verify we're offline
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
@ -797,14 +795,13 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -826,7 +823,6 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -855,7 +851,6 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -863,17 +858,17 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
};
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorized_pubkey: payer_pubkey,
|
||||
stake_authorize: StakeAuthorize::Staker,
|
||||
authority: Some(offline_pubkey.into()),
|
||||
authority: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config`'s balance again has not changed
|
||||
|
@ -911,8 +906,6 @@ fn test_stake_split() {
|
|||
let mut config_offline = CliConfig::default();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
let offline_pubkey = config_offline.keypair.pubkey();
|
||||
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
// Verify we're offline
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
@ -933,14 +926,13 @@ fn test_stake_split() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: Some(offline_pubkey),
|
||||
lockup: Lockup::default(),
|
||||
lamports: 10 * minimum_stake_balance,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -963,7 +955,7 @@ fn test_stake_split() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -988,29 +980,28 @@ fn test_stake_split() {
|
|||
stake_account_pubkey: stake_account_pubkey,
|
||||
stake_authority: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: None,
|
||||
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
||||
split_stake_account: Rc::new(read_keypair_file(&split_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
fee_payer: None,
|
||||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::SplitStake {
|
||||
stake_account_pubkey: stake_account_pubkey,
|
||||
stake_authority: Some(offline_pubkey.into()),
|
||||
stake_authority: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: Some(offline_pubkey.into()),
|
||||
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
split_stake_account: Rc::new(read_keypair_file(&split_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance(
|
||||
|
@ -1045,8 +1036,6 @@ fn test_stake_set_lockup() {
|
|||
let mut config_offline = CliConfig::default();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
let offline_pubkey = config_offline.keypair.pubkey();
|
||||
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
// Verify we're offline
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
@ -1072,14 +1061,13 @@ fn test_stake_set_lockup() {
|
|||
lockup.custodian = config.keypair.pubkey();
|
||||
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: Some(offline_pubkey),
|
||||
lockup,
|
||||
lamports: 10 * minimum_stake_balance,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -1104,7 +1092,6 @@ fn test_stake_set_lockup() {
|
|||
lockup,
|
||||
custodian: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -1136,7 +1123,6 @@ fn test_stake_set_lockup() {
|
|||
lockup,
|
||||
custodian: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -1154,7 +1140,6 @@ fn test_stake_set_lockup() {
|
|||
lockup,
|
||||
custodian: Some(read_keypair_file(&online_custodian_file).unwrap().into()),
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -1181,7 +1166,6 @@ fn test_stake_set_lockup() {
|
|||
lockup,
|
||||
custodian: Some(online_custodian.into()),
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -1198,7 +1182,7 @@ fn test_stake_set_lockup() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -1225,7 +1209,6 @@ fn test_stake_set_lockup() {
|
|||
lockup,
|
||||
custodian: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: None,
|
||||
|
@ -1233,16 +1216,16 @@ fn test_stake_set_lockup() {
|
|||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::StakeSetLockup {
|
||||
stake_account_pubkey,
|
||||
lockup,
|
||||
custodian: Some(offline_pubkey.into()),
|
||||
custodian: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
|
@ -1269,14 +1252,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
let mut config = CliConfig::default();
|
||||
config.keypair = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
config.keypair = keypair_from_seed(&[1u8; 32]).unwrap().into();
|
||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
let mut config_offline = CliConfig::default();
|
||||
config_offline.keypair = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
config_offline.keypair = keypair_from_seed(&[2u8; 32]).unwrap().into();
|
||||
let offline_pubkey = config_offline.keypair.pubkey();
|
||||
let (offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
// Verfiy that we cannot reach the cluster
|
||||
|
@ -1304,7 +1285,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -1325,14 +1306,13 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
config_offline.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(read_keypair_file(&stake_keypair_file).unwrap().into()),
|
||||
seed: None,
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: None,
|
||||
|
@ -1341,20 +1321,22 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: stake_pubkey.into(),
|
||||
stake_account: presigner_from_pubkey_sigs(&stake_pubkey, &signers)
|
||||
.map(|p| Rc::new(p.into()))
|
||||
.unwrap(),
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey.into()),
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
from: Some(offline_pubkey.into()),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
from: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance(50_000, &rpc_client, &stake_pubkey);
|
||||
|
@ -1376,7 +1358,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
lamports: 42,
|
||||
withdraw_authority: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: None,
|
||||
|
@ -1384,36 +1365,35 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::WithdrawStake {
|
||||
stake_account_pubkey: stake_pubkey,
|
||||
destination_account_pubkey: recipient_pubkey,
|
||||
lamports: 42,
|
||||
withdraw_authority: Some(offline_pubkey.into()),
|
||||
withdraw_authority: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance(42, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Test that offline derived addresses fail
|
||||
config_offline.command = CliCommand::CreateStakeAccount {
|
||||
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||
stake_account: Rc::new(Box::new(read_keypair_file(&stake_keypair_file).unwrap())),
|
||||
seed: Some("fail".to_string()),
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50_000,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: Some(read_keypair_file(&offline_keypair_file).unwrap().into()),
|
||||
fee_payer: Some(read_keypair_file(&offline_keypair_file).unwrap().into()),
|
||||
from: Some(read_keypair_file(&offline_keypair_file).unwrap().into()),
|
||||
nonce_authority: None,
|
||||
fee_payer: None,
|
||||
from: None,
|
||||
};
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||
|
@ -16,6 +17,7 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[cfg(test)]
|
||||
use solana_core::validator::new_validator_for_tests_ex;
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use tempfile::NamedTempFile;
|
||||
|
@ -66,7 +68,6 @@ fn test_transfer() {
|
|||
to: recipient_pubkey,
|
||||
from: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -94,7 +95,6 @@ fn test_transfer() {
|
|||
to: recipient_pubkey,
|
||||
from: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
|
@ -102,16 +102,16 @@ fn test_transfer() {
|
|||
};
|
||||
let sign_only_reply = process_command(&offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::Transfer {
|
||||
lamports: 10,
|
||||
to: recipient_pubkey,
|
||||
from: Some(offline_pubkey.into()),
|
||||
from: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance(39, &rpc_client, &offline_pubkey);
|
||||
|
@ -125,7 +125,7 @@ fn test_transfer() {
|
|||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||
.unwrap();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: read_keypair_file(&nonce_account_file).unwrap().into(),
|
||||
nonce_account: Rc::new(read_keypair_file(&nonce_account_file).unwrap().into()),
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
lamports: minimum_nonce_balance,
|
||||
|
@ -147,7 +147,6 @@ fn test_transfer() {
|
|||
to: recipient_pubkey,
|
||||
from: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: None,
|
||||
|
@ -187,7 +186,6 @@ fn test_transfer() {
|
|||
to: recipient_pubkey,
|
||||
from: None,
|
||||
sign_only: true,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: None,
|
||||
|
@ -195,16 +193,16 @@ fn test_transfer() {
|
|||
};
|
||||
let sign_only_reply = process_command(&offline).unwrap();
|
||||
let (blockhash, signers) = parse_sign_only_reply_string(&sign_only_reply);
|
||||
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
|
||||
config.command = CliCommand::Transfer {
|
||||
lamports: 10,
|
||||
to: recipient_pubkey,
|
||||
from: Some(offline_pubkey.into()),
|
||||
from: Some(offline_presigner.clone().into()),
|
||||
sign_only: false,
|
||||
signers: Some(signers),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: Some(offline_pubkey.into()),
|
||||
fee_payer: Some(offline_pubkey.into()),
|
||||
nonce_authority: Some(offline_presigner.clone().into()),
|
||||
fee_payer: Some(offline_presigner.clone().into()),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_balance(28, &rpc_client, &offline_pubkey);
|
||||
|
|
|
@ -6,21 +6,16 @@ use clap::{
|
|||
};
|
||||
use num_cpus;
|
||||
use solana_clap_utils::{
|
||||
input_parsers::derivation_of,
|
||||
input_validators::is_derivation,
|
||||
keypair::{
|
||||
keypair_from_seed_phrase, parse_keypair_path, prompt_passphrase, KeypairUrl,
|
||||
keypair_from_seed_phrase, keypair_util_from_path, prompt_passphrase,
|
||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
},
|
||||
};
|
||||
use solana_cli_config::config::{Config, CONFIG_FILE};
|
||||
use solana_remote_wallet::remote_keypair::generate_remote_keypair;
|
||||
use solana_sdk::{
|
||||
pubkey::write_pubkey_file,
|
||||
signature::{
|
||||
keypair_from_seed, read_keypair, read_keypair_file, write_keypair, write_keypair_file,
|
||||
Keypair, Signer,
|
||||
},
|
||||
signature::{keypair_from_seed, write_keypair, write_keypair_file, Keypair, Signer},
|
||||
};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
|
@ -64,26 +59,7 @@ fn get_keypair_from_matches(
|
|||
path.extend(&[".config", "solana", "id.json"]);
|
||||
path.to_str().unwrap()
|
||||
};
|
||||
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(Box::new(keypair_from_seed_phrase(
|
||||
"pubkey recovery",
|
||||
skip_validation,
|
||||
false,
|
||||
)?))
|
||||
}
|
||||
KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)),
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(Box::new(read_keypair(&mut stdin)?))
|
||||
}
|
||||
KeypairUrl::Usb(path) => Ok(Box::new(generate_remote_keypair(
|
||||
path,
|
||||
derivation_of(matches, "derivation_path"),
|
||||
)?)),
|
||||
}
|
||||
keypair_util_from_path(matches, path, "pubkey recovery")
|
||||
}
|
||||
|
||||
fn output_keypair(
|
||||
|
|
|
@ -341,7 +341,7 @@ fn extend_and_serialize(derivation_path: &DerivationPath) -> Vec<u8> {
|
|||
pub fn get_ledger_from_info(
|
||||
info: RemoteWalletInfo,
|
||||
) -> Result<Arc<LedgerWallet>, RemoteWalletError> {
|
||||
let wallet_manager = initialize_wallet_manager();
|
||||
let wallet_manager = initialize_wallet_manager()?;
|
||||
let _device_count = wallet_manager.update_devices()?;
|
||||
let devices = wallet_manager.list_devices();
|
||||
let (pubkeys, device_paths): (Vec<Pubkey>, Vec<String>) = devices
|
||||
|
|
|
@ -294,9 +294,9 @@ pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
|
|||
}
|
||||
|
||||
/// Helper to initialize hidapi and RemoteWalletManager
|
||||
pub fn initialize_wallet_manager() -> Arc<RemoteWalletManager> {
|
||||
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().unwrap()));
|
||||
RemoteWalletManager::new(hidapi)
|
||||
pub fn initialize_wallet_manager() -> Result<Arc<RemoteWalletManager>, RemoteWalletError> {
|
||||
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new()?));
|
||||
Ok(RemoteWalletManager::new(hidapi))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -139,6 +139,18 @@ pub trait Signer {
|
|||
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError>;
|
||||
}
|
||||
|
||||
impl PartialEq for dyn Signer {
|
||||
fn eq(&self, other: &dyn Signer) -> bool {
|
||||
self.pubkey() == other.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn Signer {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(fmt, "Signer: {:?}", self.pubkey())
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for Keypair {
|
||||
/// Return the public key for the given keypair
|
||||
fn pubkey(&self) -> Pubkey {
|
||||
|
@ -167,6 +179,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Box<dyn Signer>
|
||||
where
|
||||
T: Signer + 'static,
|
||||
{
|
||||
fn from(keypair_util: T) -> Self {
|
||||
Box::new(keypair_util)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum SignerError {
|
||||
#[error("keypair-pubkey mismatch")]
|
||||
|
@ -202,15 +223,14 @@ pub enum SignerError {
|
|||
UserCancel,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Presigner {
|
||||
pubkey: Pubkey,
|
||||
signature: Signature,
|
||||
}
|
||||
|
||||
impl Presigner {
|
||||
#[allow(dead_code)]
|
||||
fn new(pubkey: &Pubkey, signature: &Signature) -> Self {
|
||||
pub fn new(pubkey: &Pubkey, signature: &Signature) -> Self {
|
||||
Self {
|
||||
pubkey: *pubkey,
|
||||
signature: *signature,
|
||||
|
|
|
@ -48,6 +48,34 @@ impl Signers for [Box<dyn Signer>] {
|
|||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for Vec<&dyn Signer> {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer; 0] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer; 1] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer; 2] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer; 3] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl Signers for [&dyn Signer; 4] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
||||
impl<T: Signer> Signers for [&T; 0] {
|
||||
default_keypairs_impl!();
|
||||
}
|
||||
|
@ -111,4 +139,22 @@ mod tests {
|
|||
vec![Signature::default(), Signature::default()],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dyn_keypairs_by_ref_compile() {
|
||||
let foo = Foo {};
|
||||
let bar = Bar {};
|
||||
let xs: Vec<&dyn Signer> = vec![&foo, &bar];
|
||||
assert_eq!(
|
||||
xs.sign_message(b""),
|
||||
vec![Signature::default(), Signature::default()],
|
||||
);
|
||||
|
||||
// Same as above, but less compiler magic.
|
||||
let xs_ref: &[&dyn Signer] = &xs;
|
||||
assert_eq!(
|
||||
Signers::sign_message(xs_ref, b""),
|
||||
vec![Signature::default(), Signature::default()],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,7 +379,7 @@ mod tests {
|
|||
use crate::{
|
||||
hash::hash,
|
||||
instruction::AccountMeta,
|
||||
signature::{Keypair, Signer},
|
||||
signature::{Keypair, Presigner, Signer},
|
||||
system_instruction,
|
||||
};
|
||||
use bincode::{deserialize, serialize, serialized_size};
|
||||
|
@ -671,4 +671,52 @@ mod tests {
|
|||
);
|
||||
assert!(tx.is_signed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_sign_dyn_keypairs() {
|
||||
let program_id = Pubkey::default();
|
||||
let keypair = Keypair::new();
|
||||
let pubkey = keypair.pubkey();
|
||||
let presigner_keypair = Keypair::new();
|
||||
let presigner_pubkey = presigner_keypair.pubkey();
|
||||
|
||||
let ix = Instruction::new(
|
||||
program_id,
|
||||
&0,
|
||||
vec![
|
||||
AccountMeta::new(pubkey, true),
|
||||
AccountMeta::new(presigner_pubkey, true),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![ix]);
|
||||
|
||||
let presigner_sig = presigner_keypair.sign_message(&tx.message_data());
|
||||
let presigner = Presigner::new(&presigner_pubkey, &presigner_sig);
|
||||
|
||||
let signers: Vec<&dyn Signer> = vec![&keypair, &presigner];
|
||||
|
||||
let res = tx.try_sign(&signers, Hash::default());
|
||||
assert_eq!(res, Ok(()));
|
||||
assert_eq!(tx.signatures[0], keypair.sign_message(&tx.message_data()));
|
||||
assert_eq!(tx.signatures[1], presigner_sig);
|
||||
|
||||
// Wrong key should error, not panic
|
||||
let another_pubkey = Pubkey::new_rand();
|
||||
let ix = Instruction::new(
|
||||
program_id,
|
||||
&0,
|
||||
vec![
|
||||
AccountMeta::new(another_pubkey, true),
|
||||
AccountMeta::new(presigner_pubkey, true),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![ix]);
|
||||
|
||||
let res = tx.try_sign(&signers, Hash::default());
|
||||
assert!(res.is_err());
|
||||
assert_eq!(
|
||||
tx.signatures,
|
||||
vec![Signature::default(), Signature::default()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue