Factor the rest of CLI offline module out to clap-utils and cli-output crates
This commit is contained in:
parent
325a7e9f86
commit
7cab638297
|
@ -3591,6 +3591,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"solana-clap-utils",
|
||||||
"solana-client",
|
"solana-client",
|
||||||
"solana-sdk 1.4.0",
|
"solana-sdk 1.4.0",
|
||||||
"solana-stake-program",
|
"solana-stake-program",
|
||||||
|
|
|
@ -11,6 +11,7 @@ use solana_remote_wallet::{
|
||||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{
|
signature::{
|
||||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||||
|
@ -25,6 +26,22 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct SignOnly {
|
||||||
|
pub blockhash: Hash,
|
||||||
|
pub present_signers: Vec<(Pubkey, Signature)>,
|
||||||
|
pub absent_signers: Vec<Pubkey>,
|
||||||
|
pub bad_signers: Vec<Pubkey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignOnly {
|
||||||
|
pub fn has_all_signers(&self) -> bool {
|
||||||
|
self.absent_signers.is_empty() && self.bad_signers.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
|
||||||
|
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
|
||||||
|
}
|
||||||
|
}
|
||||||
pub type CliSigners = Vec<Box<dyn Signer>>;
|
pub type CliSigners = Vec<Box<dyn Signer>>;
|
||||||
pub type SignerIndex = usize;
|
pub type SignerIndex = usize;
|
||||||
pub struct CliSignerInfo {
|
pub struct CliSignerInfo {
|
||||||
|
|
|
@ -17,6 +17,7 @@ indicatif = "0.15.0"
|
||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
||||||
|
|
|
@ -4,14 +4,19 @@ use console::{style, Emoji};
|
||||||
use inflector::cases::titlecase::to_title_case;
|
use inflector::cases::titlecase::to_title_case;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
use solana_clap_utils::keypair::SignOnly;
|
||||||
use solana_client::rpc_response::{
|
use solana_client::rpc_response::{
|
||||||
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
|
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::{self, Epoch, Slot, UnixTimestamp},
|
clock::{self, Epoch, Slot, UnixTimestamp},
|
||||||
epoch_info::EpochInfo,
|
epoch_info::EpochInfo,
|
||||||
|
hash::Hash,
|
||||||
native_token::lamports_to_sol,
|
native_token::lamports_to_sol,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::Signature,
|
||||||
stake_history::StakeHistoryEntry,
|
stake_history::StakeHistoryEntry,
|
||||||
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{Authorized, Lockup};
|
use solana_stake_program::stake_state::{Authorized, Lockup};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
|
@ -21,6 +26,7 @@ use solana_vote_program::{
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
fmt,
|
fmt,
|
||||||
|
str::FromStr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1142,3 +1148,149 @@ impl fmt::Display for CliFees {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn return_signers(
|
||||||
|
tx: &Transaction,
|
||||||
|
output_format: &OutputFormat,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let verify_results = tx.verify_with_results();
|
||||||
|
let mut signers = Vec::new();
|
||||||
|
let mut absent = Vec::new();
|
||||||
|
let mut bad_sig = Vec::new();
|
||||||
|
tx.signatures
|
||||||
|
.iter()
|
||||||
|
.zip(tx.message.account_keys.iter())
|
||||||
|
.zip(verify_results.into_iter())
|
||||||
|
.for_each(|((sig, key), res)| {
|
||||||
|
if res {
|
||||||
|
signers.push(format!("{}={}", key, sig))
|
||||||
|
} else if *sig == Signature::default() {
|
||||||
|
absent.push(key.to_string());
|
||||||
|
} else {
|
||||||
|
bad_sig.push(key.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let cli_command = CliSignOnlyData {
|
||||||
|
blockhash: tx.message.recent_blockhash.to_string(),
|
||||||
|
signers,
|
||||||
|
absent,
|
||||||
|
bad_sig,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(output_format.formatted_string(&cli_command))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
||||||
|
let object: Value = serde_json::from_str(&reply).unwrap();
|
||||||
|
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
||||||
|
let blockhash = blockhash_str.parse::<Hash>().unwrap();
|
||||||
|
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
|
||||||
|
let signer_strings = object.get("signers");
|
||||||
|
if let Some(sig_strings) = signer_strings {
|
||||||
|
present_signers = sig_strings
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|signer_string| {
|
||||||
|
let mut signer = signer_string.as_str().unwrap().split('=');
|
||||||
|
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
||||||
|
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
||||||
|
(key, sig)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
let mut absent_signers: Vec<Pubkey> = Vec::new();
|
||||||
|
let signer_strings = object.get("absent");
|
||||||
|
if let Some(sig_strings) = signer_strings {
|
||||||
|
absent_signers = sig_strings
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|val| {
|
||||||
|
let s = val.as_str().unwrap();
|
||||||
|
Pubkey::from_str(s).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
let mut bad_signers: Vec<Pubkey> = Vec::new();
|
||||||
|
let signer_strings = object.get("badSig");
|
||||||
|
if let Some(sig_strings) = signer_strings {
|
||||||
|
bad_signers = sig_strings
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|val| {
|
||||||
|
let s = val.as_str().unwrap();
|
||||||
|
Pubkey::from_str(s).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
SignOnly {
|
||||||
|
blockhash,
|
||||||
|
present_signers,
|
||||||
|
absent_signers,
|
||||||
|
bad_signers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use solana_sdk::{
|
||||||
|
message::Message,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError},
|
||||||
|
system_instruction,
|
||||||
|
transaction::Transaction,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_return_signers() {
|
||||||
|
struct BadSigner {
|
||||||
|
pubkey: Pubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BadSigner {
|
||||||
|
pub fn new(pubkey: Pubkey) -> Self {
|
||||||
|
Self { pubkey }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signer for BadSigner {
|
||||||
|
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
|
||||||
|
Ok(self.pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
|
||||||
|
Ok(Signature::new(&[1u8; 64]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
|
||||||
|
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
|
||||||
|
let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32])));
|
||||||
|
let to = Pubkey::new(&[5u8; 32]);
|
||||||
|
let nonce = Pubkey::new(&[6u8; 32]);
|
||||||
|
let from = present.pubkey();
|
||||||
|
let fee_payer = absent.pubkey();
|
||||||
|
let nonce_auth = bad.pubkey();
|
||||||
|
let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
|
||||||
|
vec![system_instruction::transfer(&from, &to, 42)],
|
||||||
|
Some(&fee_payer),
|
||||||
|
&nonce,
|
||||||
|
&nonce_auth,
|
||||||
|
));
|
||||||
|
|
||||||
|
let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
|
||||||
|
let blockhash = Hash::new(&[7u8; 32]);
|
||||||
|
tx.try_partial_sign(&signers, blockhash).unwrap();
|
||||||
|
let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
|
||||||
|
let sign_only = parse_sign_only_reply_string(&res);
|
||||||
|
assert_eq!(sign_only.blockhash, blockhash);
|
||||||
|
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
|
||||||
|
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
|
||||||
|
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
checks::*, cluster_query::*, nonce::*, offline::return_signers, spend_utils::*, stake::*,
|
checks::*, cluster_query::*, nonce::*, spend_utils::*, stake::*, validator_info::*, vote::*,
|
||||||
validator_info::*, vote::*,
|
|
||||||
};
|
};
|
||||||
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
@ -21,7 +20,7 @@ use solana_cli_output::{
|
||||||
display::{
|
display::{
|
||||||
build_balance_message, new_spinner_progress_bar, println_name_value, println_transaction,
|
build_balance_message, new_spinner_progress_bar, println_name_value, println_transaction,
|
||||||
},
|
},
|
||||||
CliAccount, CliSignature, OutputFormat,
|
return_signers, CliAccount, CliSignature, OutputFormat,
|
||||||
};
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::BlockhashQuery,
|
blockhash_query::BlockhashQuery,
|
||||||
|
|
|
@ -24,7 +24,6 @@ pub mod checks;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod cluster_query;
|
pub mod cluster_query;
|
||||||
pub mod nonce;
|
pub mod nonce;
|
||||||
pub mod offline;
|
|
||||||
pub mod spend_utils;
|
pub mod spend_utils;
|
||||||
pub mod stake;
|
pub mod stake;
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
|
|
|
@ -1,172 +0,0 @@
|
||||||
use serde_json::Value;
|
|
||||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
|
||||||
use solana_cli_output::{CliSignOnlyData, OutputFormat};
|
|
||||||
use solana_sdk::{
|
|
||||||
hash::Hash,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::{Presigner, Signature},
|
|
||||||
transaction::Transaction,
|
|
||||||
};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub fn return_signers(
|
|
||||||
tx: &Transaction,
|
|
||||||
output_format: &OutputFormat,
|
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let verify_results = tx.verify_with_results();
|
|
||||||
let mut signers = Vec::new();
|
|
||||||
let mut absent = Vec::new();
|
|
||||||
let mut bad_sig = Vec::new();
|
|
||||||
tx.signatures
|
|
||||||
.iter()
|
|
||||||
.zip(tx.message.account_keys.iter())
|
|
||||||
.zip(verify_results.into_iter())
|
|
||||||
.for_each(|((sig, key), res)| {
|
|
||||||
if res {
|
|
||||||
signers.push(format!("{}={}", key, sig))
|
|
||||||
} else if *sig == Signature::default() {
|
|
||||||
absent.push(key.to_string());
|
|
||||||
} else {
|
|
||||||
bad_sig.push(key.to_string());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let cli_command = CliSignOnlyData {
|
|
||||||
blockhash: tx.message.recent_blockhash.to_string(),
|
|
||||||
signers,
|
|
||||||
absent,
|
|
||||||
bad_sig,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(output_format.formatted_string(&cli_command))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SignOnly {
|
|
||||||
pub blockhash: Hash,
|
|
||||||
pub present_signers: Vec<(Pubkey, Signature)>,
|
|
||||||
pub absent_signers: Vec<Pubkey>,
|
|
||||||
pub bad_signers: Vec<Pubkey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignOnly {
|
|
||||||
pub fn has_all_signers(&self) -> bool {
|
|
||||||
self.absent_signers.is_empty() && self.bad_signers.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
|
|
||||||
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
|
||||||
let object: Value = serde_json::from_str(&reply).unwrap();
|
|
||||||
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
|
||||||
let blockhash = blockhash_str.parse::<Hash>().unwrap();
|
|
||||||
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
|
|
||||||
let signer_strings = object.get("signers");
|
|
||||||
if let Some(sig_strings) = signer_strings {
|
|
||||||
present_signers = sig_strings
|
|
||||||
.as_array()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|signer_string| {
|
|
||||||
let mut signer = signer_string.as_str().unwrap().split('=');
|
|
||||||
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
|
||||||
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
|
||||||
(key, sig)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
let mut absent_signers: Vec<Pubkey> = Vec::new();
|
|
||||||
let signer_strings = object.get("absent");
|
|
||||||
if let Some(sig_strings) = signer_strings {
|
|
||||||
absent_signers = sig_strings
|
|
||||||
.as_array()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|val| {
|
|
||||||
let s = val.as_str().unwrap();
|
|
||||||
Pubkey::from_str(s).unwrap()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
let mut bad_signers: Vec<Pubkey> = Vec::new();
|
|
||||||
let signer_strings = object.get("badSig");
|
|
||||||
if let Some(sig_strings) = signer_strings {
|
|
||||||
bad_signers = sig_strings
|
|
||||||
.as_array()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|val| {
|
|
||||||
let s = val.as_str().unwrap();
|
|
||||||
Pubkey::from_str(s).unwrap()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
SignOnly {
|
|
||||||
blockhash,
|
|
||||||
present_signers,
|
|
||||||
absent_signers,
|
|
||||||
bad_signers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use solana_sdk::{
|
|
||||||
message::Message,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError},
|
|
||||||
system_instruction,
|
|
||||||
transaction::Transaction,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_return_signers() {
|
|
||||||
struct BadSigner {
|
|
||||||
pubkey: Pubkey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BadSigner {
|
|
||||||
pub fn new(pubkey: Pubkey) -> Self {
|
|
||||||
Self { pubkey }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signer for BadSigner {
|
|
||||||
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
|
|
||||||
Ok(self.pubkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
|
|
||||||
Ok(Signature::new(&[1u8; 64]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
|
|
||||||
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
|
|
||||||
let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32])));
|
|
||||||
let to = Pubkey::new(&[5u8; 32]);
|
|
||||||
let nonce = Pubkey::new(&[6u8; 32]);
|
|
||||||
let from = present.pubkey();
|
|
||||||
let fee_payer = absent.pubkey();
|
|
||||||
let nonce_auth = bad.pubkey();
|
|
||||||
let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
|
|
||||||
vec![system_instruction::transfer(&from, &to, 42)],
|
|
||||||
Some(&fee_payer),
|
|
||||||
&nonce,
|
|
||||||
&nonce_auth,
|
|
||||||
));
|
|
||||||
|
|
||||||
let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
|
|
||||||
let blockhash = Hash::new(&[7u8; 32]);
|
|
||||||
tx.try_partial_sign(&signers, blockhash).unwrap();
|
|
||||||
let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
|
|
||||||
let sign_only = parse_sign_only_reply_string(&res);
|
|
||||||
assert_eq!(sign_only.blockhash, blockhash);
|
|
||||||
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
|
|
||||||
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
|
|
||||||
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
ProcessResult,
|
ProcessResult,
|
||||||
},
|
},
|
||||||
nonce::check_nonce_account,
|
nonce::check_nonce_account,
|
||||||
offline::return_signers,
|
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
||||||
};
|
};
|
||||||
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||||
|
@ -18,7 +17,9 @@ use solana_clap_utils::{
|
||||||
offline::*,
|
offline::*,
|
||||||
ArgConstant,
|
ArgConstant,
|
||||||
};
|
};
|
||||||
use solana_cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType};
|
use solana_cli_output::{
|
||||||
|
return_signers, CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType,
|
||||||
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
|
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
|
||||||
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
|
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
offline::parse_sign_only_reply_string,
|
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
test_utils::{check_ready, check_recent_balance},
|
||||||
};
|
};
|
||||||
use solana_cli_output::OutputFormat;
|
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::{self, BlockhashQuery},
|
blockhash_query::{self, BlockhashQuery},
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
offline::parse_sign_only_reply_string,
|
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
test_utils::{check_ready, check_recent_balance},
|
||||||
};
|
};
|
||||||
use solana_cli_output::OutputFormat;
|
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::{self, BlockhashQuery},
|
blockhash_query::{self, BlockhashQuery},
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
offline::parse_sign_only_reply_string,
|
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
test_utils::{check_ready, check_recent_balance},
|
||||||
};
|
};
|
||||||
use solana_cli_output::OutputFormat;
|
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::{self, BlockhashQuery},
|
blockhash_query::{self, BlockhashQuery},
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
|
|
Loading…
Reference in New Issue