diff --git a/Cargo.lock b/Cargo.lock index a008120a8c..59af5dbb95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3812,6 +3812,7 @@ dependencies = [ "rpassword 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "solana-remote-wallet 1.0.6", "solana-sdk 1.0.6", + "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/archiver-lib/src/archiver.rs b/archiver-lib/src/archiver.rs index 4e6ac846cb..26a1ed030f 100644 --- a/archiver-lib/src/archiver.rs +++ b/archiver-lib/src/archiver.rs @@ -379,8 +379,7 @@ impl Archiver { &archiver_keypair.pubkey(), &storage_keypair.pubkey(), ); - let message = - Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey())); + let message = Message::new_with_payer(&[ix], Some(&archiver_keypair.pubkey())); if let Err(e) = client.send_message(&[archiver_keypair.as_ref()], message) { error!("unable to redeem reward, tx failed: {:?}", e); } else { @@ -613,6 +612,7 @@ impl Archiver { ErrorKind::Other, "setup_mining_account: signature not found", ), + TransportError::Custom(e) => io::Error::new(ErrorKind::Other, e), })?; } Ok(()) @@ -655,7 +655,7 @@ impl Archiver { Signature::new(&meta.signature.as_ref()), meta.blockhash, ); - let message = Message::new_with_payer(vec![instruction], Some(&archiver_keypair.pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&archiver_keypair.pubkey())); let mut transaction = Transaction::new( &[archiver_keypair.as_ref(), storage_keypair.as_ref()], message, diff --git a/clap-utils/Cargo.toml b/clap-utils/Cargo.toml index b00620958d..9fc1b4ee26 100644 --- a/clap-utils/Cargo.toml +++ b/clap-utils/Cargo.toml @@ -13,6 +13,7 @@ clap = "2.33.0" rpassword = "4.0" solana-remote-wallet = { path = "../remote-wallet", version = "1.0.6" } solana-sdk = { path = "../sdk", version = "1.0.6" } +thiserror = "1.0.11" tiny-bip39 = "0.7.0" url = "2.1.0" chrono = "0.4" diff --git a/clap-utils/src/input_validators.rs b/clap-utils/src/input_validators.rs index 0e6b659a1c..88ec1c7b13 100644 --- a/clap-utils/src/input_validators.rs +++ b/clap-utils/src/input_validators.rs @@ -12,7 +12,7 @@ use std::str::FromStr; pub fn is_pubkey(string: String) -> Result<(), String> { match string.parse::() { Ok(_) => Ok(()), - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{}", err)), } } @@ -20,7 +20,7 @@ pub fn is_pubkey(string: String) -> Result<(), String> { pub fn is_hash(string: String) -> Result<(), String> { match string.parse::() { Ok(_) => Ok(()), - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{}", err)), } } @@ -28,7 +28,7 @@ pub fn is_hash(string: String) -> Result<(), String> { pub fn is_keypair(string: String) -> Result<(), String> { read_keypair_file(&string) .map(|_| ()) - .map_err(|err| format!("{:?}", err)) + .map_err(|err| format!("{}", err)) } // Return an error if a keypair file cannot be parsed @@ -38,7 +38,7 @@ pub fn is_keypair_or_ask_keyword(string: String) -> Result<(), String> { } read_keypair_file(&string) .map(|_| ()) - .map_err(|err| format!("{:?}", err)) + .map_err(|err| format!("{}", err)) } // Return an error if string cannot be parsed as pubkey string or keypair file location @@ -73,10 +73,10 @@ pub fn is_pubkey_sig(string: String) -> Result<(), String> { .ok_or_else(|| "Malformed signer string".to_string())?, ) { Ok(_) => Ok(()), - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{}", err)), } } - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{}", err)), } } @@ -90,20 +90,20 @@ pub fn is_url(string: String) -> Result<(), String> { Err("no host provided".to_string()) } } - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{}", err)), } } pub fn is_slot(slot: String) -> Result<(), String> { slot.parse::() .map(|_| ()) - .map_err(|e| format!("{:?}", e)) + .map_err(|e| format!("{}", e)) } pub fn is_port(port: String) -> Result<(), String> { port.parse::() .map(|_| ()) - .map_err(|e| format!("{:?}", e)) + .map_err(|e| format!("{}", e)) } pub fn is_valid_percentage(percentage: String) -> Result<(), String> { @@ -111,7 +111,7 @@ pub fn is_valid_percentage(percentage: String) -> Result<(), String> { .parse::() .map_err(|e| { format!( - "Unable to parse input percentage, provided: {}, err: {:?}", + "Unable to parse input percentage, provided: {}, err: {}", percentage, e ) }) @@ -141,7 +141,7 @@ pub fn is_amount(amount: String) -> Result<(), String> { pub fn is_rfc3339_datetime(value: String) -> Result<(), String> { DateTime::parse_from_rfc3339(&value) .map(|_| ()) - .map_err(|e| format!("{:?}", e)) + .map_err(|e| format!("{}", e)) } pub fn is_derivation(value: String) -> Result<(), String> { @@ -152,7 +152,7 @@ pub fn is_derivation(value: String) -> Result<(), String> { .parse::() .map_err(|e| { format!( - "Unable to parse derivation, provided: {}, err: {:?}", + "Unable to parse derivation, provided: {}, err: {}", account, e ) }) @@ -160,7 +160,7 @@ pub fn is_derivation(value: String) -> Result<(), String> { if let Some(change) = parts.next() { change.parse::().map_err(|e| { format!( - "Unable to parse derivation, provided: {}, err: {:?}", + "Unable to parse derivation, provided: {}, err: {}", change, e ) }) diff --git a/clap-utils/src/keypair.rs b/clap-utils/src/keypair.rs index 25b9042855..1c210255d7 100644 --- a/clap-utils/src/keypair.rs +++ b/clap-utils/src/keypair.rs @@ -1,6 +1,6 @@ use crate::{input_parsers::pubkeys_sigs_of, offline::SIGNER_ARG, ArgConstant}; use bip39::{Language, Mnemonic, Seed}; -use clap::{ArgMatches, Error, ErrorKind}; +use clap::ArgMatches; use rpassword::prompt_password_stderr; use solana_remote_wallet::{ remote_keypair::generate_remote_keypair, @@ -71,7 +71,14 @@ pub fn signer_from_path( false, )?)) } - KeypairUrl::Filepath(path) => Ok(Box::new(read_keypair_file(&path)?)), + KeypairUrl::Filepath(path) => match read_keypair_file(&path) { + Err(e) => Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("could not find keypair file: {} error: {}", path, e), + ) + .into()), + Ok(file) => Ok(Box::new(file)), + }, KeypairUrl::Stdin => { let mut stdin = std::io::stdin(); Ok(Box::new(read_keypair(&mut stdin)?)) @@ -95,9 +102,9 @@ pub fn signer_from_path( if let Some(presigner) = presigner { Ok(Box::new(presigner)) } else { - Err(Error::with_description( - "Missing signature for supplied pubkey", - ErrorKind::MissingRequiredArgument, + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "missing signature for supplied pubkey".to_string(), ) .into()) } diff --git a/clap-utils/src/lib.rs b/clap-utils/src/lib.rs index a66efa3981..37926d6fbe 100644 --- a/clap-utils/src/lib.rs +++ b/clap-utils/src/lib.rs @@ -1,3 +1,5 @@ +use thiserror::Error; + #[macro_export] macro_rules! version { () => { @@ -23,6 +25,23 @@ pub struct ArgConstant<'a> { pub help: &'a str, } +/// Error type for forwarding Errors out of `main()` of a `clap` app +/// and still using the `Display` formatter +#[derive(Error)] +#[error("{0}")] +pub struct DisplayError(Box); +impl DisplayError { + pub fn new_as_boxed(inner: Box) -> Box { + DisplayError(inner).into() + } +} + +impl std::fmt::Debug for DisplayError { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "{}", self.0) + } +} + pub mod input_parsers; pub mod input_validators; pub mod keypair; diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 8b99f661af..268daf4047 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -18,7 +18,10 @@ use solana_clap_utils::{ input_parsers::*, input_validators::*, keypair::signer_from_path, offline::SIGN_ONLY_ARG, ArgConstant, }; -use solana_client::{client_error::ClientError, rpc_client::RpcClient}; +use solana_client::{ + client_error::{ClientErrorKind, Result as ClientResult}, + rpc_client::RpcClient, +}; #[cfg(not(test))] use solana_faucet::faucet::request_airdrop_transaction; #[cfg(test)] @@ -47,14 +50,15 @@ use solana_stake_program::{ use solana_storage_program::storage_instruction::StorageAccountType; use solana_vote_program::vote_state::VoteAuthorize; use std::{ + error, fs::File, io::{Read, Write}, net::{IpAddr, SocketAddr}, sync::Arc, thread::sleep, time::Duration, - {error, fmt}, }; +use thiserror::Error; use url::Url; pub type CliSigners = Vec>; @@ -406,46 +410,34 @@ pub struct CliCommandInfo { pub signers: CliSigners, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Error, PartialEq)] pub enum CliError { + #[error("bad parameter: {0}")] BadParameter(String), + #[error("command not recognized: {0}")] CommandNotRecognized(String), + #[error("insuficient funds for fee")] InsufficientFundsForFee, + #[error(transparent)] InvalidNonce(CliNonceError), + #[error("dynamic program error: {0}")] DynamicProgramError(String), + #[error("rpc request error: {0}")] RpcRequestError(String), + #[error("keypair file not found: {0}")] KeypairFileNotFound(String), } -impl fmt::Display for CliError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid") - } -} - -impl error::Error for CliError { - fn description(&self) -> &str { - "invalid" - } - - fn cause(&self) -> Option<&dyn error::Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - impl From> for CliError { fn from(error: Box) -> Self { - CliError::DynamicProgramError(format!("{:?}", error)) + CliError::DynamicProgramError(error.to_string()) } } impl From for CliError { fn from(error: CliNonceError) -> Self { match error { - CliNonceError::Client(client_error) => { - Self::RpcRequestError(format!("{:?}", client_error)) - } + CliNonceError::Client(client_error) => Self::RpcRequestError(client_error), _ => Self::InvalidNonce(error), } } @@ -717,7 +709,7 @@ pub fn parse_command( .parse() .or_else(|err| { Err(CliError::BadParameter(format!( - "Invalid faucet port: {:?}", + "Invalid faucet port: {}", err ))) })?; @@ -725,7 +717,7 @@ pub fn parse_command( let faucet_host = if let Some(faucet_host) = matches.value_of("faucet_host") { Some(solana_net_utils::parse_host(faucet_host).or_else(|err| { Err(CliError::BadParameter(format!( - "Invalid faucet host: {:?}", + "Invalid faucet host: {}", err ))) })?) @@ -1137,13 +1129,13 @@ fn process_confirm(rpc_client: &RpcClient, signature: &Signature) -> ProcessResu if let Some(result) = status { match result { Ok(_) => Ok("Confirmed".to_string()), - Err(err) => Ok(format!("Transaction failed with error {:?}", err)), + Err(err) => Ok(format!("Transaction failed with error: {}", err)), } } else { Ok("Not found".to_string()) } } - Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {:?}", err)).into()), + Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()), } } @@ -1203,7 +1195,7 @@ fn process_deploy( program_data.len() as u64, &bpf_loader::id(), ); - let message = Message::new(vec![ix]); + let message = Message::new(&[ix]); let mut create_account_tx = Transaction::new_unsigned(message); create_account_tx.try_sign(&[config.signers[0], &program_id], blockhash)?; messages.push(&create_account_tx.message); @@ -1216,7 +1208,7 @@ fn process_deploy( (i * DATA_CHUNK_SIZE) as u32, chunk.to_vec(), ); - let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&signers, blockhash)?; write_transactions.push(tx); @@ -1226,7 +1218,7 @@ fn process_deploy( } let instruction = loader_instruction::finalize(&program_id.pubkey(), &bpf_loader::id()); - let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&signers[0].pubkey())); let mut finalize_tx = Transaction::new_unsigned(message); finalize_tx.try_sign(&signers, blockhash)?; messages.push(&finalize_tx.message); @@ -1294,7 +1286,7 @@ fn process_pay( let message = if let Some(nonce_account) = &nonce_account { Message::new_with_nonce(vec![ix], None, nonce_account, &nonce_authority.pubkey()) } else { - Message::new(vec![ix]) + Message::new(&[ix]) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, blockhash)?; @@ -1334,7 +1326,7 @@ fn process_pay( cancelable, lamports, ); - let message = Message::new(ixs); + let message = Message::new(&ixs); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; if sign_only { @@ -1377,7 +1369,7 @@ fn process_pay( cancelable, lamports, ); - let message = Message::new(ixs); + let message = Message::new(&ixs); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; if sign_only { @@ -1411,7 +1403,7 @@ fn process_cancel(rpc_client: &RpcClient, config: &CliConfig, pubkey: &Pubkey) - pubkey, &config.signers[0].pubkey(), ); - let message = Message::new(vec![ix]); + let message = Message::new(&[ix]); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, blockhash)?; check_account_for_fee( @@ -1434,7 +1426,7 @@ fn process_time_elapsed( let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let ix = budget_instruction::apply_timestamp(&config.signers[0].pubkey(), pubkey, to, dt); - let message = Message::new(vec![ix]); + let message = Message::new(&[ix]); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, blockhash)?; check_account_for_fee( @@ -1482,7 +1474,7 @@ fn process_transfer( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -1514,7 +1506,7 @@ fn process_witness( let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let ix = budget_instruction::apply_signature(&config.signers[0].pubkey(), pubkey, to); - let message = Message::new(vec![ix]); + let message = Message::new(&[ix]); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, blockhash)?; check_account_for_fee( @@ -2111,18 +2103,18 @@ pub fn request_and_confirm_airdrop( log_instruction_custom_error::(result) } -pub fn log_instruction_custom_error(result: Result) -> ProcessResult +pub fn log_instruction_custom_error(result: ClientResult) -> ProcessResult where E: 'static + std::error::Error + DecodeError + FromPrimitive, { match result { Err(err) => { - if let ClientError::TransactionError(TransactionError::InstructionError( + if let ClientErrorKind::TransactionError(TransactionError::InstructionError( _, InstructionError::CustomError(code), - )) = err + )) = err.kind() { - if let Some(specific_error) = E::decode_custom_error_to_enum(code) { + if let Some(specific_error) = E::decode_custom_error_to_enum(*code) { error!("{}::{:?}", E::type_of(), specific_error); eprintln!( "Program Error ({}::{:?}): {}", @@ -3325,7 +3317,7 @@ mod tests { assert_eq!( process_command(&config).unwrap(), format!( - "Transaction failed with error {:?}", + "Transaction failed with error: {}", TransactionError::AccountInUse ) ); diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 59c35398c6..22abe607e5 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -789,7 +789,7 @@ pub fn process_ping( last_blockhash = recent_blockhash; let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports); - let message = Message::new(vec![ix]); + let message = Message::new(&[ix]); let mut transaction = Transaction::new_unsigned(message); transaction.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( @@ -972,7 +972,7 @@ pub fn process_live_slots(url: &str) -> ProcessResult { current = Some(new_info); } Err(err) => { - eprintln!("disconnected: {:?}", err); + eprintln!("disconnected: {}", err); break; } } diff --git a/cli/src/main.rs b/cli/src/main.rs index 345448b9ee..639f8442f8 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,7 +1,9 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; use console::style; -use solana_clap_utils::{input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG}; +use solana_clap_utils::{ + input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError, +}; use solana_cli::{ cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners}, display::{println_name_value, println_name_value_or}, @@ -230,6 +232,10 @@ fn main() -> Result<(), Box> { ) .get_matches(); + do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into()) +} + +fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box> { if parse_settings(&matches)? { let wallet_manager = maybe_wallet_manager()?; @@ -237,6 +243,6 @@ fn main() -> Result<(), Box> { config.signers = signers.iter().map(|s| s.as_ref()).collect(); let result = process_command(&config)?; println!("{}", result); - } + }; Ok(()) } diff --git a/cli/src/nonce.rs b/cli/src/nonce.rs index c7aab073cc..f6e744e48f 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -238,7 +238,7 @@ pub fn get_account( ) -> Result { rpc_client .get_account(nonce_pubkey) - .map_err(|e| CliNonceError::Client(format!("{:?}", e))) + .map_err(|e| CliNonceError::Client(format!("{}", e))) .and_then(|a| match account_identity_ok(&a) { Ok(()) => Ok(a), Err(e) => Err(e), @@ -441,7 +441,7 @@ pub fn process_authorize_nonce_account( let nonce_authority = config.signers[nonce_authority]; let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority); - let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -518,7 +518,7 @@ pub fn process_create_nonce_account( let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -560,7 +560,7 @@ pub fn process_new_nonce( let nonce_authority = config.signers[nonce_authority]; let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey()); let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( @@ -633,7 +633,7 @@ pub fn process_withdraw_from_nonce_account( destination_account_pubkey, lamports, ); - let message = Message::new_with_payer(vec![ix], Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( diff --git a/cli/src/stake.rs b/cli/src/stake.rs index e1be2b2e44..2c05b03e60 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -827,7 +827,7 @@ pub fn process_create_stake_account( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -889,7 +889,7 @@ pub fn process_stake_authorize( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -942,7 +942,7 @@ pub fn process_deactivate_stake_account( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -1001,7 +1001,7 @@ pub fn process_withdraw_stake( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -1134,7 +1134,7 @@ pub fn process_split_stake( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -1190,7 +1190,7 @@ pub fn process_stake_set_lockup( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; @@ -1300,7 +1300,7 @@ pub fn process_show_stake_account( Ok("".to_string()) } Err(err) => Err(CliError::RpcRequestError(format!( - "Account data could not be deserialized to stake state: {:?}", + "Account data could not be deserialized to stake state: {}", err )) .into()), @@ -1396,11 +1396,11 @@ pub fn process_delegate_stake( } }; - if sanity_check_result.is_err() { + if let Err(err) = &sanity_check_result { if !force { sanity_check_result?; } else { - println!("--force supplied, ignoring: {:?}", sanity_check_result); + println!("--force supplied, ignoring: {}", err); } } } @@ -1424,7 +1424,7 @@ pub fn process_delegate_stake( &nonce_authority.pubkey(), ) } else { - Message::new_with_payer(ixs, Some(&fee_payer.pubkey())) + Message::new_with_payer(&ixs, Some(&fee_payer.pubkey())) }; let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; diff --git a/cli/src/storage.rs b/cli/src/storage.rs index faa01081dd..1f6595c90d 100644 --- a/cli/src/storage.rs +++ b/cli/src/storage.rs @@ -212,7 +212,7 @@ pub fn process_create_storage_account( ); let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let message = Message::new(ixs); + let message = Message::new(&ixs); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( @@ -236,7 +236,7 @@ pub fn process_claim_storage_reward( let instruction = storage_instruction::claim_reward(node_account_pubkey, storage_account_pubkey); let signers = [config.signers[0]]; - let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&signers, recent_blockhash)?; check_account_for_fee( @@ -266,7 +266,7 @@ pub fn process_show_storage_account( use solana_storage_program::storage_contract::StorageContract; let storage_contract: StorageContract = account.state().map_err(|err| { - CliError::RpcRequestError(format!("Unable to deserialize storage account: {:?}", err)) + CliError::RpcRequestError(format!("Unable to deserialize storage account: {}", err)) })?; println!("{:#?}", storage_contract); println!("Account Lamports: {}", account.lamports); diff --git a/cli/src/validator_info.rs b/cli/src/validator_info.rs index c902758eaa..672f1bee09 100644 --- a/cli/src/validator_info.rs +++ b/cli/src/validator_info.rs @@ -274,7 +274,7 @@ pub fn process_set_validator_info( println!("--force supplied, ignoring: {:?}", result); } else { result.map_err(|err| { - CliError::BadParameter(format!("Invalid validator keybase username: {:?}", err)) + CliError::BadParameter(format!("Invalid validator keybase username: {}", err)) })?; } } @@ -339,7 +339,7 @@ pub fn process_set_validator_info( &validator_info, )]); let signers = vec![config.signers[0], &info_keypair]; - let message = Message::new(instructions); + let message = Message::new(&instructions); (message, signers) } else { println!( @@ -353,7 +353,7 @@ pub fn process_set_validator_info( keys, &validator_info, )]; - let message = Message::new_with_payer(instructions, Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&instructions, Some(&config.signers[0].pubkey())); let signers = vec![config.signers[0]]; (message, signers) }; diff --git a/cli/src/vote.rs b/cli/src/vote.rs index c56aa3196c..90eaf7b2c9 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -359,7 +359,7 @@ pub fn process_create_vote_account( }; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let message = Message::new(ixs); + let message = Message::new(&ixs); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( @@ -391,7 +391,7 @@ pub fn process_vote_authorize( vote_authorize, // vote or withdraw )]; - let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( @@ -422,7 +422,7 @@ pub fn process_vote_update_validator( new_identity_pubkey, )]; - let message = Message::new_with_payer(ixs, Some(&config.signers[0].pubkey())); + let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); tx.try_sign(&config.signers, recent_blockhash)?; check_account_for_fee( diff --git a/client/src/client_error.rs b/client/src/client_error.rs index a9ccaf469d..131f1cefee 100644 --- a/client/src/client_error.rs +++ b/client/src/client_error.rs @@ -1,20 +1,161 @@ use crate::rpc_request; -use solana_sdk::{signature::SignerError, transaction::TransactionError}; -use std::{fmt, io}; +use solana_sdk::{ + signature::SignerError, transaction::TransactionError, transport::TransportError, +}; +use std::io; use thiserror::Error; #[derive(Error, Debug)] -pub enum ClientError { +pub enum ClientErrorKind { + #[error(transparent)] Io(#[from] io::Error), + #[error(transparent)] Reqwest(#[from] reqwest::Error), + #[error(transparent)] RpcError(#[from] rpc_request::RpcError), + #[error(transparent)] SerdeJson(#[from] serde_json::error::Error), + #[error(transparent)] SigningError(#[from] SignerError), + #[error(transparent)] TransactionError(#[from] TransactionError), + #[error("Custom: {0}")] + Custom(String), } -impl fmt::Display for ClientError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "solana client error") +impl From for ClientErrorKind { + fn from(err: TransportError) -> Self { + match err { + TransportError::IoError(err) => Self::Io(err), + TransportError::TransactionError(err) => Self::TransactionError(err), + TransportError::Custom(err) => Self::Custom(err), + } } } + +impl Into for ClientErrorKind { + fn into(self) -> TransportError { + match self { + Self::Io(err) => TransportError::IoError(err), + Self::TransactionError(err) => TransportError::TransactionError(err), + Self::Reqwest(err) => TransportError::Custom(format!("{:?}", err)), + Self::RpcError(err) => TransportError::Custom(format!("{:?}", err)), + Self::SerdeJson(err) => TransportError::Custom(format!("{:?}", err)), + Self::SigningError(err) => TransportError::Custom(format!("{:?}", err)), + Self::Custom(err) => TransportError::Custom(format!("{:?}", err)), + } + } +} + +#[derive(Error, Debug)] +#[error("{kind}")] +pub struct ClientError { + command: Option<&'static str>, + #[source] + #[error(transparent)] + kind: ClientErrorKind, +} + +impl ClientError { + pub fn new_with_command(kind: ClientErrorKind, command: &'static str) -> Self { + Self { + command: Some(command), + kind, + } + } + + pub fn into_with_command(self, command: &'static str) -> Self { + Self { + command: Some(command), + ..self + } + } + + pub fn command(&self) -> Option<&'static str> { + self.command + } + + pub fn kind(&self) -> &ClientErrorKind { + &self.kind + } +} + +impl From for ClientError { + fn from(kind: ClientErrorKind) -> Self { + Self { + command: None, + kind, + } + } +} + +impl From for ClientError { + fn from(err: TransportError) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl Into for ClientError { + fn into(self) -> TransportError { + self.kind.into() + } +} + +impl From for ClientError { + fn from(err: std::io::Error) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl From for ClientError { + fn from(err: reqwest::Error) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl From for ClientError { + fn from(err: rpc_request::RpcError) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl From for ClientError { + fn from(err: serde_json::error::Error) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl From for ClientError { + fn from(err: SignerError) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +impl From for ClientError { + fn from(err: TransactionError) -> Self { + Self { + command: None, + kind: err.into(), + } + } +} + +pub type Result = std::result::Result; diff --git a/client/src/generic_rpc_client_request.rs b/client/src/generic_rpc_client_request.rs index 5c9f337542..509c98fd38 100644 --- a/client/src/generic_rpc_client_request.rs +++ b/client/src/generic_rpc_client_request.rs @@ -1,4 +1,4 @@ -use crate::{client_error::ClientError, rpc_request::RpcRequest}; +use crate::{client_error::Result, rpc_request::RpcRequest}; pub(crate) trait GenericRpcClientRequest { fn send( @@ -6,5 +6,5 @@ pub(crate) trait GenericRpcClientRequest { request: &RpcRequest, params: serde_json::Value, retries: usize, - ) -> Result; + ) -> Result; } diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index 90a7d3d098..db464b6175 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -1,5 +1,5 @@ use crate::{ - client_error::ClientError, + client_error::Result, generic_rpc_client_request::GenericRpcClientRequest, rpc_request::RpcRequest, rpc_response::{Response, RpcResponseContext}, @@ -41,7 +41,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest { request: &RpcRequest, params: serde_json::Value, _retries: usize, - ) -> Result { + ) -> Result { if let Some(value) = self.mocks.write().unwrap().remove(request) { return Ok(value); } diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index a760e155bd..9d387b2d40 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -1,13 +1,13 @@ use crate::{ - client_error::ClientError, + client_error::{ClientError, Result as ClientResult}, generic_rpc_client_request::GenericRpcClientRequest, mock_rpc_client_request::{MockRpcClientRequest, Mocks}, rpc_client_request::RpcClientRequest, - rpc_request::RpcRequest, + rpc_request::{RpcError, RpcRequest}, rpc_response::{ Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount, - RpcLeaderSchedule, RpcResponse, RpcVersionInfo, RpcVoteAccountStatus, + RpcLeaderSchedule, RpcResult, RpcVersionInfo, RpcVoteAccountStatus, }, }; use bincode::serialize; @@ -27,7 +27,7 @@ use solana_sdk::{ transaction::{self, Transaction, TransactionError}, }; use std::{ - error, io, + error, net::SocketAddr, thread::sleep, time::{Duration, Instant}, @@ -67,7 +67,7 @@ impl RpcClient { } } - pub fn confirm_transaction(&self, signature: &str) -> io::Result { + pub fn confirm_transaction(&self, signature: &str) -> ClientResult { Ok(self .confirm_transaction_with_commitment(signature, CommitmentConfig::default())? .value) @@ -77,7 +77,7 @@ impl RpcClient { &self, signature: &str, commitment_config: CommitmentConfig, - ) -> RpcResponse { + ) -> RpcResult { let response = self .client .send( @@ -85,32 +85,19 @@ impl RpcClient { json!([signature, commitment_config]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("ConfirmTransaction request failure {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("ConfirmTransaction"))?; - serde_json::from_value::>(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("Received result of an unexpected type {:?}", err), - ) - }) + serde_json::from_value::>(response) + .map_err(|err| ClientError::new_with_command(err.into(), "ConfirmTransaction")) } - pub fn send_transaction(&self, transaction: &Transaction) -> Result { + pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult { let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string(); let signature = self.client .send(&RpcRequest::SendTransaction, json!([serialized_encoded]), 5)?; if signature.as_str().is_none() { - Err(io::Error::new( - io::ErrorKind::Other, - "Received result of an unexpected type", - ) - .into()) + Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into()) } else { Ok(signature.as_str().unwrap().to_string()) } @@ -119,7 +106,7 @@ impl RpcClient { pub fn get_signature_status( &self, signature: &str, - ) -> Result>, ClientError> { + ) -> ClientResult>> { self.get_signature_status_with_commitment(signature, CommitmentConfig::default()) } @@ -127,7 +114,7 @@ impl RpcClient { &self, signature: &str, commitment_config: CommitmentConfig, - ) -> Result>, ClientError> { + ) -> ClientResult>> { let signature_status = self.client.send( &RpcRequest::GetSignatureStatus, json!([signature.to_string(), commitment_config]), @@ -138,101 +125,65 @@ impl RpcClient { Ok(result) } - pub fn get_slot(&self) -> io::Result { + pub fn get_slot(&self) -> ClientResult { self.get_slot_with_commitment(CommitmentConfig::default()) } pub fn get_slot_with_commitment( &self, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { let response = self .client .send(&RpcRequest::GetSlot, json!([commitment_config]), 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetSlot request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetSlot"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetSlot parse failure: {}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetSlot")) } - pub fn get_vote_accounts(&self) -> io::Result { + pub fn get_vote_accounts(&self) -> ClientResult { self.get_vote_accounts_with_commitment(CommitmentConfig::default()) } pub fn get_vote_accounts_with_commitment( &self, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { let response = self .client .send(&RpcRequest::GetVoteAccounts, json!([commitment_config]), 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetVoteAccounts request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetVoteAccounts"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetVoteAccounts parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetVoteAccounts")) } - pub fn get_cluster_nodes(&self) -> io::Result> { + pub fn get_cluster_nodes(&self) -> ClientResult> { let response = self .client .send(&RpcRequest::GetClusterNodes, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetClusterNodes request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetClusterNodes"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetClusterNodes parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetClusterNodes")) } - pub fn get_confirmed_block(&self, slot: Slot) -> io::Result { + pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult { let response = self .client .send(&RpcRequest::GetConfirmedBlock, json!([slot]), 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetConfirmedBlock request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetConfirmedBlock"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetConfirmedBlock parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetConfirmedBlock")) } pub fn get_confirmed_blocks( &self, start_slot: Slot, end_slot: Option, - ) -> io::Result> { + ) -> ClientResult> { let response = self .client .send( @@ -240,22 +191,13 @@ impl RpcClient { json!([start_slot, end_slot]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetConfirmedBlocks request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetConfirmedBlocks"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetConfirmedBlocks parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetConfirmedBlocks")) } - pub fn get_block_time(&self, slot: Slot) -> io::Result { + pub fn get_block_time(&self, slot: Slot) -> ClientResult { let response = self .client .send(&RpcRequest::GetBlockTime, json!([slot]), 0); @@ -263,50 +205,37 @@ impl RpcClient { response .map(|result_json| { if result_json.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Block Not Found: slot={}", slot), - )); + return Err(RpcError::ForUser(format!("Block Not Found: slot={}", slot)).into()); } - let result = serde_json::from_value(result_json)?; + let result = serde_json::from_value(result_json) + .map_err(|err| ClientError::new_with_command(err.into(), "GetBlockTime"))?; trace!("Response block timestamp {:?} {:?}", slot, result); Ok(result) }) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetBlockTime request failure: {:?}", err), - ) - })? + .map_err(|err| err.into_with_command("GetBlockTime"))? } - pub fn get_epoch_info(&self) -> io::Result { + pub fn get_epoch_info(&self) -> ClientResult { self.get_epoch_info_with_commitment(CommitmentConfig::default()) } pub fn get_epoch_info_with_commitment( &self, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { let response = self .client .send(&RpcRequest::GetEpochInfo, json!([commitment_config]), 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetEpochInfo request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetEpochInfo"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetEpochInfo parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetEpochInfo")) } - pub fn get_leader_schedule(&self, slot: Option) -> io::Result> { + pub fn get_leader_schedule( + &self, + slot: Option, + ) -> ClientResult> { self.get_leader_schedule_with_commitment(slot, CommitmentConfig::default()) } @@ -314,7 +243,7 @@ impl RpcClient { &self, slot: Option, commitment_config: CommitmentConfig, - ) -> io::Result> { + ) -> ClientResult> { let response = self .client .send( @@ -322,130 +251,75 @@ impl RpcClient { json!([slot, commitment_config]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetLeaderSchedule request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetLeaderSchedule"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetLeaderSchedule failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetLeaderSchedule")) } - pub fn get_epoch_schedule(&self) -> io::Result { + pub fn get_epoch_schedule(&self) -> ClientResult { let response = self .client .send(&RpcRequest::GetEpochSchedule, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetEpochSchedule request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetEpochSchedule"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetEpochSchedule parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetEpochSchedule")) } - pub fn get_identity(&self) -> io::Result { + pub fn get_identity(&self) -> ClientResult { let response = self .client .send(&RpcRequest::GetIdentity, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetIdentity request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetIdentity"))?; serde_json::from_value(response) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetIdentity failure: {:?}", err), - ) - }) + .map_err(|err| ClientError::new_with_command(err.into(), "GetIdentity")) .and_then(|rpc_identity: RpcIdentity| { - rpc_identity.identity.parse::().map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetIdentity invalid pubkey failure: {:?}", err), + rpc_identity.identity.parse::().map_err(|_| { + ClientError::new_with_command( + RpcError::ParseError("Pubkey".to_string()).into(), + "GetIdentity", ) }) }) } - pub fn get_inflation(&self) -> io::Result { + pub fn get_inflation(&self) -> ClientResult { let response = self .client .send(&RpcRequest::GetInflation, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetInflation request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetInflation"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetInflation parse failure: {}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetInflation")) } - pub fn get_version(&self) -> io::Result { + pub fn get_version(&self) -> ClientResult { let response = self .client .send(&RpcRequest::GetVersion, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetVersion request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetVersion"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetVersion parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetVersion")) } - pub fn minimum_ledger_slot(&self) -> io::Result { + pub fn minimum_ledger_slot(&self) -> ClientResult { let response = self .client .send(&RpcRequest::MinimumLedgerSlot, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("MinimumLedgerSlot request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("MinimumLedgerSlot"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("MinimumLedgerSlot parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "MinimumLedgerSlot")) } pub fn send_and_confirm_transaction( &self, transaction: &mut Transaction, signer_keys: &T, - ) -> Result { + ) -> ClientResult { let mut send_retries = 20; loop { let mut status_retries = 15; @@ -482,11 +356,9 @@ impl RpcClient { if let Some(err) = status { return Err(err.unwrap_err().into()); } else { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Transaction {:?} failed: {:?}", signature_str, status), - ) - .into()); + return Err( + RpcError::ForUser("unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds".to_string()).into(), + ); } } } @@ -545,7 +417,7 @@ impl RpcClient { } if send_retries == 0 { - return Err(io::Error::new(io::ErrorKind::Other, "Transactions failed").into()); + return Err(RpcError::ForUser("Transactions failed".to_string()).into()); } send_retries -= 1; @@ -564,7 +436,7 @@ impl RpcClient { &self, tx: &mut Transaction, signer_keys: &T, - ) -> Result<(), ClientError> { + ) -> ClientResult<()> { let (blockhash, _fee_calculator) = self.get_new_blockhash(&tx.message().recent_blockhash)?; tx.try_sign(signer_keys, blockhash)?; @@ -583,41 +455,26 @@ impl RpcClient { json!([pubkey.to_string()]), retries, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("RetryGetBalance request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("RetryGetBalance"))?; Ok(Some( serde_json::from_value::>(balance_json) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("RetryGetBalance parse failure: {:?}", err), - ) - })? + .map_err(|err| ClientError::new_with_command(err.into(), "RetryGetBalance"))? .value, )) } - pub fn get_account(&self, pubkey: &Pubkey) -> io::Result { + pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult { self.get_account_with_commitment(pubkey, CommitmentConfig::default())? .value - .ok_or_else(|| { - io::Error::new( - io::ErrorKind::Other, - format!("AccountNotFound: pubkey={}", pubkey), - ) - }) + .ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into()) } pub fn get_account_with_commitment( &self, pubkey: &Pubkey, commitment_config: CommitmentConfig, - ) -> RpcResponse> { + ) -> RpcResult> { let response = self.client.send( &RpcRequest::GetAccountInfo, json!([pubkey.to_string(), commitment_config]), @@ -627,10 +484,9 @@ impl RpcClient { response .map(|result_json| { if result_json.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("AccountNotFound: pubkey={}", pubkey), - )); + return Err( + RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into(), + ); } let Response { context, @@ -644,18 +500,18 @@ impl RpcClient { }) }) .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("AccountNotFound: pubkey={}: {:?}", pubkey, err), - ) + Into::::into(RpcError::ForUser(format!( + "AccountNotFound: pubkey={}: {}", + pubkey, err + ))) })? } - pub fn get_account_data(&self, pubkey: &Pubkey) -> io::Result> { + pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult> { Ok(self.get_account(pubkey)?.data) } - pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> io::Result { + pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> ClientResult { let minimum_balance_json = self .client .send( @@ -663,21 +519,10 @@ impl RpcClient { json!([data_len]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!( - "GetMinimumBalanceForRentExemption request failure: {:?}", - err - ), - ) - })?; + .map_err(|err| err.into_with_command("GetMinimumBalanceForRentExemption"))?; let minimum_balance: u64 = serde_json::from_value(minimum_balance_json).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetMinimumBalanceForRentExemption parse failure: {:?}", err), - ) + ClientError::new_with_command(err.into(), "GetMinimumBalanceForRentExemption") })?; trace!( "Response minimum balance {:?} {:?}", @@ -688,7 +533,7 @@ impl RpcClient { } /// Request the balance of the account `pubkey`. - pub fn get_balance(&self, pubkey: &Pubkey) -> io::Result { + pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult { Ok(self .get_balance_with_commitment(pubkey, CommitmentConfig::default())? .value) @@ -698,7 +543,7 @@ impl RpcClient { &self, pubkey: &Pubkey, commitment_config: CommitmentConfig, - ) -> RpcResponse { + ) -> RpcResult { let balance_json = self .client .send( @@ -706,22 +551,13 @@ impl RpcClient { json!([pubkey.to_string(), commitment_config]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetBalance request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetBalance"))?; - serde_json::from_value::>(balance_json).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetBalance parse failure: {:?}", err), - ) - }) + serde_json::from_value::>(balance_json) + .map_err(|err| ClientError::new_with_command(err.into(), "GetBalance")) } - pub fn get_program_accounts(&self, pubkey: &Pubkey) -> io::Result> { + pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult> { let response = self .client .send( @@ -729,27 +565,18 @@ impl RpcClient { json!([pubkey.to_string()]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("AccountNotFound: pubkey={}: {:?}", pubkey, err), - ) - })?; + .map_err(|err| err.into_with_command("GetProgramAccounts"))?; let accounts: Vec = - serde_json::from_value::>(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetProgramAccounts parse failure: {:?}", err), - ) - })?; + serde_json::from_value::>(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetProgramAccounts"))?; let mut pubkey_accounts: Vec<(Pubkey, Account)> = Vec::new(); for RpcKeyedAccount { pubkey, account } in accounts.into_iter() { - let pubkey = pubkey.parse().map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetProgramAccounts parse failure: {:?}", err), + let pubkey = pubkey.parse().map_err(|_| { + ClientError::new_with_command( + RpcError::ParseError("Pubkey".to_string()).into(), + "GetProgramAccounts", ) })?; pubkey_accounts.push((pubkey, account.decode().unwrap())); @@ -758,14 +585,14 @@ impl RpcClient { } /// Request the transaction count. - pub fn get_transaction_count(&self) -> io::Result { + pub fn get_transaction_count(&self) -> ClientResult { self.get_transaction_count_with_commitment(CommitmentConfig::default()) } pub fn get_transaction_count_with_commitment( &self, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { let response = self .client .send( @@ -773,22 +600,13 @@ impl RpcClient { json!([commitment_config]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetTransactionCount request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetTransactionCount"))?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetTransactionCount parse failure: {:?}", err), - ) - }) + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetTransactionCount")) } - pub fn get_recent_blockhash(&self) -> io::Result<(Hash, FeeCalculator)> { + pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> { Ok(self .get_recent_blockhash_with_commitment(CommitmentConfig::default())? .value) @@ -797,7 +615,7 @@ impl RpcClient { pub fn get_recent_blockhash_with_commitment( &self, commitment_config: CommitmentConfig, - ) -> RpcResponse<(Hash, FeeCalculator)> { + ) -> RpcResult<(Hash, FeeCalculator)> { let response = self .client .send( @@ -805,12 +623,7 @@ impl RpcClient { json!([commitment_config]), 0, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetRecentBlockhash request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetRecentBlockhash"))?; let Response { context, @@ -819,18 +632,12 @@ impl RpcClient { blockhash, fee_calculator, }, - } = serde_json::from_value::>(response).map_err( - |err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetRecentBlockhash parse failure: {:?}", err), - ) - }, - )?; - let blockhash = blockhash.parse().map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetRecentBlockhash hash parse failure: {:?}", err), + } = serde_json::from_value::>(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetRecentBlockhash"))?; + let blockhash = blockhash.parse().map_err(|_| { + ClientError::new_with_command( + RpcError::ParseError("Hash".to_string()).into(), + "GetRecentBlockhash", ) })?; Ok(Response { @@ -842,7 +649,7 @@ impl RpcClient { pub fn get_fee_calculator_for_blockhash( &self, blockhash: &Hash, - ) -> io::Result> { + ) -> ClientResult> { let response = self .client .send( @@ -850,50 +657,31 @@ impl RpcClient { json!([blockhash.to_string()]), 0, ) - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("GetFeeCalculatorForBlockhash request failure: {:?}", e), - ) - })?; + .map_err(|err| err.into_with_command("GetFeeCalculatorForBlockhash"))?; let Response { value, .. } = serde_json::from_value::>>( response, ) - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("GetFeeCalculatorForBlockhash parse failure: {:?}", e), - ) - })?; + .map_err(|e| ClientError::new_with_command(e.into(), "GetFeeCalculatorForBlockhash"))?; Ok(value.map(|rf| rf.fee_calculator)) } - pub fn get_fee_rate_governor(&self) -> RpcResponse { + pub fn get_fee_rate_governor(&self) -> RpcResult { let response = self .client .send(&RpcRequest::GetFeeRateGovernor, Value::Null, 0) - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("GetFeeRateGovernor request failure: {:?}", e), - ) - })?; + .map_err(|err| err.into_with_command("GetFeeRateGovernor"))?; let Response { context, value: RpcFeeRateGovernor { fee_rate_governor }, - } = serde_json::from_value::>(response).map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("GetFeeRateGovernor parse failure: {:?}", e), - ) - })?; + } = serde_json::from_value::>(response) + .map_err(|e| ClientError::new_with_command(e.into(), "GetFeeRateGovernor"))?; Ok(Response { context, value: fee_rate_governor, }) } - pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)> { + pub fn get_new_blockhash(&self, blockhash: &Hash) -> ClientResult<(Hash, FeeCalculator)> { let mut num_retries = 0; let start = Instant::now(); while start.elapsed().as_secs() < 5 { @@ -910,39 +698,28 @@ impl RpcClient { )); num_retries += 1; } - Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Unable to get new blockhash after {}ms (retried {} times), stuck at {}", - start.elapsed().as_millis(), - num_retries, - blockhash - ), + Err(RpcError::ForUser(format!( + "Unable to get new blockhash after {}ms (retried {} times), stuck at {}", + start.elapsed().as_millis(), + num_retries, + blockhash )) + .into()) } - pub fn get_genesis_hash(&self) -> io::Result { + pub fn get_genesis_hash(&self) -> ClientResult { let response = self .client .send(&RpcRequest::GetGenesisHash, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetGenesisHash request failure: {:?}", err), - ) - })?; + .map_err(|err| err.into_with_command("GetGenesisHash"))?; - let hash = serde_json::from_value::(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetGenesisHash parse failure: {:?}", err), - ) - })?; + let hash = serde_json::from_value::(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetGenesisHash"))?; - let hash = hash.parse().map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("GetGenesisHash hash parse failure: {:?}", err), + let hash = hash.parse().map_err(|_| { + ClientError::new_with_command( + RpcError::ParseError("Hash".to_string()).into(), + "GetGenesisHash", ) })?; Ok(hash) @@ -954,7 +731,7 @@ impl RpcClient { polling_frequency: &Duration, timeout: &Duration, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { let now = Instant::now(); loop { match self.get_balance_with_commitment(&pubkey, commitment_config.clone()) { @@ -975,7 +752,7 @@ impl RpcClient { &self, pubkey: &Pubkey, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> ClientResult { self.poll_balance_with_timeout_and_commitment( pubkey, &Duration::from_millis(100), @@ -1014,7 +791,7 @@ impl RpcClient { } /// Poll the server to confirm a transaction. - pub fn poll_for_signature(&self, signature: &Signature) -> io::Result<()> { + pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> { self.poll_for_signature_with_commitment(signature, CommitmentConfig::default()) } @@ -1023,7 +800,7 @@ impl RpcClient { &self, signature: &Signature, commitment_config: CommitmentConfig, - ) -> io::Result<()> { + ) -> ClientResult<()> { let now = Instant::now(); loop { if let Ok(Some(_)) = self.get_signature_status_with_commitment( @@ -1033,13 +810,11 @@ impl RpcClient { break; } if now.elapsed().as_secs() > 15 { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "signature not found after {} seconds", - now.elapsed().as_secs() - ), - )); + return Err(RpcError::ForUser(format!( + "signature not found after {} seconds", + now.elapsed().as_secs() + )) + .into()); } sleep(Duration::from_millis(250)); } @@ -1088,7 +863,7 @@ impl RpcClient { &self, signature: &Signature, min_confirmed_blocks: usize, - ) -> io::Result { + ) -> ClientResult { let mut now = Instant::now(); let mut confirmed_blocks = 0; loop { @@ -1125,13 +900,11 @@ impl RpcClient { if confirmed_blocks > 0 { return Ok(confirmed_blocks); } else { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "signature not found after {} seconds", - now.elapsed().as_secs() - ), - )); + return Err(RpcError::ForUser(format!( + "signature not found after {} seconds", + now.elapsed().as_secs() + )) + .into()); } } sleep(Duration::from_millis(250)); @@ -1142,7 +915,7 @@ impl RpcClient { pub fn get_num_blocks_since_signature_confirmation( &self, signature: &Signature, - ) -> io::Result { + ) -> ClientResult { let response = self .client .send( @@ -1150,50 +923,22 @@ impl RpcClient { json!([signature.to_string(), CommitmentConfig::recent().ok()]), 1, ) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!( - "GetNumBlocksSinceSignatureConfirmation request failure: {}", - err - ), - ) - })?; + .map_err(|err| err.into_with_command("GetNumBlocksSinceSignatureConfirmation"))?; serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!( - "GetNumBlocksSinceSignatureConfirmation parse failure: {}", - err - ), - ) + ClientError::new_with_command(err.into(), "GetNumBlocksSinceSignatureConfirmation") }) } - pub fn validator_exit(&self) -> io::Result { + pub fn validator_exit(&self) -> ClientResult { let response = self .client .send(&RpcRequest::ValidatorExit, Value::Null, 0) - .map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("ValidatorExit request failure: {:?}", err), - ) - })?; - serde_json::from_value(response).map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("ValidatorExit parse failure: {:?}", err), - ) - }) + .map_err(|err| err.into_with_command("ValidatorExit"))?; + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "ValidatorExit")) } - pub fn send( - &self, - request: &RpcRequest, - params: Value, - retries: usize, - ) -> Result { + pub fn send(&self, request: &RpcRequest, params: Value, retries: usize) -> ClientResult { assert!(params.is_array() || params.is_null()); self.client.send(request, params, retries) } @@ -1210,7 +955,10 @@ pub fn get_rpc_request_str(rpc_addr: SocketAddr, tls: bool) -> String { #[cfg(test)] mod tests { use super::*; - use crate::mock_rpc_client_request::{PUBKEY, SIGNATURE}; + use crate::{ + client_error::ClientErrorKind, + mock_rpc_client_request::{PUBKEY, SIGNATURE}, + }; use assert_matches::assert_matches; use jsonrpc_core::{Error, IoHandler, Params}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; @@ -1220,7 +968,7 @@ mod tests { instruction::InstructionError, signature::Keypair, system_transaction, transaction::TransactionError, }; - use std::{sync::mpsc::channel, thread}; + use std::{io, sync::mpsc::channel, thread}; #[test] fn test_send() { @@ -1381,8 +1129,8 @@ mod tests { let rpc_client = RpcClient::new_mock("instruction_error".to_string()); let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]); assert_matches!( - result.unwrap_err(), - ClientError::TransactionError(TransactionError::InstructionError( + result.unwrap_err().kind(), + ClientErrorKind::TransactionError(TransactionError::InstructionError( 0, InstructionError::UninitializedAccount )) @@ -1390,7 +1138,7 @@ mod tests { let rpc_client = RpcClient::new_mock("sig_not_found".to_string()); let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]); - if let ClientError::Io(err) = result.unwrap_err() { + if let ClientErrorKind::Io(err) = result.unwrap_err().kind() { assert_eq!(err.kind(), io::ErrorKind::Other); } } diff --git a/client/src/rpc_client_request.rs b/client/src/rpc_client_request.rs index c076a2c544..a3f53ffb96 100644 --- a/client/src/rpc_client_request.rs +++ b/client/src/rpc_client_request.rs @@ -1,5 +1,5 @@ use crate::{ - client_error::ClientError, + client_error::Result, generic_rpc_client_request::GenericRpcClientRequest, rpc_request::{RpcError, RpcRequest}, }; @@ -34,7 +34,7 @@ impl GenericRpcClientRequest for RpcClientRequest { request: &RpcRequest, params: serde_json::Value, mut retries: usize, - ) -> Result { + ) -> Result { // Concurrent requests are not supported so reuse the same request id for all requests let request_id = 1; diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index d10ec03d83..6e9e100863 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -1,5 +1,5 @@ use serde_json::{json, Value}; -use std::{error, fmt}; +use thiserror::Error; #[derive(Debug, PartialEq, Eq, Hash)] pub enum RpcRequest { @@ -93,26 +93,16 @@ impl RpcRequest { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Error)] pub enum RpcError { + #[error("rpc reques error: {0}")] RpcRequestError(String), -} - -impl fmt::Display for RpcError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid") - } -} - -impl error::Error for RpcError { - fn description(&self) -> &str { - "invalid" - } - - fn cause(&self) -> Option<&dyn error::Error> { - // Generic error, underlying cause isn't tracked. - None - } + #[error("parse error: expected {0}")] + ParseError(String), /* "expected" */ + // Anything in a `ForUser` needs to die. The caller should be + // deciding what to tell their user + #[error("{0}")] + ForUser(String), /* "direct-to-user message" */ } #[cfg(test)] diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index d2b0bfc465..212eb3a9f1 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -1,6 +1,5 @@ -use crate::rpc_request::RpcError; +use crate::{client_error, rpc_request::RpcError}; use bincode::serialize; -use jsonrpc_core::Result as JsonResult; use solana_sdk::{ account::Account, clock::{Epoch, Slot}, @@ -9,10 +8,9 @@ use solana_sdk::{ pubkey::Pubkey, transaction::{Result, Transaction}, }; -use std::{collections::HashMap, io, net::SocketAddr, str::FromStr}; +use std::{collections::HashMap, net::SocketAddr, str::FromStr}; -pub type RpcResponseIn = JsonResult>; -pub type RpcResponse = io::Result>; +pub type RpcResult = client_error::Result>; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RpcResponseContext { diff --git a/client/src/thin_client.rs b/client/src/thin_client.rs index 110cbe4c1b..e3c601bddc 100644 --- a/client/src/thin_client.rs +++ b/client/src/thin_client.rs @@ -188,7 +188,7 @@ impl ThinClient { transaction: &mut Transaction, tries: usize, min_confirmed_blocks: usize, - ) -> io::Result { + ) -> TransportResult { self.send_and_confirm_transaction(&[keypair], transaction, tries, min_confirmed_blocks) } @@ -198,7 +198,7 @@ impl ThinClient { keypair: &Keypair, transaction: &mut Transaction, tries: usize, - ) -> io::Result { + ) -> TransportResult { self.send_and_confirm_transaction(&[keypair], transaction, tries, 0) } @@ -209,7 +209,7 @@ impl ThinClient { transaction: &mut Transaction, tries: usize, pending_confirmations: usize, - ) -> io::Result { + ) -> TransportResult { for x in 0..tries { let now = Instant::now(); let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize]; @@ -243,13 +243,14 @@ impl ThinClient { } } info!("{} tries failed transfer to {}", x, self.tpu_addr()); - let (blockhash, _fee_calculator) = self.rpc_client().get_recent_blockhash()?; + let (blockhash, _fee_calculator) = self.get_recent_blockhash()?; transaction.sign(keypairs, blockhash); } Err(io::Error::new( io::ErrorKind::Other, format!("retry_transfer failed in {} retries", tries), - )) + ) + .into()) } pub fn poll_balance_with_timeout_and_commitment( @@ -258,13 +259,15 @@ impl ThinClient { polling_frequency: &Duration, timeout: &Duration, commitment_config: CommitmentConfig, - ) -> io::Result { - self.rpc_client().poll_balance_with_timeout_and_commitment( - pubkey, - polling_frequency, - timeout, - commitment_config, - ) + ) -> TransportResult { + self.rpc_client() + .poll_balance_with_timeout_and_commitment( + pubkey, + polling_frequency, + timeout, + commitment_config, + ) + .map_err(|e| e.into()) } pub fn poll_balance_with_timeout( @@ -272,8 +275,8 @@ impl ThinClient { pubkey: &Pubkey, polling_frequency: &Duration, timeout: &Duration, - ) -> io::Result { - self.rpc_client().poll_balance_with_timeout_and_commitment( + ) -> TransportResult { + self.poll_balance_with_timeout_and_commitment( pubkey, polling_frequency, timeout, @@ -281,18 +284,18 @@ impl ThinClient { ) } - pub fn poll_get_balance(&self, pubkey: &Pubkey) -> io::Result { - self.rpc_client() - .poll_get_balance_with_commitment(pubkey, CommitmentConfig::default()) + pub fn poll_get_balance(&self, pubkey: &Pubkey) -> TransportResult { + self.poll_get_balance_with_commitment(pubkey, CommitmentConfig::default()) } pub fn poll_get_balance_with_commitment( &self, pubkey: &Pubkey, commitment_config: CommitmentConfig, - ) -> io::Result { + ) -> TransportResult { self.rpc_client() .poll_get_balance_with_commitment(pubkey, commitment_config) + .map_err(|e| e.into()) } pub fn wait_for_balance(&self, pubkey: &Pubkey, expected_balance: Option) -> Option { @@ -321,9 +324,9 @@ impl ThinClient { signature: &Signature, commitment_config: CommitmentConfig, ) -> TransportResult<()> { - Ok(self - .rpc_client() - .poll_for_signature_with_commitment(signature, commitment_config)?) + self.rpc_client() + .poll_for_signature_with_commitment(signature, commitment_config) + .map_err(|e| e.into()) } /// Check a signature in the bank. This method blocks @@ -332,16 +335,17 @@ impl ThinClient { self.rpc_client().check_signature(signature) } - pub fn validator_exit(&self) -> io::Result { - self.rpc_client().validator_exit() + pub fn validator_exit(&self) -> TransportResult { + self.rpc_client().validator_exit().map_err(|e| e.into()) } pub fn get_num_blocks_since_signature_confirmation( &mut self, sig: &Signature, - ) -> io::Result { + ) -> TransportResult { self.rpc_client() .get_num_blocks_since_signature_confirmation(sig) + .map_err(|e| e.into()) } } @@ -368,7 +372,7 @@ impl SyncClient for ThinClient { keypair: &Keypair, instruction: Instruction, ) -> TransportResult { - let message = Message::new(vec![instruction]); + let message = Message::new(&[instruction]); self.send_message(&[keypair], message) } @@ -400,14 +404,14 @@ impl SyncClient for ThinClient { pubkey: &Pubkey, commitment_config: CommitmentConfig, ) -> TransportResult> { - Ok(self - .rpc_client() - .get_account_with_commitment(pubkey, commitment_config)? - .value) + self.rpc_client() + .get_account_with_commitment(pubkey, commitment_config) + .map_err(|e| e.into()) + .map(|r| r.value) } fn get_balance(&self, pubkey: &Pubkey) -> TransportResult { - Ok(self.rpc_client().get_balance(pubkey)?) + self.rpc_client().get_balance(pubkey).map_err(|e| e.into()) } fn get_balance_with_commitment( @@ -415,10 +419,10 @@ impl SyncClient for ThinClient { pubkey: &Pubkey, commitment_config: CommitmentConfig, ) -> TransportResult { - let balance = self - .rpc_client() - .get_balance_with_commitment(pubkey, commitment_config)?; - Ok(balance.value) + self.rpc_client() + .get_balance_with_commitment(pubkey, commitment_config) + .map_err(|e| e.into()) + .map(|r| r.value) } fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> { @@ -449,15 +453,16 @@ impl SyncClient for ThinClient { &self, blockhash: &Hash, ) -> TransportResult> { - let fee_calculator = self - .rpc_client() - .get_fee_calculator_for_blockhash(blockhash)?; - Ok(fee_calculator) + self.rpc_client() + .get_fee_calculator_for_blockhash(blockhash) + .map_err(|e| e.into()) } fn get_fee_rate_governor(&self) -> TransportResult { - let fee_rate_governor = self.rpc_client().get_fee_rate_governor()?; - Ok(fee_rate_governor.value) + self.rpc_client() + .get_fee_rate_governor() + .map_err(|e| e.into()) + .map(|r| r.value) } fn get_signature_status( @@ -555,23 +560,26 @@ impl SyncClient for ThinClient { signature: &Signature, min_confirmed_blocks: usize, ) -> TransportResult { - Ok(self - .rpc_client() - .poll_for_signature_confirmation(signature, min_confirmed_blocks)?) + self.rpc_client() + .poll_for_signature_confirmation(signature, min_confirmed_blocks) + .map_err(|e| e.into()) } fn poll_for_signature(&self, signature: &Signature) -> TransportResult<()> { - Ok(self.rpc_client().poll_for_signature(signature)?) + self.rpc_client() + .poll_for_signature(signature) + .map_err(|e| e.into()) } fn get_new_blockhash(&self, blockhash: &Hash) -> TransportResult<(Hash, FeeCalculator)> { - let new_blockhash = self.rpc_client().get_new_blockhash(blockhash)?; - Ok(new_blockhash) + self.rpc_client() + .get_new_blockhash(blockhash) + .map_err(|e| e.into()) } } impl AsyncClient for ThinClient { - fn async_send_transaction(&self, transaction: Transaction) -> io::Result { + fn async_send_transaction(&self, transaction: Transaction) -> TransportResult { let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize]; let mut wr = std::io::Cursor::new(&mut buf[..]); serialize_into(&mut wr, &transaction) @@ -586,7 +594,7 @@ impl AsyncClient for ThinClient { keypairs: &T, message: Message, recent_blockhash: Hash, - ) -> io::Result { + ) -> TransportResult { let transaction = Transaction::new(keypairs, message, recent_blockhash); self.async_send_transaction(transaction) } @@ -595,8 +603,8 @@ impl AsyncClient for ThinClient { keypair: &Keypair, instruction: Instruction, recent_blockhash: Hash, - ) -> io::Result { - let message = Message::new(vec![instruction]); + ) -> TransportResult { + let message = Message::new(&[instruction]); self.async_send_message(&[keypair], message, recent_blockhash) } fn async_transfer( @@ -605,7 +613,7 @@ impl AsyncClient for ThinClient { keypair: &Keypair, pubkey: &Pubkey, recent_blockhash: Hash, - ) -> io::Result { + ) -> TransportResult { let transfer_instruction = system_instruction::transfer(&keypair.pubkey(), pubkey, lamports); self.async_send_instruction(keypair, transfer_instruction, recent_blockhash) diff --git a/core/src/storage_stage.rs b/core/src/storage_stage.rs index bf88c6575c..2a3ad88ac3 100644 --- a/core/src/storage_stage.rs +++ b/core/src/storage_stage.rs @@ -312,7 +312,7 @@ impl StorageStage { } let signer_keys = vec![keypair.as_ref(), storage_keypair.as_ref()]; - let message = Message::new_with_payer(vec![instruction], Some(&signer_keys[0].pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&signer_keys[0].pubkey())); let transaction = Transaction::new(&signer_keys, message, blockhash); // try sending the transaction upto 5 times for _ in 0..5 { diff --git a/core/tests/storage_stage.rs b/core/tests/storage_stage.rs index 57223b337b..708734641a 100644 --- a/core/tests/storage_stage.rs +++ b/core/tests/storage_stage.rs @@ -120,7 +120,7 @@ mod tests { None, ) .unwrap(); - let message = Message::new_with_payer(vec![mining_proof_ix], Some(&mint_keypair.pubkey())); + let message = Message::new_with_payer(&[mining_proof_ix], Some(&mint_keypair.pubkey())); let mining_proof_tx = Transaction::new( &[&mint_keypair, archiver_keypair.as_ref()], message, diff --git a/faucet/src/faucet.rs b/faucet/src/faucet.rs index 9f8dca94a0..dd67ab633e 100644 --- a/faucet/src/faucet.rs +++ b/faucet/src/faucet.rs @@ -127,7 +127,7 @@ impl Faucet { let create_instruction = system_instruction::transfer(&self.mint_keypair.pubkey(), &to, lamports); - let message = Message::new(vec![create_instruction]); + let message = Message::new(&[create_instruction]); Ok(Transaction::new(&[&self.mint_keypair], message, blockhash)) } else { Err(Error::new( @@ -413,7 +413,7 @@ mod tests { let keypair = Keypair::new(); let expected_instruction = system_instruction::transfer(&keypair.pubkey(), &to, lamports); - let message = Message::new(vec![expected_instruction]); + let message = Message::new(&[expected_instruction]); let expected_tx = Transaction::new(&[&keypair], message, blockhash); let expected_bytes = serialize(&expected_tx).unwrap(); let mut expected_vec_with_length = vec![0; 2]; diff --git a/faucet/tests/local-faucet.rs b/faucet/tests/local-faucet.rs index f0ebfabc03..5b1749f652 100644 --- a/faucet/tests/local-faucet.rs +++ b/faucet/tests/local-faucet.rs @@ -16,7 +16,7 @@ fn test_local_faucet() { let lamports = 50; let blockhash = Hash::new(&to.as_ref()); let create_instruction = system_instruction::transfer(&keypair.pubkey(), &to, lamports); - let message = Message::new(vec![create_instruction]); + let message = Message::new(&[create_instruction]); let expected_tx = Transaction::new(&[&keypair], message, blockhash); let (sender, receiver) = channel(); diff --git a/install/src/command.rs b/install/src/command.rs index a80265b2f2..423457c0d4 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -244,7 +244,7 @@ fn store_update_manifest( update_manifest, ); - let message = Message::new_with_payer(vec![instruction], Some(&from_keypair.pubkey())); + let message = Message::new_with_payer(&[instruction], Some(&from_keypair.pubkey())); let mut transaction = Transaction::new(&signers, message, recent_blockhash); rpc_client.send_and_confirm_transaction(&mut transaction, &[from_keypair])?; Ok(()) diff --git a/keygen/src/keygen.rs b/keygen/src/keygen.rs index 57c85e7541..999fd0ee46 100644 --- a/keygen/src/keygen.rs +++ b/keygen/src/keygen.rs @@ -11,6 +11,7 @@ use solana_clap_utils::{ keypair_from_seed_phrase, prompt_passphrase, signer_from_path, SKIP_SEED_PHRASE_VALIDATION_ARG, }, + DisplayError, }; use solana_cli_config::{Config, CONFIG_FILE}; use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager}; @@ -378,6 +379,11 @@ fn main() -> Result<(), Box> { ) .get_matches(); + + do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into()) +} + +fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box> { let config = if let Some(config_file) = matches.value_of("config_file") { Config::load(config_file).unwrap_or_default() } else { @@ -566,7 +572,7 @@ fn main() -> Result<(), Box> { } ("verify", Some(matches)) => { let keypair = get_keypair_from_matches(matches, config, wallet_manager)?; - let simple_message = Message::new(vec![Instruction::new( + let simple_message = Message::new(&[Instruction::new( Pubkey::default(), &0, vec![AccountMeta::new(keypair.pubkey(), true)], diff --git a/ledger/src/entry.rs b/ledger/src/entry.rs index 9875e5c59b..e354e7cac5 100644 --- a/ledger/src/entry.rs +++ b/ledger/src/entry.rs @@ -685,8 +685,7 @@ mod tests { fn test_verify_tick_hash_count() { let hashes_per_tick = 10; let keypairs: Vec<&Keypair> = Vec::new(); - let tx: Transaction = - Transaction::new(&keypairs, Message::new(Vec::new()), Hash::default()); + let tx: Transaction = Transaction::new(&keypairs, Message::new(&[]), Hash::default()); let tx_entry = Entry::new(&Hash::default(), 1, vec![tx]); let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default()); let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default()); diff --git a/local-cluster/src/cluster_tests.rs b/local-cluster/src/cluster_tests.rs index b40157c289..ad37320303 100644 --- a/local-cluster/src/cluster_tests.rs +++ b/local-cluster/src/cluster_tests.rs @@ -262,7 +262,7 @@ pub fn kill_entry_and_spend_and_verify_rest( ); match sig { Err(e) => { - result = Err(TransportError::IoError(e)); + result = Err(e); continue; } diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index 81f5bcb111..020ecf39de 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -23,6 +23,7 @@ use solana_sdk::{ signature::{Keypair, Signer}, system_transaction, transaction::Transaction, + transport::Result as TransportResult, }; use solana_stake_program::{ config as stake_config, stake_instruction, @@ -607,14 +608,14 @@ impl LocalCluster { storage_keypair: &Keypair, from_keypair: &Arc, archiver: bool, - ) -> Result<()> { + ) -> TransportResult<()> { let storage_account_type = if archiver { StorageAccountType::Archiver } else { StorageAccountType::Validator }; let message = Message::new_with_payer( - storage_instruction::create_storage_account( + &storage_instruction::create_storage_account( &from_keypair.pubkey(), &from_keypair.pubkey(), &storage_keypair.pubkey(), diff --git a/programs/budget/src/budget_processor.rs b/programs/budget/src/budget_processor.rs index a2facf404c..bcc29c058c 100644 --- a/programs/budget/src/budget_processor.rs +++ b/programs/budget/src/budget_processor.rs @@ -256,7 +256,7 @@ mod tests { budget_instruction::payment(&alice_pubkey, &bob_pubkey, &budget_pubkey, 1); instructions[1].accounts = vec![]; //