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}