Skip to main content

ckb_rpc/
error.rs

1use ckb_error::{AnyError, Error as CKBError, ErrorKind, InternalError, InternalErrorKind};
2use ckb_tx_pool::error::Reject;
3use jsonrpc_core::{Error, ErrorCode, Value};
4use schemars::JsonSchema;
5use std::fmt::{Debug, Display};
6
7/// CKB RPC error codes.
8///
9/// CKB RPC follows the JSON RPC specification about the [error object](https://www.jsonrpc.org/specification#error_object).
10///
11/// Besides the pre-defined errors, all CKB defined errors are listed here.
12///
13/// Here is a reference to the pre-defined errors:
14///
15/// | code             | message          | meaning                                            |
16/// | ---------------- | ---------------- | -------------------------------------------------- |
17/// | -32700           | Parse error      | Invalid JSON was received by the server.           |
18/// | -32600           | Invalid Request  | The JSON sent is not a valid Request object.       |
19/// | -32601           | Method not found | The method does not exist / is not available.      |
20/// | -32602           | Invalid params   | Invalid method parameter(s).                       |
21/// | -32603           | Internal error   | Internal JSON-RPC error.                           |
22/// | -32000 to -32099 | Server error     | Reserved for implementation-defined server-errors. |
23///
24/// CKB application-defined errors follow some patterns to assign the codes:
25///
26/// * -1 ~ -999 are general errors
27/// * -1000 ~ -2999 are module-specific errors. Each module generally gets 100 reserved error
28///   codes.
29///
30/// Unless otherwise noted, all the errors return optional detailed information as `string` in the error
31/// object `data` field.
32#[derive(Debug, PartialEq, Clone, Copy, Eq, JsonSchema)]
33pub enum RPCError {
34    /// (-1): CKB internal errors are considered to never happen or only happen when the system
35    /// resources are exhausted.
36    CKBInternalError = -1,
37    /// (-2): The CKB method has been deprecated and disabled.
38    ///
39    /// Set `rpc.enable_deprecated_rpc` to `true` in the config file to enable all deprecated
40    /// methods.
41    Deprecated = -2,
42    /// (-3): Error code -3 is no longer used.
43    ///
44    /// Before v0.35.0, CKB returns all RPC errors using the error code -3. CKB no longer uses
45    /// -3 since v0.35.0.
46    Invalid = -3,
47    /// (-4): The RPC method is not enabled.
48    ///
49    /// CKB groups RPC methods into modules, and a method is enabled only when the module is
50    /// explicitly enabled in the config file.
51    RPCModuleIsDisabled = -4,
52    /// (-5): DAO related errors.
53    DaoError = -5,
54    /// (-6): Integer operation overflow.
55    IntegerOverflow = -6,
56    /// (-7): The error is caused by a config file option.
57    ///
58    /// Users have to edit the config file to fix the error.
59    ConfigError = -7,
60    /// (-101): The CKB local node failed to broadcast a message to its peers.
61    P2PFailedToBroadcast = -101,
62    /// (-200): Internal database error.
63    ///
64    /// The CKB node persists data to the database. This is the error from the underlying database
65    /// module.
66    DatabaseError = -200,
67    /// (-201): The chain index is inconsistent.
68    ///
69    /// An example of an inconsistent index is that the chain index says a block hash is in the chain
70    /// but the block cannot be read from the database.
71    ///
72    /// This is a fatal error usually due to a serious bug. Please back up the data directory and
73    /// re-sync the chain from scratch.
74    ChainIndexIsInconsistent = -201,
75    /// (-202): The underlying database is corrupt.
76    ///
77    /// This is a fatal error usually caused by the underlying database used by CKB. Please back up
78    /// the data directory and re-sync the chain from scratch.
79    DatabaseIsCorrupt = -202,
80    /// (-301): Failed to resolve the referenced cells and headers used in the transaction, as inputs or
81    /// dependencies.
82    TransactionFailedToResolve = -301,
83    /// (-302): Failed to verify the transaction.
84    TransactionFailedToVerify = -302,
85    /// (-1000): Some signatures in the submit alert are invalid.
86    AlertFailedToVerifySignatures = -1000,
87    /// (-1102): The transaction is rejected by the outputs validator specified by the RPC parameter.
88    PoolRejectedTransactionByOutputsValidator = -1102,
89    /// (-1103): Pool rejects some transactions which seem contain invalid VM instructions. See the issue
90    /// link in the error message for details.
91    PoolRejectedTransactionByIllTransactionChecker = -1103,
92    /// (-1104): The transaction fee rate must be greater than or equal to the config option `tx_pool.min_fee_rate`
93    ///
94    /// The fee rate is calculated as:
95    ///
96    /// ```text
97    /// fee / (1000 * tx_serialization_size_in_block_in_bytes)
98    /// ```
99    PoolRejectedTransactionByMinFeeRate = -1104,
100    /// (-1105): The in-pool ancestors count must be less than or equal to the config option `tx_pool.max_ancestors_count`
101    ///
102    /// Pool rejects a large package of chained transactions to avoid certain kinds of DoS attacks.
103    PoolRejectedTransactionByMaxAncestorsCountLimit = -1105,
104    /// (-1106): The transaction is rejected because the pool has reached its limit.
105    PoolIsFull = -1106,
106    /// (-1107): The transaction is already in the pool.
107    PoolRejectedDuplicatedTransaction = -1107,
108    /// (-1108): The transaction is rejected because it does not make sense in the context.
109    ///
110    /// For example, a cellbase transaction is not allowed in `send_transaction` RPC.
111    PoolRejectedMalformedTransaction = -1108,
112    /// (-1109): The transaction is expired from tx-pool after `expiry_hours`.
113    TransactionExpired = -1109,
114    /// (-1110): The transaction exceeded maximum size limit.
115    PoolRejectedTransactionBySizeLimit = -1110,
116    /// (-1111): The transaction is rejected for RBF checking.
117    PoolRejectedRBF = -1111,
118    /// (-1112): The transaction is rejected for ref cell consuming.
119    PoolRejectedInvalidated = -1112,
120    /// (-1200): The indexer error.
121    Indexer = -1200,
122}
123
124/// Removes the backtrace portion from an error string.
125fn remove_backtrace(err_str: &str) -> &str {
126    match err_str.find("\nStack backtrace:") {
127        Some(idx) => &err_str[..idx],
128        None => err_str,
129    }
130}
131
132impl RPCError {
133    /// Invalid method parameter(s).
134    pub fn invalid_params<T: Display>(message: T) -> Error {
135        Error {
136            code: ErrorCode::InvalidParams,
137            message: format!("InvalidParams: {message}"),
138            data: None,
139        }
140    }
141
142    /// Creates an RPC error with custom error code and message.
143    pub fn custom<T: Display>(error_code: RPCError, message: T) -> Error {
144        Error {
145            code: ErrorCode::ServerError(error_code as i64),
146            message: format!("{error_code:?}: {message}"),
147            data: None,
148        }
149    }
150
151    /// Creates an RPC error with custom error code, message and data.
152    pub fn custom_with_data<T: Display, F: Debug>(
153        error_code: RPCError,
154        message: T,
155        data: F,
156    ) -> Error {
157        Error {
158            code: ErrorCode::ServerError(error_code as i64),
159            message: format!("{error_code:?}: {message}"),
160            data: Some(Value::String(format!("{data:?}"))),
161        }
162    }
163
164    /// Creates an RPC error from std error with the custom error code.
165    ///
166    /// The parameter `err` is usually an std error. The Display form is used as the error message,
167    /// and the Debug form is used as the data.
168    pub fn custom_with_error<T: Display + Debug>(error_code: RPCError, err: T) -> Error {
169        let err_str_with_backtrace = format!("{err:?}");
170        let err_str = remove_backtrace(&err_str_with_backtrace);
171        Error {
172            code: ErrorCode::ServerError(error_code as i64),
173            message: format!("{error_code:?}: {err}"),
174            data: Some(Value::String(err_str.to_string())),
175        }
176    }
177
178    /// Creates an RPC error from the reason that a transaction is rejected to be submitted.
179    pub fn from_submit_transaction_reject(reject: &Reject) -> Error {
180        let code = match reject {
181            Reject::LowFeeRate(_, _, _) => RPCError::PoolRejectedTransactionByMinFeeRate,
182            Reject::ExceededMaximumAncestorsCount => {
183                RPCError::PoolRejectedTransactionByMaxAncestorsCountLimit
184            }
185            Reject::Full(_) => RPCError::PoolIsFull,
186            Reject::Duplicated(_) => RPCError::PoolRejectedDuplicatedTransaction,
187            Reject::Malformed(_, _) => RPCError::PoolRejectedMalformedTransaction,
188            Reject::DeclaredWrongCycles(..) => RPCError::PoolRejectedMalformedTransaction,
189            Reject::Resolve(_) => RPCError::TransactionFailedToResolve,
190            Reject::Verification(_) => RPCError::TransactionFailedToVerify,
191            Reject::RBFRejected(_) => RPCError::PoolRejectedRBF,
192            Reject::Invalidated(_) => RPCError::PoolRejectedInvalidated,
193            Reject::ExceededTransactionSizeLimit(_, _) => {
194                RPCError::PoolRejectedTransactionBySizeLimit
195            }
196            Reject::Expiry(_) => RPCError::TransactionExpired,
197        };
198        RPCError::custom_with_error(code, reject)
199    }
200
201    /// Creates an CKB error from `CKBError`.
202    pub fn from_ckb_error(err: CKBError) -> Error {
203        match err.kind() {
204            ErrorKind::Dao => Self::custom_with_error(RPCError::DaoError, err.root_cause()),
205            ErrorKind::OutPoint => {
206                Self::custom_with_error(RPCError::TransactionFailedToResolve, err)
207            }
208            ErrorKind::Transaction => {
209                Self::custom_with_error(RPCError::TransactionFailedToVerify, err.root_cause())
210            }
211            ErrorKind::Internal => {
212                let internal_err = match err.downcast_ref::<InternalError>() {
213                    Some(err) => err,
214                    None => return Self::ckb_internal_error(err),
215                };
216
217                let kind = match internal_err.kind() {
218                    InternalErrorKind::CapacityOverflow => RPCError::IntegerOverflow,
219                    InternalErrorKind::DataCorrupted => RPCError::DatabaseIsCorrupt,
220                    InternalErrorKind::Database => RPCError::DatabaseError,
221                    InternalErrorKind::Config => RPCError::ConfigError,
222                    _ => RPCError::CKBInternalError,
223                };
224
225                RPCError::custom_with_error(kind, internal_err)
226            }
227            _ => Self::custom_with_error(RPCError::CKBInternalError, err),
228        }
229    }
230
231    /// Creates an RPC error from `AnyError`.
232    pub fn from_any_error(err: AnyError) -> Error {
233        match err.downcast_ref::<CKBError>() {
234            Some(ckb_error) => Self::from_ckb_error(ckb_error.clone()),
235            None => Self::ckb_internal_error(err.clone()),
236        }
237    }
238
239    /// CKB internal error.
240    ///
241    /// CKB internal errors are considered to never happen or only happen when the system
242    /// resources are exhausted.
243    pub fn ckb_internal_error<T: Display + Debug>(err: T) -> Error {
244        Self::custom_with_error(RPCError::CKBInternalError, err)
245    }
246
247    /// RPC error which indicates that the method is disabled.
248    ///
249    /// RPC methods belong to modules and they are only enabled when the belonging module is
250    /// included in the config.
251    pub fn rpc_module_is_disabled(module: &str) -> Error {
252        Self::custom(
253            RPCError::RPCModuleIsDisabled,
254            format!(
255                "This RPC method is in the module `{module}`. \
256                 Please modify `rpc.modules`{miner_info} in ckb.toml and restart the ckb node to enable it.",
257                module = module,
258                miner_info = if module == "Miner" {
259                    " and `block_assembler`"
260                } else {
261                    ""
262                }
263            ),
264        )
265    }
266
267    /// RPC error which indicates that the method is deprecated.
268    ///
269    /// Deprecated methods are disabled by default unless they are enabled via the config options
270    /// `enable_deprecated_rpc`.
271    pub fn rpc_method_is_deprecated() -> Error {
272        Self::custom(
273            RPCError::Deprecated,
274            "This RPC method is deprecated, it will be removed in future release. \
275            Please check the related information in the CKB release notes and RPC document. \
276            You may enable deprecated methods via adding `enable_deprecated_rpc = true` to the `[rpc]` section in ckb.toml.",
277        )
278    }
279}