#![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::large_enum_variant)] #![allow(clippy::too_many_arguments)] use frame_support::{ pallet_prelude::*, storage::PrefixIterator, traits::{tokens::fungible::Inspect, EnsureOrigin}, }; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_runtime::{ traits::{CheckedSub, CheckedAdd, AtLeast32BitUnsigned, Member}, curve::PiecewiseLinear, DispatchResult, }; use sp_std::{prelude::*, convert::TryInto}; pub use ghost_traits::networks::{ NetworkDataBasicHandler, NetworkDataInspectHandler, NetworkDataMutateHandler, }; mod weights; pub use module::*; pub use crate::weights::WeightInfo; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; #[cfg(all(feature = "std", test))] mod mock; #[cfg(all(feature = "std", test))] mod tests; pub type BalanceOf<T> = <<T as Config>::Currency as Inspect< <T as frame_system::Config>::AccountId, >>::Balance; #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum NetworkType { Evm = 0, Utxo = 1, Undefined = 2, } impl Default for NetworkType { fn default() -> Self { NetworkType::Evm } } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct NetworkData { pub chain_name: Vec<u8>, pub default_endpoint: Vec<u8>, pub gatekeeper: Vec<u8>, pub topic_name: Vec<u8>, pub finality_delay: Option<u64>, pub release_delay: Option<u64>, pub network_type: NetworkType, pub incoming_fee: u32, pub outgoing_fee: u32, } #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct BridgeAdjustment<Balance> { pub bridged_out: Balance, pub bridged_in: Balance, } pub struct BridgedInflationCurve<RewardCurve, T>(core::marker::PhantomData<(RewardCurve, T)>); impl<Balance, RewardCurve, T> pallet_staking::EraPayout<Balance> for BridgedInflationCurve<RewardCurve, T> where Balance: Default + AtLeast32BitUnsigned + Clone + Copy + From<u128>, RewardCurve: Get<&'static PiecewiseLinear<'static>>, T: Config, { fn era_payout( total_staked: Balance, total_issuance: Balance, _era_duration_in_millis: u64, ) -> (Balance, Balance) { let piecewise_linear = RewardCurve::get(); let bridge_adjustment = BridgedImbalance::<T>::get(); let accumulated_commission = AccumulatedCommission::<T>::get(); let bridged_out: u128 = bridge_adjustment.bridged_out.try_into().unwrap_or_default(); let bridged_in: u128 = bridge_adjustment.bridged_in.try_into().unwrap_or_default(); let accumulated_commission: u128 = accumulated_commission.try_into().unwrap_or_default(); let accumulated_balance = Balance::from(accumulated_commission); let adjusted_issuance = match bridged_out > bridged_in { true => total_issuance.saturating_add(Balance::from(bridged_out - bridged_in)), false => total_issuance.saturating_sub(Balance::from(bridged_in - bridged_out)), }; NullifyNeeded::<T>::set(true); match piecewise_linear .calculate_for_fraction_times_denominator(total_staked, adjusted_issuance) .checked_mul(&accumulated_balance) .and_then(|product| product.checked_div(&adjusted_issuance)) { Some(payout) => (payout, accumulated_balance.saturating_sub(payout)), None => (Balance::default(), Balance::default()), } } } #[frame_support::pallet] pub mod module { use super::*; #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// The type used for the internal balance storage. type Currency: Inspect<Self::AccountId>; /// The type used as a unique network id. type NetworkId: Parameter + Member + Parameter + AtLeast32BitUnsigned + Default + Copy + Ord + TypeInfo + MaybeSerializeDeserialize + MaxEncodedLen; /// The origin required to register new network. type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>; /// The origin required to update network information. type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>; /// The origin required to remove network. type RemoveOrigin: EnsureOrigin<Self::RuntimeOrigin>; /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; } #[pallet::error] pub enum Error<T> { /// Network already registered. NetworkAlreadyRegistered, /// Network does not exist. NetworkDoesNotExist, /// Gatekeeper address length not 42 or prefix `0x` missed. WrongGatekeeperAddress, /// Topic name length not 66 or prefix `0x` missed. WrongTopicName, } #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event<T: Config> { NetworkRegistered { chain_id: T::NetworkId, network: NetworkData }, NetworkNameUpdated { chain_id: T::NetworkId, chain_name: Vec<u8> }, NetworkEndpointUpdated { chain_id: T::NetworkId, default_endpoint: Vec<u8> }, NetworkFinalityDelayUpdated { chain_id: T::NetworkId, finality_delay: Option<u64> }, NetworkReleaseDelayUpdated { chain_id: T::NetworkId, release_delay: Option<u64> }, NetworkTypeUpdated { chain_id: T::NetworkId, network_type: NetworkType }, NetworkGatekeeperUpdated { chain_id: T::NetworkId, gatekeeper: Vec<u8> }, NetworkTopicNameUpdated { chain_id: T::NetworkId, topic_name: Vec<u8> }, NetworkIncomingFeeUpdated { chain_id: T::NetworkId, incoming_fee: u32 }, NetworkOutgoingFeeUpdated { chain_id: T::NetworkId, outgoing_fee: u32 }, NetworkRemoved { chain_id: T::NetworkId }, } #[pallet::storage] #[pallet::getter(fn nullify_needed)] pub type NullifyNeeded<T: Config> = StorageValue<_, bool, ValueQuery>; #[pallet::storage] #[pallet::getter(fn bridged_imbalance)] pub type BridgedImbalance<T: Config> = StorageValue<_, BridgeAdjustment<BalanceOf<T>>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn accumulated_commission)] pub type AccumulatedCommission<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn networks)] pub type Networks<T: Config> = StorageMap<_, Twox64Concat, T::NetworkId, NetworkData, OptionQuery>; #[pallet::storage] #[pallet::getter(fn gatekeeper_amount)] pub type GatekeeperAmount<T: Config> = StorageMap< _, Twox64Concat, T::NetworkId, BalanceOf<T>, ValueQuery, >; #[pallet::genesis_config] pub struct GenesisConfig<T: Config> { pub networks: Vec<(T::NetworkId, Vec<u8>)>, } impl<T: Config> Default for GenesisConfig<T> { fn default() -> Self { Self { networks: vec![] } } } #[pallet::genesis_build] impl<T: Config> BuildGenesisConfig for GenesisConfig<T> { fn build(&self) { if !self.networks.is_empty() { self.networks.iter().for_each(|(chain_id, network_metadata)| { let network = NetworkData::decode(&mut &network_metadata[..]) .expect("Error decoding NetworkData"); Pallet::<T>::do_register_network(chain_id.clone(), network) .expect("Error registering network"); }); } } } #[pallet::pallet] #[pallet::without_storage_info] pub struct Pallet<T>(PhantomData<T>); #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_initialize(_: BlockNumberFor<T>) -> Weight { T::DbWeight::get().reads_writes(1, 1) } fn on_finalize(_: BlockNumberFor<T>) { if Self::nullify_needed() { Self::nullify_commission(); NullifyNeeded::<T>::put(false); } } } #[pallet::call] impl<T: Config> Pallet<T> { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::register_network( network.chain_name.len() as u32, network.default_endpoint.len() as u32, ))] pub fn register_network( origin: OriginFor<T>, chain_id: T::NetworkId, network: NetworkData, ) -> DispatchResult { T::RegisterOrigin::ensure_origin_or_root(origin)?; Self::do_register_network(chain_id, network) } #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::update_network_name( chain_name.len() as u32, ))] pub fn update_network_name( origin: OriginFor<T>, chain_id: T::NetworkId, chain_name: Vec<u8>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_name( chain_id, chain_name, ) } #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::update_network_endpoint( default_endpoint.len() as u32 ))] pub fn update_network_endpoint( origin: OriginFor<T>, chain_id: T::NetworkId, default_endpoint: Vec<u8>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_endpoint( chain_id, default_endpoint, ) } #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::update_network_finality_delay())] pub fn update_network_finality_delay( origin: OriginFor<T>, chain_id: T::NetworkId, finality_delay: Option<u64>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_finality_delay( chain_id, finality_delay, ) } #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::update_network_release_delay())] pub fn update_network_release_delay( origin: OriginFor<T>, chain_id: T::NetworkId, release_delay: Option<u64>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_release_delay( chain_id, release_delay, ) } #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::update_network_type())] pub fn update_network_type( origin: OriginFor<T>, chain_id: T::NetworkId, network_type: NetworkType, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_type( chain_id, network_type, ) } #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::update_network_gatekeeper())] pub fn update_network_gatekeeper( origin: OriginFor<T>, chain_id: T::NetworkId, gatekeeper: Vec<u8>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_gatekeeper( chain_id, gatekeeper, ) } #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::update_network_topic_name())] pub fn update_network_topic_name( origin: OriginFor<T>, chain_id: T::NetworkId, topic_name: Vec<u8>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_network_topic_name( chain_id, topic_name, ) } #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::update_incoming_network_fee())] pub fn update_incoming_network_fee( origin: OriginFor<T>, chain_id: T::NetworkId, incoming_fee: u32, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_incoming_network_fee( chain_id, incoming_fee, ) } #[pallet::call_index(9)] #[pallet::weight(T::WeightInfo::update_outgoing_network_fee())] pub fn update_outgoing_network_fee( origin: OriginFor<T>, chain_id: T::NetworkId, outgoing_fee: u32, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; Self::do_update_outgoing_network_fee( chain_id, outgoing_fee, ) } #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::remove_network())] pub fn remove_network( origin: OriginFor<T>, chain_id: T::NetworkId, ) -> DispatchResult { T::RemoveOrigin::ensure_origin_or_root(origin)?; Self::do_remove_network(chain_id) } } } impl<T: Config> Pallet<T> { /// Register a new network. pub fn do_register_network( chain_id: T::NetworkId, network: NetworkData, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_none(), Error::<T>::NetworkAlreadyRegistered); *maybe_network = Some(network.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkRegistered { chain_id, network }); Ok(()) } /// Remove existent network. pub fn do_remove_network(chain_id: T::NetworkId) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); *maybe_network = None; Ok(()) })?; Self::deposit_event(Event::<T>::NetworkRemoved { chain_id }); Ok(()) } /// Update existent network name. pub fn do_update_network_name( chain_id: T::NetworkId, chain_name: Vec<u8>, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.chain_name = chain_name.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkNameUpdated { chain_id, chain_name, }); Ok(()) } /// Update existent network default endpoint. pub fn do_update_network_endpoint( chain_id: T::NetworkId, default_endpoint: Vec<u8>, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.default_endpoint = default_endpoint.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkEndpointUpdated { chain_id, default_endpoint, }); Ok(()) } /// Update existent network default endpoint. pub fn do_update_network_finality_delay( chain_id: T::NetworkId, finality_delay: Option<u64>, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.finality_delay = finality_delay; *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkFinalityDelayUpdated { chain_id, finality_delay, }); Ok(()) } /// Update existent network default endpoint. pub fn do_update_network_release_delay( chain_id: T::NetworkId, release_delay: Option<u64>, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.release_delay = release_delay; *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkReleaseDelayUpdated { chain_id, release_delay, }); Ok(()) } /// Update existent network type. pub fn do_update_network_type( chain_id: T::NetworkId, network_type: NetworkType, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.network_type = network_type.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkTypeUpdated { chain_id, network_type, }); Ok(()) } /// Update existent network gatekeeper. pub fn do_update_network_gatekeeper( chain_id: T::NetworkId, gatekeeper: Vec<u8>, ) -> DispatchResult { ensure!(gatekeeper.len() == 42 && gatekeeper[0] == 48 && gatekeeper[1] == 120, Error::<T>::WrongGatekeeperAddress); Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.gatekeeper = gatekeeper.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkGatekeeperUpdated { chain_id, gatekeeper, }); Ok(()) } /// Update existent network gatekeeper's topic name. pub fn do_update_network_topic_name( chain_id: T::NetworkId, topic_name: Vec<u8>, ) -> DispatchResult { ensure!(topic_name.len() == 66 && topic_name[0] == 48 && topic_name[1] == 120, Error::<T>::WrongTopicName); Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.topic_name = topic_name.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkTopicNameUpdated { chain_id, topic_name, }); Ok(()) } pub fn do_update_incoming_network_fee( chain_id: T::NetworkId, incoming_fee: u32, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.incoming_fee = incoming_fee.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkIncomingFeeUpdated { chain_id, incoming_fee, }); Ok(()) } pub fn do_update_outgoing_network_fee( chain_id: T::NetworkId, outgoing_fee: u32, ) -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.outgoing_fee = outgoing_fee.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::<T>::NetworkOutgoingFeeUpdated { chain_id, outgoing_fee, }); Ok(()) } } impl<T: Config> NetworkDataBasicHandler for Pallet<T> { type NetworkId = T::NetworkId; } impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> { fn get(n: &Self::NetworkId) -> Option<NetworkData> { Networks::<T>::get(n) } fn iter() -> PrefixIterator<(Self::NetworkId, NetworkData)> { Networks::<T>::iter() } fn is_nullification_period() -> bool { NullifyNeeded::<T>::get() } } impl<T: Config> NetworkDataMutateHandler<NetworkData, BalanceOf<T>> for Pallet<T> { fn register(chain_id: Self::NetworkId, network: NetworkData) -> DispatchResult { Self::do_register_network(chain_id, network) } fn remove(chain_id: Self::NetworkId) -> DispatchResult { Self::do_remove_network(chain_id) } fn increase_gatekeeper_amount( network_id: &T::NetworkId, amount: &BalanceOf<T>, ) -> Result<BalanceOf<T>, ()> { let new_gatekeeper_amount = GatekeeperAmount::<T>::mutate(network_id, |gatekeeper_amount| { match gatekeeper_amount.checked_add(amount) { Some(value) => { *gatekeeper_amount = value; Ok(value) }, None => Err(()) } })?; Ok(new_gatekeeper_amount) } fn decrease_gatekeeper_amount( network_id: &T::NetworkId, amount: &BalanceOf<T>, ) -> Result<BalanceOf<T>, ()> { let new_gatekeeper_amount = GatekeeperAmount::<T>::mutate(network_id, |gatekeeper_amount| { match gatekeeper_amount.checked_sub(amount) { Some(value) => { *gatekeeper_amount = value; Ok(value) }, None => Err(()) } })?; Ok(new_gatekeeper_amount) } fn accumulate_outgoing_imbalance(amount: &BalanceOf<T>) -> Result<BalanceOf<T>, ()> { let new_bridged_out_amount = BridgedImbalance::<T>::mutate(|bridged_imbalance| { match bridged_imbalance.bridged_out.checked_add(amount) { Some(value) => { (*bridged_imbalance).bridged_out = value; Ok(value) }, None => Err(()) } })?; Ok(new_bridged_out_amount) } fn accumulate_incoming_imbalance(amount: &BalanceOf<T>) -> Result<BalanceOf<T>, ()> { let new_bridged_in_amount = BridgedImbalance::<T>::mutate(|bridged_imbalance| { match bridged_imbalance.bridged_in.checked_add(amount) { Some(value) => { (*bridged_imbalance).bridged_in = value; Ok(value) }, None => Err(()) } })?; Ok(new_bridged_in_amount) } fn accumulate_commission(commission: &BalanceOf<T>) -> Result<BalanceOf<T>, ()> { AccumulatedCommission::<T>::mutate(|accumulated| { match accumulated.checked_add(commission) { Some(value) => { *accumulated = value; Ok(value) }, None => Err(()), } }) } fn nullify_commission() { AccumulatedCommission::<T>::set(Default::default()); } fn trigger_nullification() { if NullifyNeeded::<T>::get() { Self::nullify_commission(); NullifyNeeded::<T>::put(false); } else { NullifyNeeded::<T>::put(true); } } }