#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
use frame_support::{
pallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue,
};
use frame_system::RawOrigin;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use sp_core::storage::StorageKey;
use sp_runtime::{
traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto},
RuntimeDebug,
};
use sp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec};
pub use chain::{
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf,
HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf,
UnderlyingChainOf, UnderlyingChainProvider, __private,
};
pub use frame_support::storage::storage_prefix as storage_value_final_key;
use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero};
#[cfg(feature = "std")]
pub use storage_proof::craft_valid_storage_proof;
#[cfg(feature = "test-helpers")]
pub use storage_proof::{
grow_storage_proof, grow_storage_value, record_all_keys as record_all_trie_keys,
UnverifiedStorageProofParams,
};
pub use storage_proof::{
raw_storage_proof_size, RawStorageProof, StorageProofChecker, StorageProofError,
};
pub use storage_types::BoundedStorageValue;
pub mod extensions;
pub mod messages;
mod chain;
mod storage_proof;
mod storage_types;
pub use sp_runtime::paste;
pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0];
#[derive(
RuntimeDebug,
Default,
Clone,
Encode,
Decode,
Copy,
Eq,
Hash,
MaxEncodedLen,
PartialEq,
PartialOrd,
Ord,
TypeInfo,
)]
pub struct HeaderId<Hash, Number>(pub Number, pub Hash);
impl<Hash: Copy, Number: Copy> HeaderId<Hash, Number> {
pub fn number(&self) -> Number {
self.0
}
pub fn hash(&self) -> Hash {
self.1
}
}
pub type HeaderIdOf<C> = HeaderId<HashOf<C>, BlockNumberOf<C>>;
pub trait HeaderIdProvider<Header: HeaderT> {
fn id(&self) -> HeaderId<Header::Hash, Header::Number>;
fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>>;
}
impl<Header: HeaderT> HeaderIdProvider<Header> for Header {
fn id(&self) -> HeaderId<Header::Hash, Header::Number> {
HeaderId(*self.number(), self.hash())
}
fn parent_id(&self) -> Option<HeaderId<Header::Hash, Header::Number>> {
self.number()
.checked_sub(&One::one())
.map(|parent_number| HeaderId(parent_number, *self.parent_hash()))
}
}
pub type ChainId = [u8; 4];
pub trait Size {
fn size(&self) -> u32;
}
impl Size for () {
fn size(&self) -> u32 {
0
}
}
impl Size for Vec<u8> {
fn size(&self) -> u32 {
self.len() as _
}
}
pub struct PreComputedSize(pub usize);
impl Size for PreComputedSize {
fn size(&self) -> u32 {
u32::try_from(self.0).unwrap_or(u32::MAX)
}
}
#[derive(RuntimeDebug, Clone, Copy, PartialEq)]
pub enum TransactionEra<BlockNumber, BlockHash> {
Immortal,
Mortal(HeaderId<BlockHash, BlockNumber>, u32),
}
impl<BlockNumber: Copy + UniqueSaturatedInto<u64>, BlockHash: Copy>
TransactionEra<BlockNumber, BlockHash>
{
pub fn new(
best_block_id: HeaderId<BlockHash, BlockNumber>,
mortality_period: Option<u32>,
) -> Self {
mortality_period
.map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period))
.unwrap_or(TransactionEra::Immortal)
}
pub fn immortal() -> Self {
TransactionEra::Immortal
}
pub fn mortality_period(&self) -> Option<u32> {
match *self {
TransactionEra::Immortal => None,
TransactionEra::Mortal(_, period) => Some(period),
}
}
pub fn frame_era(&self) -> sp_runtime::generic::Era {
match *self {
TransactionEra::Immortal => sp_runtime::generic::Era::immortal(),
TransactionEra::Mortal(header_id, period) =>
sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()),
}
}
pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash {
match *self {
TransactionEra::Immortal => genesis_hash,
TransactionEra::Mortal(header_id, _) => header_id.1,
}
}
}
pub fn storage_map_final_key<H: StorageHasher>(
pallet_prefix: &str,
map_name: &str,
key: &[u8],
) -> StorageKey {
let key_hashed = H::hash(key);
let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes());
let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes());
let mut final_key = Vec::with_capacity(
pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(),
);
final_key.extend_from_slice(&pallet_prefix_hashed[..]);
final_key.extend_from_slice(&storage_prefix_hashed[..]);
final_key.extend_from_slice(key_hashed.as_ref());
StorageKey(final_key)
}
pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey {
let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes());
let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes());
let mut final_key = vec![0u8; 32];
final_key[..16].copy_from_slice(&pallet_hash);
final_key[16..].copy_from_slice(&storage_hash);
StorageKey(final_key)
}
pub trait StorageMapKeyProvider {
const MAP_NAME: &'static str;
type Hasher: StorageHasher;
type Key: FullCodec + Send + Sync;
type Value: 'static + FullCodec;
fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey {
storage_map_final_key::<Self::Hasher>(pallet_prefix, Self::MAP_NAME, &key.encode())
}
}
pub trait StorageDoubleMapKeyProvider {
const MAP_NAME: &'static str;
type Hasher1: StorageHasher;
type Key1: FullCodec + Send + Sync;
type Hasher2: StorageHasher;
type Key2: FullCodec + Send + Sync;
type Value: 'static + FullCodec;
fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey {
let key1_hashed = Self::Hasher1::hash(&key1.encode());
let key2_hashed = Self::Hasher2::hash(&key2.encode());
let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes());
let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes());
let mut final_key = Vec::with_capacity(
pallet_prefix_hashed.len() +
storage_prefix_hashed.len() +
key1_hashed.as_ref().len() +
key2_hashed.as_ref().len(),
);
final_key.extend_from_slice(&pallet_prefix_hashed[..]);
final_key.extend_from_slice(&storage_prefix_hashed[..]);
final_key.extend_from_slice(key1_hashed.as_ref());
final_key.extend_from_slice(key2_hashed.as_ref());
StorageKey(final_key)
}
}
#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, PalletError)]
pub enum OwnedBridgeModuleError {
Halted,
}
pub trait OperatingMode: Send + Copy + Debug + FullCodec {
fn is_halted(&self) -> bool;
}
#[derive(
Encode,
Decode,
Clone,
Copy,
PartialEq,
Eq,
RuntimeDebug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum BasicOperatingMode {
Normal,
Halted,
}
impl Default for BasicOperatingMode {
fn default() -> Self {
Self::Normal
}
}
impl OperatingMode for BasicOperatingMode {
fn is_halted(&self) -> bool {
*self == BasicOperatingMode::Halted
}
}
pub trait OwnedBridgeModule<T: frame_system::Config> {
const LOG_TARGET: &'static str;
type OwnerStorage: StorageValue<T::AccountId, Query = Option<T::AccountId>>;
type OperatingMode: OperatingMode;
type OperatingModeStorage: StorageValue<Self::OperatingMode, Query = Self::OperatingMode>;
fn is_halted() -> bool {
Self::OperatingModeStorage::get().is_halted()
}
fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> {
match origin.into() {
Ok(RawOrigin::Root) => Ok(()),
Ok(RawOrigin::Signed(ref signer))
if Self::OwnerStorage::get().as_ref() == Some(signer) =>
Ok(()),
_ => Err(BadOrigin),
}
}
fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> {
match Self::is_halted() {
true => Err(OwnedBridgeModuleError::Halted),
false => Ok(()),
}
}
fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option<T::AccountId>) -> DispatchResult {
Self::ensure_owner_or_root(origin)?;
match maybe_owner {
Some(owner) => {
Self::OwnerStorage::put(&owner);
log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner);
},
None => {
Self::OwnerStorage::kill();
log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet.");
},
}
Ok(())
}
fn set_operating_mode(
origin: T::RuntimeOrigin,
operating_mode: Self::OperatingMode,
) -> DispatchResult {
Self::ensure_owner_or_root(origin)?;
Self::OperatingModeStorage::put(operating_mode);
log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode);
Ok(())
}
}
pub trait WeightExtraOps {
fn min_components_checked_div(&self, other: Weight) -> Option<u64>;
}
impl WeightExtraOps for Weight {
fn min_components_checked_div(&self, other: Weight) -> Option<u64> {
Some(sp_std::cmp::min(
self.ref_time().checked_div(other.ref_time())?,
self.proof_size().checked_div(other.proof_size())?,
))
}
}
pub trait StaticStrProvider {
const STR: &'static str;
}
#[macro_export]
macro_rules! generate_static_str_provider {
($str:expr) => {
$crate::paste::item! {
pub struct [<Str $str>];
impl $crate::StaticStrProvider for [<Str $str>] {
const STR: &'static str = stringify!($str);
}
}
};
}
pub trait RangeInclusiveExt<Idx> {
fn checked_len(&self) -> Option<Idx>;
fn saturating_len(&self) -> Idx;
}
impl<Idx> RangeInclusiveExt<Idx> for RangeInclusive<Idx>
where
Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero,
{
fn checked_len(&self) -> Option<Idx> {
self.end()
.checked_sub(self.start())
.and_then(|len| len.checked_add(&Idx::one()))
}
fn saturating_len(&self) -> Idx {
let len = match self.end().checked_sub(self.start()) {
Some(len) => len,
None => return Idx::zero(),
};
len.saturating_add(&Idx::one())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn storage_value_key_works() {
assert_eq!(
storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"),
StorageKey(
hex_literal::hex!(
"f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc"
)
.to_vec()
),
);
}
#[test]
fn generate_static_str_provider_works() {
generate_static_str_provider!(Test);
assert_eq!(StrTest::STR, "Test");
}
}