Skip to content

Commit

Permalink
feat: revamp provider error handling (#511)
Browse files Browse the repository at this point in the history
Greatly simplify provider error code matching. Previously, the error
code returned from the RPC is an enum with 2 variants for known and
unknown codes to accommodate the fact that JSON-RPC nodes sometimes
return codes that are part of the JSON-RPC standard, but are not from
the Starknet specifications. This commit changes to move the unknown
codes to be handled by the implementation-specific error type instead,
making it much easier to match against specification error codes, at the
cost of being harder to match the unknown codes.
  • Loading branch information
xJonathanLEI authored Dec 5, 2023
1 parent e685b5e commit 4107312
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 79 deletions.
11 changes: 4 additions & 7 deletions starknet-accounts/src/factory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use starknet_core::{
SimulationFlag, StarknetError,
},
};
use starknet_providers::{
MaybeUnknownErrorCode, Provider, ProviderError, StarknetErrorWithMessage,
};
use starknet_providers::{Provider, ProviderError};
use std::error::Error;

pub mod argent;
Expand Down Expand Up @@ -178,10 +176,9 @@ where
.await
{
Ok(nonce) => Ok(nonce),
Err(ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(StarknetError::ContractNotFound),
..
})) => Ok(FieldElement::ZERO),
Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => {
Ok(FieldElement::ZERO)
}
Err(err) => Err(err),
}
}
Expand Down
35 changes: 34 additions & 1 deletion starknet-core/src/types/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen

// Code generated with version:
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen#3215ab04aee89aafa48973cdcf027219f662f49d
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen#51260963a0723fdbc715598efb7198ce5a1d49b9

// Code generation requested but not implemented for these types:
// - `BLOCK_ID`
Expand Down Expand Up @@ -1397,6 +1397,39 @@ impl core::fmt::Display for StarknetError {
}
}

impl StarknetError {
pub fn message(&self) -> &'static str {
match self {
Self::FailedToReceiveTransaction => "Failed to write transaction",
Self::ContractNotFound => "Contract not found",
Self::BlockNotFound => "Block not found",
Self::InvalidTransactionIndex => "Invalid transaction index in a block",
Self::ClassHashNotFound => "Class hash not found",
Self::TransactionHashNotFound => "Transaction hash not found",
Self::PageSizeTooBig => "Requested page size is too big",
Self::NoBlocks => "There are no blocks",
Self::InvalidContinuationToken => "The supplied continuation token is invalid or unknown",
Self::TooManyKeysInFilter => "Too many keys provided in a filter",
Self::ContractError => "Contract error",
Self::ClassAlreadyDeclared => "Class already declared",
Self::InvalidTransactionNonce => "Invalid transaction nonce",
Self::InsufficientMaxFee => "Max fee is smaller than the minimal transaction cost (validation plus fee transfer)",
Self::InsufficientAccountBalance => "Account balance is smaller than the transaction's max_fee",
Self::ValidationFailure => "Account validation failed",
Self::CompilationFailed => "Compilation failed",
Self::ContractClassSizeIsTooLarge => "Contract class size it too large",
Self::NonAccount => "Sender address in not an account contract",
Self::DuplicateTx => "A transaction with the same hash already exists in the mempool",
Self::CompiledClassHashMismatch => "the compiled class hash did not match the one supplied in the transaction",
Self::UnsupportedTxVersion => "the transaction version is not supported",
Self::UnsupportedContractClassVersion => "the contract class version is not supported",
Self::UnexpectedError => "An unexpected error occurred",
Self::NoTraceAvailable => "No trace available for transaction",
Self::InvalidTransactionHash => "Invalid transaction hash",
}
}
}

/// The change in state applied in this block, given as a mapping of addresses to the new values
/// and/or new contracts.
#[serde_as]
Expand Down
32 changes: 8 additions & 24 deletions starknet-providers/src/jsonrpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ use starknet_core::{
EventFilterWithPage, EventsPage, FeeEstimate, FieldElement, FunctionCall,
InvokeTransactionResult, MaybePendingBlockWithTxHashes, MaybePendingBlockWithTxs,
MaybePendingStateUpdate, MaybePendingTransactionReceipt, MsgFromL1, ResultPageRequest,
SimulatedTransaction, SimulationFlag, StarknetError, SyncStatusType, Transaction,
TransactionStatus, TransactionTrace, TransactionTraceWithHash,
SimulatedTransaction, SimulationFlag, SyncStatusType, Transaction, TransactionStatus,
TransactionTrace, TransactionTraceWithHash,
},
};

use crate::{
provider::{MaybeUnknownErrorCode, ProviderImplError, StarknetErrorWithMessage},
Provider, ProviderError,
};
use crate::{provider::ProviderImplError, Provider, ProviderError};

mod transports;
pub use transports::{HttpTransport, HttpTransportError, JsonRpcTransport};
Expand Down Expand Up @@ -135,15 +132,7 @@ pub enum JsonRpcClientError<T> {
#[error(transparent)]
TransportError(T),
#[error(transparent)]
RpcError(RpcError),
}

#[derive(Debug, thiserror::Error)]
pub enum RpcError {
#[error(transparent)]
Code(StarknetError),
#[error(transparent)]
Unknown(JsonRpcError),
JsonRpcError(JsonRpcError),
}

#[derive(Debug, thiserror::Error, Deserialize)]
Expand Down Expand Up @@ -190,15 +179,10 @@ where
.map_err(JsonRpcClientError::TransportError)?
{
JsonRpcResponse::Success { result, .. } => Ok(result),
JsonRpcResponse::Error { error, .. } => {
Err(ProviderError::StarknetError(StarknetErrorWithMessage {
code: match error.code.try_into() {
Ok(code) => MaybeUnknownErrorCode::Known(code),
Err(_) => MaybeUnknownErrorCode::Unknown(error.code),
},
message: error.message,
}))
}
JsonRpcResponse::Error { error, .. } => Err(match error.code.try_into() {
Ok(code) => ProviderError::StarknetError(code),
Err(_) => JsonRpcClientError::<T::Error>::JsonRpcError(error).into(),
}),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion starknet-providers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![doc = include_str!("../README.md")]

mod provider;
pub use provider::{MaybeUnknownErrorCode, Provider, ProviderError, StarknetErrorWithMessage};
pub use provider::{Provider, ProviderError};

pub mod sequencer;
pub use sequencer::{
Expand Down
24 changes: 1 addition & 23 deletions starknet-providers/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,33 +297,11 @@ pub trait ProviderImplError: Error + Debug + Send + Sync {
#[derive(Debug, thiserror::Error)]
pub enum ProviderError {
#[error(transparent)]
StarknetError(StarknetErrorWithMessage),
StarknetError(StarknetError),
#[error("Request rate limited")]
RateLimited,
#[error("Array length mismatch")]
ArrayLengthMismatch,
#[error("{0}")]
Other(Box<dyn ProviderImplError>),
}

#[derive(Debug, thiserror::Error)]
#[error("code={code}, message=\"{message}\"")]
pub struct StarknetErrorWithMessage {
pub code: MaybeUnknownErrorCode,
pub message: String,
}

#[derive(Debug)]
pub enum MaybeUnknownErrorCode {
Known(StarknetError),
Unknown(i64),
}

impl core::fmt::Display for MaybeUnknownErrorCode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
MaybeUnknownErrorCode::Known(code) => write!(f, "{}", code),
MaybeUnknownErrorCode::Unknown(code) => write!(f, "{}", code),
}
}
}
7 changes: 2 additions & 5 deletions starknet-providers/src/sequencer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::provider::{MaybeUnknownErrorCode, ProviderError, StarknetErrorWithMessage};
use crate::provider::ProviderError;

use log::trace;
use reqwest::{Client, Error as ReqwestError, StatusCode};
Expand Down Expand Up @@ -730,10 +730,7 @@ impl From<SequencerError> for ProviderError {
};

match matching_code {
Some(code) => ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(code),
message: value.message,
}),
Some(code) => ProviderError::StarknetError(code),
None => GatewayClientError::SequencerError(value).into(),
}
}
Expand Down
23 changes: 10 additions & 13 deletions starknet-providers/src/sequencer/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use starknet_core::types::{
};

use crate::{
provider::{MaybeUnknownErrorCode, ProviderImplError, StarknetErrorWithMessage},
provider::ProviderImplError,
sequencer::{
models::conversions::{ConversionError, TransactionWithReceipt},
GatewayClientError,
Expand Down Expand Up @@ -106,10 +106,9 @@ impl Provider for SequencerGatewayProvider {

// `NotReceived` is not a valid status for JSON-RPC. It's an error.
if let Some(TransactionFinalityStatus::NotReceived) = &status.finality_status {
return Err(ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(StarknetError::TransactionHashNotFound),
message: "Transaction hash not found".into(),
}));
return Err(ProviderError::StarknetError(
StarknetError::TransactionHashNotFound,
));
}

Ok(status.try_into()?)
Expand Down Expand Up @@ -142,10 +141,9 @@ impl Provider for SequencerGatewayProvider {
if index < block.transactions.len() {
Ok(block.transactions.remove(index).try_into()?)
} else {
Err(ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(StarknetError::InvalidTransactionIndex),
message: "Invalid transaction index in a block".into(),
}))
Err(ProviderError::StarknetError(
StarknetError::InvalidTransactionIndex,
))
}
}

Expand All @@ -164,10 +162,9 @@ impl Provider for SequencerGatewayProvider {
if receipt.status == super::models::TransactionStatus::NotReceived
|| receipt.status == super::models::TransactionStatus::Received
{
Err(ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(StarknetError::TransactionHashNotFound),
message: "Transaction hash not found".into(),
}))
Err(ProviderError::StarknetError(
StarknetError::TransactionHashNotFound,
))
} else {
// JSON-RPC also sends tx type, which is not available in our receipt type
let tx = self.get_transaction(*transaction_hash.as_ref()).await?;
Expand Down
7 changes: 2 additions & 5 deletions starknet-providers/tests/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use starknet_core::{
};
use starknet_providers::{
jsonrpc::{HttpTransport, JsonRpcClient},
MaybeUnknownErrorCode, Provider, ProviderError, StarknetErrorWithMessage,
Provider, ProviderError,
};
use url::Url;

Expand Down Expand Up @@ -371,10 +371,7 @@ async fn jsonrpc_get_transaction_by_hash_non_existent_tx() {
.unwrap_err();

match err {
ProviderError::StarknetError(StarknetErrorWithMessage {
code: MaybeUnknownErrorCode::Known(StarknetError::TransactionHashNotFound),
..
}) => {
ProviderError::StarknetError(StarknetError::TransactionHashNotFound) => {
// TXN_HASH_NOT_FOUND
}
_ => panic!("Unexpected error"),
Expand Down

0 comments on commit 4107312

Please sign in to comment.