Custom error decoder (#3783)

automerge
This commit is contained in:
Tyera Eulberg
2019-04-25 11:29:44 -06:00
committed by Grimes
parent b67b0bff05
commit 5a79676b8a
17 changed files with 218 additions and 33 deletions

7
Cargo.lock generated
View File

@ -2338,6 +2338,8 @@ dependencies = [
"bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-runtime 0.14.0", "solana-runtime 0.14.0",
@ -2650,6 +2652,8 @@ dependencies = [
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2713,6 +2717,8 @@ version = "0.14.0"
dependencies = [ dependencies = [
"bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-logger 0.14.0", "solana-logger 0.14.0",
@ -2787,6 +2793,7 @@ dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"solana 0.14.0", "solana 0.14.0",
"solana-budget-api 0.14.0", "solana-budget-api 0.14.0",

View File

@ -0,0 +1,50 @@
use crate::rpc_request;
use solana_sdk::transaction::TransactionError;
use std::{fmt, io};
#[derive(Debug)]
pub enum ClientError {
Io(io::Error),
Reqwest(reqwest::Error),
RpcError(rpc_request::RpcError),
SerdeJson(serde_json::error::Error),
TransactionError(TransactionError),
}
impl fmt::Display for ClientError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "solana client error")
}
}
impl std::error::Error for ClientError {}
impl From<io::Error> for ClientError {
fn from(err: io::Error) -> ClientError {
ClientError::Io(err)
}
}
impl From<reqwest::Error> for ClientError {
fn from(err: reqwest::Error) -> ClientError {
ClientError::Reqwest(err)
}
}
impl From<rpc_request::RpcError> for ClientError {
fn from(err: rpc_request::RpcError) -> ClientError {
ClientError::RpcError(err)
}
}
impl From<serde_json::error::Error> for ClientError {
fn from(err: serde_json::error::Error) -> ClientError {
ClientError::SerdeJson(err)
}
}
impl From<TransactionError> for ClientError {
fn from(err: TransactionError) -> ClientError {
ClientError::TransactionError(err)
}
}

View File

@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::rpc_request::RpcRequest; use crate::rpc_request::RpcRequest;
pub(crate) trait GenericRpcClientRequest { pub(crate) trait GenericRpcClientRequest {
@ -6,5 +7,5 @@ pub(crate) trait GenericRpcClientRequest {
request: &RpcRequest, request: &RpcRequest,
params: Option<serde_json::Value>, params: Option<serde_json::Value>,
retries: usize, retries: usize,
) -> Result<serde_json::Value, Box<dyn std::error::Error>>; ) -> Result<serde_json::Value, ClientError>;
} }

View File

@ -1,3 +1,4 @@
pub mod client_error;
mod generic_rpc_client_request; mod generic_rpc_client_request;
pub mod mock_rpc_client_request; pub mod mock_rpc_client_request;
pub mod rpc_client; pub mod rpc_client;

View File

@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::rpc_request::RpcRequest; use crate::rpc_request::RpcRequest;
use serde_json::{Number, Value}; use serde_json::{Number, Value};
@ -23,7 +24,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
request: &RpcRequest, request: &RpcRequest,
params: Option<serde_json::Value>, params: Option<serde_json::Value>,
_retries: usize, _retries: usize,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> { ) -> Result<serde_json::Value, ClientError> {
if self.url == "fails" { if self.url == "fails" {
return Ok(Value::Null); return Ok(Value::Null);
} }

View File

@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::mock_rpc_client_request::MockRpcClientRequest; use crate::mock_rpc_client_request::MockRpcClientRequest;
use crate::rpc_client_request::RpcClientRequest; use crate::rpc_client_request::RpcClientRequest;
@ -46,10 +47,7 @@ impl RpcClient {
} }
} }
pub fn send_transaction( pub fn send_transaction(&self, transaction: &Transaction) -> Result<String, ClientError> {
&self,
transaction: &Transaction,
) -> Result<String, Box<dyn error::Error>> {
let serialized = serialize(transaction).unwrap(); let serialized = serialize(transaction).unwrap();
let params = json!([serialized]); let params = json!([serialized]);
let signature = self let signature = self
@ -67,7 +65,7 @@ impl RpcClient {
pub fn get_signature_status( pub fn get_signature_status(
&self, &self,
signature: &str, signature: &str,
) -> Result<Option<transaction::Result<()>>, Box<dyn error::Error>> { ) -> Result<Option<transaction::Result<()>>, ClientError> {
let params = json!([signature.to_string()]); let params = json!([signature.to_string()]);
let signature_status = let signature_status =
self.client self.client
@ -81,7 +79,7 @@ impl RpcClient {
&self, &self,
transaction: &mut Transaction, transaction: &mut Transaction,
signer: &T, signer: &T,
) -> Result<String, Box<dyn error::Error>> { ) -> Result<String, ClientError> {
let mut send_retries = 5; let mut send_retries = 5;
loop { loop {
let mut status_retries = 4; let mut status_retries = 4;
@ -117,10 +115,14 @@ impl RpcClient {
send_retries - 1 send_retries - 1
}; };
if send_retries == 0 { if send_retries == 0 {
Err(io::Error::new( if status.is_some() {
io::ErrorKind::Other, status.unwrap()?
format!("Transaction {:?} failed: {:?}", signature_str, status), } else {
))?; Err(io::Error::new(
io::ErrorKind::Other,
format!("Transaction {:?} failed: {:?}", signature_str, status),
))?;
}
} }
} }
} }
@ -201,7 +203,7 @@ impl RpcClient {
&self, &self,
tx: &mut Transaction, tx: &mut Transaction,
signer_key: &T, signer_key: &T,
) -> Result<(), Box<dyn error::Error>> { ) -> Result<(), ClientError> {
let blockhash = self.get_new_blockhash(&tx.message().recent_blockhash)?; let blockhash = self.get_new_blockhash(&tx.message().recent_blockhash)?;
tx.sign(&[signer_key], blockhash); tx.sign(&[signer_key], blockhash);
Ok(()) Ok(())
@ -482,7 +484,7 @@ impl RpcClient {
) )
.map_err(|error| { .map_err(|error| {
debug!( debug!(
"Response get_num_blocks_since_signature_confirmation: {}", "Response get_num_blocks_since_signature_confirmation: {:?}",
error error
); );
io::Error::new( io::Error::new(
@ -526,7 +528,7 @@ impl RpcClient {
request: &RpcRequest, request: &RpcRequest,
params: Option<Value>, params: Option<Value>,
retries: usize, retries: usize,
) -> Result<Value, Box<dyn error::Error>> { ) -> Result<Value, ClientError> {
self.client.send(request, params, retries) self.client.send(request, params, retries)
} }
} }

View File

@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::rpc_request::{RpcError, RpcRequest}; use crate::rpc_request::{RpcError, RpcRequest};
use log::*; use log::*;
@ -36,7 +37,7 @@ impl GenericRpcClientRequest for RpcClientRequest {
request: &RpcRequest, request: &RpcRequest,
params: Option<serde_json::Value>, params: Option<serde_json::Value>,
mut retries: usize, mut retries: usize,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> { ) -> Result<serde_json::Value, ClientError> {
// Concurrent requests are not supported so reuse the same request id for all requests // Concurrent requests are not supported so reuse the same request id for all requests
let request_id = 1; let request_id = 1;

View File

@ -221,7 +221,7 @@ impl SyncClient for ThinClient {
.map_err(|err| { .map_err(|err| {
io::Error::new( io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
format!("send_transaction failed with error {}", err), format!("send_transaction failed with error {:?}", err),
) )
})?; })?;
Ok(status) Ok(status)

View File

@ -12,6 +12,8 @@ edition = "2018"
bincode = "1.1.3" bincode = "1.1.3"
chrono = { version = "0.4.0", features = ["serde"] } chrono = { version = "0.4.0", features = ["serde"] }
log = "0.4.2" log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
serde = "1.0.90" serde = "1.0.90"
serde_derive = "1.0.90" serde_derive = "1.0.90"
solana-sdk = { path = "../../sdk", version = "0.14.0" } solana-sdk = { path = "../../sdk", version = "0.14.0" }

View File

@ -1,14 +1,29 @@
//! budget state //! budget state
use crate::budget_expr::BudgetExpr; use crate::budget_expr::BudgetExpr;
use bincode::{self, deserialize, serialize_into}; use bincode::{self, deserialize, serialize_into};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::DecodeError;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum BudgetError { pub enum BudgetError {
DestinationMissing, DestinationMissing,
} }
impl<T> DecodeError<T> for BudgetError {
fn type_of(&self) -> &'static str {
"BudgetError"
}
}
impl std::fmt::Display for BudgetError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
}
}
impl std::error::Error for BudgetError {}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct BudgetState { pub struct BudgetState {
pub initialized: bool, pub initialized: bool,

View File

@ -11,6 +11,8 @@ edition = "2018"
[dependencies] [dependencies]
bincode = "1.1.3" bincode = "1.1.3"
log = "0.4.2" log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
serde = "1.0.90" serde = "1.0.90"
serde_derive = "1.0.90" serde_derive = "1.0.90"
solana-logger = { path = "../../logger", version = "0.14.0" } solana-logger = { path = "../../logger", version = "0.14.0" }

View File

@ -1,15 +1,23 @@
use log::*; use log::*;
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction_processor_utils::DecodeError;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
#[derive(Serialize, Debug, PartialEq)] #[derive(Serialize, Debug, PartialEq, FromPrimitive)]
pub enum TokenError { pub enum TokenError {
InvalidArgument, InvalidArgument,
InsufficentFunds, InsufficentFunds,
NotOwner, NotOwner,
} }
impl<T> DecodeError<T> for TokenError {
fn type_of(&self) -> &'static str {
"TokenError"
}
}
impl std::fmt::Display for TokenError { impl std::fmt::Display for TokenError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error") write!(f, "error")

View File

@ -18,6 +18,8 @@ chrono = { version = "0.4.0", features = ["serde"] }
generic-array = { version = "0.13.0", default-features = false, features = ["serde"] } generic-array = { version = "0.13.0", default-features = false, features = ["serde"] }
itertools = "0.8.0" itertools = "0.8.0"
log = "0.4.2" log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
rand = "0.6.5" rand = "0.6.5"
rayon = "1.0.0" rayon = "1.0.0"
sha2 = "0.8.0" sha2 = "0.8.0"

View File

@ -2,6 +2,7 @@ use crate::account::{Account, KeyedAccount};
use crate::instruction::InstructionError; use crate::instruction::InstructionError;
use crate::pubkey::Pubkey; use crate::pubkey::Pubkey;
use bincode::ErrorKind; use bincode::ErrorKind;
use num_traits::FromPrimitive;
// All native programs export a symbol named process() // All native programs export a symbol named process()
pub const ENTRYPOINT: &str = "process"; pub const ENTRYPOINT: &str = "process";
@ -64,3 +65,39 @@ where
self.account.set_state(state) self.account.set_state(state)
} }
} }
pub trait DecodeError<E> {
fn decode_custom_error_to_enum(int: u32) -> Option<E>
where
E: FromPrimitive,
{
E::from_u32(int)
}
fn type_of(&self) -> &'static str;
}
#[cfg(test)]
mod tests {
use super::*;
use num_derive::FromPrimitive;
#[test]
fn test_decode_custom_error_to_enum() {
#[derive(Debug, FromPrimitive, PartialEq)]
enum TestEnum {
A,
B,
C,
}
impl<T> DecodeError<T> for TestEnum {
fn type_of(&self) -> &'static str {
"TestEnum"
}
}
assert_eq!(TestEnum::decode_custom_error_to_enum(0), Some(TestEnum::A));
assert_eq!(TestEnum::decode_custom_error_to_enum(1), Some(TestEnum::B));
assert_eq!(TestEnum::decode_custom_error_to_enum(2), Some(TestEnum::C));
let option: Option<TestEnum> = TestEnum::decode_custom_error_to_enum(3);
assert_eq!(option, None);
}
}

View File

@ -1,14 +1,29 @@
use crate::instruction::{AccountMeta, Instruction}; use crate::instruction::{AccountMeta, Instruction};
use crate::instruction_processor_utils::DecodeError;
use crate::pubkey::Pubkey; use crate::pubkey::Pubkey;
use crate::system_program; use crate::system_program;
use num_derive::FromPrimitive;
#[derive(Serialize, Debug, Clone, PartialEq)] #[derive(Serialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum SystemError { pub enum SystemError {
AccountAlreadyInUse, AccountAlreadyInUse,
ResultWithNegativeLamports, ResultWithNegativeLamports,
SourceNotSystemAccount, SourceNotSystemAccount,
} }
impl<T> DecodeError<T> for SystemError {
fn type_of(&self) -> &'static str {
"SystemError"
}
}
impl std::fmt::Display for SystemError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
}
}
impl std::error::Error for SystemError {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum SystemInstruction { pub enum SystemInstruction {
/// Create a new account /// Create a new account

View File

@ -15,6 +15,7 @@ clap = "2.33.0"
chrono = { version = "0.4.0", features = ["serde"] } chrono = { version = "0.4.0", features = ["serde"] }
dirs = "1.0.5" dirs = "1.0.5"
log = "0.4.2" log = "0.4.2"
num-traits = "0.2"
serde_json = "1.0.39" serde_json = "1.0.39"
solana-budget-api = { path = "../programs/budget_api", version = "0.14.0" } solana-budget-api = { path = "../programs/budget_api", version = "0.14.0" }
solana-client = { path = "../client", version = "0.14.0" } solana-client = { path = "../client", version = "0.14.0" }

View File

@ -2,10 +2,13 @@ use bs58;
use chrono::prelude::*; use chrono::prelude::*;
use clap::ArgMatches; use clap::ArgMatches;
use log::*; use log::*;
use num_traits::FromPrimitive;
use serde_json; use serde_json;
use serde_json::json; use serde_json::json;
use solana_budget_api; use solana_budget_api;
use solana_budget_api::budget_instruction; use solana_budget_api::budget_instruction;
use solana_budget_api::budget_state::BudgetError;
use solana_client::client_error::ClientError;
use solana_client::rpc_client::{get_rpc_request_str, RpcClient}; use solana_client::rpc_client::{get_rpc_request_str, RpcClient};
#[cfg(not(test))] #[cfg(not(test))]
use solana_drone::drone::request_airdrop_transaction; use solana_drone::drone::request_airdrop_transaction;
@ -14,12 +17,15 @@ use solana_drone::drone::DRONE_PORT;
use solana_drone::drone_mock::request_airdrop_transaction; use solana_drone::drone_mock::request_airdrop_transaction;
use solana_sdk::bpf_loader; use solana_sdk::bpf_loader;
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::DecodeError;
use solana_sdk::loader_instruction; use solana_sdk::loader_instruction;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::rpc_port::DEFAULT_RPC_PORT; use solana_sdk::rpc_port::DEFAULT_RPC_PORT;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::system_instruction::SystemError;
use solana_sdk::system_transaction; use solana_sdk::system_transaction;
use solana_sdk::transaction::Transaction; use solana_sdk::transaction::{Transaction, TransactionError};
use solana_vote_api::vote_instruction; use solana_vote_api::vote_instruction;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
@ -448,11 +454,10 @@ fn process_deploy(
0, 0,
); );
trace!("Creating program account"); trace!("Creating program account");
rpc_client let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
.send_and_confirm_transaction(&mut tx, &config.keypair) log_instruction_custom_error::<SystemError>(result).map_err(|_| {
.map_err(|_| { WalletError::DynamicProgramError("Program allocate space failed".to_string())
WalletError::DynamicProgramError("Program allocate space failed".to_string()) })?;
})?;
trace!("Writing program data"); trace!("Writing program data");
let write_transactions: Vec<_> = program_data let write_transactions: Vec<_> = program_data
@ -499,7 +504,8 @@ fn process_pay(
if timestamp == None && *witnesses == None { if timestamp == None && *witnesses == None {
let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash, 0); let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash, 0);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<SystemError>(result)?;
Ok(signature_str.to_string()) Ok(signature_str.to_string())
} else if *witnesses == None { } else if *witnesses == None {
let dt = timestamp.unwrap(); let dt = timestamp.unwrap();
@ -521,7 +527,8 @@ fn process_pay(
lamports, lamports,
); );
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(json!({ Ok(json!({
"signature": signature_str, "signature": signature_str,
@ -551,7 +558,8 @@ fn process_pay(
lamports, lamports,
); );
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(json!({ Ok(json!({
"signature": signature_str, "signature": signature_str,
@ -571,7 +579,8 @@ fn process_cancel(rpc_client: &RpcClient, config: &WalletConfig, pubkey: &Pubkey
&config.keypair.pubkey(), &config.keypair.pubkey(),
); );
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(signature_str.to_string()) Ok(signature_str.to_string())
} }
@ -598,7 +607,8 @@ fn process_time_elapsed(
let ix = budget_instruction::apply_timestamp(&config.keypair.pubkey(), pubkey, to, dt); let ix = budget_instruction::apply_timestamp(&config.keypair.pubkey(), pubkey, to, dt);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(signature_str.to_string()) Ok(signature_str.to_string())
} }
@ -619,7 +629,8 @@ fn process_witness(
let blockhash = rpc_client.get_recent_blockhash()?; let blockhash = rpc_client.get_recent_blockhash()?;
let ix = budget_instruction::apply_signature(&config.keypair.pubkey(), pubkey, to); let ix = budget_instruction::apply_signature(&config.keypair.pubkey(), pubkey, to);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &config.keypair);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(signature_str.to_string()) Ok(signature_str.to_string())
} }
@ -766,10 +777,39 @@ pub fn request_and_confirm_airdrop(
let blockhash = rpc_client.get_recent_blockhash()?; let blockhash = rpc_client.get_recent_blockhash()?;
let keypair = DroneKeypair::new_keypair(drone_addr, to_pubkey, lamports, blockhash)?; let keypair = DroneKeypair::new_keypair(drone_addr, to_pubkey, lamports, blockhash)?;
let mut tx = keypair.airdrop_transaction(); let mut tx = keypair.airdrop_transaction();
rpc_client.send_and_confirm_transaction(&mut tx, &keypair)?; let result = rpc_client.send_and_confirm_transaction(&mut tx, &keypair);
log_instruction_custom_error::<SystemError>(result)?;
Ok(()) Ok(())
} }
fn log_instruction_custom_error<E>(result: Result<String, ClientError>) -> ProcessResult
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
if result.is_err() {
let err = result.unwrap_err();
if let ClientError::TransactionError(TransactionError::InstructionError(
_,
InstructionError::CustomError(code),
)) = err
{
if let Some(specific_error) = E::decode_custom_error_to_enum(code) {
error!(
"{:?}: {}::{:?}",
err,
specific_error.type_of(),
specific_error
);
Err(specific_error)?
}
}
error!("{:?}", err);
Err(err)?
} else {
Ok(result.unwrap())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;