1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// This file is part of Substrate.

// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Authoring RPC module errors.

use jsonrpsee::{
	core::Error as JsonRpseeError,
	types::error::{CallError, ErrorObject},
};
use sp_runtime::transaction_validity::InvalidTransaction;

/// Author RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;

/// Author RPC errors.
#[derive(Debug, thiserror::Error)]
pub enum Error {
	/// Client error.
	#[error("Client error: {}", .0)]
	Client(Box<dyn std::error::Error + Send + Sync>),
	/// Transaction pool error,
	#[error("Transaction pool error: {}", .0)]
	Pool(#[from] sc_transaction_pool_api::error::Error),
	/// Verification error
	#[error("Extrinsic verification error: {}", .0)]
	Verification(Box<dyn std::error::Error + Send + Sync>),
	/// Incorrect extrinsic format.
	#[error("Invalid extrinsic format: {}", .0)]
	BadFormat(#[from] codec::Error),
	/// Key type ID has an unknown format.
	#[error("Invalid key type ID format (should be of length four)")]
	BadKeyType,
	/// Some random issue with the key store. Shouldn't happen.
	#[error("The key store is unavailable")]
	KeyStoreUnavailable,
	/// Invalid session keys encoding.
	#[error("Session keys are not encoded correctly")]
	InvalidSessionKeys,
	/// Call to an unsafe RPC was denied.
	#[error(transparent)]
	UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError),
}

/// Base code for all authorship errors.
const BASE_ERROR: i32 = 1000;
/// Extrinsic has an invalid format.
const BAD_FORMAT: i32 = BASE_ERROR + 1;
/// Error during transaction verification in runtime.
const VERIFICATION_ERROR: i32 = BASE_ERROR + 2;

/// Pool rejected the transaction as invalid
const POOL_INVALID_TX: i32 = BASE_ERROR + 10;
/// Cannot determine transaction validity.
const POOL_UNKNOWN_VALIDITY: i32 = POOL_INVALID_TX + 1;
/// The transaction is temporarily banned.
const POOL_TEMPORARILY_BANNED: i32 = POOL_INVALID_TX + 2;
/// The transaction is already in the pool
const POOL_ALREADY_IMPORTED: i32 = POOL_INVALID_TX + 3;
/// Transaction has too low priority to replace existing one in the pool.
const POOL_TOO_LOW_PRIORITY: i32 = POOL_INVALID_TX + 4;
/// Including this transaction would cause a dependency cycle.
const POOL_CYCLE_DETECTED: i32 = POOL_INVALID_TX + 5;
/// The transaction was not included to the pool because of the limits.
const POOL_IMMEDIATELY_DROPPED: i32 = POOL_INVALID_TX + 6;
/// The transaction was not included to the pool since it is unactionable,
/// it is not propagable and the local node does not author blocks.
const POOL_UNACTIONABLE: i32 = POOL_INVALID_TX + 8;
/// Transaction does not provide any tags, so the pool can't identify it.
const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9;
/// Invalid block ID.
const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10;
/// The pool is not accepting future transactions.
const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11;

impl From<Error> for JsonRpseeError {
	fn from(e: Error) -> Self {
		use sc_transaction_pool_api::error::Error as PoolError;

		match e {
			Error::BadFormat(e) => CallError::Custom(ErrorObject::owned(
				BAD_FORMAT,
				format!("Extrinsic has invalid format: {}", e),
				None::<()>,
			)),
			Error::Verification(e) => CallError::Custom(ErrorObject::owned(
				VERIFICATION_ERROR,
				format!("Verification Error: {}", e),
				Some(format!("{:?}", e)),
			)),
			Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => {
				CallError::Custom(ErrorObject::owned(
					POOL_INVALID_TX,
					"Invalid Transaction",
					Some(format!("Custom error: {}", e)),
				))
			},
			Error::Pool(PoolError::InvalidTransaction(e)) => {
				let msg: &str = e.into();
				CallError::Custom(ErrorObject::owned(
					POOL_INVALID_TX,
					"Invalid Transaction",
					Some(msg),
				))
			},
			Error::Pool(PoolError::UnknownTransaction(e)) => {
				CallError::Custom(ErrorObject::owned(
					POOL_UNKNOWN_VALIDITY,
					"Unknown Transaction Validity",
					Some(format!("{:?}", e)),
				))
			},
			Error::Pool(PoolError::TemporarilyBanned) =>
				CallError::Custom(ErrorObject::owned(
				POOL_TEMPORARILY_BANNED,
				"Transaction is temporarily banned",
				None::<()>,
			)),
			Error::Pool(PoolError::AlreadyImported(hash)) =>
				CallError::Custom(ErrorObject::owned(
				POOL_ALREADY_IMPORTED,
				"Transaction Already Imported",
				Some(format!("{:?}", hash)),
			)),
			Error::Pool(PoolError::TooLowPriority { old, new }) => CallError::Custom(ErrorObject::owned(
				POOL_TOO_LOW_PRIORITY,
				format!("Priority is too low: ({} vs {})", old, new),
				Some("The transaction has too low priority to replace another transaction already in the pool.")
			)),
			Error::Pool(PoolError::CycleDetected) =>
				CallError::Custom(ErrorObject::owned(
				POOL_CYCLE_DETECTED,
				"Cycle Detected",
				None::<()>
			)),
			Error::Pool(PoolError::ImmediatelyDropped) => CallError::Custom(ErrorObject::owned(
				POOL_IMMEDIATELY_DROPPED,
				"Immediately Dropped",
				Some("The transaction couldn't enter the pool because of the limit"),
			)),
			Error::Pool(PoolError::Unactionable) => CallError::Custom(ErrorObject::owned(
				POOL_UNACTIONABLE,
				"Unactionable",
				Some("The transaction is unactionable since it is not propagable and \
				the local node does not author blocks")
			)),
			Error::Pool(PoolError::NoTagsProvided) => CallError::Custom(ErrorObject::owned(
				POOL_NO_TAGS,
				"No tags provided",
				Some("Transaction does not provide any tags, so the pool can't identify it")
			)),
			Error::Pool(PoolError::InvalidBlockId(_)) =>
				CallError::Custom(ErrorObject::owned(
				POOL_INVALID_BLOCK_ID,
				"The provided block ID is not valid",
				None::<()>
			)),
			Error::Pool(PoolError::RejectedFutureTransaction) => {
				CallError::Custom(ErrorObject::owned(
					POOL_FUTURE_TX,
					"The pool is not accepting future transactions",
					None::<()>,
				))
			},
			Error::UnsafeRpcCalled(e) => e.into(),
			e => CallError::Failed(e.into()),
		}.into()
	}
}