use ckb_error::{AnyError, Error as CKBError, ErrorKind, InternalError, InternalErrorKind};
use ckb_tx_pool::error::Reject;
use jsonrpc_core::{Error, ErrorCode, Value};
use std::fmt::{Debug, Display};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RPCError {
CKBInternalError = -1,
Deprecated = -2,
Invalid = -3,
RPCModuleIsDisabled = -4,
DaoError = -5,
IntegerOverflow = -6,
ConfigError = -7,
P2PFailedToBroadcast = -101,
DatabaseError = -200,
ChainIndexIsInconsistent = -201,
DatabaseIsCorrupt = -202,
TransactionFailedToResolve = -301,
TransactionFailedToVerify = -302,
AlertFailedToVerifySignatures = -1000,
PoolRejectedTransactionByOutputsValidator = -1102,
PoolRejectedTransactionByIllTransactionChecker = -1103,
PoolRejectedTransactionByMinFeeRate = -1104,
PoolRejectedTransactionByMaxAncestorsCountLimit = -1105,
PoolIsFull = -1106,
PoolRejectedDuplicatedTransaction = -1107,
PoolRejectedMalformedTransaction = -1108,
}
impl RPCError {
pub fn invalid_params<T: Display>(message: T) -> Error {
Error {
code: ErrorCode::InvalidParams,
message: format!("InvalidParams: {}", message),
data: None,
}
}
pub fn custom<T: Display>(error_code: RPCError, message: T) -> Error {
Error {
code: ErrorCode::ServerError(error_code as i64),
message: format!("{:?}: {}", error_code, message),
data: None,
}
}
pub fn custom_with_data<T: Display, F: Debug>(
error_code: RPCError,
message: T,
data: F,
) -> Error {
Error {
code: ErrorCode::ServerError(error_code as i64),
message: format!("{:?}: {}", error_code, message),
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn custom_with_error<T: Display + Debug>(error_code: RPCError, err: T) -> Error {
Error {
code: ErrorCode::ServerError(error_code as i64),
message: format!("{:?}: {}", error_code, err),
data: Some(Value::String(format!("{:?}", err))),
}
}
pub fn from_submit_transaction_reject(reject: &Reject) -> Error {
let code = match reject {
Reject::LowFeeRate(_, _, _) => RPCError::PoolRejectedTransactionByMinFeeRate,
Reject::ExceededMaximumAncestorsCount => {
RPCError::PoolRejectedTransactionByMaxAncestorsCountLimit
}
Reject::Full(_, _) => RPCError::PoolIsFull,
Reject::Duplicated(_) => RPCError::PoolRejectedDuplicatedTransaction,
Reject::Malformed(_) => RPCError::PoolRejectedMalformedTransaction,
Reject::DeclaredWrongCycles(..) => RPCError::PoolRejectedMalformedTransaction,
Reject::Resolve(_) => RPCError::TransactionFailedToResolve,
Reject::Verification(_) => RPCError::TransactionFailedToVerify,
};
RPCError::custom_with_error(code, reject)
}
pub fn downcast_submit_transaction_reject(err: &CKBError) -> Option<&Reject> {
use ckb_error::ErrorKind::SubmitTransaction;
match err.kind() {
SubmitTransaction => err.downcast_ref::<Reject>(),
_ => None,
}
}
pub fn from_ckb_error(err: CKBError) -> Error {
match err.kind() {
ErrorKind::Dao => Self::custom_with_error(RPCError::DaoError, err.root_cause()),
ErrorKind::OutPoint => {
Self::custom_with_error(RPCError::TransactionFailedToResolve, err)
}
ErrorKind::Transaction => {
Self::custom_with_error(RPCError::TransactionFailedToVerify, err.root_cause())
}
ErrorKind::Internal => {
let internal_err = match err.downcast_ref::<InternalError>() {
Some(err) => err,
None => return Self::ckb_internal_error(err),
};
let kind = match internal_err.kind() {
InternalErrorKind::CapacityOverflow => RPCError::IntegerOverflow,
InternalErrorKind::DataCorrupted => RPCError::DatabaseIsCorrupt,
InternalErrorKind::Database => RPCError::DatabaseError,
InternalErrorKind::Config => RPCError::ConfigError,
_ => RPCError::CKBInternalError,
};
RPCError::custom_with_error(kind, internal_err)
}
_ => Self::custom_with_error(RPCError::CKBInternalError, err),
}
}
pub fn from_any_error(err: AnyError) -> Error {
match err.downcast_ref::<CKBError>() {
Some(ckb_error) => Self::from_ckb_error(ckb_error.clone()),
None => Self::ckb_internal_error(err.clone()),
}
}
pub fn ckb_internal_error<T: Display + Debug>(err: T) -> Error {
Self::custom_with_error(RPCError::CKBInternalError, err)
}
pub fn rpc_module_is_disabled(module: &str) -> Error {
Self::custom(
RPCError::RPCModuleIsDisabled,
format!(
"This RPC method is in the module `{module}`. \
Please modify `rpc.modules`{miner_info} in ckb.toml and restart the ckb node to enable it.",
module = module, miner_info = if module == "Miner" {" and `block_assembler`"} else {""}
)
)
}
pub fn rpc_method_is_deprecated() -> Error {
Self::custom(
RPCError::Deprecated,
"This RPC method is deprecated, it will be removed in future release. \
Please check the related information in the CKB release notes and RPC document. \
You may enable deprecated methods via adding `enable_deprecated_rpc = true` to the `[rpc]` section in ckb.toml.",
)
}
}