pezbp_runtime/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Primitives that may be used at (bridges) runtime level.
18
19#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
23use pezframe_support::{
24	pezpallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue,
25};
26use pezframe_system::RawOrigin;
27use pezsp_core::storage::StorageKey;
28use pezsp_runtime::{
29	traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto},
30	RuntimeDebug,
31};
32use pezsp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec};
33use scale_info::TypeInfo;
34use serde::{Deserialize, Serialize};
35
36pub use chain::{
37	AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf,
38	HasherOf, HeaderOf, NonceOf, SignatureOf, Teyrchain, TeyrchainIdOf, TransactionEraOf,
39	UnderlyingChainOf, UnderlyingChainProvider, __private,
40};
41use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
42pub use pezframe_support::storage::storage_prefix as storage_value_final_key;
43#[cfg(feature = "std")]
44pub use storage_proof::craft_valid_storage_proof;
45#[cfg(feature = "test-helpers")]
46pub use storage_proof::{
47	grow_storage_proof, grow_storage_value, record_all_keys as record_all_trie_keys,
48	UnverifiedStorageProofParams,
49};
50pub use storage_proof::{
51	raw_storage_proof_size, RawStorageProof, StorageProofChecker, StorageProofError,
52};
53pub use storage_types::BoundedStorageValue;
54
55extern crate alloc;
56
57pub mod extensions;
58pub mod messages;
59
60mod chain;
61mod storage_proof;
62mod storage_types;
63
64// Re-export macro to avoid include paste dependency everywhere
65pub use pezsp_runtime::paste;
66
67// Re-export for usage in macro.
68#[doc(hidden)]
69pub mod private {
70	#[doc(hidden)]
71	pub use alloc::vec::Vec;
72}
73
74/// Use this when something must be shared among all instances.
75pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0];
76
77/// Generic header Id.
78#[derive(
79	RuntimeDebug,
80	Default,
81	Clone,
82	Encode,
83	Decode,
84	Copy,
85	Eq,
86	Hash,
87	MaxEncodedLen,
88	PartialEq,
89	PartialOrd,
90	Ord,
91	TypeInfo,
92)]
93pub struct HeaderId<Hash, Number>(pub Number, pub Hash);
94
95impl<Hash: Copy, Number: Copy> HeaderId<Hash, Number> {
96	/// Return header number.
97	pub fn number(&self) -> Number {
98		self.0
99	}
100
101	/// Return header hash.
102	pub fn hash(&self) -> Hash {
103		self.1
104	}
105}
106
107/// Header id used by the chain.
108pub type HeaderIdOf<C> = HeaderId<HashOf<C>, BlockNumberOf<C>>;
109
110/// Generic header id provider.
111pub trait HeaderIdProvider<Header: HeaderT> {
112	/// Get the header id.
113	fn id(&self) -> HeaderId<Header::Hash, Header::Number>;
114
115	/// Get the header id for the parent block.
116	fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>>;
117}
118
119impl<Header: HeaderT> HeaderIdProvider<Header> for Header {
120	fn id(&self) -> HeaderId<Header::Hash, Header::Number> {
121		HeaderId(*self.number(), self.hash())
122	}
123
124	fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>> {
125		self.number()
126			.checked_sub(&One::one())
127			.map(|parent_number| HeaderId(parent_number, *self.parent_hash()))
128	}
129}
130
131/// Unique identifier of the chain.
132///
133/// In addition to its main function (identifying the chain), this type may also be used to
134/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g.
135/// messages pezpallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and
136/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be
137/// used for that.
138pub type ChainId = [u8; 4];
139
140/// Anything that has size.
141pub trait Size {
142	/// Return size of this object (in bytes).
143	fn size(&self) -> u32;
144}
145
146impl Size for () {
147	fn size(&self) -> u32 {
148		0
149	}
150}
151
152impl Size for Vec<u8> {
153	fn size(&self) -> u32 {
154		self.len() as _
155	}
156}
157
158/// Pre-computed size.
159pub struct PreComputedSize(pub usize);
160
161impl Size for PreComputedSize {
162	fn size(&self) -> u32 {
163		u32::try_from(self.0).unwrap_or(u32::MAX)
164	}
165}
166
167/// Era of specific transaction.
168#[derive(RuntimeDebug, Clone, Copy, PartialEq)]
169pub enum TransactionEra<BlockNumber, BlockHash> {
170	/// Transaction is immortal.
171	Immortal,
172	/// Transaction is valid for a given number of blocks, starting from given block.
173	Mortal(HeaderId<BlockHash, BlockNumber>, u32),
174}
175
176impl<BlockNumber: Copy + UniqueSaturatedInto<u64>, BlockHash: Copy>
177	TransactionEra<BlockNumber, BlockHash>
178{
179	/// Prepare transaction era, based on mortality period and current best block number.
180	pub fn new(
181		best_block_id: HeaderId<BlockHash, BlockNumber>,
182		mortality_period: Option<u32>,
183	) -> Self {
184		mortality_period
185			.map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period))
186			.unwrap_or(TransactionEra::Immortal)
187	}
188
189	/// Create new immortal transaction era.
190	pub fn immortal() -> Self {
191		TransactionEra::Immortal
192	}
193
194	/// Returns mortality period if transaction is mortal.
195	pub fn mortality_period(&self) -> Option<u32> {
196		match *self {
197			TransactionEra::Immortal => None,
198			TransactionEra::Mortal(_, period) => Some(period),
199		}
200	}
201
202	/// Returns era that is used by FRAME-based runtimes.
203	pub fn frame_era(&self) -> pezsp_runtime::generic::Era {
204		match *self {
205			TransactionEra::Immortal => pezsp_runtime::generic::Era::immortal(),
206			// `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we
207			// expect to see on any chain
208			TransactionEra::Mortal(header_id, period) => pezsp_runtime::generic::Era::mortal(
209				period as _,
210				header_id.0.unique_saturated_into(),
211			),
212		}
213	}
214
215	/// Returns header hash that needs to be included in the signature payload.
216	pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash {
217		match *self {
218			TransactionEra::Immortal => genesis_hash,
219			TransactionEra::Mortal(header_id, _) => header_id.1,
220		}
221	}
222}
223
224/// This is a copy of the
225/// `pezframe_support::storage::generator::StorageMap::storage_map_final_key` for maps based
226/// on selected hasher.
227///
228/// We're using it because to call `storage_map_final_key` directly, we need access to the runtime
229/// and pezpallet instance, which (sometimes) is impossible.
230pub fn storage_map_final_key<H: StorageHasher>(
231	pezpallet_prefix: &str,
232	map_name: &str,
233	key: &[u8],
234) -> StorageKey {
235	let key_hashed = H::hash(key);
236	let pezpallet_prefix_hashed = pezframe_support::Twox128::hash(pezpallet_prefix.as_bytes());
237	let storage_prefix_hashed = pezframe_support::Twox128::hash(map_name.as_bytes());
238
239	let mut final_key = Vec::with_capacity(
240		pezpallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(),
241	);
242
243	final_key.extend_from_slice(&pezpallet_prefix_hashed[..]);
244	final_key.extend_from_slice(&storage_prefix_hashed[..]);
245	final_key.extend_from_slice(key_hashed.as_ref());
246
247	StorageKey(final_key)
248}
249
250/// This is how a storage key of storage value is computed.
251///
252/// Copied from `pezframe_support::storage::storage_prefix`.
253pub fn storage_value_key(pezpallet_prefix: &str, value_name: &str) -> StorageKey {
254	let pezpallet_hash = pezsp_io::hashing::twox_128(pezpallet_prefix.as_bytes());
255	let storage_hash = pezsp_io::hashing::twox_128(value_name.as_bytes());
256
257	let mut final_key = vec![0u8; 32];
258	final_key[..16].copy_from_slice(&pezpallet_hash);
259	final_key[16..].copy_from_slice(&storage_hash);
260
261	StorageKey(final_key)
262}
263
264/// Can be use to access the runtime storage key of a `StorageMap`.
265pub trait StorageMapKeyProvider {
266	/// The name of the variable that holds the `StorageMap`.
267	const MAP_NAME: &'static str;
268
269	/// The same as `StorageMap::Hasher1`.
270	type Hasher: StorageHasher;
271	/// The same as `StorageMap::Key1`.
272	type Key: FullCodec + Send + Sync;
273	/// The same as `StorageMap::Value`.
274	type Value: 'static + FullCodec;
275
276	/// This is a copy of the
277	/// `pezframe_support::storage::generator::StorageMap::storage_map_final_key`.
278	///
279	/// We're using it because to call `storage_map_final_key` directly, we need access
280	/// to the runtime and pezpallet instance, which (sometimes) is impossible.
281	fn final_key(pezpallet_prefix: &str, key: &Self::Key) -> StorageKey {
282		storage_map_final_key::<Self::Hasher>(pezpallet_prefix, Self::MAP_NAME, &key.encode())
283	}
284}
285
286/// Can be used to access the runtime storage key of a `StorageDoubleMap`.
287pub trait StorageDoubleMapKeyProvider {
288	/// The name of the variable that holds the `StorageDoubleMap`.
289	const MAP_NAME: &'static str;
290
291	/// The same as `StorageDoubleMap::Hasher1`.
292	type Hasher1: StorageHasher;
293	/// The same as `StorageDoubleMap::Key1`.
294	type Key1: FullCodec + Send + Sync;
295	/// The same as `StorageDoubleMap::Hasher2`.
296	type Hasher2: StorageHasher;
297	/// The same as `StorageDoubleMap::Key2`.
298	type Key2: FullCodec + Send + Sync;
299	/// The same as `StorageDoubleMap::Value`.
300	type Value: 'static + FullCodec;
301
302	/// This is a copy of the
303	/// `pezframe_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`.
304	///
305	/// We're using it because to call `storage_double_map_final_key` directly, we need access
306	/// to the runtime and pezpallet instance, which (sometimes) is impossible.
307	fn final_key(pezpallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey {
308		let key1_hashed = Self::Hasher1::hash(&key1.encode());
309		let key2_hashed = Self::Hasher2::hash(&key2.encode());
310		let pezpallet_prefix_hashed = pezframe_support::Twox128::hash(pezpallet_prefix.as_bytes());
311		let storage_prefix_hashed = pezframe_support::Twox128::hash(Self::MAP_NAME.as_bytes());
312
313		let mut final_key = Vec::with_capacity(
314			pezpallet_prefix_hashed.len()
315				+ storage_prefix_hashed.len()
316				+ key1_hashed.as_ref().len()
317				+ key2_hashed.as_ref().len(),
318		);
319
320		final_key.extend_from_slice(&pezpallet_prefix_hashed[..]);
321		final_key.extend_from_slice(&storage_prefix_hashed[..]);
322		final_key.extend_from_slice(key1_hashed.as_ref());
323		final_key.extend_from_slice(key2_hashed.as_ref());
324
325		StorageKey(final_key)
326	}
327}
328
329/// Error generated by the `OwnedBridgeModule` trait.
330#[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
331pub enum OwnedBridgeModuleError {
332	/// All pezpallet operations are halted.
333	Halted,
334}
335
336/// Operating mode for a bridge module.
337pub trait OperatingMode: Send + Copy + Debug + FullCodec {
338	/// Returns true if the bridge module is halted.
339	fn is_halted(&self) -> bool;
340}
341
342/// Basic operating modes for a bridges module (Normal/Halted).
343#[derive(
344	Encode,
345	Decode,
346	DecodeWithMemTracking,
347	Clone,
348	Copy,
349	PartialEq,
350	Eq,
351	RuntimeDebug,
352	TypeInfo,
353	MaxEncodedLen,
354	Serialize,
355	Deserialize,
356)]
357pub enum BasicOperatingMode {
358	/// Normal mode, when all operations are allowed.
359	Normal,
360	/// The pezpallet is halted. All operations (except operating mode change) are prohibited.
361	Halted,
362}
363
364impl Default for BasicOperatingMode {
365	fn default() -> Self {
366		Self::Normal
367	}
368}
369
370impl OperatingMode for BasicOperatingMode {
371	fn is_halted(&self) -> bool {
372		*self == BasicOperatingMode::Halted
373	}
374}
375
376const COMMON_LOG_TARGET: &'static str = "runtime::bridge-module";
377
378/// Bridge module that has owner and operating mode
379pub trait OwnedBridgeModule<T: pezframe_system::Config> {
380	/// The target that will be used when publishing logs related to this module.
381	const LOG_TARGET: &'static str;
382
383	/// A storage entry that holds the module `Owner` account.
384	type OwnerStorage: StorageValue<T::AccountId, Query = Option<T::AccountId>>;
385	/// Operating mode type of the pezpallet.
386	type OperatingMode: OperatingMode;
387	/// A storage value that holds the pezpallet operating mode.
388	type OperatingModeStorage: StorageValue<Self::OperatingMode, Query = Self::OperatingMode>;
389
390	/// Check if the module is halted.
391	fn is_halted() -> bool {
392		Self::OperatingModeStorage::get().is_halted()
393	}
394
395	/// Ensure that the origin is either root, or `PalletOwner`.
396	fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> {
397		match origin.into() {
398			Ok(RawOrigin::Root) => Ok(()),
399			Ok(RawOrigin::Signed(ref signer))
400				if Self::OwnerStorage::get().as_ref() == Some(signer) =>
401			{
402				Ok(())
403			},
404			_ => Err(BadOrigin),
405		}
406	}
407
408	/// Ensure that the module is not halted.
409	fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> {
410		match Self::is_halted() {
411			true => Err(OwnedBridgeModuleError::Halted),
412			false => Ok(()),
413		}
414	}
415
416	/// Change the owner of the module.
417	fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option<T::AccountId>) -> DispatchResult {
418		Self::ensure_owner_or_root(origin)?;
419		match maybe_owner {
420			Some(owner) => {
421				Self::OwnerStorage::put(&owner);
422				tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?owner, "Setting pezpallet.");
423			},
424			None => {
425				Self::OwnerStorage::kill();
426				tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, "Removed Owner of pezpallet.");
427			},
428		}
429
430		Ok(())
431	}
432
433	/// Halt or resume all/some module operations.
434	fn set_operating_mode(
435		origin: T::RuntimeOrigin,
436		operating_mode: Self::OperatingMode,
437	) -> DispatchResult {
438		Self::ensure_owner_or_root(origin)?;
439		Self::OperatingModeStorage::put(operating_mode);
440		tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?operating_mode, "Setting operating mode.");
441		Ok(())
442	}
443
444	/// Pezpallet owner has a right to halt all module operations and then resume it. If it is
445	/// `None`, then there are no direct ways to halt/resume module operations, but other runtime
446	/// methods may still be used to do that (i.e. democracy::referendum to update halt flag
447	/// directly or call the `set_operating_mode`).
448	fn module_owner() -> Option<T::AccountId> {
449		Self::OwnerStorage::get()
450	}
451
452	/// The current operating mode of the module.
453	/// Depending on the mode either all, some, or no transactions will be allowed.
454	fn operating_mode() -> Self::OperatingMode {
455		Self::OperatingModeStorage::get()
456	}
457}
458
459/// All extra operations with weights that we need in bridges.
460pub trait WeightExtraOps {
461	/// Checked division of individual components of two weights.
462	///
463	/// Divides components and returns minimal division result. Returns `None` if one
464	/// of `other` weight components is zero.
465	fn min_components_checked_div(&self, other: Weight) -> Option<u64>;
466}
467
468impl WeightExtraOps for Weight {
469	fn min_components_checked_div(&self, other: Weight) -> Option<u64> {
470		Some(pezsp_std::cmp::min(
471			self.ref_time().checked_div(other.ref_time())?,
472			self.proof_size().checked_div(other.proof_size())?,
473		))
474	}
475}
476
477/// Trait that provides a static `str`.
478pub trait StaticStrProvider {
479	/// Static string.
480	const STR: &'static str;
481}
482
483/// A macro that generates `StaticStrProvider` with the string set to its stringified argument.
484#[macro_export]
485macro_rules! generate_static_str_provider {
486	($str:expr) => {
487		$crate::paste::item! {
488			pub struct [<Str $str>];
489
490			impl $crate::StaticStrProvider for [<Str $str>] {
491				const STR: &'static str = stringify!($str);
492			}
493		}
494	};
495}
496
497/// A trait defining helper methods for `RangeInclusive` (start..=end)
498pub trait RangeInclusiveExt<Idx> {
499	/// Computes the length of the `RangeInclusive`, checking for underflow and overflow.
500	fn checked_len(&self) -> Option<Idx>;
501	/// Computes the length of the `RangeInclusive`, saturating in case of underflow or overflow.
502	fn saturating_len(&self) -> Idx;
503}
504
505impl<Idx> RangeInclusiveExt<Idx> for RangeInclusive<Idx>
506where
507	Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero,
508{
509	fn checked_len(&self) -> Option<Idx> {
510		self.end()
511			.checked_sub(self.start())
512			.and_then(|len| len.checked_add(&Idx::one()))
513	}
514
515	fn saturating_len(&self) -> Idx {
516		let len = match self.end().checked_sub(self.start()) {
517			Some(len) => len,
518			None => return Idx::zero(),
519		};
520		len.saturating_add(&Idx::one())
521	}
522}
523
524#[cfg(test)]
525mod tests {
526	use super::*;
527
528	#[test]
529	fn storage_value_key_works() {
530		assert_eq!(
531			storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"),
532			StorageKey(
533				hex_literal::hex!(
534					"f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc"
535				)
536				.to_vec()
537			),
538		);
539	}
540
541	#[test]
542	fn generate_static_str_provider_works() {
543		generate_static_str_provider!(Test);
544		assert_eq!(StrTest::STR, "Test");
545	}
546}