referrerpolicy=no-referrer-when-downgrade

sc_rpc_api/author/
error.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Authoring RPC module errors.
20
21use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
22use sp_runtime::transaction_validity::InvalidTransaction;
23
24#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
25#[serde(rename_all = "camelCase")]
26struct ModuleInvalidityDetails {
27	pallet_index: u8,
28	error: u16,
29}
30
31/// Author RPC Result type.
32pub type Result<T> = std::result::Result<T, Error>;
33
34/// Author RPC errors.
35#[derive(Debug, thiserror::Error)]
36pub enum Error {
37	/// Client error.
38	#[error("Client error: {}", .0)]
39	Client(Box<dyn std::error::Error + Send + Sync>),
40	/// Transaction pool error,
41	#[error("Transaction pool error: {}", .0)]
42	Pool(#[from] sc_transaction_pool_api::error::Error),
43	/// Verification error
44	#[error("Extrinsic verification error: {}", .0)]
45	Verification(Box<dyn std::error::Error + Send + Sync>),
46	/// Incorrect extrinsic format.
47	#[error("Invalid extrinsic format: {}", .0)]
48	BadFormat(#[from] codec::Error),
49	/// Key type ID has an unknown format.
50	#[error("Invalid key type ID format (should be of length four)")]
51	BadKeyType,
52	/// Some random issue with the key store. Shouldn't happen.
53	#[error("The key store is unavailable")]
54	KeystoreUnavailable,
55	/// Invalid session keys encoding.
56	#[error("Session keys are not encoded correctly")]
57	InvalidSessionKeys,
58	/// `SessionKeys` runtime api missing.
59	#[error("`SessionKeys` runtime api not present in the runtime")]
60	MissingSessionKeysApi,
61	/// Call to an unsafe RPC was denied.
62	#[error(transparent)]
63	UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError),
64}
65
66/// Base code for all authorship errors.
67const BASE_ERROR: i32 = crate::error::base::AUTHOR;
68/// Extrinsic has an invalid format.
69const BAD_FORMAT: i32 = BASE_ERROR + 1;
70/// Error during transaction verification in runtime.
71const VERIFICATION_ERROR: i32 = BASE_ERROR + 2;
72
73/// Pool rejected the transaction as invalid
74const POOL_INVALID_TX: i32 = BASE_ERROR + 10;
75/// Cannot determine transaction validity.
76const POOL_UNKNOWN_VALIDITY: i32 = POOL_INVALID_TX + 1;
77/// The transaction is temporarily banned.
78const POOL_TEMPORARILY_BANNED: i32 = POOL_INVALID_TX + 2;
79/// The transaction is already in the pool
80const POOL_ALREADY_IMPORTED: i32 = POOL_INVALID_TX + 3;
81/// Transaction has too low priority to replace existing one in the pool.
82const POOL_TOO_LOW_PRIORITY: i32 = POOL_INVALID_TX + 4;
83/// Including this transaction would cause a dependency cycle.
84const POOL_CYCLE_DETECTED: i32 = POOL_INVALID_TX + 5;
85/// The transaction was not included to the pool because of the limits.
86const POOL_IMMEDIATELY_DROPPED: i32 = POOL_INVALID_TX + 6;
87/// The transaction was not included to the pool since it is unactionable,
88/// it is not propagable and the local node does not author blocks.
89const POOL_UNACTIONABLE: i32 = POOL_INVALID_TX + 8;
90/// Transaction does not provide any tags, so the pool can't identify it.
91const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9;
92/// Invalid block ID.
93const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10;
94/// The pool is not accepting future transactions.
95const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11;
96/// Other error.
97const OTHER_ERR: i32 = BASE_ERROR + 40;
98
99impl From<Error> for ErrorObjectOwned {
100	fn from(e: Error) -> ErrorObjectOwned {
101		use sc_transaction_pool_api::error::Error as PoolError;
102
103		match e {
104			Error::BadFormat(e) => ErrorObject::owned(
105				BAD_FORMAT,
106				format!("Extrinsic has invalid format: {}", e),
107				None::<()>,
108			),
109			Error::Verification(e) => ErrorObject::owned(
110				VERIFICATION_ERROR,
111				format!("Verification Error: {}", e),
112				Some(format!("{:?}", e)),
113			),
114			Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => {
115				ErrorObject::owned(
116					POOL_INVALID_TX,
117					"Invalid Transaction",
118					Some(format!("Custom error: {}", e)),
119				)
120			},
121			Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Module(e))) => {
122				ErrorObject::owned(
123					POOL_INVALID_TX,
124					"Invalid Transaction",
125					Some(ModuleInvalidityDetails { pallet_index: e.index, error: e.error }),
126				)
127			},
128			Error::Pool(PoolError::InvalidTransaction(e)) => {
129				let msg: &str = e.into();
130				ErrorObject::owned(
131					POOL_INVALID_TX,
132					"Invalid Transaction",
133					Some(msg),
134				)
135			},
136			Error::Pool(PoolError::UnknownTransaction(e)) => {
137				ErrorObject::owned(
138					POOL_UNKNOWN_VALIDITY,
139					"Unknown Transaction Validity",
140					Some(format!("{:?}", e)),
141				)
142			},
143			Error::Pool(PoolError::TemporarilyBanned) =>
144				ErrorObject::owned(
145				POOL_TEMPORARILY_BANNED,
146				"Transaction is temporarily banned",
147				None::<()>,
148			),
149			Error::Pool(PoolError::AlreadyImported(hash)) =>
150				ErrorObject::owned(
151				POOL_ALREADY_IMPORTED,
152				"Transaction Already Imported",
153				Some(format!("{:?}", hash)),
154			),
155			Error::Pool(PoolError::TooLowPriority { old, new }) => ErrorObject::owned(
156				POOL_TOO_LOW_PRIORITY,
157				format!("Priority is too low: ({} vs {})", old, new),
158				Some("The transaction has too low priority to replace another transaction already in the pool.")
159			),
160			Error::Pool(PoolError::CycleDetected) =>
161				ErrorObject::owned(
162				POOL_CYCLE_DETECTED,
163				"Cycle Detected",
164				None::<()>
165			),
166			Error::Pool(PoolError::ImmediatelyDropped) => ErrorObject::owned(
167				POOL_IMMEDIATELY_DROPPED,
168				"Immediately Dropped",
169				Some("The transaction couldn't enter the pool because of the limit"),
170			),
171			Error::Pool(PoolError::Unactionable) => ErrorObject::owned(
172				POOL_UNACTIONABLE,
173				"Unactionable",
174				Some("The transaction is unactionable since it is not propagable and \
175				the local node does not author blocks")
176			),
177			Error::Pool(PoolError::NoTagsProvided) => ErrorObject::owned(
178				POOL_NO_TAGS,
179				"No tags provided",
180				Some("Transaction does not provide any tags, so the pool can't identify it")
181			),
182			Error::Pool(PoolError::InvalidBlockId(_)) =>
183				ErrorObject::owned(
184				POOL_INVALID_BLOCK_ID,
185				"The provided block ID is not valid",
186				None::<()>
187			),
188			Error::Pool(PoolError::RejectedFutureTransaction) => {
189				ErrorObject::owned(
190					POOL_FUTURE_TX,
191					"The pool is not accepting future transactions",
192					None::<()>,
193				)
194			},
195			Error::UnsafeRpcCalled(e) => e.into(),
196			other => ErrorObject::owned(
197				OTHER_ERR,
198				other.to_string(),
199				None::<()>,
200			)
201		}
202	}
203}
204
205#[cfg(test)]
206mod tests {
207	use super::*;
208	use jsonrpsee::types::ErrorObjectOwned;
209	use sc_transaction_pool_api::error::Error as PoolError;
210	use sp_runtime::ModuleInvalidity;
211
212	/// After validation, the node exposes pallet index and error number for diagnostics.
213	#[test]
214	fn author_submit_extrinsic_module_invalidity_rpc_data() {
215		let on_node = ModuleInvalidity { index: 42, error: 1 };
216		let err = Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Module(on_node)));
217		let rpc_error: ErrorObjectOwned = err.into();
218
219		let raw_data = rpc_error.data().unwrap().get();
220		// Pin the actual field names on the wire, since round-tripping through
221		// `ModuleInvalidityDetails` alone wouldn't notice `rename_all = "camelCase"` being
222		// removed from the type.
223		assert_eq!(raw_data, r#"{"palletIndex":42,"error":1}"#);
224
225		let details: ModuleInvalidityDetails = serde_json::from_str(raw_data).unwrap();
226		assert_eq!(details.pallet_index, 42);
227		assert_eq!(details.error, 1);
228	}
229}