Simplify runtime api error handling (#8114)

* Ahh

* Work work work

* Fix all the compilation errors

* Fix test

* More fixes...
This commit is contained in:
Bastian Köcher
2021-02-15 12:55:40 +01:00
committed by GitHub
parent b5e692104c
commit 33f9becf41
48 changed files with 270 additions and 415 deletions
+42 -60
View File
@@ -67,7 +67,7 @@ pub use sp_std::{slice, mem};
#[cfg(feature = "std")]
use sp_std::result;
#[doc(hidden)]
pub use codec::{Encode, Decode, DecodeLimit};
pub use codec::{Encode, Decode, DecodeLimit, self};
use sp_core::OpaqueMetadata;
#[cfg(feature = "std")]
use std::{panic::UnwindSafe, cell::RefCell};
@@ -246,8 +246,8 @@ pub use sp_api_proc_macro::impl_runtime_apis;
/// and the error type can be specified as associated type. If no error type is specified [`String`]
/// is used as error type.
///
/// Besides implementing the given traits, the [`Core`](sp_api::Core), [`ApiExt`](sp_api::ApiExt)
/// and [`ApiErrorExt`](sp_api::ApiErrorExt) are implemented automatically.
/// Besides implementing the given traits, the [`Core`](sp_api::Core) and [`ApiExt`](sp_api::ApiExt)
/// are implemented automatically.
///
/// # Example
///
@@ -284,11 +284,6 @@ pub use sp_api_proc_macro::impl_runtime_apis;
/// }
///
/// impl BlockBuilder<Block> for MockApi {
/// /// Sets the error type that is being used by the mock implementation.
/// /// The error type is used by all runtime apis. It is only required to
/// /// be specified in one trait implementation.
/// type Error = sp_api::ApiError;
///
/// fn build_block() -> Block {
/// unimplemented!("Not Required in tests")
/// }
@@ -331,15 +326,14 @@ pub use sp_api_proc_macro::impl_runtime_apis;
///
/// sp_api::mock_impl_runtime_apis! {
/// impl Balance<Block> for MockApi {
/// type Error = sp_api::ApiError;
/// #[advanced]
/// fn get_balance(&self, at: &BlockId<Block>) -> Result<NativeOrEncoded<u64>, Self::Error> {
/// fn get_balance(&self, at: &BlockId<Block>) -> Result<NativeOrEncoded<u64>, sp_api::ApiError> {
/// println!("Being called at: {}", at);
///
/// Ok(self.balance.into())
/// }
/// #[advanced]
/// fn set_balance(at: &BlockId<Block>, val: u64) -> Result<NativeOrEncoded<()>, Self::Error> {
/// fn set_balance(at: &BlockId<Block>, val: u64) -> Result<NativeOrEncoded<()>, sp_api::ApiError> {
/// if let BlockId::Number(1) = at {
/// println!("Being called to set balance to: {}", val);
/// }
@@ -393,46 +387,35 @@ pub trait ConstructRuntimeApi<Block: BlockT, C: CallApiAt<Block>> {
}
/// An error describing which API call failed.
#[cfg_attr(feature = "std", derive(Debug, thiserror::Error, Eq, PartialEq))]
#[cfg_attr(feature = "std", error("Failed to execute API call {tag}"))]
#[cfg(feature = "std")]
pub struct ApiError {
tag: &'static str,
#[source]
error: codec::Error,
}
#[cfg(feature = "std")]
impl From<(&'static str, codec::Error)> for ApiError {
fn from((tag, error): (&'static str, codec::Error)) -> Self {
Self {
tag,
error,
}
}
}
#[cfg(feature = "std")]
impl ApiError {
pub fn new(tag: &'static str, error: codec::Error) -> Self {
Self {
tag,
error,
}
}
}
/// Extends the runtime api traits with an associated error type. This trait is given as super
/// trait to every runtime api trait.
#[cfg(feature = "std")]
pub trait ApiErrorExt {
/// Error type used by the runtime apis.
type Error: std::fmt::Debug + From<ApiError>;
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error("Failed to decode return value of {function}")]
FailedToDecodeReturnValue {
function: &'static str,
#[source]
error: codec::Error,
},
#[error("Failed to convert return value from runtime to node of {function}")]
FailedToConvertReturnValue {
function: &'static str,
#[source]
error: codec::Error,
},
#[error("Failed to convert parameter `{parameter}` from node to runtime of {function}")]
FailedToConvertParameter {
function: &'static str,
parameter: &'static str,
#[source]
error: codec::Error,
},
#[error(transparent)]
Application(#[from] Box<dyn std::error::Error + Send + Sync>),
}
/// Extends the runtime api implementation with some common functionality.
#[cfg(feature = "std")]
pub trait ApiExt<Block: BlockT>: ApiErrorExt {
pub trait ApiExt<Block: BlockT> {
/// The state backend that is used to store the block states.
type StateBackend: StateBackend<HashFor<Block>>;
@@ -450,14 +433,14 @@ pub trait ApiExt<Block: BlockT>: ApiErrorExt {
fn has_api<A: RuntimeApiInfo + ?Sized>(
&self,
at: &BlockId<Block>,
) -> Result<bool, Self::Error> where Self: Sized;
) -> Result<bool, ApiError> where Self: Sized;
/// Check if the given api is implemented and the version passes a predicate.
fn has_api_with<A: RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
at: &BlockId<Block>,
pred: P,
) -> Result<bool, Self::Error> where Self: Sized;
) -> Result<bool, ApiError> where Self: Sized;
/// Start recording all accessed trie nodes for generating proofs.
fn record_proof(&mut self);
@@ -478,7 +461,10 @@ pub trait ApiExt<Block: BlockT>: ApiErrorExt {
backend: &Self::StateBackend,
changes_trie_state: Option<&ChangesTrieState<HashFor<Block>, NumberFor<Block>>>,
parent_hash: Block::Hash,
) -> Result<StorageChanges<Self::StateBackend, Block>, String> where Self: Sized;
) -> Result<
StorageChanges<Self::StateBackend, Block>,
String
> where Self: Sized;
}
/// Before calling any runtime api function, the runtime need to be initialized
@@ -533,9 +519,6 @@ pub struct CallApiAtParams<'a, Block: BlockT, C, NC, Backend: StateBackend<HashF
/// Something that can call into the an api at a given block.
#[cfg(feature = "std")]
pub trait CallApiAt<Block: BlockT> {
/// Error type used by the implementation.
type Error: std::fmt::Debug + From<ApiError>;
/// The state backend that is used to store the block states.
type StateBackend: StateBackend<HashFor<Block>>;
@@ -544,15 +527,18 @@ pub trait CallApiAt<Block: BlockT> {
fn call_api_at<
'a,
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
C: Core<Block, Error = Self::Error>,
NC: FnOnce() -> result::Result<R, ApiError> + UnwindSafe,
C: Core<Block>,
>(
&self,
params: CallApiAtParams<'a, Block, C, NC, Self::StateBackend>,
) -> Result<NativeOrEncoded<R>, Self::Error>;
) -> Result<NativeOrEncoded<R>, ApiError>;
/// Returns the runtime version at the given block.
fn runtime_version_at(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, Self::Error>;
fn runtime_version_at(
&self,
at: &BlockId<Block>,
) -> Result<RuntimeVersion, ApiError>;
}
/// Auxiliary wrapper that holds an api instance and binds it to the given lifetime.
@@ -605,10 +591,6 @@ pub trait RuntimeApiInfo {
const VERSION: u32;
}
/// Extracts the `Api::Error` for a type that provides a runtime api.
#[cfg(feature = "std")]
pub type ApiErrorFor<T, Block> = <<T as ProvideRuntimeApi<Block>>::Api as ApiErrorExt>::Error;
#[derive(codec::Encode, codec::Decode)]
pub struct OldRuntimeVersion {
pub spec_name: RuntimeString,