diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index bddd53b39f8c4..80db593acbfa4 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -271,6 +271,10 @@ impl Client { Ok(SubstrateChainClient::::finalized_head(&*client).await?) }) .await + .map_err(|e| Error::FailedToReadBestFinalizedHeaderHash { + chain: C::NAME.into(), + error: e.boxed(), + }) } /// Return number of the best finalized block. @@ -292,6 +296,7 @@ impl Client { Ok(SubstrateChainClient::::header(&*client, None).await?) }) .await + .map_err(|e| Error::FailedToReadBestHeader { chain: C::NAME.into(), error: e.boxed() }) } /// Get a Substrate block from its hash. @@ -311,6 +316,11 @@ impl Client { Ok(SubstrateChainClient::::header(&*client, Some(block_hash)).await?) }) .await + .map_err(|e| Error::FailedToReadHeaderByHash { + chain: C::NAME.into(), + hash: format!("{block_hash}"), + error: e.boxed(), + }) } /// Get a Substrate block hash by its number. @@ -394,10 +404,17 @@ impl Client { storage_key: StorageKey, block_hash: Option, ) -> Result> { + let cloned_storage_key = storage_key.clone(); self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::storage(&*client, storage_key, block_hash).await?) + Ok(SubstrateStateClient::::storage(&*client, storage_key.clone(), block_hash) + .await?) }) .await + .map_err(|e| Error::FailedToReadRuntimeStorageValue { + chain: C::NAME.into(), + key: cloned_storage_key, + error: e.boxed(), + }) } /// Return native tokens balance of the account. @@ -640,7 +657,14 @@ impl Client { input: Input, at_block: Option, ) -> Result { - let encoded_output = self.state_call(method_name, Bytes(input.encode()), at_block).await?; + let encoded_output = self + .state_call(method_name.clone(), Bytes(input.encode()), at_block) + .await + .map_err(|e| Error::ErrorExecutingRuntimeCall { + chain: C::NAME.into(), + method: method_name, + error: e.boxed(), + })?; Output::decode(&mut &encoded_output.0[..]).map_err(Error::ResponseParseFailed) } diff --git a/bridges/relays/client-substrate/src/error.rs b/bridges/relays/client-substrate/src/error.rs index 3b83f917bf8e8..54247c5dc1768 100644 --- a/bridges/relays/client-substrate/src/error.rs +++ b/bridges/relays/client-substrate/src/error.rs @@ -20,6 +20,7 @@ use bp_polkadot_core::parachains::ParaId; use jsonrpsee::core::Error as RpcError; use relay_utils::MaybeConnectionError; use sc_rpc_api::system::Health; +use sp_core::storage::StorageKey; use sp_runtime::transaction_validity::TransactionValidityError; use thiserror::Error; @@ -55,6 +56,52 @@ pub enum Error { /// The client we're connected to is not synced, so we can't rely on its state. #[error("Substrate client is not synced {0}.")] ClientNotSynced(Health), + /// Failed to read best finalized header hash from given chain. + #[error("Failed to read best finalized header hash of {chain}: {error:?}.")] + FailedToReadBestFinalizedHeaderHash { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read best finalized header from given chain. + #[error("Failed to read best header of {chain}: {error:?}.")] + FailedToReadBestHeader { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read header by hash from given chain. + #[error("Failed to read header {hash} of {chain}: {error:?}.")] + FailedToReadHeaderByHash { + /// Name of the chain where the error has happened. + chain: String, + /// Hash of the header we've tried to read. + hash: String, + /// Underlying error. + error: Box, + }, + /// Failed to execute runtime call at given chain. + #[error("Failed to execute runtime call {method} at {chain}: {error:?}.")] + ErrorExecutingRuntimeCall { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime method name. + method: String, + /// Underlying error. + error: Box, + }, + /// Failed to read sotrage value at given chain. + #[error("Failed to read storage value {key:?} at {chain}: {error:?}.")] + FailedToReadRuntimeStorageValue { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime storage key + key: StorageKey, + /// Underlying error. + error: Box, + }, /// The bridge pallet is halted and all transactions will be rejected. #[error("Bridge pallet is halted.")] BridgePalletIsHalted, @@ -81,16 +128,28 @@ impl From for Error { } } +impl Error { + /// Box the error. + pub fn boxed(self) -> Box { + Box::new(self) + } +} + impl MaybeConnectionError for Error { fn is_connection_error(&self) -> bool { - matches!( - *self, + match *self { Error::RpcError(RpcError::Transport(_)) // right now if connection to the ws server is dropped (after it is already established), // we're getting this error | Error::RpcError(RpcError::Internal(_)) | Error::RpcError(RpcError::RestartNeeded(_)) - | Error::ClientNotSynced(_), - ) + | Error::ClientNotSynced(_) => true, + Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => error.is_connection_error(), + Error::FailedToReadBestHeader { ref error, .. } => error.is_connection_error(), + Error::FailedToReadHeaderByHash { ref error, .. } => error.is_connection_error(), + Error::ErrorExecutingRuntimeCall { ref error, .. } => error.is_connection_error(), + Error::FailedToReadRuntimeStorageValue { ref error, .. } => error.is_connection_error(), + _ => false, + } } }