Skip to content

Commit

Permalink
feat: revamp provider error handling
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 committed Dec 5, 2023
1 parent e685b5e commit 18a8f6f
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 18a8f6f

Please sign in to comment.