Factor the rest of CLI offline module out to clap-utils and cli-output crates

This commit is contained in:
Trent Nelson
2020-09-22 20:29:32 -06:00
committed by Trent Nelson
parent 325a7e9f86
commit 7cab638297
11 changed files with 179 additions and 184 deletions

View File

@ -1,6 +1,5 @@
use crate::{
checks::*, cluster_query::*, nonce::*, offline::return_signers, spend_utils::*, stake::*,
validator_info::*, vote::*,
checks::*, cluster_query::*, nonce::*, spend_utils::*, stake::*, validator_info::*, vote::*,
};
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use log::*;
@ -21,7 +20,7 @@ use solana_cli_output::{
display::{
build_balance_message, new_spinner_progress_bar, println_name_value, println_transaction,
},
CliAccount, CliSignature, OutputFormat,
return_signers, CliAccount, CliSignature, OutputFormat,
};
use solana_client::{
blockhash_query::BlockhashQuery,

View File

@ -24,7 +24,6 @@ pub mod checks;
pub mod cli;
pub mod cluster_query;
pub mod nonce;
pub mod offline;
pub mod spend_utils;
pub mod stake;
pub mod test_utils;

View File

@ -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());
}
}

View File

@ -5,7 +5,6 @@ use crate::{
ProcessResult,
},
nonce::check_nonce_account,
offline::return_signers,
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
};
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
@ -18,7 +17,9 @@ use solana_clap_utils::{
offline::*,
ArgConstant,
};
use solana_cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType};
use solana_cli_output::{
return_signers, CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType,
};
use solana_client::{
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,

View File

@ -1,10 +1,9 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::parse_sign_only_reply_string,
spend_utils::SpendAmount,
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::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,

View File

@ -1,10 +1,9 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::parse_sign_only_reply_string,
spend_utils::SpendAmount,
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::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,

View File

@ -1,10 +1,9 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::parse_sign_only_reply_string,
spend_utils::SpendAmount,
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::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,