Skip to content

Commit

Permalink
chainHead: Backport error codes from spec (#2539)
Browse files Browse the repository at this point in the history
This PR backports the error codes from the spec.

This relies on two specs for defining the error codes:
- Our rpc-spec-v2 https://github.com/paritytech/json-rpc-interface-spec.
- JSON-RPC spec https://www.jsonrpc.org/specification#error_object.

To better describe the error codes, they are divided into two separate
modules `rpc_spec_v2` and `json_rpc_spec` respectively.

The `InvalidSubscriptionID` and `FetchBlockHeader` are merged into the
JSON-RPC spec `INTERNAL_ERROR`.
While the other error codes are adjusted from spec.

Errors that are currently in use:
- -32801 block hash not reported by chainHead_follow or block hash has
been unpinned
- -32802 chainHead_follow started with withRuntime == false
- -32803 chainHead_follow did not generate an
operationWaitingForContinue event

The following are errors defined in the [JSON-RPC
spec](https://www.jsonrpc.org/specification#error_object):
- -32602 The provided parameter isn't one of the expected values, has
different format or is missing
- -32603 Internal server error

Note: Error `-32801` must be introduced and generated by the outstanding
#1505

Closes: #2530

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
  • Loading branch information
lexnv authored Nov 29, 2023
1 parent eb46b99 commit ecdf343
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 42 deletions.
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(_) =>
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

0 comments on commit ecdf343

Please sign in to comment.