From 18a8f6f853d970b44cc251c20fd4858b4ea04157 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Tue, 5 Dec 2023 03:17:18 +0000 Subject: [PATCH] feat: revamp provider error handling 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. --- starknet-accounts/src/factory/mod.rs | 11 +++--- starknet-core/src/types/codegen.rs | 35 +++++++++++++++++++- starknet-providers/src/jsonrpc/mod.rs | 32 +++++------------- starknet-providers/src/lib.rs | 2 +- starknet-providers/src/provider.rs | 24 +------------- starknet-providers/src/sequencer/mod.rs | 7 ++-- starknet-providers/src/sequencer/provider.rs | 23 ++++++------- starknet-providers/tests/jsonrpc.rs | 7 ++-- 8 files changed, 62 insertions(+), 79 deletions(-) diff --git a/starknet-accounts/src/factory/mod.rs b/starknet-accounts/src/factory/mod.rs index 0bb68cbd..3737be5a 100644 --- a/starknet-accounts/src/factory/mod.rs +++ b/starknet-accounts/src/factory/mod.rs @@ -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; @@ -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), } } diff --git a/starknet-core/src/types/codegen.rs b/starknet-core/src/types/codegen.rs index 6b1bcd8d..15201af4 100644 --- a/starknet-core/src/types/codegen.rs +++ b/starknet-core/src/types/codegen.rs @@ -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` @@ -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] diff --git a/starknet-providers/src/jsonrpc/mod.rs b/starknet-providers/src/jsonrpc/mod.rs index d45a0f14..0d5e9e76 100644 --- a/starknet-providers/src/jsonrpc/mod.rs +++ b/starknet-providers/src/jsonrpc/mod.rs @@ -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}; @@ -135,15 +132,7 @@ pub enum JsonRpcClientError { #[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)] @@ -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::::JsonRpcError(error).into(), + }), } } } diff --git a/starknet-providers/src/lib.rs b/starknet-providers/src/lib.rs index 491a3278..0d330ef6 100644 --- a/starknet-providers/src/lib.rs +++ b/starknet-providers/src/lib.rs @@ -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::{ diff --git a/starknet-providers/src/provider.rs b/starknet-providers/src/provider.rs index 86ad7ef7..2214cb88 100644 --- a/starknet-providers/src/provider.rs +++ b/starknet-providers/src/provider.rs @@ -297,7 +297,7 @@ 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")] @@ -305,25 +305,3 @@ pub enum ProviderError { #[error("{0}")] Other(Box), } - -#[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), - } - } -} diff --git a/starknet-providers/src/sequencer/mod.rs b/starknet-providers/src/sequencer/mod.rs index 429b98a3..aacc17ea 100644 --- a/starknet-providers/src/sequencer/mod.rs +++ b/starknet-providers/src/sequencer/mod.rs @@ -1,4 +1,4 @@ -use crate::provider::{MaybeUnknownErrorCode, ProviderError, StarknetErrorWithMessage}; +use crate::provider::ProviderError; use log::trace; use reqwest::{Client, Error as ReqwestError, StatusCode}; @@ -730,10 +730,7 @@ impl From 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(), } } diff --git a/starknet-providers/src/sequencer/provider.rs b/starknet-providers/src/sequencer/provider.rs index c6e58529..6c24a323 100644 --- a/starknet-providers/src/sequencer/provider.rs +++ b/starknet-providers/src/sequencer/provider.rs @@ -14,7 +14,7 @@ use starknet_core::types::{ }; use crate::{ - provider::{MaybeUnknownErrorCode, ProviderImplError, StarknetErrorWithMessage}, + provider::ProviderImplError, sequencer::{ models::conversions::{ConversionError, TransactionWithReceipt}, GatewayClientError, @@ -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()?) @@ -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, + )) } } @@ -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?; diff --git a/starknet-providers/tests/jsonrpc.rs b/starknet-providers/tests/jsonrpc.rs index 41466fd5..51ee615c 100644 --- a/starknet-providers/tests/jsonrpc.rs +++ b/starknet-providers/tests/jsonrpc.rs @@ -11,7 +11,7 @@ use starknet_core::{ }; use starknet_providers::{ jsonrpc::{HttpTransport, JsonRpcClient}, - MaybeUnknownErrorCode, Provider, ProviderError, StarknetErrorWithMessage, + Provider, ProviderError, }; use url::Url; @@ -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"),