ac_node_api/error/
dispatch_error.rs

1// This file was taken from subxt (Parity Technologies (UK))
2// https://github.com/paritytech/subxt/
3// And was adapted by Supercomputing Systems AG.
4//
5// Copyright 2019-2022 Parity Technologies (UK) Ltd, Supercomputing Systems AG.
6// This file is licensed as Apache-2.0
7// see LICENSE for license details.
8
9//! Substrate Dispatch Error representation.
10
11use super::{Error, MetadataError};
12use crate::metadata::Metadata;
13use alloc::{
14	borrow::Cow,
15	string::{String, ToString},
16	vec::Vec,
17};
18use codec::{Decode, Encode};
19use core::{fmt::Debug, marker::PhantomData};
20use derive_more::From;
21use log::*;
22use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, TypeResolver};
23
24/// An error dispatching a transaction. See Substrate DispatchError
25// https://github.com/paritytech/polkadot-sdk/blob/0c5dcca9e3cef6b2f456fccefd9f6c5e43444053/substrate/primitives/runtime/src/lib.rs#L561-L598
26#[derive(Debug, From, PartialEq)]
27pub enum DispatchError {
28	/// Some error occurred.
29	Other,
30	/// Failed to lookup some data.
31	CannotLookup,
32	/// A bad origin.
33	BadOrigin,
34	/// A custom error in a module.
35	Module(ModuleError),
36	/// At least one consumer is remaining so the account cannot be destroyed.
37	ConsumerRemaining,
38	/// There are no providers so the account cannot be created.
39	NoProviders,
40	/// There are too many consumers so the account cannot be created.
41	TooManyConsumers,
42	/// An error to do with tokens.
43	Token(TokenError),
44	/// An arithmetic error.
45	Arithmetic(ArithmeticError),
46	/// The number of transactional layers has been reached, or we are not in a transactional layer.
47	Transactional(TransactionalError),
48	/// Resources exhausted, e.g. attempt to read/write data which is too large to manipulate.
49	Exhausted,
50	/// The state is corrupt; this is generally not going to fix itself.
51	Corruption,
52	/// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later.
53	Unavailable,
54	/// Root origin is not allowed.
55	RootNotAllowed,
56}
57
58impl DispatchError {
59	/// Attempt to decode a runtime [`DispatchError`].
60	// https://github.com/paritytech/subxt/blob/0d1cc92f27c0c6d43de16fe7276484a141149096/subxt/src/error/dispatch_error.rs#L229-L338
61	pub fn decode_from<'a>(
62		bytes: impl Into<Cow<'a, [u8]>>,
63		metadata: &Metadata,
64	) -> Result<Self, Error> {
65		let bytes = bytes.into();
66		let dispatch_error_ty_id =
67			metadata.dispatch_error_ty().ok_or(MetadataError::DispatchErrorNotFound)?;
68
69		// The aim is to decode our bytes into roughly this shape. This is copied from
70		// `sp_runtime::DispatchError`; we need the variant names and any inner variant
71		// names/shapes to line up in order for decoding to be successful.
72		#[derive(DecodeAsType)]
73		enum DecodedDispatchError {
74			Other,
75			CannotLookup,
76			BadOrigin,
77			Module(DecodedModuleErrorBytes),
78			ConsumerRemaining,
79			NoProviders,
80			TooManyConsumers,
81			Token(TokenError),
82			Arithmetic(ArithmeticError),
83			Transactional(TransactionalError),
84			Exhausted,
85			Corruption,
86			Unavailable,
87			RootNotAllowed,
88		}
89
90		// ModuleError is a bit special; we want to support being decoded from either
91		// a legacy format of 2 bytes, or a newer format of 5 bytes. So, just grab the bytes
92		// out when decoding to manually work with them.
93		struct DecodedModuleErrorBytes(Vec<u8>);
94		struct DecodedModuleErrorBytesVisitor<R: TypeResolver>(PhantomData<R>);
95		impl<R: TypeResolver> scale_decode::Visitor for DecodedModuleErrorBytesVisitor<R> {
96			type Error = scale_decode::Error;
97			type Value<'scale, 'info> = DecodedModuleErrorBytes;
98			type TypeResolver = R;
99
100			fn unchecked_decode_as_type<'scale, 'info>(
101				self,
102				input: &mut &'scale [u8],
103				_type_id: R::TypeId,
104				_types: &'info R,
105			) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>> {
106				DecodeAsTypeResult::Decoded(Ok(DecodedModuleErrorBytes(input.to_vec())))
107			}
108		}
109
110		impl scale_decode::IntoVisitor for DecodedModuleErrorBytes {
111			type AnyVisitor<R: TypeResolver> = DecodedModuleErrorBytesVisitor<R>;
112			fn into_visitor<R: TypeResolver>() -> DecodedModuleErrorBytesVisitor<R> {
113				DecodedModuleErrorBytesVisitor(PhantomData)
114			}
115		}
116
117		// Decode into our temporary error:
118		let decoded_dispatch_err = DecodedDispatchError::decode_as_type(
119			&mut &*bytes,
120			dispatch_error_ty_id,
121			metadata.types(),
122		)?;
123
124		// Convert into the outward-facing error, mainly by handling the Module variant.
125		let dispatch_error = match decoded_dispatch_err {
126			// Mostly we don't change anything from our decoded to our outward-facing error:
127			DecodedDispatchError::Other => DispatchError::Other,
128			DecodedDispatchError::CannotLookup => DispatchError::CannotLookup,
129			DecodedDispatchError::BadOrigin => DispatchError::BadOrigin,
130			DecodedDispatchError::ConsumerRemaining => DispatchError::ConsumerRemaining,
131			DecodedDispatchError::NoProviders => DispatchError::NoProviders,
132			DecodedDispatchError::TooManyConsumers => DispatchError::TooManyConsumers,
133			DecodedDispatchError::Token(val) => DispatchError::Token(val),
134			DecodedDispatchError::Arithmetic(val) => DispatchError::Arithmetic(val),
135			DecodedDispatchError::Transactional(val) => DispatchError::Transactional(val),
136			DecodedDispatchError::Exhausted => DispatchError::Exhausted,
137			DecodedDispatchError::Corruption => DispatchError::Corruption,
138			DecodedDispatchError::Unavailable => DispatchError::Unavailable,
139			DecodedDispatchError::RootNotAllowed => DispatchError::RootNotAllowed,
140			// But we apply custom logic to transform the module error into the outward facing version:
141			DecodedDispatchError::Module(module_bytes) => {
142				let module_bytes = module_bytes.0;
143
144				// The old version is 2 bytes; a pallet and error index.
145				// The new version is 5 bytes; a pallet and error index and then 3 extra bytes.
146				let raw = if module_bytes.len() == 2 {
147					RawModuleError {
148						pallet_index: module_bytes[0],
149						error: [module_bytes[1], 0, 0, 0],
150					}
151				} else if module_bytes.len() == 5 {
152					RawModuleError {
153						pallet_index: module_bytes[0],
154						error: [module_bytes[1], module_bytes[2], module_bytes[3], module_bytes[4]],
155					}
156				} else {
157					warn!("Can't decode error sp_runtime::DispatchError: bytes do not match known shapes");
158					// Return _all_ of the bytes; every "unknown" return should be consistent.
159					return Err(Error::Unknown(bytes.to_vec()))
160				};
161
162				let pallet_metadata = metadata.pallet_by_index_err(raw.pallet_index)?;
163				let error_details = pallet_metadata
164					.error_variant_by_index(raw.error[0])
165					.ok_or(MetadataError::ErrorNotFound(raw.pallet_index, raw.error[0]))?;
166
167				// And return our outward-facing version:
168				DispatchError::Module(ModuleError {
169					pallet: pallet_metadata.name().to_string(),
170					error: error_details.name.clone(),
171					description: error_details.docs.clone(),
172					raw,
173				})
174			},
175		};
176
177		Ok(dispatch_error)
178	}
179}
180
181/// An error relating to tokens when dispatching a transaction.
182// https://github.com/paritytech/polkadot-sdk/blob/0c5dcca9e3cef6b2f456fccefd9f6c5e43444053/substrate/primitives/runtime/src/lib.rs#L646-L671
183#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
184pub enum TokenError {
185	/// Funds are unavailable.
186	FundsUnavailable,
187	/// Some part of the balance gives the only provider reference to the account and thus cannot be (re)moved.
188	OnlyProvider,
189	/// Account cannot exist with the funds that would be given.
190	BelowMinimum,
191	/// Account cannot be created.
192	CannotCreate,
193	/// The asset in question is unknown.
194	UnknownAsset,
195	/// Funds exist but are frozen.
196	Frozen,
197	/// Operation is not supported by the asset.
198	Unsupported,
199	/// Account cannot be created for a held balance.
200	CannotCreateHold,
201	/// Withdrawal would cause unwanted loss of account.
202	NotExpendable,
203	/// Account cannot receive the assets.
204	Blocked,
205}
206
207/// An error relating to arithmetic when dispatching a transaction.
208// https://github.com/paritytech/polkadot-sdk/blob/0c5dcca9e3cef6b2f456fccefd9f6c5e43444053/substrate/primitives/arithmetic/src/lib.rs#L61-L71
209#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
210pub enum ArithmeticError {
211	/// Underflow.
212	Underflow,
213	/// Overflow.
214	Overflow,
215	/// Division by zero.
216	DivisionByZero,
217}
218
219/// An error relating to the transactional layers when dispatching a transaction.
220// https://github.com/paritytech/polkadot-sdk/blob/0c5dcca9e3cef6b2f456fccefd9f6c5e43444053/substrate/primitives/runtime/src/lib.rs#L536-L544
221#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
222pub enum TransactionalError {
223	/// Too many transactional layers have been spawned.
224	LimitReached,
225	/// A transactional layer was expected, but does not exist.
226	NoLayer,
227}
228
229/// Details about a module error that has occurred.
230#[derive(Clone, Debug)]
231pub struct ModuleError {
232	/// The name of the pallet that the error came from.
233	pub pallet: String,
234	/// The name of the error.
235	pub error: String,
236	/// A description of the error.
237	pub description: Vec<String>,
238	/// A byte representation of the error.
239	pub raw: RawModuleError,
240}
241
242impl PartialEq for ModuleError {
243	fn eq(&self, other: &Self) -> bool {
244		// A module error is the same if the raw underlying details are the same.
245		self.raw == other.raw
246	}
247}
248
249/// The error details about a module error that has occurred.
250///
251/// **Note**: Structure used to obtain the underlying bytes of a ModuleError.
252#[derive(Clone, Copy, Debug, PartialEq, Eq)]
253pub struct RawModuleError {
254	/// Index of the pallet that the error came from.
255	pub pallet_index: u8,
256	/// Raw error bytes.
257	pub error: [u8; 4],
258}
259
260impl RawModuleError {
261	/// Obtain the error index from the underlying byte data.
262	pub fn error_index(&self) -> u8 {
263		// Error index is utilized as the first byte from the error array.
264		self.error[0]
265	}
266}