diff --git a/crates/stratus_macros/src/lib.rs b/crates/stratus_macros/src/lib.rs index f195cc854..f6c47851d 100644 --- a/crates/stratus_macros/src/lib.rs +++ b/crates/stratus_macros/src/lib.rs @@ -9,7 +9,7 @@ use syn::Fields; use syn::Lit; use syn::Meta; -#[proc_macro_derive(ErrorCode, attributes(error_code))] +#[proc_macro_derive(ErrorCode, attributes(error_code, major_error_code))] pub fn derive_error_code(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); derive_error_code_impl(input).into() @@ -86,7 +86,7 @@ fn derive_error_code_impl(input: DeriveInput) -> proc_macro2::TokenStream { // Add reverse mapping reverse_match_arms.push(quote! { - #error_code => stringify!(#variant_name) + #error_code => Some(stringify!(#variant_name)) }); } @@ -98,10 +98,10 @@ fn derive_error_code_impl(input: DeriveInput) -> proc_macro2::TokenStream { } } - fn str_repr_from_err_code(code: i32) -> &'static str { + fn str_repr_from_err_code(code: i32) -> Option<&'static str> { match code { #(#reverse_match_arms),*, - _ => "Unknown" + _ => None } } } @@ -144,12 +144,12 @@ mod tests { } } - fn str_repr_from_err_code(code: i32) -> &'static str { + fn str_repr_from_err_code(code: i32) -> Option<&'static str> { match code { - 103i32 => stringify ! (First), - 104i32 => stringify ! (Second), - 100i32 => stringify ! (Third), - _ => "Unknown" + 103i32 => Some(stringify ! (First)), + 104i32 => Some(stringify ! (Second)), + 100i32 => Some(stringify ! (Third)), + _ => None } } } diff --git a/src/eth/primitives/stratus_error.rs b/src/eth/primitives/stratus_error.rs index 80de68072..017798d62 100644 --- a/src/eth/primitives/stratus_error.rs +++ b/src/eth/primitives/stratus_error.rs @@ -18,11 +18,11 @@ use crate::ext::to_json_value; pub trait ErrorCode { fn error_code(&self) -> i32; - #[allow(unused)] - fn str_repr_from_err_code(code: i32) -> &'static str; + fn str_repr_from_err_code(code: i32) -> Option<&'static str>; } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 1000] pub enum RpcError { #[error("Block filter does not point to a valid block.")] #[error_code = 1] @@ -60,7 +60,9 @@ pub enum RpcError { #[error_code = 9] MinerModeParamInvalid, } + #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 2000] pub enum TransactionError { #[error("Account at {address} is not a contract.")] #[error_code = 1] @@ -92,6 +94,7 @@ pub enum TransactionError { } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 3000] pub enum StorageError { #[error("Block conflict: {number} already exists in the permanent storage.")] #[error_code = 1] @@ -131,6 +134,7 @@ pub enum StorageError { } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 4000] pub enum ImporterError { #[error("Importer is already running.")] #[error_code = 1] @@ -150,6 +154,7 @@ pub enum ImporterError { } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 5000] pub enum ConsensusError { #[error("Consensus is temporarily unavailable for follower node.")] #[error_code = 1] @@ -165,6 +170,7 @@ pub enum ConsensusError { } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 6000] pub enum UnexpectedError { #[error("Unexpected channel {channel} closed.")] #[error_code = 1] @@ -176,6 +182,7 @@ pub enum UnexpectedError { } #[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[major_error_code = 7000] pub enum StateError { #[error("Stratus is not ready to start servicing requests.")] #[error_code = 1] @@ -206,41 +213,33 @@ pub enum StateError { TransactionsEnabled, } -#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)] +#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr)] pub enum StratusError { #[error(transparent)] - #[error_code = 1000] RPC(#[from] RpcError), #[error(transparent)] - #[error_code = 2000] Transaction(#[from] TransactionError), #[error(transparent)] - #[error_code = 3000] Storage(#[from] StorageError), #[error(transparent)] - #[error_code = 4000] Importer(#[from] ImporterError), #[error(transparent)] - #[error_code = 5000] Consensus(#[from] ConsensusError), #[error(transparent)] - #[error_code = 6000] Unexpected(#[from] UnexpectedError), #[error(transparent)] - #[error_code = 7000] State(#[from] StateError), } -impl StratusError { - /// Error code to be used in JSON-RPC response. - pub fn rpc_code(&self) -> i32 { - let inner_error = match self { +impl ErrorCode for StratusError { + fn error_code(&self) -> i32 { + match self { Self::RPC(err) => err.error_code(), Self::Transaction(err) => err.error_code(), Self::Storage(err) => err.error_code(), @@ -248,11 +247,25 @@ impl StratusError { Self::Consensus(err) => err.error_code(), Self::Unexpected(err) => err.error_code(), Self::State(err) => err.error_code(), - }; + } + } - self.error_code() + inner_error + fn str_repr_from_err_code(code: i32) -> Option<&'static str> { + let major = code % 1000; + match major { + 1 => RpcError::str_repr_from_err_code(code), + 2 => TransactionError::str_repr_from_err_code(code), + 3 => StorageError::str_repr_from_err_code(code), + 4 => ImporterError::str_repr_from_err_code(code), + 5 => ConsensusError::str_repr_from_err_code(code), + 6 => UnexpectedError::str_repr_from_err_code(code), + 7 => StateError::str_repr_from_err_code(code), + _ => None, + } } +} +impl StratusError { /// Error message to be used in JSON-RPC response. pub fn rpc_message(&self) -> String { self.to_string() @@ -312,6 +325,6 @@ impl From for ErrorObjectOwned { data => data, }; - Self::owned(value.rpc_code(), value.rpc_message(), Some(data)) + Self::owned(value.error_code(), value.rpc_message(), Some(data)) } } diff --git a/src/eth/rpc/mod.rs b/src/eth/rpc/mod.rs index 71865f69f..e21da3146 100644 --- a/src/eth/rpc/mod.rs +++ b/src/eth/rpc/mod.rs @@ -5,7 +5,6 @@ mod rpc_client_app; mod rpc_config; mod rpc_context; mod rpc_http_middleware; -mod rpc_method_wrapper; mod rpc_middleware; mod rpc_parser; mod rpc_server; diff --git a/src/eth/rpc/rpc_method_wrapper.rs b/src/eth/rpc/rpc_method_wrapper.rs deleted file mode 100644 index 79bfb41bc..000000000 --- a/src/eth/rpc/rpc_method_wrapper.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::sync::Arc; - -use cfg_if::cfg_if; -use jsonrpsee::types::Params; -use jsonrpsee::Extensions; - -use crate::eth::primitives::StratusError; -use crate::eth::rpc::rpc_parser::RpcExtensionsExt; -use crate::eth::rpc::RpcContext; - -cfg_if! { - if #[cfg(feature = "metrics")] { - use crate::infra::metrics::inc_rpc_error_response; - - fn metrify_stratus_error(err: &StratusError, extensions: &Extensions, method_name: &'static str) { - let error_type = <&'static str>::from(err); - let client = extensions.rpc_client(); - inc_rpc_error_response(error_type, client, method_name); - } - - pub fn metrics_wrapper(function: F, method_name: &'static str) -> impl Fn(Params<'_>, Arc, Extensions) -> Result + Clone - where - F: Fn(Params<'_>, Arc, &Extensions) -> Result + Clone, - { - move |params, ctx, extensions| { - function(params, ctx, &extensions).inspect_err(|e| metrify_stratus_error(e, &extensions, method_name)) - } - } - } else { - pub fn metrics_wrapper(function: F, _method_name: &'static str) -> impl Fn(Params<'_>, Arc, Extensions) -> Result + Clone - where - F: Fn(Params<'_>, Arc, &Extensions) -> Result + Clone, - { - move |params, ctx, extensions| { - function(params, ctx, &extensions) - } - } - } -} diff --git a/src/eth/rpc/rpc_middleware.rs b/src/eth/rpc/rpc_middleware.rs index 03d51481a..62a359b99 100644 --- a/src/eth/rpc/rpc_middleware.rs +++ b/src/eth/rpc/rpc_middleware.rs @@ -27,6 +27,7 @@ use crate::eth::codegen::SoliditySignature; use crate::eth::primitives::Address; use crate::eth::primitives::Bytes; use crate::eth::primitives::CallInput; +use crate::eth::primitives::ErrorCode; use crate::eth::primitives::Hash; use crate::eth::primitives::Nonce; use crate::eth::primitives::RpcError; @@ -246,7 +247,7 @@ impl<'a> Future for RpcResponse<'a> { { let rpc_result = match response_result.get("result") { Some(result) => if_else!(result.is_null(), metrics::LABEL_MISSING, metrics::LABEL_PRESENT), - None => metrics::LABEL_ERROR, + None => StratusError::str_repr_from_err_code(error_code).unwrap_or("Unknown"), }; let tx_ref = resp.tx.as_ref(); diff --git a/src/eth/rpc/rpc_server.rs b/src/eth/rpc/rpc_server.rs index 6af000aaf..eb5da93c6 100644 --- a/src/eth/rpc/rpc_server.rs +++ b/src/eth/rpc/rpc_server.rs @@ -17,11 +17,9 @@ use jsonrpsee::server::RpcServiceBuilder; use jsonrpsee::server::Server; use jsonrpsee::types::Params; use jsonrpsee::Extensions; -use jsonrpsee::IntoResponse; use jsonrpsee::IntoSubscriptionCloseResponse; use jsonrpsee::PendingSubscriptionSink; use once_cell::sync::Lazy; -use serde::Serialize; use serde_json::json; use tokio::runtime::Handle; use tokio::select; @@ -34,7 +32,6 @@ use tracing::info_span; use tracing::Instrument; use tracing::Span; -use super::rpc_method_wrapper::metrics_wrapper; use crate::alias::EthersReceipt; use crate::alias::JsonValue; use crate::eth::executor::Executor; @@ -215,32 +212,32 @@ fn register_methods(mut module: RpcModule) -> anyhow::Result) -> anyhow::Result( - module: &mut RpcModule, - method_name: &'static str, - method: fn(Params<'_>, Arc, &Extensions) -> Result, -) -> anyhow::Result<()> -where - T: IntoResponse + Clone + Serialize + 'static, -{ - module.register_blocking_method(method_name, metrics_wrapper(method, method_name))?; - Ok(()) -} - // ----------------------------------------------------------------------------- // Debug // ----------------------------------------------------------------------------- @@ -650,7 +634,7 @@ fn eth_gas_price(_: Params<'_>, _: &RpcContext, _: &Extensions) -> String { // Block // ----------------------------------------------------------------------------- -fn eth_block_number(_params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_block_number(_params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_blockNumber", block_number = field::Empty).entered(); @@ -662,7 +646,7 @@ fn eth_block_number(_params: Params<'_>, ctx: Arc, ext: &Extensions) Ok(to_json_value(block_number)) } -fn stratus_get_block_and_receipts(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn stratus_get_block_and_receipts(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::stratus_getBlockAndReceipts").entered(); @@ -687,16 +671,16 @@ fn stratus_get_block_and_receipts(params: Params<'_>, ctx: Arc, ext: })) } -fn eth_get_block_by_hash(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_block_by_hash(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { eth_get_block_by_selector::<'h'>(params, ctx, ext) } -fn eth_get_block_by_number(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_block_by_number(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { eth_get_block_by_selector::<'n'>(params, ctx, ext) } #[inline(always)] -fn eth_get_block_by_selector(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_block_by_selector(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = if KIND == 'h' { @@ -757,7 +741,7 @@ fn eth_get_uncle_by_block_hash_and_index(_: Params<'_>, _: &RpcContext, _: &Exte // Transaction // ----------------------------------------------------------------------------- -fn eth_get_transaction_by_hash(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_transaction_by_hash(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getTransactionByHash", tx_hash = field::Empty, found = field::Empty).entered(); @@ -787,7 +771,7 @@ fn eth_get_transaction_by_hash(params: Params<'_>, ctx: Arc, ext: &E } } -fn eth_get_transaction_receipt(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_transaction_receipt(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getTransactionReceipt", tx_hash = field::Empty, found = field::Empty).entered(); @@ -817,7 +801,7 @@ fn eth_get_transaction_receipt(params: Params<'_>, ctx: Arc, ext: &E } } -fn eth_estimate_gas(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_estimate_gas(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_estimateGas", tx_from = field::Empty, tx_to = field::Empty).entered(); @@ -855,7 +839,7 @@ fn eth_estimate_gas(params: Params<'_>, ctx: Arc, ext: &Extensions) } } -fn eth_call(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_call(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_call", tx_from = field::Empty, tx_to = field::Empty, filter = field::Empty).entered(); @@ -895,7 +879,7 @@ fn eth_call(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Resul } } -fn eth_send_raw_transaction(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_send_raw_transaction(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!( @@ -951,7 +935,7 @@ fn eth_send_raw_transaction(params: Params<'_>, ctx: Arc, ext: &Exte // Logs // ----------------------------------------------------------------------------- -fn eth_get_logs(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_logs(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { const MAX_BLOCK_RANGE: u64 = 5_000; // enter span @@ -1011,7 +995,7 @@ fn eth_accounts(_: Params<'_>, _ctx: &RpcContext, _: &Extensions) -> Result, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_transaction_count(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getTransactionCount", address = field::Empty, filter = field::Empty).entered(); @@ -1032,7 +1016,7 @@ fn eth_get_transaction_count(params: Params<'_>, ctx: Arc, ext: &Ext Ok(hex_num(account.nonce)) } -fn eth_get_balance(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_balance(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getBalance", address = field::Empty, filter = field::Empty).entered(); @@ -1054,7 +1038,7 @@ fn eth_get_balance(params: Params<'_>, ctx: Arc, ext: &Extensions) - Ok(hex_num(account.balance)) } -fn eth_get_code(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_code(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getCode", address = field::Empty, filter = field::Empty).entered(); @@ -1141,7 +1125,7 @@ async fn eth_subscribe(params: Params<'_>, pending: PendingSubscriptionSink, ctx // Storage // ----------------------------------------------------------------------------- -fn eth_get_storage_at(params: Params<'_>, ctx: Arc, ext: &Extensions) -> Result { +fn eth_get_storage_at(params: Params<'_>, ctx: Arc, ext: Extensions) -> Result { // enter span let _middleware_enter = ext.enter_middleware_span(); let _method_enter = info_span!("rpc::eth_getStorageAt", address = field::Empty, index = field::Empty).entered(); diff --git a/src/infra/metrics/metrics_definitions.rs b/src/infra/metrics/metrics_definitions.rs index 1d6b49882..df03fefe2 100644 --- a/src/infra/metrics/metrics_definitions.rs +++ b/src/infra/metrics/metrics_definitions.rs @@ -14,10 +14,7 @@ metrics! { histogram_duration rpc_requests_finished{client, method, contract, function, result, result_code, success}, "Number of JSON-RPC subscriptions active right now." - gauge rpc_subscriptions_active{subscription, client}, - - "Number of times we respons a client with an error." - counter rpc_error_response{error_type, client, method} + gauge rpc_subscriptions_active{subscription, client} } // Storage reads.