Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Custom error decoder #3783

Merged
merged 5 commits into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions client/src/client_error.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
3 changes: 2 additions & 1 deletion client/src/generic_rpc_client_request.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::rpc_request::RpcRequest;

pub(crate) trait GenericRpcClientRequest {
Expand All @@ -6,5 +7,5 @@ pub(crate) trait GenericRpcClientRequest {
request: &RpcRequest,
params: Option<serde_json::Value>,
retries: usize,
) -> Result<serde_json::Value, Box<dyn std::error::Error>>;
) -> Result<serde_json::Value, ClientError>;
}
1 change: 1 addition & 0 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod client_error;
mod generic_rpc_client_request;
pub mod mock_rpc_client_request;
pub mod rpc_client;
Expand Down
3 changes: 2 additions & 1 deletion client/src/mock_rpc_client_request.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::rpc_request::RpcRequest;
use serde_json::{Number, Value};
Expand All @@ -23,7 +24,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
request: &RpcRequest,
params: Option<serde_json::Value>,
_retries: usize,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
) -> Result<serde_json::Value, ClientError> {
if self.url == "fails" {
return Ok(Value::Null);
}
Expand Down
28 changes: 15 additions & 13 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::mock_rpc_client_request::MockRpcClientRequest;
use crate::rpc_client_request::RpcClientRequest;
Expand Down Expand Up @@ -46,10 +47,7 @@ impl RpcClient {
}
}

pub fn send_transaction(
&self,
transaction: &Transaction,
) -> Result<String, Box<dyn error::Error>> {
pub fn send_transaction(&self, transaction: &Transaction) -> Result<String, ClientError> {
let serialized = serialize(transaction).unwrap();
let params = json!([serialized]);
let signature = self
Expand All @@ -67,7 +65,7 @@ impl RpcClient {
pub fn get_signature_status(
&self,
signature: &str,
) -> Result<Option<transaction::Result<()>>, Box<dyn error::Error>> {
) -> Result<Option<transaction::Result<()>>, ClientError> {
let params = json!([signature.to_string()]);
let signature_status =
self.client
Expand All @@ -81,7 +79,7 @@ impl RpcClient {
&self,
transaction: &mut Transaction,
signer: &T,
) -> Result<String, Box<dyn error::Error>> {
) -> Result<String, ClientError> {
let mut send_retries = 5;
loop {
let mut status_retries = 4;
Expand Down Expand Up @@ -117,10 +115,14 @@ impl RpcClient {
send_retries - 1
};
if send_retries == 0 {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Transaction {:?} failed: {:?}", signature_str, status),
))?;
if status.is_some() {
status.unwrap()?
} else {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Transaction {:?} failed: {:?}", signature_str, status),
))?;
}
}
}
}
Expand Down Expand Up @@ -201,7 +203,7 @@ impl RpcClient {
&self,
tx: &mut Transaction,
signer_key: &T,
) -> Result<(), Box<dyn error::Error>> {
) -> Result<(), ClientError> {
let blockhash = self.get_new_blockhash(&tx.message().recent_blockhash)?;
tx.sign(&[signer_key], blockhash);
Ok(())
Expand Down Expand Up @@ -482,7 +484,7 @@ impl RpcClient {
)
.map_err(|error| {
debug!(
"Response get_num_blocks_since_signature_confirmation: {}",
"Response get_num_blocks_since_signature_confirmation: {:?}",
error
);
io::Error::new(
Expand Down Expand Up @@ -526,7 +528,7 @@ impl RpcClient {
request: &RpcRequest,
params: Option<Value>,
retries: usize,
) -> Result<Value, Box<dyn error::Error>> {
) -> Result<Value, ClientError> {
self.client.send(request, params, retries)
}
}
Expand Down
3 changes: 2 additions & 1 deletion client/src/rpc_client_request.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::client_error::ClientError;
use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::rpc_request::{RpcError, RpcRequest};
use log::*;
Expand Down Expand Up @@ -36,7 +37,7 @@ impl GenericRpcClientRequest for RpcClientRequest {
request: &RpcRequest,
params: Option<serde_json::Value>,
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
let request_id = 1;

Expand Down
2 changes: 1 addition & 1 deletion client/src/thin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl SyncClient for ThinClient {
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("send_transaction failed with error {}", err),
format!("send_transaction failed with error {:?}", err),
)
})?;
Ok(status)
Expand Down
2 changes: 2 additions & 0 deletions programs/budget_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ edition = "2018"
bincode = "1.1.3"
chrono = { version = "0.4.0", features = ["serde"] }
log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
serde = "1.0.90"
serde_derive = "1.0.90"
solana-sdk = { path = "../../sdk", version = "0.14.0" }
Expand Down
17 changes: 16 additions & 1 deletion programs/budget_api/src/budget_state.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
//! budget state
use crate::budget_expr::BudgetExpr;
use bincode::{self, deserialize, serialize_into};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize};
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 {
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)]
pub struct BudgetState {
pub initialized: bool,
Expand Down
2 changes: 2 additions & 0 deletions programs/token_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ edition = "2018"
[dependencies]
bincode = "1.1.3"
log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
serde = "1.0.90"
serde_derive = "1.0.90"
solana-logger = { path = "../../logger", version = "0.14.0" }
Expand Down
10 changes: 9 additions & 1 deletion programs/token_api/src/token_state.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
use log::*;
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction_processor_utils::DecodeError;
use solana_sdk::pubkey::Pubkey;

#[derive(Serialize, Debug, PartialEq)]
#[derive(Serialize, Debug, PartialEq, FromPrimitive)]
pub enum TokenError {
InvalidArgument,
InsufficentFunds,
NotOwner,
}

impl<T> DecodeError<T> for TokenError {
fn type_of(&self) -> &'static str {
"TokenError"
}
}

impl std::fmt::Display for TokenError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
Expand Down
2 changes: 2 additions & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ chrono = { version = "0.4.0", features = ["serde"] }
generic-array = { version = "0.13.0", default-features = false, features = ["serde"] }
itertools = "0.8.0"
log = "0.4.2"
num-derive = "0.2"
num-traits = "0.2"
rand = "0.6.5"
rayon = "1.0.0"
sha2 = "0.8.0"
Expand Down
37 changes: 37 additions & 0 deletions sdk/src/instruction_processor_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::account::{Account, KeyedAccount};
use crate::instruction::InstructionError;
use crate::pubkey::Pubkey;
use bincode::ErrorKind;
use num_traits::FromPrimitive;

// All native programs export a symbol named process()
pub const ENTRYPOINT: &str = "process";
Expand Down Expand Up @@ -64,3 +65,39 @@ where
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);
}
}
17 changes: 16 additions & 1 deletion sdk/src/system_instruction.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
use crate::instruction::{AccountMeta, Instruction};
use crate::instruction_processor_utils::DecodeError;
use crate::pubkey::Pubkey;
use crate::system_program;
use num_derive::FromPrimitive;

#[derive(Serialize, Debug, Clone, PartialEq)]
#[derive(Serialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum SystemError {
AccountAlreadyInUse,
ResultWithNegativeLamports,
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)]
pub enum SystemInstruction {
/// Create a new account
Expand Down
1 change: 1 addition & 0 deletions wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ clap = "2.33.0"
chrono = { version = "0.4.0", features = ["serde"] }
dirs = "1.0.5"
log = "0.4.2"
num-traits = "0.2"
serde_json = "1.0.39"
solana-budget-api = { path = "../programs/budget_api", version = "0.14.0" }
solana-client = { path = "../client", version = "0.14.0" }
Expand Down
Loading