1#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
23use frame_support::{
24	pallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue,
25};
26use frame_system::RawOrigin;
27use scale_info::TypeInfo;
28use serde::{Deserialize, Serialize};
29use sp_core::storage::StorageKey;
30use sp_runtime::{
31	traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto},
32	RuntimeDebug,
33};
34use sp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec};
35
36pub use chain::{
37	AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf,
38	HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf,
39	UnderlyingChainOf, UnderlyingChainProvider, __private,
40};
41pub use frame_support::storage::storage_prefix as storage_value_final_key;
42use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
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
64pub use sp_runtime::paste;
66
67#[doc(hidden)]
69pub mod private {
70	#[doc(hidden)]
71	pub use alloc::vec::Vec;
72}
73
74pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0];
76
77#[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	pub fn number(&self) -> Number {
98		self.0
99	}
100
101	pub fn hash(&self) -> Hash {
103		self.1
104	}
105}
106
107pub type HeaderIdOf<C> = HeaderId<HashOf<C>, BlockNumberOf<C>>;
109
110pub trait HeaderIdProvider<Header: HeaderT> {
112	fn id(&self) -> HeaderId<Header::Hash, Header::Number>;
114
115	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
131pub type ChainId = [u8; 4];
139
140pub trait Size {
142	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
158pub 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#[derive(RuntimeDebug, Clone, Copy, PartialEq)]
169pub enum TransactionEra<BlockNumber, BlockHash> {
170	Immortal,
172	Mortal(HeaderId<BlockHash, BlockNumber>, u32),
174}
175
176impl<BlockNumber: Copy + UniqueSaturatedInto<u64>, BlockHash: Copy>
177	TransactionEra<BlockNumber, BlockHash>
178{
179	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	pub fn immortal() -> Self {
191		TransactionEra::Immortal
192	}
193
194	pub fn mortality_period(&self) -> Option<u32> {
196		match *self {
197			TransactionEra::Immortal => None,
198			TransactionEra::Mortal(_, period) => Some(period),
199		}
200	}
201
202	pub fn frame_era(&self) -> sp_runtime::generic::Era {
204		match *self {
205			TransactionEra::Immortal => sp_runtime::generic::Era::immortal(),
206			TransactionEra::Mortal(header_id, period) =>
209				sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()),
210		}
211	}
212
213	pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash {
215		match *self {
216			TransactionEra::Immortal => genesis_hash,
217			TransactionEra::Mortal(header_id, _) => header_id.1,
218		}
219	}
220}
221
222pub fn storage_map_final_key<H: StorageHasher>(
229	pallet_prefix: &str,
230	map_name: &str,
231	key: &[u8],
232) -> StorageKey {
233	let key_hashed = H::hash(key);
234	let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes());
235	let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes());
236
237	let mut final_key = Vec::with_capacity(
238		pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(),
239	);
240
241	final_key.extend_from_slice(&pallet_prefix_hashed[..]);
242	final_key.extend_from_slice(&storage_prefix_hashed[..]);
243	final_key.extend_from_slice(key_hashed.as_ref());
244
245	StorageKey(final_key)
246}
247
248pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey {
252	let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes());
253	let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes());
254
255	let mut final_key = vec![0u8; 32];
256	final_key[..16].copy_from_slice(&pallet_hash);
257	final_key[16..].copy_from_slice(&storage_hash);
258
259	StorageKey(final_key)
260}
261
262pub trait StorageMapKeyProvider {
264	const MAP_NAME: &'static str;
266
267	type Hasher: StorageHasher;
269	type Key: FullCodec + Send + Sync;
271	type Value: 'static + FullCodec;
273
274	fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey {
280		storage_map_final_key::<Self::Hasher>(pallet_prefix, Self::MAP_NAME, &key.encode())
281	}
282}
283
284pub trait StorageDoubleMapKeyProvider {
286	const MAP_NAME: &'static str;
288
289	type Hasher1: StorageHasher;
291	type Key1: FullCodec + Send + Sync;
293	type Hasher2: StorageHasher;
295	type Key2: FullCodec + Send + Sync;
297	type Value: 'static + FullCodec;
299
300	fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey {
306		let key1_hashed = Self::Hasher1::hash(&key1.encode());
307		let key2_hashed = Self::Hasher2::hash(&key2.encode());
308		let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes());
309		let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes());
310
311		let mut final_key = Vec::with_capacity(
312			pallet_prefix_hashed.len() +
313				storage_prefix_hashed.len() +
314				key1_hashed.as_ref().len() +
315				key2_hashed.as_ref().len(),
316		);
317
318		final_key.extend_from_slice(&pallet_prefix_hashed[..]);
319		final_key.extend_from_slice(&storage_prefix_hashed[..]);
320		final_key.extend_from_slice(key1_hashed.as_ref());
321		final_key.extend_from_slice(key2_hashed.as_ref());
322
323		StorageKey(final_key)
324	}
325}
326
327#[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
329pub enum OwnedBridgeModuleError {
330	Halted,
332}
333
334pub trait OperatingMode: Send + Copy + Debug + FullCodec {
336	fn is_halted(&self) -> bool;
338}
339
340#[derive(
342	Encode,
343	Decode,
344	DecodeWithMemTracking,
345	Clone,
346	Copy,
347	PartialEq,
348	Eq,
349	RuntimeDebug,
350	TypeInfo,
351	MaxEncodedLen,
352	Serialize,
353	Deserialize,
354)]
355pub enum BasicOperatingMode {
356	Normal,
358	Halted,
360}
361
362impl Default for BasicOperatingMode {
363	fn default() -> Self {
364		Self::Normal
365	}
366}
367
368impl OperatingMode for BasicOperatingMode {
369	fn is_halted(&self) -> bool {
370		*self == BasicOperatingMode::Halted
371	}
372}
373
374const COMMON_LOG_TARGET: &'static str = "runtime::bridge-module";
375
376pub trait OwnedBridgeModule<T: frame_system::Config> {
378	const LOG_TARGET: &'static str;
380
381	type OwnerStorage: StorageValue<T::AccountId, Query = Option<T::AccountId>>;
383	type OperatingMode: OperatingMode;
385	type OperatingModeStorage: StorageValue<Self::OperatingMode, Query = Self::OperatingMode>;
387
388	fn is_halted() -> bool {
390		Self::OperatingModeStorage::get().is_halted()
391	}
392
393	fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> {
395		match origin.into() {
396			Ok(RawOrigin::Root) => Ok(()),
397			Ok(RawOrigin::Signed(ref signer))
398				if Self::OwnerStorage::get().as_ref() == Some(signer) =>
399				Ok(()),
400			_ => Err(BadOrigin),
401		}
402	}
403
404	fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> {
406		match Self::is_halted() {
407			true => Err(OwnedBridgeModuleError::Halted),
408			false => Ok(()),
409		}
410	}
411
412	fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option<T::AccountId>) -> DispatchResult {
414		Self::ensure_owner_or_root(origin)?;
415		match maybe_owner {
416			Some(owner) => {
417				Self::OwnerStorage::put(&owner);
418				tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?owner, "Setting pallet.");
419			},
420			None => {
421				Self::OwnerStorage::kill();
422				tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, "Removed Owner of pallet.");
423			},
424		}
425
426		Ok(())
427	}
428
429	fn set_operating_mode(
431		origin: T::RuntimeOrigin,
432		operating_mode: Self::OperatingMode,
433	) -> DispatchResult {
434		Self::ensure_owner_or_root(origin)?;
435		Self::OperatingModeStorage::put(operating_mode);
436		tracing::info!(target: COMMON_LOG_TARGET, module=%Self::LOG_TARGET, ?operating_mode, "Setting operating mode.");
437		Ok(())
438	}
439
440	fn module_owner() -> Option<T::AccountId> {
445		Self::OwnerStorage::get()
446	}
447
448	fn operating_mode() -> Self::OperatingMode {
451		Self::OperatingModeStorage::get()
452	}
453}
454
455pub trait WeightExtraOps {
457	fn min_components_checked_div(&self, other: Weight) -> Option<u64>;
462}
463
464impl WeightExtraOps for Weight {
465	fn min_components_checked_div(&self, other: Weight) -> Option<u64> {
466		Some(sp_std::cmp::min(
467			self.ref_time().checked_div(other.ref_time())?,
468			self.proof_size().checked_div(other.proof_size())?,
469		))
470	}
471}
472
473pub trait StaticStrProvider {
475	const STR: &'static str;
477}
478
479#[macro_export]
481macro_rules! generate_static_str_provider {
482	($str:expr) => {
483		$crate::paste::item! {
484			pub struct [<Str $str>];
485
486			impl $crate::StaticStrProvider for [<Str $str>] {
487				const STR: &'static str = stringify!($str);
488			}
489		}
490	};
491}
492
493pub trait RangeInclusiveExt<Idx> {
495	fn checked_len(&self) -> Option<Idx>;
497	fn saturating_len(&self) -> Idx;
499}
500
501impl<Idx> RangeInclusiveExt<Idx> for RangeInclusive<Idx>
502where
503	Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero,
504{
505	fn checked_len(&self) -> Option<Idx> {
506		self.end()
507			.checked_sub(self.start())
508			.and_then(|len| len.checked_add(&Idx::one()))
509	}
510
511	fn saturating_len(&self) -> Idx {
512		let len = match self.end().checked_sub(self.start()) {
513			Some(len) => len,
514			None => return Idx::zero(),
515		};
516		len.saturating_add(&Idx::one())
517	}
518}
519
520#[cfg(test)]
521mod tests {
522	use super::*;
523
524	#[test]
525	fn storage_value_key_works() {
526		assert_eq!(
527			storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"),
528			StorageKey(
529				hex_literal::hex!(
530					"f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc"
531				)
532				.to_vec()
533			),
534		);
535	}
536
537	#[test]
538	fn generate_static_str_provider_works() {
539		generate_static_str_provider!(Test);
540		assert_eq!(StrTest::STR, "Test");
541	}
542}