Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chainHead: Backport error codes from spec #2539

Merged
merged 6 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ where
let sub_id = match self.accept_subscription(&mut sink) {
Ok(sub_id) => sub_id,
Err(err) => {
sink.close(ChainHeadRpcError::InvalidSubscriptionID);
sink.close(ChainHeadRpcError::InternalError(
"Cannot generate subscription ID".into(),
));
return Err(err)
},
};
Expand Down Expand Up @@ -306,7 +308,7 @@ where
self.client
.header(hash)
.map(|opt_header| opt_header.map(|h| hex_string(&h.encode())))
.map_err(ChainHeadRpcError::FetchBlockHeader)
.map_err(|err| ChainHeadRpcError::InternalError(err.to_string()))
.map_err(Into::into)
}

Expand Down Expand Up @@ -393,7 +395,7 @@ where

// Reject subscription if with_runtime is false.
if !block_guard.has_runtime() {
return Err(ChainHeadRpcError::InvalidParam(
return Err(ChainHeadRpcError::InvalidRuntimeCall(
"The runtime updates flag must be set".to_string(),
)
.into())
Expand Down
64 changes: 37 additions & 27 deletions substrate/client/rpc-spec-v2/src/chain_head/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,62 @@ use jsonrpsee::{
core::Error as RpcError,
types::error::{CallError, ErrorObject},
};
use sp_blockchain::Error as BlockchainError;

/// ChainHead RPC errors.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// The provided block hash is invalid.
#[error("Invalid block hash")]
InvalidBlock,
/// Fetch block header error.
#[error("Could not fetch block header: {0}")]
FetchBlockHeader(BlockchainError),
/// The follow subscription was started with `withRuntime` set to `false`.
#[error("The `chainHead_follow` subscription was started with `withRuntime` set to `false`")]
InvalidRuntimeCall(String),
/// Wait-for-continue event not generated.
#[error("Wait for continue event was not generated for the subscription")]
InvalidContinue,
/// Invalid parameter provided to the RPC method.
#[error("Invalid parameter: {0}")]
InvalidParam(String),
/// Invalid subscription ID provided by the RPC server.
#[error("Invalid subscription ID")]
InvalidSubscriptionID,
/// Internal error.
#[error("Internal error: {0}")]
InternalError(String),
}

/// Errors for `chainHead` RPC module, as defined in
/// <https://github.com/paritytech/json-rpc-interface-spec>.
pub mod rpc_spec_v2 {
/// The provided block hash is invalid.
pub const INVALID_BLOCK_ERROR: i32 = -32801;
/// The follow subscription was started with `withRuntime` set to `false`.
pub const INVALID_RUNTIME_CALL: i32 = -32802;
/// Wait-for-continue event not generated.
#[error("Wait for continue event was not generated for the subscription")]
InvalidContinue,
pub const INVALID_CONTINUE: i32 = -32803;
}

// Base code for all `chainHead` errors.
const BASE_ERROR: i32 = 2000;
/// The provided block hash is invalid.
const INVALID_BLOCK_ERROR: i32 = BASE_ERROR + 1;
/// Fetch block header error.
const FETCH_BLOCK_HEADER_ERROR: i32 = BASE_ERROR + 2;
/// Invalid parameter error.
const INVALID_PARAM_ERROR: i32 = BASE_ERROR + 3;
/// Invalid subscription ID.
const INVALID_SUB_ID: i32 = BASE_ERROR + 4;
/// Wait-for-continue event not generated.
const INVALID_CONTINUE: i32 = BASE_ERROR + 5;
/// General purpose errors, as defined in
/// <https://www.jsonrpc.org/specification#error_object>.
pub mod json_rpc_spec {
/// Invalid parameter error.
pub const INVALID_PARAM_ERROR: i32 = -32602;
/// Internal error.
pub const INTERNAL_ERROR: i32 = -32603;
}

impl From<Error> for ErrorObject<'static> {
fn from(e: Error) -> Self {
let msg = e.to_string();

match e {
Error::InvalidBlock => ErrorObject::owned(INVALID_BLOCK_ERROR, msg, None::<()>),
Error::FetchBlockHeader(_) =>
ErrorObject::owned(FETCH_BLOCK_HEADER_ERROR, msg, None::<()>),
Error::InvalidParam(_) => ErrorObject::owned(INVALID_PARAM_ERROR, msg, None::<()>),
Error::InvalidSubscriptionID => ErrorObject::owned(INVALID_SUB_ID, msg, None::<()>),
Error::InvalidContinue => ErrorObject::owned(INVALID_CONTINUE, msg, None::<()>),
Error::InvalidBlock =>
ErrorObject::owned(rpc_spec_v2::INVALID_BLOCK_ERROR, msg, None::<()>),
Error::InvalidRuntimeCall(_) =>
ErrorObject::owned(rpc_spec_v2::INVALID_RUNTIME_CALL, msg, None::<()>),
Error::InvalidContinue =>
ErrorObject::owned(rpc_spec_v2::INVALID_CONTINUE, msg, None::<()>),
Error::InvalidParam(_) =>
ErrorObject::owned(json_rpc_spec::INVALID_PARAM_ERROR, msg, None::<()>),
Error::InternalError(_) =>
Copy link
Member

@niklasad1 niklasad1 Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically according to https://www.jsonrpc.org/specification#error_object the message should be "Internal error" but this should be fine anyway.

ErrorObject::owned(json_rpc_spec::INTERNAL_ERROR, msg, None::<()>),
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions substrate/client/rpc-spec-v2/src/chain_head/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ async fn get_header() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Obtain the valid header.
Expand Down Expand Up @@ -389,7 +389,7 @@ async fn get_body() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Valid call.
Expand Down Expand Up @@ -474,7 +474,7 @@ async fn call_runtime() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Pass an invalid parameters that cannot be decode.
Expand All @@ -487,7 +487,7 @@ async fn call_runtime() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2003 && err.message().contains("Invalid parameter")
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter")
);

// Valid call.
Expand Down Expand Up @@ -590,7 +590,7 @@ async fn call_runtime_without_flag() {
.unwrap_err();

assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2003 && err.message().contains("The runtime updates flag must be set")
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`")
);
}

Expand Down Expand Up @@ -628,7 +628,7 @@ async fn get_storage_hash() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Valid call without storage at the key.
Expand Down Expand Up @@ -896,7 +896,7 @@ async fn get_storage_value() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Valid call without storage at the key.
Expand Down Expand Up @@ -1571,7 +1571,7 @@ async fn follow_with_unpin() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// To not exceed the number of pinned blocks, we need to unpin before the next import.
Expand Down Expand Up @@ -1720,7 +1720,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

let _res: () = api
Expand All @@ -1737,7 +1737,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

// Unpin multiple blocks.
Expand All @@ -1755,7 +1755,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);

let err = api
Expand All @@ -1766,7 +1766,7 @@ async fn follow_with_multiple_unpin_hashes() {
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash"
);
}

Expand Down