CLI: Consolidate sign-only helpers
This commit is contained in:
committed by
Trent Nelson
parent
5e34af5a75
commit
eaf79152b3
@ -1,10 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::*,
|
checks::*,
|
||||||
cli_output::{CliAccount, CliSignOnlyData, CliSignature, OutputFormat},
|
cli_output::{CliAccount, CliSignature, OutputFormat},
|
||||||
cluster_query::*,
|
cluster_query::*,
|
||||||
display::{new_spinner_progress_bar, println_name_value, println_transaction},
|
display::{new_spinner_progress_bar, println_name_value, println_transaction},
|
||||||
nonce::*,
|
nonce::*,
|
||||||
offline::blockhash_query::BlockhashQuery,
|
offline::{blockhash_query::BlockhashQuery, return_signers},
|
||||||
spend_utils::*,
|
spend_utils::*,
|
||||||
stake::*,
|
stake::*,
|
||||||
validator_info::*,
|
validator_info::*,
|
||||||
@ -909,35 +909,6 @@ pub fn get_blockhash_and_fee_calculator(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_signers(tx: &Transaction, output_format: &OutputFormat) -> ProcessResult {
|
|
||||||
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_create_address_with_seed(
|
pub fn parse_create_address_with_seed(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
default_signer_path: &str,
|
default_signer_path: &str,
|
||||||
@ -2351,9 +2322,7 @@ mod tests {
|
|||||||
use solana_client::mock_sender::SIGNATURE;
|
use solana_client::mock_sender::SIGNATURE;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{
|
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Presigner},
|
||||||
keypair_from_seed, read_keypair_file, write_keypair_file, NullSigner, Presigner,
|
|
||||||
},
|
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -3133,52 +3102,4 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,5 +1,6 @@
|
|||||||
pub mod blockhash_query;
|
pub mod blockhash_query;
|
||||||
|
|
||||||
|
use crate::cli_output::{CliSignOnlyData, OutputFormat};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
@ -8,9 +9,42 @@ use solana_sdk::{
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Presigner, Signature},
|
signature::{Presigner, Signature},
|
||||||
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
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 struct SignOnly {
|
||||||
pub blockhash: Hash,
|
pub blockhash: Hash,
|
||||||
pub present_signers: Vec<(Pubkey, Signature)>,
|
pub present_signers: Vec<(Pubkey, Signature)>,
|
||||||
@ -80,3 +114,63 @@ pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
|||||||
bad_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,12 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||||
cli::{
|
cli::{
|
||||||
generate_unique_signers, log_instruction_custom_error, return_signers, CliCommand,
|
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||||
CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex,
|
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||||
},
|
},
|
||||||
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
|
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
|
||||||
nonce::check_nonce_account,
|
nonce::check_nonce_account,
|
||||||
offline::blockhash_query::BlockhashQuery,
|
offline::{blockhash_query::BlockhashQuery, 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};
|
||||||
|
Reference in New Issue
Block a user