Cli error cleanup 1.0 (#8834)

* Don't use move semantics if not needed (#8793)

* SDK: Deboilerplate `TransportError` with thiserror

* Enable interchange between `TransportError` and `ClientError`

* SDK: Retval consistency between `Client` and `AsyncClient` traits

* Client: Introduce/use `Result` type

* Client: Remove unused `RpcResponseIn` type

* Client: Rename `RpcResponse` to more appropriate `RpcResult`

* Client: Death to `io::Result` return types

* Client: Struct-ify `ClientError`

* Client: Add optional `command` parameter to `ClientError`

* RpcClient: Stop abusing `io::Error` (low-fruit)

* ClientError: Use `thiserror`'s `Display` impl

* Extend `RpcError`'s utility

* RpcClient: Stop abusing `io::Error` (the rest)

* CLI: Shim `main()` so we can `Display` format errors

* claputils: format input validator errors with `Display`

They are intended to be displayed to users

* SDK: `thiserror` for hash and sig parse erros

* Keygen: Shim main to format errors with `Display`

* SDK: `thiserror` for `InstructionError`

* CLI: `thiserror` for `CliError`

* CLI: Format user messages with `Display`

* Client: Tweak `Display` for `ClientError`

* RpcClient: Improve messaging when TX cannot be confirmed

* fu death io res retval

* CLI/Keygen - fix shell return value on error

* Tweak `InstructionError` `Display` messages as per review

* Cleanup hackjob return code fix

* Embrace that which you hate most

* Too much...

Co-authored-by: Jack May <jack@solana.com>
This commit is contained in:
Trent Nelson
2020-03-13 07:42:25 -06:00
committed by GitHub
parent 976d744b0d
commit 4a42cfc42a
52 changed files with 689 additions and 760 deletions

View File

@@ -21,7 +21,6 @@ use crate::{
transaction,
transport::Result,
};
use std::io;
pub trait Client: SyncClient + AsyncClient {
fn tpu_addr(&self) -> String;
@@ -122,10 +121,7 @@ pub trait SyncClient {
pub trait AsyncClient {
/// Send a signed transaction, but don't wait to see if the server accepted it.
fn async_send_transaction(
&self,
transaction: transaction::Transaction,
) -> io::Result<Signature>;
fn async_send_transaction(&self, transaction: transaction::Transaction) -> Result<Signature>;
/// Create a transaction from the given message, and send it to the
/// server, but don't wait for to see if the server accepted it.
@@ -134,7 +130,7 @@ pub trait AsyncClient {
keypairs: &T,
message: Message,
recent_blockhash: Hash,
) -> io::Result<Signature>;
) -> Result<Signature>;
/// Create a transaction from a single instruction that only requires
/// a single signer. Then send it to the server, but don't wait for a reply.
@@ -143,7 +139,7 @@ pub trait AsyncClient {
keypair: &Keypair,
instruction: Instruction,
recent_blockhash: Hash,
) -> io::Result<Signature>;
) -> Result<Signature>;
/// Attempt to transfer lamports from `keypair` to `pubkey`, but don't wait to confirm.
fn async_transfer(
@@ -152,5 +148,5 @@ pub trait AsyncClient {
keypair: &Keypair,
pubkey: &Pubkey,
recent_blockhash: Hash,
) -> io::Result<Signature>;
) -> Result<Signature>;
}

View File

@@ -183,7 +183,7 @@ mod tests {
#[test]
fn test_fee_calculator_calculate_fee() {
// Default: no fee.
let message = Message::new(vec![]);
let message = Message::new(&[]);
assert_eq!(FeeCalculator::default().calculate_fee(&message), 0);
// No signature, no fee.
@@ -193,13 +193,13 @@ mod tests {
let pubkey0 = Pubkey::new(&[0; 32]);
let pubkey1 = Pubkey::new(&[1; 32]);
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
let message = Message::new(vec![ix0]);
let message = Message::new(&[ix0]);
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
// Two signatures, double the fee.
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
let message = Message::new(vec![ix0, ix1]);
let message = Message::new(&[ix0, ix1]);
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
}

View File

@@ -2,6 +2,7 @@
use sha2::{Digest, Sha256};
use std::{convert::TryFrom, fmt, mem, str::FromStr};
use thiserror::Error;
pub const HASH_BYTES: usize = 32;
#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
@@ -47,9 +48,11 @@ impl fmt::Display for Hash {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseHashError {
#[error("string decoded to wrong size for hash")]
WrongSize,
#[error("failed to decoded string to hash")]
Invalid,
}

View File

@@ -3,96 +3,124 @@
use crate::{pubkey::Pubkey, short_vec, system_instruction::SystemError};
use bincode::serialize;
use serde::Serialize;
use thiserror::Error;
/// Reasons the runtime might have rejected an instruction.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Debug, Error, PartialEq, Eq, Clone)]
pub enum InstructionError {
/// Deprecated! Use CustomError instead!
/// The program instruction returned an error
#[error("generic instruction error")]
GenericError,
/// The arguments provided to a program instruction where invalid
/// The arguments provided to a program were invalid
#[error("invalid program argument")]
InvalidArgument,
/// An instruction's data contents was invalid
/// An instruction's data contents were invalid
#[error("invalid instruction data")]
InvalidInstructionData,
/// An account's data contents was invalid
#[error("invalid account data for instruction")]
InvalidAccountData,
/// An account's data was too small
#[error("account data too small for instruction")]
AccountDataTooSmall,
/// An account's balance was too small to complete the instruction
#[error("insufficient funds for instruction")]
InsufficientFunds,
/// The account did not have the expected program id
#[error("incorrect program id for instruction")]
IncorrectProgramId,
/// A signature was required but not found
#[error("missing required signature for instruction")]
MissingRequiredSignature,
/// An initialize instruction was sent to an account that has already been initialized.
#[error("instruction requires an uninitialized account")]
AccountAlreadyInitialized,
/// An attempt to operate on an account that hasn't been initialized.
#[error("instruction requires an initialized account")]
UninitializedAccount,
/// Program's instruction lamport balance does not equal the balance after the instruction
#[error("sum of account balances before and after instruction do not match")]
UnbalancedInstruction,
/// Program modified an account's program id
#[error("instruction modified the program id of an account")]
ModifiedProgramId,
/// Program spent the lamports of an account that doesn't belong to it
#[error("instruction spent from the balance of an account it does not own")]
ExternalAccountLamportSpend,
/// Program modified the data of an account that doesn't belong to it
#[error("instruction modified data of an account it does not own")]
ExternalAccountDataModified,
/// Read-only account modified lamports
#[error("instruction changed balance of a read-only account")]
ReadonlyLamportChange,
/// Read-only account modified data
#[error("instruction modified data of a read-only account")]
ReadonlyDataModified,
/// An account was referenced more than once in a single instruction
// Deprecated, instructions can now contain duplicate accounts
#[error("instruction contains duplicate accounts")]
DuplicateAccountIndex,
/// Executable bit on account changed, but shouldn't have
#[error("instruction changed executable bit of an account")]
ExecutableModified,
/// Rent_epoch account changed, but shouldn't have
#[error("instruction modified rent epoch of an account")]
RentEpochModified,
/// The instruction expected additional account keys
#[error("insufficient account key count for instruction")]
NotEnoughAccountKeys,
/// A non-system program changed the size of the account data
#[error("non-system instruction changed account size")]
AccountDataSizeChanged,
/// The instruction expected an executable account
#[error("instruction expected an executable account")]
AccountNotExecutable,
/// Failed to borrow a reference to account data, already borrowed
#[error("instruction tries to borrow reference for an account which is already borrowed")]
AccountBorrowFailed,
/// Account data has an outstanding reference after a program's execution
#[error("instruction left account with an outstanding reference borrowed")]
AccountBorrowOutstanding,
/// The same account was multiply passed to an on-chain program's entrypoint, but the program
/// modified them differently. A program can only modify one instance of the account because
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
#[error("instruction modifications of multiply-passed account differ")]
DuplicateAccountOutOfSync,
/// Allows on-chain programs to implement program-specific error types and see them returned
/// by the Solana runtime. A program-specific error may be any type that is represented as
/// or serialized to a u32 integer.
#[error("program error: {0}")]
CustomError(u32),
/// The return value from the program was invalid. Valid errors are either a defined builtin
/// error value or a user-defined error in the lower 32 bits.
#[error("program returned invalid error code")]
InvalidError,
}

View File

@@ -13,7 +13,7 @@ fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
keys.iter().position(|k| k == key).unwrap() as u8
}
fn compile_instruction(ix: Instruction, keys: &[Pubkey]) -> CompiledInstruction {
fn compile_instruction(ix: &Instruction, keys: &[Pubkey]) -> CompiledInstruction {
let accounts: Vec<_> = ix
.accounts
.iter()
@@ -27,10 +27,8 @@ fn compile_instruction(ix: Instruction, keys: &[Pubkey]) -> CompiledInstruction
}
}
fn compile_instructions(ixs: Vec<Instruction>, keys: &[Pubkey]) -> Vec<CompiledInstruction> {
ixs.into_iter()
.map(|ix| compile_instruction(ix, keys))
.collect()
fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec<CompiledInstruction> {
ixs.iter().map(|ix| compile_instruction(ix, keys)).collect()
}
/// A helper struct to collect pubkeys referenced by a set of instructions and read-only counts
@@ -185,17 +183,17 @@ impl Message {
}
}
pub fn new(instructions: Vec<Instruction>) -> Self {
pub fn new(instructions: &[Instruction]) -> Self {
Self::new_with_payer(instructions, None)
}
pub fn new_with_payer(instructions: Vec<Instruction>, payer: Option<&Pubkey>) -> Self {
pub fn new_with_payer(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
let InstructionKeys {
mut signed_keys,
unsigned_keys,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
} = get_keys(&instructions, payer);
} = get_keys(instructions, payer);
let num_required_signatures = signed_keys.len() as u8;
signed_keys.extend(&unsigned_keys);
let instructions = compile_instructions(instructions, &signed_keys);
@@ -220,7 +218,7 @@ impl Message {
&nonce_authority_pubkey,
);
instructions.insert(0, nonce_ix);
Self::new_with_payer(instructions, payer)
Self::new_with_payer(&instructions, payer)
}
pub fn serialize(&self) -> Vec<u8> {
@@ -426,11 +424,11 @@ mod tests {
let program_id = Pubkey::default();
let id0 = Pubkey::default();
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
let message = Message::new(vec![ix]);
let message = Message::new(&[ix]);
assert_eq!(message.header.num_required_signatures, 0);
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
let message = Message::new(vec![ix]);
let message = Message::new(&[ix]);
assert_eq!(message.header.num_required_signatures, 1);
}
@@ -463,7 +461,7 @@ mod tests {
let id0 = Pubkey::default();
let keypair1 = Keypair::new();
let id1 = keypair1.pubkey();
let message = Message::new(vec![
let message = Message::new(&[
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
Instruction::new(program_id1, &0, vec![AccountMeta::new(id1, true)]),
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, false)]),
@@ -489,11 +487,11 @@ mod tests {
let id0 = Pubkey::default();
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
let message = Message::new_with_payer(vec![ix], Some(&payer));
let message = Message::new_with_payer(&[ix], Some(&payer));
assert_eq!(message.header.num_required_signatures, 1);
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
let message = Message::new_with_payer(vec![ix], Some(&payer));
let message = Message::new_with_payer(&[ix], Some(&payer));
assert_eq!(message.header.num_required_signatures, 2);
let ix = Instruction::new(
@@ -501,7 +499,7 @@ mod tests {
&0,
vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
);
let message = Message::new_with_payer(vec![ix], Some(&payer));
let message = Message::new_with_payer(&[ix], Some(&payer));
assert_eq!(message.header.num_required_signatures, 2);
}
@@ -528,7 +526,7 @@ mod tests {
let program_id0 = Pubkey::default();
let program_id1 = Pubkey::new_rand();
let id = Pubkey::new_rand();
let message = Message::new(vec![
let message = Message::new(&[
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
Instruction::new(program_id1, &0, vec![AccountMeta::new(id, true)]),
]);
@@ -571,7 +569,7 @@ mod tests {
let id1 = Pubkey::new_rand();
let id2 = Pubkey::new_rand();
let id3 = Pubkey::new_rand();
let message = Message::new(vec![
let message = Message::new(&[
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, true)]),
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id2, false)]),

View File

@@ -107,9 +107,11 @@ impl Into<[u8; 64]> for Signature {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseSignatureError {
#[error("string decoded to wrong size for signature")]
WrongSize,
#[error("failed to decode string to signature")]
Invalid,
}

View File

@@ -92,7 +92,7 @@ impl Transaction {
}
pub fn new_with_payer(instructions: Vec<Instruction>, payer: Option<&Pubkey>) -> Self {
let message = Message::new_with_payer(instructions, payer);
let message = Message::new_with_payer(&instructions, payer);
Self::new_unsigned(message)
}
@@ -102,7 +102,7 @@ impl Transaction {
signing_keypairs: &T,
recent_blockhash: Hash,
) -> Self {
let message = Message::new_with_payer(instructions, payer);
let message = Message::new_with_payer(&instructions, payer);
Self::new(signing_keypairs, message, recent_blockhash)
}
@@ -123,7 +123,7 @@ impl Transaction {
}
pub fn new_unsigned_instructions(instructions: Vec<Instruction>) -> Self {
let message = Message::new(instructions);
let message = Message::new(&instructions);
Self::new_unsigned(message)
}
@@ -142,7 +142,7 @@ impl Transaction {
instructions: Vec<Instruction>,
recent_blockhash: Hash,
) -> Transaction {
let message = Message::new(instructions);
let message = Message::new(&instructions);
Self::new(from_keypairs, message, recent_blockhash)
}
@@ -482,7 +482,7 @@ mod tests {
AccountMeta::new(to, false),
];
let instruction = Instruction::new(program_id, &(1u8, 2u8, 3u8), account_metas);
let message = Message::new(vec![instruction]);
let message = Message::new(&[instruction]);
Transaction::new(&[&keypair], message, Hash::default())
}
@@ -513,7 +513,7 @@ mod tests {
let expected_instruction_size = 1 + 1 + ix.accounts.len() + 1 + expected_data_size;
assert_eq!(expected_instruction_size, 17);
let message = Message::new(vec![ix]);
let message = Message::new(&[ix]);
assert_eq!(
serialized_size(&message.instructions[0]).unwrap() as usize,
expected_instruction_size,

View File

@@ -1,20 +1,15 @@
use crate::transaction::TransactionError;
use std::{error, fmt, io};
use std::io;
use thiserror::Error;
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum TransportError {
IoError(io::Error),
TransactionError(TransactionError),
}
impl error::Error for TransportError {}
impl fmt::Display for TransportError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
TransportError::IoError(err) => write!(formatter, "{:?}", err),
TransportError::TransactionError(err) => write!(formatter, "{:?}", err),
}
}
#[error("transport io error: {0}")]
IoError(#[from] io::Error),
#[error("transport transaction error: {0}")]
TransactionError(#[from] TransactionError),
#[error("transport custom error: {0}")]
Custom(String),
}
impl TransportError {
@@ -27,16 +22,4 @@ impl TransportError {
}
}
impl From<io::Error> for TransportError {
fn from(err: io::Error) -> TransportError {
TransportError::IoError(err)
}
}
impl From<TransactionError> for TransportError {
fn from(err: TransactionError) -> TransportError {
TransportError::TransactionError(err)
}
}
pub type Result<T> = std::result::Result<T, TransportError>;