#![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 = <::Currency as Inspect< ::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, pub default_endpoint: Vec, pub gatekeeper: Vec, pub topic_name: Vec, pub finality_delay: Option, pub release_delay: Option, pub network_type: NetworkType, pub incoming_fee: u32, pub outgoing_fee: u32, } #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct BridgeAdjustment { pub bridged_out: Balance, pub bridged_in: Balance, } pub struct BridgedInflationCurve(core::marker::PhantomData<(RewardCurve, T)>); impl pallet_staking::EraPayout for BridgedInflationCurve where Balance: Default + AtLeast32BitUnsigned + Clone + Copy + From, 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::::get(); let accumulated_commission = AccumulatedCommission::::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::::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> + IsType<::RuntimeEvent>; /// The type used for the internal balance storage. type Currency: Inspect; /// 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; /// The origin required to update network information. type UpdateOrigin: EnsureOrigin; /// The origin required to remove network. type RemoveOrigin: EnsureOrigin; /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; } #[pallet::error] pub enum Error { /// 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 { NetworkRegistered { chain_id: T::NetworkId, network: NetworkData }, NetworkNameUpdated { chain_id: T::NetworkId, chain_name: Vec }, NetworkEndpointUpdated { chain_id: T::NetworkId, default_endpoint: Vec }, NetworkFinalityDelayUpdated { chain_id: T::NetworkId, finality_delay: Option }, NetworkReleaseDelayUpdated { chain_id: T::NetworkId, release_delay: Option }, NetworkTypeUpdated { chain_id: T::NetworkId, network_type: NetworkType }, NetworkGatekeeperUpdated { chain_id: T::NetworkId, gatekeeper: Vec }, NetworkTopicNameUpdated { chain_id: T::NetworkId, topic_name: Vec }, 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 = StorageValue<_, bool, ValueQuery>; #[pallet::storage] #[pallet::getter(fn bridged_imbalance)] pub type BridgedImbalance = StorageValue<_, BridgeAdjustment>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn accumulated_commission)] pub type AccumulatedCommission = StorageValue<_, BalanceOf, ValueQuery>; #[pallet::storage] #[pallet::getter(fn networks)] pub type Networks = StorageMap<_, Twox64Concat, T::NetworkId, NetworkData, OptionQuery>; #[pallet::storage] #[pallet::getter(fn gatekeeper_amount)] pub type GatekeeperAmount = StorageMap< _, Twox64Concat, T::NetworkId, BalanceOf, ValueQuery, >; #[pallet::genesis_config] pub struct GenesisConfig { pub networks: Vec<(T::NetworkId, Vec)>, } impl Default for GenesisConfig { fn default() -> Self { Self { networks: vec![] } } } #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { 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::::do_register_network(chain_id.clone(), network) .expect("Error registering network"); }); } } } #[pallet::pallet] #[pallet::without_storage_info] pub struct Pallet(PhantomData); #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: BlockNumberFor) -> Weight { T::DbWeight::get().reads_writes(1, 1) } fn on_finalize(_: BlockNumberFor) { if Self::nullify_needed() { Self::nullify_commission(); NullifyNeeded::::put(false); } } } #[pallet::call] impl Pallet { #[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, 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, chain_id: T::NetworkId, chain_name: Vec, ) -> 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, chain_id: T::NetworkId, default_endpoint: Vec, ) -> 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, chain_id: T::NetworkId, finality_delay: Option, ) -> 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, chain_id: T::NetworkId, release_delay: Option, ) -> 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, 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, chain_id: T::NetworkId, gatekeeper: Vec, ) -> 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, chain_id: T::NetworkId, topic_name: Vec, ) -> 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, 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, 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, chain_id: T::NetworkId, ) -> DispatchResult { T::RemoveOrigin::ensure_origin_or_root(origin)?; Self::do_remove_network(chain_id) } } } impl Pallet { /// Register a new network. pub fn do_register_network( chain_id: T::NetworkId, network: NetworkData, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_none(), Error::::NetworkAlreadyRegistered); *maybe_network = Some(network.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkRegistered { chain_id, network }); Ok(()) } /// Remove existent network. pub fn do_remove_network(chain_id: T::NetworkId) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); *maybe_network = None; Ok(()) })?; Self::deposit_event(Event::::NetworkRemoved { chain_id }); Ok(()) } /// Update existent network name. pub fn do_update_network_name( chain_id: T::NetworkId, chain_name: Vec, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.chain_name = chain_name.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkNameUpdated { chain_id, chain_name, }); Ok(()) } /// Update existent network default endpoint. pub fn do_update_network_endpoint( chain_id: T::NetworkId, default_endpoint: Vec, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.default_endpoint = default_endpoint.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::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, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.finality_delay = finality_delay; *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::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, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.release_delay = release_delay; *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::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::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.network_type = network_type.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkTypeUpdated { chain_id, network_type, }); Ok(()) } /// Update existent network gatekeeper. pub fn do_update_network_gatekeeper( chain_id: T::NetworkId, gatekeeper: Vec, ) -> DispatchResult { ensure!(gatekeeper.len() == 42 && gatekeeper[0] == 48 && gatekeeper[1] == 120, Error::::WrongGatekeeperAddress); Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.gatekeeper = gatekeeper.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::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, ) -> DispatchResult { ensure!(topic_name.len() == 66 && topic_name[0] == 48 && topic_name[1] == 120, Error::::WrongTopicName); Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.topic_name = topic_name.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkTopicNameUpdated { chain_id, topic_name, }); Ok(()) } pub fn do_update_incoming_network_fee( chain_id: T::NetworkId, incoming_fee: u32, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.incoming_fee = incoming_fee.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkIncomingFeeUpdated { chain_id, incoming_fee, }); Ok(()) } pub fn do_update_outgoing_network_fee( chain_id: T::NetworkId, outgoing_fee: u32, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); let net = maybe_network.as_mut().unwrap(); net.outgoing_fee = outgoing_fee.clone(); *maybe_network = Some(net.clone()); Ok(()) })?; Self::deposit_event(Event::::NetworkOutgoingFeeUpdated { chain_id, outgoing_fee, }); Ok(()) } } impl NetworkDataBasicHandler for Pallet { type NetworkId = T::NetworkId; } impl NetworkDataInspectHandler for Pallet { fn get(n: &Self::NetworkId) -> Option { Networks::::get(n) } fn iter() -> PrefixIterator<(Self::NetworkId, NetworkData)> { Networks::::iter() } fn is_nullification_period() -> bool { NullifyNeeded::::get() } } impl NetworkDataMutateHandler> for Pallet { 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, ) -> Result<(BalanceOf, BalanceOf), ()> { let new_bridged_in_amount = BridgedImbalance::::mutate(|bridged_imbalance| { match bridged_imbalance.bridged_in.checked_add(amount) { Some(value) => { (*bridged_imbalance).bridged_in = value; Ok(value) }, None => Err(()) } })?; let new_gatekeeper_amount = GatekeeperAmount::::mutate(network_id, |gatekeeper_amount| { match gatekeeper_amount.checked_add(amount) { Some(value) => { *gatekeeper_amount = value; Ok(value) }, None => Err(()) } })?; Ok((new_gatekeeper_amount, new_bridged_in_amount)) } fn decrease_gatekeeper_amount( network_id: &T::NetworkId, amount: &BalanceOf, ) -> Result<(BalanceOf, BalanceOf), ()> { let new_gatekeeper_amount = GatekeeperAmount::::mutate(network_id, |gatekeeper_amount| { match gatekeeper_amount.checked_sub(amount) { Some(value) => { *gatekeeper_amount = value; Ok(value) }, None => Err(()) } })?; let new_bridged_out_amount = BridgedImbalance::::mutate(|bridged_imbalance| { match bridged_imbalance.bridged_out.checked_add(amount) { Some(value) => { (*bridged_imbalance).bridged_out = value; Ok(value) }, None => Err(()) } })?; Ok((new_gatekeeper_amount, new_bridged_out_amount)) } fn accumulate_commission(commission: &BalanceOf) -> Result, ()> { AccumulatedCommission::::mutate(|accumulated| { match accumulated.checked_add(commission) { Some(value) => { *accumulated = value; Ok(value) }, None => Err(()), } }) } fn nullify_commission() { AccumulatedCommission::::set(Default::default()); } }