diff --git a/Cargo.lock b/Cargo.lock index b4443dd438490..a48998b76be3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6574,6 +6574,7 @@ dependencies = [ "sp-version", "substrate-prometheus-endpoint", "substrate-test-runtime", + "thiserror", ] [[package]] @@ -6837,6 +6838,7 @@ dependencies = [ "sp-state-machine", "sp-trie", "substrate-test-runtime-client", + "thiserror", ] [[package]] @@ -6897,14 +6899,13 @@ name = "sc-executor-common" version = "0.8.0" dependencies = [ "derive_more", - "log", "parity-scale-codec", "parity-wasm 0.41.0", "sp-allocator", "sp-core", - "sp-runtime-interface", "sp-serializer", "sp-wasm-interface", + "thiserror", "wasmi", ] @@ -7328,7 +7329,6 @@ name = "sc-service" version = "0.8.0" dependencies = [ "async-std", - "derive_more", "directories 3.0.1", "exit-future", "futures 0.1.30", @@ -7387,6 +7387,7 @@ dependencies = [ "substrate-test-runtime", "substrate-test-runtime-client", "tempfile", + "thiserror", "tokio 0.2.23", "tracing", "tracing-futures", @@ -7440,6 +7441,7 @@ dependencies = [ "parking_lot 0.10.2", "sc-client-api", "sp-core", + "thiserror", ] [[package]] @@ -7458,6 +7460,7 @@ dependencies = [ "serde_json", "sp-blockchain", "sp-runtime", + "thiserror", ] [[package]] @@ -7519,6 +7522,7 @@ dependencies = [ "sp-transaction-pool", "sp-utils", "substrate-test-runtime", + "thiserror", "wasm-timer", ] @@ -7527,7 +7531,6 @@ name = "sc-transaction-pool" version = "2.0.0" dependencies = [ "assert_matches", - "derive_more", "futures 0.3.8", "futures-diagnose", "hex", @@ -7551,6 +7554,7 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-test-runtime-client", "substrate-test-runtime-transaction-pool", + "thiserror", "wasm-timer", ] @@ -7992,6 +7996,7 @@ dependencies = [ "sp-std", "sp-test-primitives", "sp-version", + "thiserror", ] [[package]] @@ -8110,10 +8115,12 @@ dependencies = [ name = "sp-blockchain" version = "2.0.0" dependencies = [ + "futures 0.3.8", "log", "lru 0.6.1", "parity-scale-codec", "parking_lot 0.10.2", + "sp-api", "sp-consensus", "sp-database", "sp-runtime", @@ -8669,6 +8676,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-runtime", + "thiserror", ] [[package]] diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 7d770912f271f..07036bfb414a2 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -46,3 +46,4 @@ prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0. kvdb-memorydb = "0.7.0" sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } +thiserror = "1.0.21" diff --git a/client/api/src/light.rs b/client/api/src/light.rs index 144851dac0075..f9ba64544a8c0 100644 --- a/client/api/src/light.rs +++ b/client/api/src/light.rs @@ -312,13 +312,21 @@ pub mod tests { use sp_test_primitives::{Block, Header, Extrinsic}; use super::*; + #[derive(Debug, thiserror::Error)] + #[error("Not implemented on test node")] + struct MockError; + + impl Into for MockError { + fn into(self) -> ClientError { + ClientError::Application(Box::new(self)) + } + } + pub type OkCallFetcher = Mutex>; - fn not_implemented_in_tests() -> Ready> - where - E: std::convert::From<&'static str>, + fn not_implemented_in_tests() -> Ready> { - futures::future::ready(Err("Not implemented on test node".into())) + futures::future::ready(Err(MockError.into())) } impl Fetcher for OkCallFetcher { diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index 2fe7ba72ec7b9..8c022ef3a9741 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -208,10 +208,7 @@ impl sp_consensus::Proposer for })); async move { - match rx.await { - Ok(x) => x, - Err(err) => Err(sp_blockchain::Error::Msg(err.to_string())) - } + rx.await? }.boxed() } } diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index 8a38bb8478003..cc1431ea349bf 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -212,7 +212,7 @@ where &state, changes_trie_state.as_ref(), parent_hash, - )?; + ).map_err(|e| sp_blockchain::Error::StorageChanges(e))?; Ok(BuiltBlock { block: ::new(header, self.extrinsics), diff --git a/client/cli/src/error.rs b/client/cli/src/error.rs index 36c963f3e8c97..5190cae2c2ff8 100644 --- a/client/cli/src/error.rs +++ b/client/cli/src/error.rs @@ -25,35 +25,32 @@ pub type Result = std::result::Result; /// Error type for the CLI. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum Error { - /// Io error #[error(transparent)] Io(#[from] std::io::Error), - /// Cli error + #[error(transparent)] Cli(#[from] structopt::clap::Error), - /// Service error + #[error(transparent)] Service(#[from] sc_service::Error), - /// Client error + #[error(transparent)] Client(#[from] sp_blockchain::Error), - /// scale codec error + #[error(transparent)] Codec(#[from] parity_scale_codec::Error), - /// Input error + #[error("Invalid input: {0}")] Input(String), - /// Invalid listen multiaddress + #[error("Invalid listen multiaddress")] InvalidListenMultiaddress, - /// Application specific error chain sequence forwarder. - #[error(transparent)] - Application(#[from] Box), - /// URI error. + #[error("Invalid URI; expecting either a secret URI or a public URI.")] InvalidUri(crypto::PublicError), - /// Signature length mismatch. + #[error("Signature has an invalid length. Read {read} bytes, expected {expected} bytes")] SignatureInvalidLength { /// Amount of signature bytes read. @@ -61,28 +58,28 @@ pub enum Error { /// Expected number of signature bytes. expected: usize, }, - /// Missing base path argument. + #[error("The base path is missing, please provide one")] MissingBasePath, - /// Unknown key type specifier or missing key type specifier. + #[error("Unknown key type, must be a known 4-character sequence")] KeyTypeInvalid, - /// Signature verification failed. + #[error("Signature verification failed")] SignatureInvalid, - /// Storing a given key failed. + #[error("Key store operation failed")] KeyStoreOperation, - /// An issue with the underlying key storage was encountered. + #[error("Key storage issue encountered")] KeyStorage(#[from] sc_keystore::Error), - /// Bytes are not decodable when interpreted as hexadecimal string. - #[error("Invalid hex base data")] + + #[error("Invalid hexadecimal string data")] HexDataConversion(#[from] hex::FromHexError), - /// Shortcut type to specify types on the fly, discouraged. - #[deprecated = "Use `Forwarded` with an error type instead."] - #[error("Other: {0}")] - Other(String), + + /// Application specific error chain sequence forwarder. + #[error(transparent)] + Application(#[from] Box), } impl std::convert::From<&str> for Error { @@ -93,7 +90,7 @@ impl std::convert::From<&str> for Error { impl std::convert::From for Error { fn from(s: String) -> Error { - Error::Input(s.to_string()) + Error::Input(s) } } diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index d07ef49835b2b..e8bd1f33631ea 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -31,7 +31,8 @@ sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } futures = "0.3.4" futures-timer = "3.0.1" parking_lot = "0.10.0" -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index d8601a7c12c68..ab8fc16007ce9 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -20,7 +20,8 @@ //! time during which certain events can and/or must occur. This crate //! provides generic functionality for slots. -#![forbid(unsafe_code, missing_docs)] +#![forbid(unsafe_code)] +#![deny(missing_docs)] mod slots; mod aux_schema; @@ -470,6 +471,15 @@ pub enum CheckedHeader { Checked(H, S), } + + +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error where T: SlotData + Clone + Debug + Send + Sync + 'static { + #[error("Slot duration is invalid: {0:?}")] + SlotDurationInvalid(SlotDuration), +} + /// A slot duration. Create with `get_or_compute`. // The internal member should stay private here to maintain invariants of // `get_or_compute`. @@ -494,7 +504,7 @@ impl SlotData for SlotDuration { const SLOT_KEY: &'static [u8] = T::SLOT_KEY; } -impl SlotDuration { +impl SlotDuration { /// Either fetch the slot duration from disk or compute it from the /// genesis state. /// @@ -532,10 +542,8 @@ impl SlotDuration { } }?; - if slot_duration.slot_duration() == 0 { - return Err(sp_blockchain::Error::Msg( - "Invalid value for slot_duration: the value must be greater than 0.".into(), - )) + if slot_duration.slot_duration() == 0u64 { + return Err(sp_blockchain::Error::Application(Box::new(Error::SlotDurationInvalid(slot_duration)))) } Ok(slot_duration) @@ -939,7 +947,7 @@ mod test { true, true, true, true, ]; - assert_eq!(backoff, expected); + assert_eq!(backoff.as_slice(), &expected[..]); } #[test] diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 983459cfebe33..8254e652f68b0 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -891,9 +891,7 @@ impl Backend { let is_archive_pruning = config.pruning.is_archive(); let blockchain = BlockchainDb::new(db.clone())?; let meta = blockchain.meta.clone(); - let map_e = |e: sc_state_db::Error| sp_blockchain::Error::from( - format!("State database error: {:?}", e) - ); + let map_e = |e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e); let state_db: StateDb<_, _> = StateDb::new( config.pruning.clone(), !config.source.supports_ref_counting(), @@ -1082,7 +1080,7 @@ impl Backend { trace!(target: "db", "Canonicalize block #{} ({:?})", new_canonical, hash); let commit = self.storage.state_db.canonicalize_block(&hash) - .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from(format!("State database error: {:?}", e)))?; + .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; apply_state_commit(transaction, commit); }; @@ -1212,9 +1210,7 @@ impl Backend { number_u64, &pending_block.header.parent_hash(), changeset, - ).map_err(|e: sc_state_db::Error| - sp_blockchain::Error::from(format!("State database error: {:?}", e)) - )?; + ).map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; apply_state_commit(&mut transaction, commit); // Check if need to finalize. Genesis is always finalized instantly. @@ -1379,7 +1375,7 @@ impl Backend { transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key); let commit = self.storage.state_db.canonicalize_block(&f_hash) - .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from(format!("State database error: {:?}", e)))?; + .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; apply_state_commit(transaction, commit); if !f_num.is_zero() { diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index 64ed23598f47c..8501144a9a989 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -14,7 +14,6 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.8" derive_more = "0.99.2" parity-wasm = "0.41.0" codec = { package = "parity-scale-codec", version = "1.3.4" } @@ -22,8 +21,8 @@ wasmi = "0.6.2" sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } -sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } sp-serializer = { version = "2.0.0", path = "../../../primitives/serializer" } +thiserror = "1.0.21" [features] default = [] diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index caed63c183e68..caf6159da0727 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -25,92 +25,95 @@ use wasmi; pub type Result = std::result::Result; /// Error type. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum Error { /// Unserializable Data - InvalidData(sp_serializer::Error), + #[error("Unserializable data encountered")] + InvalidData(#[from] sp_serializer::Error), /// Trap occurred during execution - Trap(wasmi::Trap), + #[error(transparent)] + Trap(#[from] wasmi::Trap), /// Wasmi loading/instantiating error - Wasmi(wasmi::Error), + #[error(transparent)] + Wasmi(#[from] wasmi::Error), /// Error in the API. Parameter is an error message. - #[from(ignore)] + #[error("API Error: {0}")] ApiError(String), /// Method is not found - #[display(fmt="Method not found: '{}'", _0)] - #[from(ignore)] + #[error("Method not found: '{0}'")] MethodNotFound(String), /// Code is invalid (expected single byte) - #[display(fmt="Invalid Code: {}", _0)] - #[from(ignore)] + #[error("Invalid Code: '{0}'")] InvalidCode(String), /// Could not get runtime version. - #[display(fmt="On-chain runtime does not specify version")] + #[error("On-chain runtime does not specify version")] VersionInvalid, /// Externalities have failed. - #[display(fmt="Externalities error")] + #[error("Externalities error")] Externalities, /// Invalid index. - #[display(fmt="Invalid index provided")] + #[error("Invalid index provided")] InvalidIndex, /// Invalid return type. - #[display(fmt="Invalid type returned (should be u64)")] + #[error("Invalid type returned (should be u64)")] InvalidReturn, /// Runtime failed. - #[display(fmt="Runtime error")] + #[error("Runtime error")] Runtime, /// Runtime panicked. - #[display(fmt="Runtime panicked: {}", _0)] - #[from(ignore)] + #[error("Runtime panicked: {0}")] RuntimePanicked(String), /// Invalid memory reference. - #[display(fmt="Invalid memory reference")] + #[error("Invalid memory reference")] InvalidMemoryReference, /// The runtime must provide a global named `__heap_base` of type i32 for specifying where the /// allocator is allowed to place its data. - #[display(fmt="The runtime doesn't provide a global named `__heap_base`")] + #[error("The runtime doesn't provide a global named `__heap_base`")] HeapBaseNotFoundOrInvalid, /// The runtime WebAssembly module is not allowed to have the `start` function. - #[display(fmt="The runtime has the `start` function")] + #[error("The runtime has the `start` function")] RuntimeHasStartFn, /// Some other error occurred + #[error("Other: {0}")] Other(String), /// Some error occurred in the allocator - #[display(fmt="Error in allocator: {}", _0)] - Allocator(sp_allocator::Error), + #[error("Allocation Error")] + Allocator(#[from] sp_allocator::Error), /// Execution of a host function failed. - #[display(fmt="Host function {} execution failed with: {}", _0, _1)] + #[error("Host function {0} execution failed with: {1}")] FunctionExecution(String, String), /// No table is present. /// /// Call was requested that requires table but none was present in the instance. - #[display(fmt="No table exported by wasm blob")] + #[error("No table exported by wasm blob")] NoTable, /// No table entry is present. /// /// Call was requested that requires specific entry in the table to be present. - #[display(fmt="No table entry with index {} in wasm blob exported table", _0)] - #[from(ignore)] + #[error("No table entry with index {0} in wasm blob exported table")] NoTableEntryWithIndex(u32), /// Table entry is not a function. - #[display(fmt="Table element with index {} is not a function in wasm blob exported table", _0)] - #[from(ignore)] + #[error("Table element with index {0} is not a function in wasm blob exported table")] TableElementIsNotAFunction(u32), /// Function in table is null and thus cannot be called. - #[display(fmt="Table entry with index {} in wasm blob is null", _0)] - #[from(ignore)] + #[error("Table entry with index {0} in wasm blob is null")] FunctionRefIsNull(u32), -} -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::InvalidData(ref err) => Some(err), - Error::Trap(ref err) => Some(err), - Error::Wasmi(ref err) => Some(err), - _ => None, - } - } + #[error(transparent)] + RuntimeConstruction(#[from] WasmError), + + #[error("Shared memory is not supported")] + SharedMemUnsupported, + + #[error("Imported globals are not supported yet")] + ImportedGlobalsUnsupported, + + #[error("initializer expression can have only up to 2 expressions in wasm 1.0")] + InitializerHasTooManyExpressions, + + #[error("Invalid initializer expression provided {0}")] + InvalidInitializerExpression(String), } impl wasmi::HostError for Error {} @@ -121,9 +124,9 @@ impl From<&'static str> for Error { } } -impl From for Error { - fn from(err: WasmError) -> Error { - Error::Other(err.to_string()) +impl From for Error { + fn from(err: String) -> Error { + Error::Other(err) } } @@ -151,3 +154,5 @@ pub enum WasmError { /// Other error happenend. Other(String), } + +impl std::error::Error for WasmError {} diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs index 7f3864e6152fb..df839d4ab6523 100644 --- a/client/executor/common/src/lib.rs +++ b/client/executor/common/src/lib.rs @@ -17,6 +17,7 @@ //! A set of common definitions that are needed for defining execution engines. #![warn(missing_docs)] +#![deny(unused_crate_dependencies)] pub mod error; pub mod sandbox; diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/util.rs index 92a48e1401814..564f9dadcbec6 100644 --- a/client/executor/common/src/util.rs +++ b/client/executor/common/src/util.rs @@ -87,15 +87,12 @@ impl DataSegmentsSnapshot { let init_expr = match segment.offset() { Some(offset) => offset.code(), // Return if the segment is passive - None => return Err(Error::from("Shared memory is not supported".to_string())), + None => return Err(Error::SharedMemUnsupported), }; // [op, End] if init_expr.len() != 2 { - return Err(Error::from( - "initializer expression can have only up to 2 expressions in wasm 1.0" - .to_string(), - )); + return Err(Error::InitializerHasTooManyExpressions); } let offset = match &init_expr[0] { Instruction::I32Const(v) => *v as u32, @@ -106,15 +103,10 @@ impl DataSegmentsSnapshot { // At the moment of writing the Substrate Runtime Interface does not provide // any globals. There is nothing that prevents us from supporting this // if/when we gain those. - return Err(Error::from( - "Imported globals are not supported yet".to_string(), - )); + return Err(Error::ImportedGlobalsUnsupported); } insn => { - return Err(Error::from(format!( - "{:?} is not supported as initializer expression in wasm 1.0", - insn - ))) + return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))) } }; diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index c8b763a6b1936..08771847c25fd 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -523,7 +523,7 @@ fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] -#[should_panic(expected = "Allocator ran out of space")] +#[should_panic] fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); diff --git a/client/light/src/call_executor.rs b/client/light/src/call_executor.rs index fa0f02cd5aed9..458ea2bd6b844 100644 --- a/client/light/src/call_executor.rs +++ b/client/light/src/call_executor.rs @@ -276,7 +276,8 @@ pub fn check_execution_proof_with_make_header( // TODO: Remove when solved: https://github.com/paritytech/substrate/issues/5047 let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_backend); - let runtime_code = backend_runtime_code.runtime_code()?; + let runtime_code = backend_runtime_code.runtime_code() + .map_err(|_e| ClientError::RuntimeCodeMissing)?; execution_proof_check_on_trie_backend::( &trie_backend, diff --git a/client/light/src/fetcher.rs b/client/light/src/fetcher.rs index 33113c2fc7df0..60fce87b8d0c2 100644 --- a/client/light/src/fetcher.rs +++ b/client/light/src/fetcher.rs @@ -239,7 +239,7 @@ impl FetchChecker for LightDataChecker convert_hash(request.header.state_root()), remote_proof, request.keys.iter(), - ).map_err(Into::into) + ).map_err(|e| ClientError::from(e)) } fn check_read_child_proof( @@ -249,14 +249,14 @@ impl FetchChecker for LightDataChecker ) -> ClientResult, Option>>> { let child_info = match ChildType::from_prefixed_key(&request.storage_key) { Some((ChildType::ParentKeyId, storage_key)) => ChildInfo::new_default(storage_key), - None => return Err("Invalid child type".into()), + None => return Err(ClientError::InvalidChildType), }; read_child_proof_check::( convert_hash(request.header.state_root()), remote_proof, &child_info, request.keys.iter(), - ).map_err(Into::into) + ).map_err(|e| ClientError::from(e)) } fn check_execution_proof( @@ -292,10 +292,10 @@ impl FetchChecker for LightDataChecker if *request.header.extrinsics_root() == extrinsics_root { Ok(body) } else { - Err(format!("RemoteBodyRequest: invalid extrinsics root expected: {} but got {}", - *request.header.extrinsics_root(), - extrinsics_root, - ).into()) + Err(ClientError::ExtrinsicRootInvalid { + received: request.header.extrinsics_root().to_string(), + expected: extrinsics_root.to_string(), + }) } } diff --git a/client/network/src/light_client_handler.rs b/client/network/src/light_client_handler.rs index b72362fdfc362..919accbe6f235 100644 --- a/client/network/src/light_client_handler.rs +++ b/client/network/src/light_client_handler.rs @@ -627,7 +627,7 @@ where let prefixed_key = PrefixedStorageKey::new_ref(&request.storage_key); let child_info = match ChildType::from_prefixed_key(prefixed_key) { Some((ChildType::ParentKeyId, storage_key)) => Ok(ChildInfo::new_default(storage_key)), - None => Err("Invalid child storage key".into()), + None => Err(sp_blockchain::Error::InvalidChildStorageKey), }; let proof = match child_info.and_then(|child_info| self.chain.read_child_proof( &BlockId::Hash(block), diff --git a/client/network/src/on_demand_layer.rs b/client/network/src/on_demand_layer.rs index 084172ee57c4f..6e0add18adb02 100644 --- a/client/network/src/on_demand_layer.rs +++ b/client/network/src/on_demand_layer.rs @@ -51,6 +51,17 @@ pub struct OnDemand { requests_send: TracingUnboundedSender>, } + +#[derive(Debug, thiserror::Error)] +#[error("AlwaysBadChecker")] +struct ErrorAlwaysBadChecker; + +impl Into for ErrorAlwaysBadChecker { + fn into(self) -> ClientError { + ClientError::Application(Box::new(self)) + } +} + /// Dummy implementation of `FetchChecker` that always assumes that responses are bad. /// /// Considering that it is the responsibility of the client to build the fetcher, it can use this @@ -65,7 +76,7 @@ impl FetchChecker for AlwaysBadChecker { _remote_header: Option, _remote_proof: StorageProof, ) -> Result { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } fn check_read_proof( @@ -73,7 +84,7 @@ impl FetchChecker for AlwaysBadChecker { _request: &RemoteReadRequest, _remote_proof: StorageProof, ) -> Result,Option>>, ClientError> { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } fn check_read_child_proof( @@ -81,7 +92,7 @@ impl FetchChecker for AlwaysBadChecker { _request: &RemoteReadChildRequest, _remote_proof: StorageProof, ) -> Result, Option>>, ClientError> { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } fn check_execution_proof( @@ -89,7 +100,7 @@ impl FetchChecker for AlwaysBadChecker { _request: &RemoteCallRequest, _remote_proof: StorageProof, ) -> Result, ClientError> { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } fn check_changes_proof( @@ -97,7 +108,7 @@ impl FetchChecker for AlwaysBadChecker { _request: &RemoteChangesRequest, _remote_proof: ChangesProof ) -> Result, u32)>, ClientError> { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } fn check_body_proof( @@ -105,7 +116,7 @@ impl FetchChecker for AlwaysBadChecker { _request: &RemoteBodyRequest, _body: Vec ) -> Result, ClientError> { - Err(ClientError::Msg("AlwaysBadChecker".into())) + Err(ErrorAlwaysBadChecker.into()) } } diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index fda73cea27110..a1b9fbc4eebc5 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -541,7 +541,7 @@ impl ChildStateBackend for FullState ChildInfo::new_default(storage_key), - None => return Err("Invalid child storage key".into()), + None => return Err(sp_blockchain::Error::InvalidChildStorageKey), }; self.client.child_storage_keys( &BlockId::Hash(block), @@ -563,7 +563,7 @@ impl ChildStateBackend for FullState ChildInfo::new_default(storage_key), - None => return Err("Invalid child storage key".into()), + None => return Err(sp_blockchain::Error::InvalidChildStorageKey), }; self.client.child_storage( &BlockId::Hash(block), @@ -585,7 +585,7 @@ impl ChildStateBackend for FullState ChildInfo::new_default(storage_key), - None => return Err("Invalid child storage key".into()), + None => return Err(sp_blockchain::Error::InvalidChildStorageKey), }; self.client.child_storage_hash( &BlockId::Hash(block), diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index 23af49d38b12a..4350e1a2bf2a9 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -24,7 +24,7 @@ wasmtime = [ test-helpers = [] [dependencies] -derive_more = "0.99.2" +thiserror = "1.0.21" futures01 = { package = "futures", version = "0.1.29" } futures = { version = "0.3.4", features = ["compat"] } jsonrpc-pubsub = "15.1" @@ -32,7 +32,7 @@ jsonrpc-core = "15.1" rand = "0.7.3" parking_lot = "0.10.0" lazy_static = "1.4.0" -log = "0.4.8" +log = "0.4.11" slog = { version = "2.5.2", features = ["nested-values"] } futures-timer = "3.0.1" wasm-timer = "0.2" diff --git a/client/service/src/client/call_executor.rs b/client/service/src/client/call_executor.rs index 164976ecfe871..cd01a5877758d 100644 --- a/client/service/src/client/call_executor.rs +++ b/client/service/src/client/call_executor.rs @@ -137,7 +137,9 @@ where )?; let state = self.backend.state_at(*id)?; let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); - let runtime_code = self.check_override(state_runtime_code.runtime_code()?, id)?; + let runtime_code = state_runtime_code.runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + let runtime_code = self.check_override(runtime_code, id)?; let return_data = StateMachine::new( &state, @@ -211,7 +213,10 @@ where let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_state); // It is important to extract the runtime code here before we create the proof // recorder. - let runtime_code = self.check_override(state_runtime_code.runtime_code()?, at)?; + + let runtime_code = state_runtime_code.runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + let runtime_code = self.check_override(runtime_code, at)?; let backend = sp_state_machine::ProvingBackend::new_with_recorder( trie_state, @@ -236,7 +241,9 @@ where }, None => { let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); - let runtime_code = self.check_override(state_runtime_code.runtime_code()?, at)?; + let runtime_code = state_runtime_code.runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + let runtime_code = self.check_override(runtime_code, at)?; let mut state_machine = StateMachine::new( &state, @@ -273,7 +280,9 @@ where None, ); let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state); - self.executor.runtime_version(&mut ext, &state_runtime_code.runtime_code()?) + let runtime_code = state_runtime_code.runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + self.executor.runtime_version(&mut ext, &runtime_code) .map_err(|e| sp_blockchain::Error::VersionInvalid(format!("{:?}", e)).into()) } @@ -284,6 +293,9 @@ where method: &str, call_data: &[u8] ) -> Result<(Vec, StorageProof), sp_blockchain::Error> { + let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_state); + let runtime_code = state_runtime_code.runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; sp_state_machine::prove_execution_on_trie_backend::<_, _, NumberFor, _, _>( trie_state, overlay, @@ -291,7 +303,7 @@ where self.spawn_handle.clone(), method, call_data, - &sp_state_machine::backend::BackendRuntimeCode::new(trie_state).runtime_code()?, + &runtime_code, ) .map_err(Into::into) } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index e8d748011bc17..f72fcb810769f 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -297,7 +297,8 @@ impl Client where config: ClientConfig, ) -> sp_blockchain::Result { if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { - let genesis_storage = build_genesis_storage.build_storage()?; + let genesis_storage = build_genesis_storage.build_storage() + .map_err(sp_blockchain::Error::Storage)?; let mut op = backend.begin_operation()?; backend.begin_state_operation(&mut op, BlockId::Hash(Default::default()))?; let state_root = op.reset_storage(genesis_storage)?; @@ -880,7 +881,7 @@ impl Client where &state, changes_trie_state.as_ref(), *parent_hash, - )?; + ).map_err(sp_blockchain::Error::Storage)?; if import_block.header.state_root() != &gen_storage_changes.transaction_storage_root diff --git a/client/service/src/client/wasm_override.rs b/client/service/src/client/wasm_override.rs index 1025b9633887d..ba76f7a0fcf29 100644 --- a/client/service/src/client/wasm_override.rs +++ b/client/service/src/client/wasm_override.rs @@ -37,7 +37,8 @@ //! needed must be provided in the given directory. //! use std::{ - fs, collections::{HashMap, hash_map::DefaultHasher}, path::Path, + fs, collections::{HashMap, hash_map::DefaultHasher}, + path::{Path, PathBuf}, hash::Hasher as _, }; use sp_core::traits::FetchRuntimeCode; @@ -82,6 +83,29 @@ impl FetchRuntimeCode for WasmBlob { } } +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum WasmOverrideError { + #[error("Failed to get runtime version: {0}")] + VersionInvalid(String), + + #[error("WASM override IO error")] + Io(PathBuf, #[source] std::io::Error), + + #[error("Overwriting WASM requires a directory where local \ + WASM is stored. {} is not a directory", .0.display())] + NotADirectory(PathBuf), + + #[error("Duplicate WASM Runtimes found: \n{}\n", .0.join("\n") )] + DuplicateRuntime(Vec), +} + +impl From for sp_blockchain::Error { + fn from(err: WasmOverrideError) -> Self { + Self::Application(Box::new(err)) + } +} + /// Scrapes WASM from a folder and returns WASM from that folder /// if the runtime spec version matches. #[derive(Clone, Debug)] @@ -119,16 +143,13 @@ where /// Scrapes a folder for WASM runtimes. /// Returns a hashmap of the runtime version and wasm runtime code. fn scrape_overrides(dir: &Path, executor: &E) -> Result> { + let handle_err = |e: std::io::Error | -> sp_blockchain::Error { - sp_blockchain::Error::Msg(format!("{}", e.to_string())) + WasmOverrideError::Io(dir.to_owned(), e).into() }; if !dir.is_dir() { - return Err(sp_blockchain::Error::Msg(format!( - "Overwriting WASM requires a directory where \ - local WASM is stored. {:?} is not a directory", - dir, - ))); + return Err(WasmOverrideError::NotADirectory(dir.to_owned()).into()); } let mut overrides = HashMap::new(); @@ -149,9 +170,7 @@ where } if !duplicates.is_empty() { - let duplicate_file_list = duplicates.join("\n"); - let msg = format!("Duplicate WASM Runtimes found: \n{}\n", duplicate_file_list); - return Err(sp_blockchain::Error::Msg(msg)); + return Err(WasmOverrideError::DuplicateRuntime(duplicates).into()); } Ok(overrides) @@ -164,7 +183,7 @@ where ) -> Result { let mut ext = BasicExternalities::default(); executor.runtime_version(&mut ext, &code.runtime_code(heap_pages)) - .map_err(|e| sp_blockchain::Error::VersionInvalid(format!("{:?}", e)).into()) + .map_err(|e| WasmOverrideError::VersionInvalid(format!("{:?}", e)).into()) } } @@ -236,14 +255,10 @@ mod tests { let scraped = WasmOverride::scrape_overrides(dir, exec); match scraped { - Err(e) => { - match e { - sp_blockchain::Error::Msg(msg) => { - let is_match = msg - .matches("Duplicate WASM Runtimes found") - .map(ToString::to_string) - .collect::>(); - assert!(is_match.len() >= 1) + Err(sp_blockchain::Error::Application(e)) => { + match e.downcast_ref::() { + Some(WasmOverrideError::DuplicateRuntime(duplicates)) => { + assert_eq!(duplicates.len(), 1); }, _ => panic!("Test should end with Msg Error Variant") } diff --git a/client/service/src/error.rs b/client/service/src/error.rs index ffe1b39405501..3515df78be876 100644 --- a/client/service/src/error.rs +++ b/client/service/src/error.rs @@ -27,25 +27,38 @@ use sp_blockchain; pub type Result = std::result::Result; /// Service errors. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +#[non_exhaustive] pub enum Error { - /// Client error. - Client(sp_blockchain::Error), - /// IO error. - Io(std::io::Error), - /// Consensus error. - Consensus(sp_consensus::Error), - /// Network error. - Network(sc_network::error::Error), - /// Keystore error. - Keystore(sc_keystore::Error), - /// Best chain selection strategy is missing. - #[display(fmt="Best chain selection strategy (SelectChain) is not provided.")] + #[error(transparent)] + Client(#[from] sp_blockchain::Error), + + #[error(transparent)] + Io(#[from] std::io::Error), + + #[error(transparent)] + Consensus(#[from] sp_consensus::Error), + + #[error(transparent)] + Network(#[from] sc_network::error::Error), + + #[error(transparent)] + Keystore(#[from] sc_keystore::Error), + + #[error("Best chain selection strategy (SelectChain) is not provided.")] SelectChainRequired, - /// Tasks executor is missing. - #[display(fmt="Tasks executor hasn't been provided.")] + + #[error("Tasks executor hasn't been provided.")] TaskExecutorRequired, - /// Other error. + + #[error("Prometheus metrics error")] + Prometheus(#[from] prometheus_endpoint::PrometheusError), + + #[error("Application")] + Application(#[from] Box), + + #[error("Other: {0}")] Other(String), } @@ -55,21 +68,8 @@ impl<'a> From<&'a str> for Error { } } -impl From for Error { - fn from(e: prometheus_endpoint::PrometheusError) -> Self { - Error::Other(format!("Prometheus error: {}", e)) - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Client(ref err) => Some(err), - Error::Io(ref err) => Some(err), - Error::Consensus(ref err) => Some(err), - Error::Network(ref err) => Some(err), - Error::Keystore(ref err) => Some(err), - _ => None, - } +impl<'a> From for Error { + fn from(s: String) -> Self { + Error::Other(s) } } diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index 4d3e736d9539e..18facd720db25 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -13,8 +13,9 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +thiserror = "1.0.21" parking_lot = "0.10.0" -log = "0.4.8" +log = "0.4.11" sc-client-api = { version = "2.0.0", path = "../api" } sp-core = { version = "2.0.0", path = "../../primitives/core" } codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } diff --git a/client/sync-state-rpc/Cargo.toml b/client/sync-state-rpc/Cargo.toml index 8da372db94ffc..81204365d0821 100644 --- a/client/sync-state-rpc/Cargo.toml +++ b/client/sync-state-rpc/Cargo.toml @@ -13,6 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +thiserror = "1.0.21" jsonrpc-core = "15.0" jsonrpc-core-client = "15.0" jsonrpc-derive = "15.0" diff --git a/client/sync-state-rpc/src/lib.rs b/client/sync-state-rpc/src/lib.rs index fa433e5e31d2d..573610fb2f610 100644 --- a/client/sync-state-rpc/src/lib.rs +++ b/client/sync-state-rpc/src/lib.rs @@ -17,6 +17,8 @@ //! A RPC handler to create sync states for light clients. //! Currently only usable with BABE + GRANDPA. +#![deny(unused_crate_dependencies)] + use sp_runtime::traits::{Block as BlockT, NumberFor}; use sp_blockchain::HeaderBackend; use std::sync::Arc; @@ -28,12 +30,27 @@ type SharedAuthoritySet = sc_finality_grandpa::SharedAuthoritySet<::Hash, NumberFor>; type SharedEpochChanges = sc_consensus_epochs::SharedEpochChanges; -struct Error(sp_blockchain::Error); +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +enum Error { + #[error(transparent)] + Blockchain(#[from] sp_blockchain::Error), + + #[error("Failed to load the block weight for block {0:?}")] + LoadingBlockWeightFailed(::Hash), + + #[error("JsonRpc error: {0}")] + JsonRpc(String), +} -impl From for jsonrpc_core::Error { - fn from(error: Error) -> Self { +impl From> for jsonrpc_core::Error { + fn from(error: Error) -> Self { + let message = match error { + Error::JsonRpc(s) => s, + _ => error.to_string(), + }; jsonrpc_core::Error { - message: error.0.to_string(), + message, code: jsonrpc_core::ErrorCode::ServerError(1), data: None, } @@ -76,20 +93,16 @@ impl SyncStateRpcHandler } } - fn build_sync_state(&self) -> Result, sp_blockchain::Error> { + fn build_sync_state(&self) -> Result, Error> { let finalized_hash = self.client.info().finalized_hash; let finalized_header = self.client.header(BlockId::Hash(finalized_hash))? - .ok_or_else(|| sp_blockchain::Error::Msg( - format!("Failed to get the header for block {:?}", finalized_hash) - ))?; + .ok_or_else(|| sp_blockchain::Error::MissingHeader(finalized_hash.to_string()))?; let finalized_block_weight = sc_consensus_babe::aux_schema::load_block_weight( - &*self.client, - finalized_hash, - )? - .ok_or_else(|| sp_blockchain::Error::Msg( - format!("Failed to load the block weight for block {:?}", finalized_hash) - ))?; + &*self.client, + finalized_hash, + )? + .ok_or_else(|| Error::LoadingBlockWeightFailed(finalized_hash))?; Ok(sc_chain_spec::LightSyncState { finalized_block_header: finalized_header, @@ -114,15 +127,16 @@ impl SyncStateRpcApi for SyncStateRpcHandler let mut chain_spec = self.chain_spec.cloned_box(); - let sync_state = self.build_sync_state().map_err(Error)?; + let sync_state = self.build_sync_state() + .map_err(map_error::>)?; chain_spec.set_light_sync_state(sync_state.to_serializable()); - let string = chain_spec.as_json(raw).map_err(map_error)?; + let string = chain_spec.as_json(raw).map_err(map_error::)?; - serde_json::from_str(&string).map_err(|err| map_error(err.to_string())) + serde_json::from_str(&string).map_err(|err| map_error::(err)) } } -fn map_error(error: String) -> jsonrpc_core::Error { - Error(sp_blockchain::Error::Msg(error)).into() +fn map_error(error: S) -> jsonrpc_core::Error { + Error::::JsonRpc(error.to_string()).into() } diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index 5db37f5368387..a4d7bc685c99b 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4" } -derive_more = "0.99.2" +thiserror = "1.0.21" futures = { version = "0.3.1", features = ["compat"] } futures-diagnose = "1.0" intervalier = "0.4.0" diff --git a/client/transaction-pool/graph/Cargo.toml b/client/transaction-pool/graph/Cargo.toml index c5850e765fcfa..94c80c6f298a2 100644 --- a/client/transaction-pool/graph/Cargo.toml +++ b/client/transaction-pool/graph/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" +thiserror = "1.0.21" futures = "0.3.4" log = "0.4.8" parking_lot = "0.10.0" diff --git a/client/transaction-pool/graph/src/error.rs b/client/transaction-pool/graph/src/error.rs index 392ddaa39be6f..b599715920be0 100644 --- a/client/transaction-pool/graph/src/error.rs +++ b/client/transaction-pool/graph/src/error.rs @@ -26,28 +26,29 @@ use sp_runtime::transaction_validity::{ pub type Result = std::result::Result; /// Transaction pool error type. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, thiserror::Error, derive_more::From)] +#[allow(missing_docs)] pub enum Error { /// Transaction is not verifiable yet, but might be in the future. - #[display(fmt="Unknown transaction validity: {:?}", _0)] + #[error("Unknown transaction validity: {0:?}")] UnknownTransaction(UnknownTransaction), /// Transaction is invalid. - #[display(fmt="Invalid transaction validity: {:?}", _0)] + #[error("Invalid transaction validity: {0:?}")] InvalidTransaction(InvalidTransaction), /// The transaction validity returned no "provides" tag. /// /// Such transactions are not accepted to the pool, since we use those tags /// to define identity of transactions (occupance of the same "slot"). - #[display(fmt="The transaction does not provide any tags, so the pool can't identify it.")] + #[error("The transaction does not provide any tags, so the pool can't identify it.")] NoTagsProvided, - /// The transaction is temporarily banned. - #[display(fmt="Temporarily Banned")] + + #[error("Temporarily Banned")] TemporarilyBanned, /// The transaction is already in the pool. - #[display(fmt="[{:?}] Already imported", _0)] + #[error("[{0:?}] Already imported")] AlreadyImported(Box), /// The transaction cannot be imported cause it's a replacement and has too low priority. - #[display(fmt="Too low priority ({} > {})", old, new)] + #[error("Too low priority ({0} > {1})", old, new)] TooLowPriority { /// Transaction already in the pool. old: Priority, @@ -55,17 +56,16 @@ pub enum Error { new: Priority }, /// Deps cycle detected and we couldn't import transaction. - #[display(fmt="Cycle Detected")] + #[error("Cycle Detected")] CycleDetected, /// Transaction was dropped immediately after it got inserted. - #[display(fmt="Transaction couldn't enter the pool because of the limit.")] + #[error("Transaction couldn't enter the pool because of the limit.")] ImmediatelyDropped, /// Invalid block id. + #[error("Invlaid block id: {0}")] InvalidBlockId(String), } -impl std::error::Error for Error {} - /// Transaction pool error conversion. pub trait IntoPoolError: ::std::error::Error + Send + Sized { /// Try to extract original `Error` diff --git a/client/transaction-pool/src/error.rs b/client/transaction-pool/src/error.rs index c0f795df1801a..49fc433e320cc 100644 --- a/client/transaction-pool/src/error.rs +++ b/client/transaction-pool/src/error.rs @@ -24,30 +24,22 @@ use sp_transaction_pool::error::Error as TxPoolError; pub type Result = std::result::Result; /// Transaction pool error type. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum Error { - /// Pool error. - Pool(TxPoolError), - /// Blockchain error. - Blockchain(sp_blockchain::Error), - /// Error while converting a `BlockId`. - #[from(ignore)] + #[error("Transaction pool error")] + Pool(#[from] TxPoolError), + + #[error("Blockchain error")] + Blockchain(#[from] sp_blockchain::Error), + + #[error("Block conversion error: {0}")] BlockIdConversion(String), - /// Error while calling the runtime api. - #[from(ignore)] + + #[error("Runtime error: {0}")] RuntimeApi(String), } -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Pool(ref err) => Some(err), - Error::Blockchain(ref err) => Some(err), - Error::BlockIdConversion(_) => None, - Error::RuntimeApi(_) => None, - } - } -} impl sp_transaction_pool::error::IntoPoolError for Error { fn into_pool_error(self) -> std::result::Result { diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index a3c480e92135f..92bf9bea2bdc7 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -21,6 +21,7 @@ sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" sp-version = { version = "2.0.0", default-features = false, path = "../version" } sp-state-machine = { version = "0.8.0", optional = true, path = "../../primitives/state-machine" } hash-db = { version = "0.15.2", optional = true } +thiserror = { version = "1.0.21", optional = true } [dev-dependencies] sp-test-primitives = { version = "2.0.0", path = "../test-primitives" } @@ -35,4 +36,5 @@ std = [ "sp-state-machine", "sp-version/std", "hash-db", + "thiserror", ] diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index a628ade6f9b47..aebefe7ea03a4 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -708,13 +708,7 @@ impl<'a> ToClientSideDecl<'a> { }, #crate_::NativeOrEncoded::Encoded(r) => { <#ret_type as #crate_::Decode>::decode(&mut &r[..]) - .map_err(|err| - format!( - "Failed to decode result of `{}`: {}", - #function_name, - err.what(), - ).into() - ) + .map_err(|err| { #crate_::ApiError::new(#function_name, err).into() }) } } ) diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index 3e2fd42951b3c..14cf47fc64b25 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -69,7 +69,9 @@ fn implement_common_api_traits( ) -> Result { let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); - let error_type = error_type.map(|e| quote!(#e)).unwrap_or_else(|| quote!(String)); + let error_type = error_type + .map(|e| quote!(#e)) + .unwrap_or_else(|| quote!( #crate_::ApiError ) ); // Quote using the span from `error_type` to generate nice error messages when the type is // not implementing a trait or similar. diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 9dadce3b55452..96da63cf2e253 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -74,6 +74,7 @@ use sp_core::OpaqueMetadata; #[cfg(feature = "std")] use std::{panic::UnwindSafe, cell::RefCell}; + /// Maximum nesting level for extrinsics. pub const MAX_EXTRINSIC_DEPTH: u32 = 256; @@ -288,7 +289,7 @@ pub use sp_api_proc_macro::impl_runtime_apis; /// /// 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 = String; +/// type Error = sp_api::ApiError; /// /// fn build_block() -> Block { /// unimplemented!("Not Required in tests") @@ -315,6 +316,7 @@ pub use sp_api_proc_macro::impl_runtime_apis; /// # use sp_runtime::{traits::Block as BlockT, generic::BlockId}; /// # use sp_test_primitives::Block; /// # use sp_core::NativeOrEncoded; +/// # use codec; /// # /// # sp_api::decl_runtime_apis! { /// # /// Declare the api trait. @@ -331,15 +333,15 @@ pub use sp_api_proc_macro::impl_runtime_apis; /// /// sp_api::mock_impl_runtime_apis! { /// impl Balance for MockApi { -/// type Error = String; +/// type Error = sp_api::ApiError; /// #[advanced] -/// fn get_balance(&self, at: &BlockId) -> Result, String> { +/// fn get_balance(&self, at: &BlockId) -> Result, Self::Error> { /// println!("Being called at: {}", at); /// /// Ok(self.balance.into()) /// } /// #[advanced] -/// fn set_balance(at: &BlockId, val: u64) -> Result, String> { +/// fn set_balance(at: &BlockId, val: u64) -> Result, Self::Error> { /// if let BlockId::Number(1) = at { /// println!("Being called to set balance to: {}", val); /// } @@ -392,12 +394,42 @@ pub trait ConstructRuntimeApi> { fn construct_runtime_api<'a>(call: &'a C) -> ApiRef<'a, Self::RuntimeApi>; } +/// 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; + type Error: std::fmt::Debug + From; } /// Extends the runtime api implementation with some common functionality. @@ -506,7 +538,7 @@ pub struct CallApiAtParams<'a, Block: BlockT, C, NC, Backend: StateBackend { /// Error type used by the implementation. - type Error: std::fmt::Debug + From; + type Error: std::fmt::Debug + From; /// The state backend that is used to store the block states. type StateBackend: StateBackend>; diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index 594882baf1e34..be549d7b7f4cd 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -17,6 +17,7 @@ use sp_api::{ RuntimeApiInfo, decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, + ApiError, ApiExt, }; use sp_runtime::{traits::{GetNodeBlockType, Block as BlockT}, generic::BlockId}; @@ -103,17 +104,27 @@ mock_impl_runtime_apis! { } #[advanced] - fn same_name(_: &BlockId) -> std::result::Result, String> { + fn same_name(_: &BlockId) -> + std::result::Result< + NativeOrEncoded<()>, + ApiError + > + { Ok(().into()) } #[advanced] - fn wild_card(at: &BlockId, _: u32) -> std::result::Result, String> { + fn wild_card(at: &BlockId, _: u32) -> + std::result::Result< + NativeOrEncoded<()>, + ApiError + > + { if let BlockId::Number(1337) = at { // yeah Ok(().into()) } else { - Err("Ohh noooo".into()) + Err(ApiError::new("MockApi", codec::Error::from("Ohh noooo"))) } } } @@ -197,5 +208,8 @@ fn mock_runtime_api_works_with_advanced() { Api::::same_name(&mock, &BlockId::Number(0)).unwrap(); mock.wild_card(&BlockId::Number(1337), 1).unwrap(); - assert_eq!(String::from("Ohh noooo"), mock.wild_card(&BlockId::Number(1336), 1).unwrap_err()); + assert_eq!( + ApiError::new("MockApi", ::codec::Error::from("Ohh noooo")), + mock.wild_card(&BlockId::Number(1336), 1).unwrap_err() + ); } diff --git a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs b/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs index 1e71730cd0a17..fd654ffdc63d6 100644 --- a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs +++ b/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.rs @@ -1,4 +1,5 @@ use substrate_test_runtime_client::runtime::Block; +use sp_api::ApiError; sp_api::decl_runtime_apis! { pub trait Api { @@ -11,7 +12,7 @@ struct MockApi; sp_api::mock_impl_runtime_apis! { impl Api for MockApi { #[advanced] - fn test(&self, _: BlockId) -> Result, String> { + fn test(&self, _: BlockId) -> Result, ApiError> { Ok(().into()) } } diff --git a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr b/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr index efddce05f51b4..47cd9e01d910f 100644 --- a/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr +++ b/primitives/api/test/tests/ui/mock_advanced_block_id_by_value.stderr @@ -1,13 +1,13 @@ error: `BlockId` needs to be taken by reference and not by value! - --> $DIR/mock_advanced_block_id_by_value.rs:11:1 + --> $DIR/mock_advanced_block_id_by_value.rs:12:1 | -11 | / sp_api::mock_impl_runtime_apis! { -12 | | impl Api for MockApi { -13 | | #[advanced] -14 | | fn test(&self, _: BlockId) -> Result, String> { +12 | / sp_api::mock_impl_runtime_apis! { +13 | | impl Api for MockApi { +14 | | #[advanced] +15 | | fn test(&self, _: BlockId) -> Result, ApiError> { ... | -17 | | } -18 | | } +18 | | } +19 | | } | |_^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs b/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs index 407ea90ee882d..a15ef133fa6c4 100644 --- a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs +++ b/primitives/api/test/tests/ui/mock_advanced_missing_blockid.rs @@ -1,4 +1,5 @@ use substrate_test_runtime_client::runtime::Block; +use sp_api::ApiError; sp_api::decl_runtime_apis! { pub trait Api { @@ -11,7 +12,7 @@ struct MockApi; sp_api::mock_impl_runtime_apis! { impl Api for MockApi { #[advanced] - fn test(&self) -> Result, String> { + fn test(&self) -> Result, ApiError> { Ok(().into()) } } diff --git a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr b/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr index e7a66ebc5dba8..87d3660316b1e 100644 --- a/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr +++ b/primitives/api/test/tests/ui/mock_advanced_missing_blockid.stderr @@ -1,5 +1,5 @@ error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `BlockId`. - --> $DIR/mock_advanced_missing_blockid.rs:14:3 + --> $DIR/mock_advanced_missing_blockid.rs:15:3 | -14 | fn test(&self) -> Result, String> { +15 | fn test(&self) -> Result, ApiError> { | ^^ diff --git a/primitives/api/test/tests/ui/mock_only_one_error_type.stderr b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr index daac5674d6ffe..82fd04e8c5e04 100644 --- a/primitives/api/test/tests/ui/mock_only_one_error_type.stderr +++ b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr @@ -10,16 +10,16 @@ error: First error type was declared here. 17 | type Error = u32; | ^^^ -error[E0277]: the trait bound `u32: std::convert::From` is not satisfied +error[E0277]: the trait bound `u32: std::convert::From` is not satisfied --> $DIR/mock_only_one_error_type.rs:17:16 | 17 | type Error = u32; - | ^^^ the trait `std::convert::From` is not implemented for `u32` + | ^^^ the trait `std::convert::From` is not implemented for `u32` | ::: $WORKSPACE/primitives/api/src/lib.rs | - | type Error: std::fmt::Debug + From; - | ------------ required by this bound in `sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ApiErrorExt` + | type Error: std::fmt::Debug + From; + | -------------- required by this bound in `sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ApiErrorExt` | = help: the following implementations were found: > diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index eac1e032e3e9a..3458b8c0846ba 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -18,8 +18,10 @@ log = "0.4.11" lru = "0.6.1" parking_lot = "0.10.0" thiserror = "1.0.21" +futures = "0.3" codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-consensus = { version = "0.8.0", path = "../consensus/common" } sp-runtime = { version = "2.0.0", path = "../runtime" } sp-state-machine = { version = "0.8.0", path = "../state-machine" } sp-database = { version = "2.0.0", path = "../database" } +sp-api = { version = "2.0.0", path = "../api" } diff --git a/primitives/blockchain/src/backend.rs b/primitives/blockchain/src/backend.rs index 326acd6b9bd43..01a7a59d6f94f 100644 --- a/primitives/blockchain/src/backend.rs +++ b/primitives/blockchain/src/backend.rs @@ -172,7 +172,7 @@ pub trait Backend: HeaderBackend + HeaderMetadata: HeaderBackend + HeaderMetadata = result::Result; /// Error when the runtime failed to apply an extrinsic. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum ApplyExtrinsicFailed { /// The transaction cannot be included into the current block. /// @@ -35,114 +37,142 @@ pub enum ApplyExtrinsicFailed { /// unappliable onto the current block. #[error("Extrinsic is not valid: {0:?}")] Validity(#[from] TransactionValidityError), - /// This is used for miscellaneous errors that can be represented by string and not handleable. - /// - /// This will become obsolete with complete migration to v4 APIs. - #[error("Extrinsic failed: {0}")] - Msg(String), + + #[error("Application specific error")] + Application(#[source] Box), } /// Substrate Client error #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +#[non_exhaustive] pub enum Error { - /// Consensus Error + #[error("Cancelled oneshot channel {0}")] + OneShotCancelled(#[from] futures::channel::oneshot::Canceled), + #[error(transparent)] Consensus(#[from] sp_consensus::Error), - /// Backend error. + #[error("Backend error: {0}")] Backend(String), - /// Unknown block. + #[error("UnknownBlock: {0}")] UnknownBlock(String), - /// The `apply_extrinsic` is not valid due to the given `TransactionValidityError`. + #[error(transparent)] ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed), - /// Execution error. + + #[error("Child type is invalid")] + InvalidChildType, + + #[error("RemoteBodyRequest: invalid extrinsics root expected: {expected} but got {received}")] + ExtrinsicRootInvalid { received: String, expected: String }, + + // `inner` cannot be made member, since it lacks `std::error::Error` trait bounds. #[error("Execution failed: {0:?}")] Execution(Box), - /// Blockchain error. + #[error("Blockchain")] Blockchain(#[source] Box), - /// Invalid authorities set received from the runtime. + + /// A error used by various storage subsystems. + /// + /// Eventually this will be replaced. + #[error("{0}")] + StorageChanges(sp_state_machine::DefaultError), + + #[error("Invalid child storage key")] + InvalidChildStorageKey, + #[error("Current state of blockchain has invalid authorities set")] InvalidAuthoritiesSet, - /// Could not get runtime version. + #[error("Failed to get runtime version: {0}")] VersionInvalid(String), - /// Genesis config is invalid. + #[error("Genesis config provided is invalid")] GenesisInvalid, - /// Error decoding header justification. + #[error("error decoding justification for header")] JustificationDecode, - /// Justification for header is correctly encoded, but invalid. + #[error("bad justification for header: {0}")] BadJustification(String), - /// Not available on light client. + #[error("This method is not currently available when running in light client mode")] NotAvailableOnLightClient, - /// Invalid remote CHT-based proof. + #[error("Remote node has responded with invalid header proof")] InvalidCHTProof, - /// Remote fetch has been cancelled. + #[error("Remote data fetch has been cancelled")] RemoteFetchCancelled, - /// Remote fetch has been failed. + #[error("Remote data fetch has been failed")] RemoteFetchFailed, - /// Error decoding call result. + #[error("Error decoding call result of {0}")] CallResultDecode(&'static str, #[source] CodecError), - /// Error converting a parameter between runtime and node. - #[error("Error converting `{0}` between runtime and node")] - RuntimeParamConversion(String), - /// Changes tries are not supported. + + #[error(transparent)] + RuntimeApiCodecError(#[from] ApiError), + + #[error("Runtime :code missing in storage")] + RuntimeCodeMissing, + #[error("Changes tries are not supported by the runtime")] ChangesTriesNotSupported, - /// Error reading changes tries configuration. + #[error("Error reading changes tries configuration")] ErrorReadingChangesTriesConfig, - /// Key changes query has failed. + #[error("Failed to check changes proof: {0}")] ChangesTrieAccessFailed(String), - /// Last finalized block not parent of current. + #[error("Did not finalize blocks in sequential order.")] NonSequentialFinalization(String), - /// Safety violation: new best block not descendent of last finalized. + #[error("Potential long-range attack: block not in finalized chain.")] NotInFinalizedChain, - /// Hash that is required for building CHT is missing. + #[error("Failed to get hash of block for building CHT")] MissingHashRequiredForCHT, - /// Invalid calculated state root on block import. + #[error("Calculated state root does not match.")] InvalidStateRoot, - /// Incomplete block import pipeline. + #[error("Incomplete block import pipeline.")] IncompletePipeline, + #[error("Transaction pool not ready for block production.")] TransactionPoolNotReady, + #[error("Database")] DatabaseError(#[from] sp_database::error::DatabaseError), - /// A convenience variant for String - #[error("{0}")] - Msg(String), -} -impl<'a> From<&'a str> for Error { - fn from(s: &'a str) -> Self { - Error::Msg(s.into()) - } -} + #[error("Failed to get header for hash {0}")] + MissingHeader(String), -impl From for Error { - fn from(s: String) -> Self { - Error::Msg(s) - } + + #[error("State Database error: {0}")] + StateDatabase(String), + + #[error(transparent)] + Application(#[from] Box), + + // Should be removed/improved once + // the storage `fn`s returns typed errors. + #[error("Runtime code error: {0}")] + RuntimeCode(&'static str), + + // Should be removed/improved once + // the storage `fn`s returns typed errors. + #[error("Storage error: {0}")] + Storage(String), } -impl From> for Error { - fn from(e: Box) -> Self { +impl From> for Error { + fn from(e: Box) -> Self { Self::from_state(e) } } @@ -163,4 +193,11 @@ impl Error { pub fn from_state(e: Box) -> Self { Error::Execution(e) } + + /// Construct from a state db error. + // Can not be done directly, since that would make cargo run out of stack if + // `sc-state-db` is lib is added as dependency. + pub fn from_state_db(e: E) -> Self where E: std::fmt::Debug { + Error::StateDatabase(format!("{:?}", e)) + } } diff --git a/primitives/consensus/common/src/error.rs b/primitives/consensus/common/src/error.rs index a21bcf6cca9b2..11b24d273d5ec 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -25,6 +25,7 @@ pub type Result = std::result::Result; /// Error type. #[derive(Debug, thiserror::Error)] +#[non_exhaustive] pub enum Error { /// Missing state at block with given descriptor. #[error("State unavailable at block {0}")] diff --git a/primitives/state-machine/src/error.rs b/primitives/state-machine/src/error.rs index 0b02c68f79f5c..f20f9e530dc7f 100644 --- a/primitives/state-machine/src/error.rs +++ b/primitives/state-machine/src/error.rs @@ -32,18 +32,18 @@ impl Error for T {} /// would not be executed unless externalities were available. This is included for completeness, /// and as a transition away from the pre-existing framework. #[derive(Debug, Eq, PartialEq)] +#[allow(missing_docs)] #[cfg_attr(feature = "std", derive(thiserror::Error))] pub enum ExecutionError { - /// Backend error. #[cfg_attr(feature = "std", error("Backend error {0:?}"))] Backend(crate::DefaultError), - /// The entry `:code` doesn't exist in storage so there's no way we can execute anything. + #[cfg_attr(feature = "std", error("`:code` entry does not exist in storage"))] CodeEntryDoesNotExist, - /// Backend is incompatible with execution proof generation process. + #[cfg_attr(feature = "std", error("Unable to generate proof"))] UnableToGenerateProof, - /// Invalid execution proof. + #[cfg_attr(feature = "std", error("Invalid execution proof"))] InvalidProof, } diff --git a/primitives/transaction-pool/Cargo.toml b/primitives/transaction-pool/Cargo.toml index 57ba3a28ac3c1..4247e1a50c9bc 100644 --- a/primitives/transaction-pool/Cargo.toml +++ b/primitives/transaction-pool/Cargo.toml @@ -14,8 +14,9 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +thiserror = { version = "1.0.21", optional = true } codec = { package = "parity-scale-codec", version = "1.3.1", optional = true } -derive_more = { version = "0.99.2", optional = true } +derive_more = { version = "0.99.11", optional = true } futures = { version = "0.3.1", optional = true } log = { version = "0.4.8", optional = true } serde = { version = "1.0.101", features = ["derive"], optional = true} @@ -31,6 +32,7 @@ std = [ "futures", "log", "serde", + "thiserror", "sp-api/std", "sp-blockchain", "sp-runtime/std", diff --git a/primitives/transaction-pool/src/error.rs b/primitives/transaction-pool/src/error.rs index 531b397cb946c..e356df75908a7 100644 --- a/primitives/transaction-pool/src/error.rs +++ b/primitives/transaction-pool/src/error.rs @@ -25,49 +25,49 @@ use sp_runtime::transaction_validity::{ pub type Result = std::result::Result; /// Transaction pool error type. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, thiserror::Error, derive_more::From)] +#[allow(missing_docs)] pub enum Error { - /// Transaction is not verifiable yet, but might be in the future. - #[display(fmt="Unknown transaction validity: {:?}", _0)] + #[error("Unknown transaction validity: {0:?}")] UnknownTransaction(UnknownTransaction), - /// Transaction is invalid. - #[display(fmt="Invalid transaction validity: {:?}", _0)] + + #[error("Invalid transaction validity: {0:?}")] InvalidTransaction(InvalidTransaction), + /// The transaction validity returned no "provides" tag. /// /// Such transactions are not accepted to the pool, since we use those tags /// to define identity of transactions (occupance of the same "slot"). - #[display(fmt="The transaction does not provide any tags, so the pool can't identify it.")] + #[error("Transaction does not provide any tags, so the pool can't identify it")] NoTagsProvided, - /// The transaction is temporarily banned. - #[display(fmt="Temporarily Banned")] + + #[error("Transaction temporarily Banned")] TemporarilyBanned, - /// The transaction is already in the pool. - #[display(fmt="[{:?}] Already imported", _0)] + + #[error("[{0:?}] Already imported")] AlreadyImported(Box), - /// The transaction cannot be imported cause it's a replacement and has too low priority. - #[display(fmt="Too low priority ({} > {})", old, new)] + + #[error("Too low priority ({} > {})", old, new)] TooLowPriority { /// Transaction already in the pool. old: Priority, /// Transaction entering the pool. new: Priority }, - /// Deps cycle detected and we couldn't import transaction. - #[display(fmt="Cycle Detected")] + #[error("Transaction with cyclic dependency")] CycleDetected, - /// Transaction was dropped immediately after it got inserted. - #[display(fmt="Transaction couldn't enter the pool because of the limit.")] + + #[error("Transaction couldn't enter the pool because of the limit")] ImmediatelyDropped, - /// Invalid block id. + + #[from(ignore)] + #[error("{0}")] InvalidBlockId(String), - /// The pool is not accepting future transactions. - #[display(fmt="The pool is not accepting future transactions")] + + #[error("The pool is not accepting future transactions")] RejectedFutureTransaction, } -impl std::error::Error for Error {} - /// Transaction pool error conversion. pub trait IntoPoolError: std::error::Error + Send + Sized { /// Try to extract original `Error`