From e694489f752b9ac163cebd7378e6bf962c00bd57 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 2 Sep 2026 02:59:26 +0300 Subject: [PATCH] slightly optimize bridge in/out logic; make variable names more self descriptive; fix benchmarking for governance Signed-off-by: Uncle Stinky --- pallets/exodus/Cargo.toml | 4 +- pallets/exodus/src/lib.rs | 12 +- pallets/exodus/src/types/exodus_request.rs | 16 +- pallets/governance/Cargo.toml | 2 +- pallets/governance/src/benchmarking.rs | 75 +- pallets/governance/src/lib.rs | 4 +- pallets/governance/src/weights.rs | 6 +- pallets/helpers/Cargo.toml | 4 +- pallets/helpers/src/networks.rs | 20 +- pallets/networks/Cargo.toml | 2 +- pallets/networks/src/benchmarking.rs | 20 +- pallets/networks/src/lib.rs | 199 +++-- pallets/networks/src/tests.rs | 811 +++++---------------- pallets/networks/src/weights.rs | 8 +- pallets/traits/Cargo.toml | 2 +- pallets/traits/src/networks.rs | 14 +- pallets/weaver/Cargo.toml | 4 +- pallets/weaver/src/lib.rs | 26 +- 18 files changed, 359 insertions(+), 870 deletions(-) diff --git a/pallets/exodus/Cargo.toml b/pallets/exodus/Cargo.toml index 579ee07..e5ba579 100644 --- a/pallets/exodus/Cargo.toml +++ b/pallets/exodus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-exodus" -version = "0.0.1" +version = "0.0.2" description = "Threshold signature generation with DKG included" license.workspace = true authors.workspace = true @@ -105,4 +105,4 @@ try-runtime = [ "pallet-session/try-runtime", "frame-executive/try-runtime", "ghost-networks/try-runtime", -] \ No newline at end of file +] diff --git a/pallets/exodus/src/lib.rs b/pallets/exodus/src/lib.rs index 6b10aae..0452d55 100644 --- a/pallets/exodus/src/lib.rs +++ b/pallets/exodus/src/lib.rs @@ -497,7 +497,7 @@ pub mod pallet { who: T::AccountId, network_id: NetworkIdOf, amount: BalanceOf, - commission: Perbill, + bounty: Perbill, receiver: EvmAddress, } } @@ -1843,7 +1843,7 @@ pub mod pallet { origin: OriginFor, network_id: NetworkIdOf, amount: BalanceOf, - commission: Perbill, + bounty: Perbill, receiver: EvmAddress, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -1858,7 +1858,7 @@ pub mod pallet { Self::do_register_evm_bridge_out_exodus( network_id, amount, - commission, + bounty, receiver, )?; @@ -1866,7 +1866,7 @@ pub mod pallet { who, network_id, amount, - commission, + bounty, receiver, }); @@ -2138,7 +2138,7 @@ impl Pallet { fn do_register_evm_bridge_out_exodus( network_id: NetworkIdOf, amount: BalanceOf, - commission: Perbill, + bounty: Perbill, receiver: EvmAddress, ) -> DispatchResult { let network = T::NetworkDataHandler::get(&network_id) @@ -2156,7 +2156,7 @@ impl Pallet { exodus_session, network_id, amount, - commission, + bounty, receiver ); diff --git a/pallets/exodus/src/types/exodus_request.rs b/pallets/exodus/src/types/exodus_request.rs index aa1f499..2134e76 100644 --- a/pallets/exodus/src/types/exodus_request.rs +++ b/pallets/exodus/src/types/exodus_request.rs @@ -120,7 +120,7 @@ pub enum ExodusRequestType { exodus_session: ExodusSession, network_id: NetworkId, amount: Balance, - commission: Perbill, + bounty: Perbill, receiver: EvmAddress, }, EvmGovernance { network_id: NetworkId }, @@ -144,22 +144,22 @@ where [exodus_session, public_key, parity] ) }, - ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, commission, receiver } => { + ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, bounty, receiver } => { let mut buffer = [0u8; 32]; buffer[0..20].copy_from_slice(receiver.as_ref()); let chain_id_u64: u64 = (*network_id).unique_saturated_into(); buffer[20..28].copy_from_slice(&chain_id_u64.to_be_bytes()); - let scaled_commission = multiply_by_rational_with_rounding( - commission.deconstruct() as u128, + let scaled_bounty = multiply_by_rational_with_rounding( + bounty.deconstruct() as u128, 1u128 << 32, 1_000_000_000, sp_runtime::Rounding::Down )?; - let commission_uint32 = scaled_commission as u32; - buffer[28..32].copy_from_slice(&commission_uint32.to_be_bytes()); + let bounty_uint32 = scaled_bounty as u32; + buffer[28..32].copy_from_slice(&bounty_uint32.to_be_bytes()); let mut amount_buffer = [0u8; 32]; let amount_u128: u128 = (*amount).unique_saturated_into(); @@ -208,14 +208,14 @@ where exodus_session: ExodusSession, network_id: NetworkId, amount: Balance, - commission: Perbill, + bounty: Perbill, receiver: EvmAddress, ) -> Self { let evm_bridge_out = ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, - commission, + bounty, receiver, }; Self::default_request_for(evm_bridge_out) diff --git a/pallets/governance/Cargo.toml b/pallets/governance/Cargo.toml index dafe092..ad1d17d 100644 --- a/pallets/governance/Cargo.toml +++ b/pallets/governance/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-governance" -version = "0.3.1" +version = "0.3.2" description = "Full-chain and cross-chain governance pallet with early adopter share claims" license.workspace = true authors.workspace = true diff --git a/pallets/governance/src/benchmarking.rs b/pallets/governance/src/benchmarking.rs index 3ee9016..571a5e2 100644 --- a/pallets/governance/src/benchmarking.rs +++ b/pallets/governance/src/benchmarking.rs @@ -2,20 +2,20 @@ use super::*; use frame_benchmarking::v2::*; - use frame_support::dispatch::RawOrigin; -use ghost_helpers::merkle_tree::{generate_tree, generate_proof}; #[benchmarks(where T: Config)] mod benchmarks { use super::*; #[benchmark] - fn claim() { + fn claim( + p: Linear<1, { T::MaxProofDepth::get() }> + ) -> Result<(), BenchmarkError> { let network_id = Default::default(); let current_account: T::AccountId = frame_benchmarking::whitelisted_caller(); + let max_proof_depth = p; - let max_proof_depth = <::MaxProofDepth as Get>::get(); let minimum_donation_u128 = <::MinimumDonation as Get>::get(); let minimum_donation: BalanceOf = minimum_donation_u128.unique_saturated_into(); @@ -31,32 +31,45 @@ mod benchmarks { evm_addr_bytes.copy_from_slice(&pubkey_hash.as_ref()[12..32]); let dummy_evm_address = EvmAddress::from(evm_addr_bytes); - let total_entries = 1u64 << max_proof_depth; - let max_index = total_entries - 1; - let raw_values = (0..total_entries) - .map(|i| i as TokenId) - .collect::>(); + let target_index = 0u32; + let claim_package_init = ClaimPackage { + shares: dummy_shares, + merkle_proof: Default::default(), + token_id: 0, + index: target_index, + }; - let merkle_tree = generate_tree::( - max_index, - raw_values, - |index: TokenId| -> Result<(usize, Vec), ()> { - let claim_package = ClaimPackage { - shares: dummy_shares, - merkle_proof: Default::default(), - token_id: index, - index: index as u32, - }; + let preimage_init = claim_package_init.get_preimage(&dummy_evm_address, network_id); + let mut current_hash = SubstrateKeccakHasher::hash(&preimage_init); - let claim_preimage = claim_package.get_preimage( - &dummy_evm_address, - network_id, - ); - Ok((index as usize, claim_preimage.to_vec())) + let mut proof_hashes = Vec::with_capacity(max_proof_depth as usize); + let mut current_index = target_index as usize; + + let hash_len = SubstrateKeccakHasher::hash_len(); + let mut combined = sp_std::vec![0u8; hash_len * 2]; + + for i in 0..max_proof_depth { + let mut sibling_bytes = [0u8; 32]; + sibling_bytes[24..32].copy_from_slice(&(i as u64).to_be_bytes()); + let sibling_hash = EvmHash::from_slice(&sibling_bytes); + proof_hashes.push(sibling_hash); + + let sibling_bytes_ref = sibling_hash.as_ref(); + let hash_bytes = current_hash.as_ref(); + + if current_index % 2 == 0 { + combined[..hash_len].copy_from_slice(hash_bytes); + combined[hash_len..].copy_from_slice(sibling_bytes_ref); + } else { + combined[..hash_len].copy_from_slice(sibling_bytes_ref); + combined[hash_len..].copy_from_slice(hash_bytes); } - ).unwrap(); - let merkle_root = *merkle_tree.last().unwrap(); + current_hash = SubstrateKeccakHasher::hash(&combined); + current_index >>= 1; + } + + let merkle_root = current_hash; let member_share = MemberShare { initiated_network: None, @@ -75,15 +88,9 @@ mod benchmarks { let global_state = ShareState::new(dummy_shares); GlobalShares::::put(global_state); - let merkle_proof = generate_proof::( - &merkle_tree, - total_entries - 1, - 0, - ); - let claim_package = ClaimPackage { index: 0, - merkle_proof: BoundedVec::try_from(merkle_proof).unwrap(), + merkle_proof: BoundedVec::try_from(proof_hashes).unwrap(), shares: dummy_shares, token_id: 0 }; @@ -130,6 +137,8 @@ mod benchmarks { dummy_shares, NetworkShares::::get(&network_id).claimed_shares(), ); + + Ok(()) } impl_benchmark_test_suite!( diff --git a/pallets/governance/src/lib.rs b/pallets/governance/src/lib.rs index 79c8490..18fd7c1 100644 --- a/pallets/governance/src/lib.rs +++ b/pallets/governance/src/lib.rs @@ -308,7 +308,9 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(::WeightInfo::claim())] + #[pallet::weight(::WeightInfo::claim( + claim_package.merkle_proof.len() as u32, + ))] pub fn claim( origin: OriginFor, network_id: NetworkIdOf, diff --git a/pallets/governance/src/weights.rs b/pallets/governance/src/weights.rs index a1116ba..cb71e13 100644 --- a/pallets/governance/src/weights.rs +++ b/pallets/governance/src/weights.rs @@ -41,14 +41,14 @@ #![allow(missing_docs)] use frame_support::{ - traits::Get, + traits::Get, weights::{Weight, constants::RocksDbWeight}, }; use core::marker::PhantomData; /// Weight functions needed for `ghost_claims`. pub trait WeightInfo { - fn claim() -> Weight; + fn claim(p: u32) -> Weight; } impl WeightInfo for () { @@ -64,7 +64,7 @@ impl WeightInfo for () { /// Proof: `CultCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// Storage: `CultCollective::IndexToId` (r:0 w:6) /// Proof: `CultCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - fn claim() -> Weight { + fn claim(_p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `896` // Estimated: `16164` diff --git a/pallets/helpers/Cargo.toml b/pallets/helpers/Cargo.toml index 13591ab..17fb3db 100644 --- a/pallets/helpers/Cargo.toml +++ b/pallets/helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-helpers" -version = "0.0.5" +version = "0.0.6" description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components." license.workspace = true authors.workspace = true @@ -40,4 +40,4 @@ std = [ "sp-io/std", "sp-core/std", ] -runtime-benchmarks = [] \ No newline at end of file +runtime-benchmarks = [] diff --git a/pallets/helpers/src/networks.rs b/pallets/helpers/src/networks.rs index bf2ae51..33dfed5 100644 --- a/pallets/helpers/src/networks.rs +++ b/pallets/helpers/src/networks.rs @@ -223,8 +223,8 @@ pub struct NetworkData { pub rate_limit_delay: u64, pub block_deviation: u64, - pub incoming_fee: u32, - pub outgoing_fee: u32, + pub incoming_share: u32, + pub outgoing_share: u32, } #[derive(Default)] @@ -240,8 +240,8 @@ pub struct NetworkDataBuilder { pub rate_limit_delay: u64, pub block_deviation: u64, - pub incoming_fee: u32, - pub outgoing_fee: u32, + pub incoming_share: u32, + pub outgoing_share: u32, } impl NetworkDataBuilder { @@ -280,13 +280,13 @@ impl NetworkDataBuilder { self } - pub fn with_incoming_fee(mut self, incoming_fee: u32) -> Self { - self.incoming_fee = incoming_fee; + pub fn with_incoming_share(mut self, incoming_share: u32) -> Self { + self.incoming_share = incoming_share; self } - pub fn with_outgoing_fee(mut self, outgoing_fee: u32) -> Self { - self.outgoing_fee = outgoing_fee; + pub fn with_outgoing_share(mut self, outgoing_share: u32) -> Self { + self.outgoing_share = outgoing_share; self } @@ -328,8 +328,8 @@ impl NetworkDataBuilder { rate_limit_delay: self.rate_limit_delay, block_deviation: self.block_deviation, - incoming_fee: self.incoming_fee, - outgoing_fee: self.outgoing_fee, + incoming_share: self.incoming_share, + outgoing_share: self.outgoing_share, } } } diff --git a/pallets/networks/Cargo.toml b/pallets/networks/Cargo.toml index 0631e56..1cc236a 100644 --- a/pallets/networks/Cargo.toml +++ b/pallets/networks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-networks" -version = "0.2.7" +version = "0.2.8" description = "Registry and lifecycle management for external network metadata and cryptographic profiles." license.workspace = true authors.workspace = true diff --git a/pallets/networks/src/benchmarking.rs b/pallets/networks/src/benchmarking.rs index 10b53ae..f37ec98 100644 --- a/pallets/networks/src/benchmarking.rs +++ b/pallets/networks/src/benchmarking.rs @@ -42,8 +42,8 @@ fn prepare_network( .with_rate_limit_delay(6) .with_finality_delay(69) .with_block_deviation(420) - .with_incoming_fee(0) - .with_outgoing_fee(0) + .with_incoming_share(0) + .with_outgoing_share(0) .build(); (chain_id, network) @@ -204,27 +204,27 @@ benchmarks! { assert_ne!(GhostNetworks::::networks(chain_id), prev_network); } - update_incoming_network_fee { - let incoming_fee = 1337; + update_incoming_network_share { + let incoming_share = 1337; let (chain_id, network) = prepare_network::(1, 1, 1); let authority = T::UpdateOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let prev_network = create_network::(chain_id, network)?; - }: _(authority, chain_id, incoming_fee) + }: _(authority, chain_id, incoming_share) verify { - assert_last_event::(Event::NetworkIncomingFeeUpdated { chain_id, incoming_fee }.into()); + assert_last_event::(Event::NetworkIncomingShareUpdated { chain_id, incoming_share }.into()); assert_ne!(GhostNetworks::::networks(chain_id.clone()), prev_network); } - update_outgoing_network_fee { - let outgoing_fee = 1337; + update_outgoing_network_share { + let outgoing_share = 1337; let (chain_id, network) = prepare_network::(1, 1, 1); let authority = T::UpdateOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let prev_network = create_network::(chain_id, network)?; - }: _(authority, chain_id, outgoing_fee) + }: _(authority, chain_id, outgoing_share) verify { - assert_last_event::(Event::NetworkOutgoingFeeUpdated { chain_id, outgoing_fee }.into()); + assert_last_event::(Event::NetworkOutgoingShareUpdated { chain_id, outgoing_share }.into()); assert_ne!(GhostNetworks::::networks(chain_id), prev_network); } diff --git a/pallets/networks/src/lib.rs b/pallets/networks/src/lib.rs index 88cf651..5094ace 100644 --- a/pallets/networks/src/lib.rs +++ b/pallets/networks/src/lib.rs @@ -1,6 +1,4 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::large_enum_variant)] -#![allow(clippy::too_many_arguments)] use frame_support::{ pallet_prelude::*, @@ -12,8 +10,11 @@ use scale_info::TypeInfo; use sp_runtime::{ curve::PiecewiseLinear, - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Member, UniqueSaturatedInto}, - DispatchResult, + traits::{ + AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Member, + Saturating, UniqueSaturatedInto, + }, + DispatchResult, Perbill, }; use sp_std::{convert::TryInto, prelude::*}; @@ -42,9 +43,10 @@ pub type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct BridgeAdjustment { - pub bridged_out: Balance, - pub bridged_in: Balance, +pub struct NetworkImbalanceState { + pub outgoing: Balance, + pub incoming: Balance, + pub curve_share: Balance, } pub struct BridgedInflationCurve(core::marker::PhantomData<(RewardCurve, T)>); @@ -71,16 +73,18 @@ where _era_duration_in_millis: u64, ) -> (Balance, Balance) { let reward_curve = RewardCurve::get(); - let bridged_imbalance = BridgedImbalance::::take(); - let accumulated_commission = AccumulatedCommission::::take(); + let state = NetworkImbalance::::take(); - let accumulated_commission: Balance = accumulated_commission.into(); + let accumulated_commission: Balance = state.curve_share.into(); let adjusted_issuance: Balance = total_issuance - .saturating_add(bridged_imbalance.bridged_out.into()) - .saturating_sub(bridged_imbalance.bridged_in.into()); + .saturating_add(state.outgoing.into()) + .saturating_sub(state.incoming.into()); let estimated_reward = - reward_curve.calculate_for_fraction_times_denominator(total_staked, adjusted_issuance); + reward_curve.calculate_for_fraction_times_denominator( + total_staked, + adjusted_issuance, + ); let payout: Balance = sp_runtime::helpers_128bit::multiply_by_rational_with_rounding( estimated_reward.unique_saturated_into(), @@ -172,20 +176,19 @@ pub mod module { NetworkTypeUpdated { chain_id: T::NetworkId, network_type: NetworkType }, NetworkCurveUpdated { chain_id: T::NetworkId, network_curve: NetworkCurve }, NetworkGatekeeperUpdated { chain_id: T::NetworkId }, - NetworkIncomingFeeUpdated { chain_id: T::NetworkId, incoming_fee: u32 }, - NetworkOutgoingFeeUpdated { chain_id: T::NetworkId, outgoing_fee: u32 }, + NetworkIncomingShareUpdated { chain_id: T::NetworkId, incoming_share: u32 }, + NetworkOutgoingShareUpdated { chain_id: T::NetworkId, outgoing_share: u32 }, NetworkAvgBlockSpeedUpdated { chain_id: T::NetworkId, avg_block_speed: u64 }, NetworkRemoved { chain_id: T::NetworkId }, } #[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::getter(fn network_imbalance)] + pub type NetworkImbalance = StorageValue< + _, + NetworkImbalanceState>, + ValueQuery, + >; #[pallet::storage] #[pallet::getter(fn network_indexes)] @@ -216,8 +219,8 @@ pub mod module { >; #[pallet::storage] - #[pallet::getter(fn gatekeeper_amount)] - pub type GatekeeperAmount = + #[pallet::getter(fn gatekeeper_amounts)] + pub type GatekeeperAmounts = StorageMap<_, Twox64Concat, T::NetworkId, BalanceOf, ValueQuery>; #[pallet::genesis_config] @@ -239,7 +242,7 @@ pub mod module { .for_each(|network| { Pallet::::do_register_network(network.id, network.data.clone()) .expect("Error registering network"); - GatekeeperAmount::::insert(network.id, network.amount); + GatekeeperAmounts::::insert(network.id, network.amount); }); } @@ -366,25 +369,25 @@ pub mod module { } #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::update_incoming_network_fee())] - pub fn update_incoming_network_fee( + #[pallet::weight(T::WeightInfo::update_incoming_network_share())] + pub fn update_incoming_network_share( origin: OriginFor, chain_id: T::NetworkId, - incoming_fee: u32, + incoming_share: u32, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; - Self::do_update_incoming_network_fee(chain_id, incoming_fee) + Self::do_update_incoming_network_share(chain_id, incoming_share) } #[pallet::call_index(10)] - #[pallet::weight(T::WeightInfo::update_outgoing_network_fee())] - pub fn update_outgoing_network_fee( + #[pallet::weight(T::WeightInfo::update_outgoing_network_share())] + pub fn update_outgoing_network_share( origin: OriginFor, chain_id: T::NetworkId, - outgoing_fee: u32, + outgoing_share: u32, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; - Self::do_update_outgoing_network_fee(chain_id, outgoing_fee) + Self::do_update_outgoing_network_share(chain_id, outgoing_share) } #[pallet::call_index(11)] @@ -688,38 +691,38 @@ impl Pallet { Ok(()) } - pub fn do_update_incoming_network_fee( + pub fn do_update_incoming_network_share( chain_id: T::NetworkId, - incoming_fee: u32, + incoming_share: 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; + net.incoming_share = incoming_share; *maybe_network = Some(net.clone()); Ok(()) })?; - Self::deposit_event(Event::::NetworkIncomingFeeUpdated { + Self::deposit_event(Event::::NetworkIncomingShareUpdated { chain_id, - incoming_fee, + incoming_share, }); Ok(()) } - pub fn do_update_outgoing_network_fee( + pub fn do_update_outgoing_network_share( chain_id: T::NetworkId, - outgoing_fee: u32, + outgoing_share: 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; + net.outgoing_share = outgoing_share; *maybe_network = Some(net.clone()); Ok(()) })?; - Self::deposit_event(Event::::NetworkOutgoingFeeUpdated { + Self::deposit_event(Event::::NetworkOutgoingShareUpdated { chain_id, - outgoing_fee, + outgoing_share, }); Ok(()) } @@ -793,79 +796,61 @@ impl NetworkDataMutateHandler> for Pallet, + amount: BalanceOf, ) -> Result, ()> { - 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(()), - })?; + let incoming_share = Networks::::get(&network_id) + .map(|network| network.incoming_share) + .ok_or(())?; - Ok(new_gatekeeper_amount) - } - - fn decrease_gatekeeper_amount( - network_id: &T::NetworkId, - amount: &BalanceOf, - ) -> Result, ()> { - 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(()), - })?; - - Ok(new_gatekeeper_amount) - } - - fn accumulate_outgoing_imbalance(amount: &BalanceOf) -> Result, ()> { - 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(()), - } + GatekeeperAmounts::::try_mutate(network_id, |gatekeeper_amount| -> Result<(), ()> { + *gatekeeper_amount = gatekeeper_amount.checked_add(&amount).ok_or(())?; + Ok(()) })?; - Ok(new_bridged_out_amount) - } + let curve_share = Perbill::from_parts(incoming_share).mul_ceil(amount); + let final_amount = amount.saturating_sub(curve_share); - fn accumulate_incoming_imbalance(amount: &BalanceOf) -> Result, ()> { - 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(()), - } + NetworkImbalance::::try_mutate(|imbalance_state| -> Result<(), ()> { + imbalance_state.incoming = imbalance_state.incoming + .checked_add(&final_amount) + .ok_or(())?; + imbalance_state.curve_share = imbalance_state.curve_share + .checked_add(&curve_share) + .ok_or(())?; + Ok(()) })?; - Ok(new_bridged_in_amount) + Ok(final_amount) } - fn accumulate_commission(commission: &BalanceOf) -> Result, ()> { - AccumulatedCommission::::mutate(|accumulated| { - match accumulated.checked_add(commission) { - Some(value) => { - *accumulated = value; - Ok(value) - } - None => Err(()), - } - }) + fn register_outgoing( + network_id: &T::NetworkId, + amount: BalanceOf + ) -> Result, ()> { + let outgoing_share = Networks::::get(&network_id) + .map(|network| network.outgoing_share) + .ok_or(())?; + + GatekeeperAmounts::::try_mutate(network_id, |gatekeeper_amount| -> Result<(), ()> { + *gatekeeper_amount = gatekeeper_amount.checked_sub(&amount).ok_or(())?; + Ok(()) + })?; + + let curve_share = Perbill::from_parts(outgoing_share).mul_ceil(amount); + let final_amount = amount.saturating_sub(curve_share); + + NetworkImbalance::::try_mutate(|imbalance_state| -> Result<(), ()> { + imbalance_state.outgoing = imbalance_state.outgoing + .checked_add(&final_amount) + .ok_or(())?; + imbalance_state.curve_share = imbalance_state.curve_share + .checked_add(&curve_share) + .ok_or(())?; + Ok(()) + })?; + + Ok(final_amount) } } diff --git a/pallets/networks/src/tests.rs b/pallets/networks/src/tests.rs index 023fb3c..2180cfd 100644 --- a/pallets/networks/src/tests.rs +++ b/pallets/networks/src/tests.rs @@ -9,7 +9,10 @@ use sp_runtime::DispatchError; const CURRENT_CURVE: NetworkCurve = NetworkCurve::Secp256k1; -fn prepare_network_data() -> (u32, NetworkData) { +fn prepare_network_data( + maybe_in_share: Option, + maybe_out_share: Option, +) -> (u32, NetworkData) { ( 1u32, ghost_helpers::networks::NetworkDataBuilder::default() @@ -27,8 +30,8 @@ fn prepare_network_data() -> (u32, NetworkData) { .with_rate_limit_delay(6) .with_finality_delay(69) .with_block_deviation(420) - .with_incoming_fee(0) - .with_outgoing_fee(0) + .with_incoming_share(maybe_in_share.unwrap_or_default()) + .with_outgoing_share(maybe_out_share.unwrap_or_default()) .build(), ) } @@ -49,7 +52,7 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) { #[test] fn could_add_network_from_authority() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); @@ -73,7 +76,7 @@ fn could_add_network_from_authority() { #[test] fn could_not_add_network_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); @@ -116,7 +119,7 @@ fn could_update_network_selector_from_authority_account() { let new_selector = BoundedVec::>::try_from(vec![69, 69, 69, 69]) .unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_selector( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -140,7 +143,7 @@ fn could_add_network_endpoint_from_authority_account() { BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -172,7 +175,7 @@ fn could_add_network_endpoint_from_authority_account() { fn could_remove_network_endpoint_from_authority_account() { ExtBuilder::build().execute_with(|| { let index_to_remove = 0u32; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -209,7 +212,7 @@ fn could_update_network_endpoint_from_authority_account() { BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -249,7 +252,7 @@ fn could_update_network_endpoint_from_authority_account() { fn could_update_network_finality_delay_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_finality_delay = 1337; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_finality_delay( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -273,7 +276,7 @@ fn could_update_network_finality_delay_from_authority_account() { fn could_update_network_rate_limit_delay_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_rate_limit_delay = 1337; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_rate_limit_delay( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -297,7 +300,7 @@ fn could_update_network_rate_limit_delay_from_authority_account() { fn could_update_network_block_deviation_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_block_deviation = 1337; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_block_deviation( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -321,7 +324,7 @@ fn could_update_network_block_deviation_from_authority_account() { fn could_update_network_type_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_type = NetworkType::Utxo; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_type( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -345,7 +348,7 @@ fn could_update_network_type_from_authority_account() { fn could_update_network_curve_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_curve = NetworkCurve::Ed25519; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert!(!NetworkCurves::::contains_key(&new_curve)); @@ -384,7 +387,7 @@ fn could_update_network_gatekeeper_from_authority_account() { hex::decode("04b2c7e3d9f9f8a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0") .expect("Invalid public key"), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -402,48 +405,48 @@ fn could_update_network_gatekeeper_from_authority_account() { } #[test] -fn could_update_incoming_network_fee_from_authority_account() { +fn could_update_incoming_network_share_from_authority_account() { ExtBuilder::build().execute_with(|| { - let new_incoming_fee = 69; - let (chain_id, network) = prepare_network_data(); + let new_incoming_share = 69; + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); - assert_ok!(GhostNetworks::update_incoming_network_fee( + assert_ok!(GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, - new_incoming_fee + new_incoming_share )); System::assert_last_event(RuntimeEvent::GhostNetworks( - crate::Event::NetworkIncomingFeeUpdated { + crate::Event::NetworkIncomingShareUpdated { chain_id, - incoming_fee: new_incoming_fee, + incoming_share: new_incoming_share, }, )); let mut final_network = network.clone(); - final_network.incoming_fee = new_incoming_fee; + final_network.incoming_share = new_incoming_share; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] -fn could_update_outgoing_network_fee_from_authority_account() { +fn could_update_outgoing_network_share_from_authority_account() { ExtBuilder::build().execute_with(|| { - let new_outgoing_fee = 69; - let (chain_id, network) = prepare_network_data(); + let new_outgoing_share = 69; + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); - assert_ok!(GhostNetworks::update_outgoing_network_fee( + assert_ok!(GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, - new_outgoing_fee + new_outgoing_share )); System::assert_last_event(RuntimeEvent::GhostNetworks( - crate::Event::NetworkOutgoingFeeUpdated { + crate::Event::NetworkOutgoingShareUpdated { chain_id, - outgoing_fee: new_outgoing_fee, + outgoing_share: new_outgoing_share, }, )); let mut final_network = network.clone(); - final_network.outgoing_fee = new_outgoing_fee; + final_network.outgoing_share = new_outgoing_share; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); @@ -456,7 +459,7 @@ fn could_not_update_network_endpoint_from_random_account() { let raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_endpoint( @@ -495,7 +498,7 @@ fn could_not_add_network_endpoint_from_random_account() { let raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_endpoint( @@ -532,7 +535,7 @@ fn could_not_add_network_endpoint_from_random_account() { fn could_not_remove_network_endpoint_from_random_account() { ExtBuilder::build().execute_with(|| { let index_to_remove = 0u32; - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_endpoint( @@ -568,7 +571,7 @@ fn could_not_remove_network_endpoint_from_random_account() { #[test] fn could_not_update_network_finality_delay_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); let finality_delay = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( @@ -602,7 +605,7 @@ fn could_not_update_network_finality_delay_from_random_account() { #[test] fn could_not_update_network_rate_limit_delay_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); let rate_limit_delay = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( @@ -636,7 +639,7 @@ fn could_not_update_network_rate_limit_delay_from_random_account() { #[test] fn could_not_update_network_release_delay_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); let block_deviation = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( @@ -670,7 +673,7 @@ fn could_not_update_network_release_delay_from_random_account() { #[test] fn could_not_update_network_type_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_type( @@ -703,7 +706,7 @@ fn could_not_update_network_type_from_random_account() { #[test] fn could_not_update_network_curve_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_curve( @@ -740,7 +743,7 @@ fn could_not_update_network_gatekeeper_from_random_account() { hex::decode("04b2c7e3d9f9f8a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0") .expect("Invalid public key"), ).unwrap(); - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_gatekeeper( @@ -771,12 +774,12 @@ fn could_not_update_network_gatekeeper_from_random_account() { } #[test] -fn could_not_update_network_incoming_fee_from_random_account() { +fn could_not_update_network_incoming_share_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( - GhostNetworks::update_incoming_network_fee( + GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, 69 @@ -784,7 +787,7 @@ fn could_not_update_network_incoming_fee_from_random_account() { DispatchError::BadOrigin ); assert_err!( - GhostNetworks::update_incoming_network_fee( + GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, 69 @@ -792,7 +795,7 @@ fn could_not_update_network_incoming_fee_from_random_account() { DispatchError::BadOrigin ); assert_err!( - GhostNetworks::update_incoming_network_fee( + GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RandomAccount::get()), chain_id, 69 @@ -804,12 +807,12 @@ fn could_not_update_network_incoming_fee_from_random_account() { } #[test] -fn could_not_update_network_outgoing_fee_from_random_account() { +fn could_not_update_network_outgoing_share_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( - GhostNetworks::update_outgoing_network_fee( + GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, 69 @@ -817,7 +820,7 @@ fn could_not_update_network_outgoing_fee_from_random_account() { DispatchError::BadOrigin ); assert_err!( - GhostNetworks::update_outgoing_network_fee( + GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, 69 @@ -825,7 +828,7 @@ fn could_not_update_network_outgoing_fee_from_random_account() { DispatchError::BadOrigin ); assert_err!( - GhostNetworks::update_outgoing_network_fee( + GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RandomAccount::get()), chain_id, 69 @@ -1003,12 +1006,12 @@ fn could_not_update_gatekeeper_for_non_existent_network() { } #[test] -fn could_not_update_incoming_fee_for_non_existent_network() { +fn could_not_update_incoming_share_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( - GhostNetworks::update_incoming_network_fee( + GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 @@ -1020,12 +1023,12 @@ fn could_not_update_incoming_fee_for_non_existent_network() { } #[test] -fn could_not_update_outgoing_fee_for_non_existent_network() { +fn could_not_update_outgoing_share_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( - GhostNetworks::update_outgoing_network_fee( + GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 @@ -1039,7 +1042,7 @@ fn could_not_update_outgoing_fee_for_non_existent_network() { #[test] fn could_remove_network_from_authority_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::remove_network( RuntimeOrigin::signed(RemoverAccount::get()), @@ -1055,7 +1058,7 @@ fn could_remove_network_from_authority_account() { #[test] fn could_not_remove_network_from_random_account() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::remove_network( @@ -1098,502 +1101,10 @@ fn could_not_remove_non_existent_network() { #[test] fn bridge_storage_is_empty_by_default() { ExtBuilder::build().execute_with(|| { - assert_eq!(AccumulatedCommission::::get(), 0); - assert_eq!(BridgedImbalance::::get(), BridgeAdjustment::default()); - }); -} - -#[test] -fn gatekeeper_amount_changes_correctly() { - ExtBuilder::build().execute_with(|| { - let chain_id: u32 = 1; - let amount_in: u128 = 420; - let amount_out: u128 = 69; - let result = amount_in - 3 * amount_out; - - assert_eq!(GatekeeperAmount::::get(&chain_id), 0); - - assert_ok!(GhostNetworks::increase_gatekeeper_amount( - &chain_id, &amount_in - )); - assert_ok!(GhostNetworks::decrease_gatekeeper_amount( - &chain_id, - &amount_out - )); - assert_ok!(GhostNetworks::decrease_gatekeeper_amount( - &chain_id, - &amount_out - )); - assert_ok!(GhostNetworks::decrease_gatekeeper_amount( - &chain_id, - &amount_out - )); - - assert_eq!(GatekeeperAmount::::get(&chain_id), result); - }); -} - -#[test] -fn bridged_imbalance_accumulated_correctly() { - ExtBuilder::build().execute_with(|| { - let amount_in: u128 = 420; - let amount_out: u128 = 69; - - let imbalance_before = BridgedImbalance::::get(); - assert_eq!(imbalance_before.bridged_in, 0); - assert_eq!(imbalance_before.bridged_out, 0); - - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount_in)); - assert_ok!(GhostNetworks::accumulate_outgoing_imbalance(&amount_out)); - - let imbalance_after = BridgedImbalance::::get(); - assert_eq!(imbalance_after.bridged_in, amount_in); - assert_eq!(imbalance_after.bridged_out, amount_out); - }); -} - -#[test] -fn commission_accumulation_is_correct() { - ExtBuilder::build().execute_with(|| { - let commission_first: u128 = 420; - let commission_second: u128 = 69; - let result = commission_first + commission_second; - - assert_eq!(AccumulatedCommission::::get(), 0); - assert_ok!(GhostNetworks::accumulate_commission(&commission_first)); - assert_ok!(GhostNetworks::accumulate_commission(&commission_second)); - assert_eq!(AccumulatedCommission::::get(), result); - }); -} - -#[test] -fn commission_overflow_and_underflow_emits_error() { - ExtBuilder::build().execute_with(|| { - let commission: u128 = u128::MAX - 69; - assert_eq!(AccumulatedCommission::::get(), 0); - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_err!(GhostNetworks::accumulate_commission(&commission), ()); - assert_eq!(AccumulatedCommission::::get(), commission); - }); -} - -#[test] -fn gatekeeper_amount_overflow_and_underflow_emits_error() { - ExtBuilder::build().execute_with(|| { - let chain_id: u32 = 1; - let commission: u128 = u128::MAX - 69; - assert_eq!(GatekeeperAmount::::get(&chain_id), 0); - assert_ok!( - GhostNetworks::increase_gatekeeper_amount(&chain_id, &commission,), - commission - ); - assert_err!( - GhostNetworks::increase_gatekeeper_amount(&chain_id, &commission,), - () - ); - assert_eq!(GatekeeperAmount::::get(&chain_id), commission); - assert_ok!( - GhostNetworks::decrease_gatekeeper_amount(&chain_id, &commission,), - 0 - ); - assert_err!( - GhostNetworks::decrease_gatekeeper_amount(&chain_id, &commission,), - () - ); - assert_eq!(GatekeeperAmount::::get(&chain_id), 0); - }); -} - -#[test] -fn bridged_imbalance_overflow_emits_error() { - ExtBuilder::build().execute_with(|| { - let chain_id: u32 = 1; - let amount: u128 = u128::MAX - 69; - assert_eq!(GatekeeperAmount::::get(&chain_id), 0); - assert_ok!( - GhostNetworks::accumulate_outgoing_imbalance(&amount), - amount - ); - assert_ok!( - GhostNetworks::accumulate_incoming_imbalance(&amount), - amount - ); - - assert_err!(GhostNetworks::accumulate_outgoing_imbalance(&amount), ()); - assert_err!(GhostNetworks::accumulate_incoming_imbalance(&amount), ()); - assert_err!(GhostNetworks::accumulate_outgoing_imbalance(&u128::MAX), ()); - assert_err!(GhostNetworks::accumulate_incoming_imbalance(&u128::MAX), ()); - - let bridged_imbalance = BridgedImbalance::::get(); - assert_eq!(bridged_imbalance.bridged_out, amount); - assert_eq!(bridged_imbalance.bridged_in, amount); - }); -} - -#[test] -fn bridged_amount_overflow_and_underflow_emits_error() { - ExtBuilder::build().execute_with(|| { - let chain_id_first: u32 = 1; - let chain_id_second: u32 = 2; - let commission: u128 = u128::MAX - 69; - - assert_ok!( - GhostNetworks::increase_gatekeeper_amount(&chain_id_first, &commission,), - commission - ); - assert_ok!( - GhostNetworks::increase_gatekeeper_amount(&chain_id_second, &commission,), - commission - ); - - assert_err!( - GhostNetworks::increase_gatekeeper_amount(&chain_id_first, &u128::MAX,), - () - ); - assert_err!( - GhostNetworks::increase_gatekeeper_amount(&chain_id_first, &commission,), - () - ); - - assert_err!( - GhostNetworks::decrease_gatekeeper_amount(&chain_id_second, &u128::MAX,), - () - ); - assert_ok!( - GhostNetworks::decrease_gatekeeper_amount(&chain_id_second, &commission,), - 0 - ); - - assert_eq!(GatekeeperAmount::::get(&chain_id_first), commission); - assert_eq!(GatekeeperAmount::::get(&chain_id_second), 0); - }); -} - -#[test] -fn accumulated_commission_nullified_after_era_payout() { - ExtBuilder::build().execute_with(|| { - let commission_first: u128 = 420; - let commission_second: u128 = 69; - - assert_eq!(AccumulatedCommission::::get(), 0); - assert_ok!( - GhostNetworks::accumulate_commission(&commission_first), - commission_first - ); - assert_ok!( - GhostNetworks::accumulate_commission(&commission_second,), - commission_first + commission_second - ); - - assert_eq!( - AccumulatedCommission::::get(), - commission_first + commission_second - ); - assert_eq!( - BridgedInflationCurve::::era_payout(1, 1, 1), - (0, commission_first + commission_second) - ); - assert_eq!(AccumulatedCommission::::get(), 0); - assert_eq!( - BridgedInflationCurve::::era_payout(1, 1, 1), - (0, 0) - ); - }); -} - -#[test] -fn bridged_inlation_reward_works() { - ExtBuilder::build().execute_with(|| { - let amount_full: u128 = 1337 * 1_000_000_000; - let commission: u128 = amount_full / 100; // 1% commission - let amount: u128 = amount_full - commission; - - let total_staked_ideal: u128 = 69; - let total_staked_not_ideal: u128 = 68; - let total_issuance: u128 = 100; - - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000, - total_issuance * 1_000, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000, - 0 - ), - (0, 0) - ); - - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000, - total_issuance * 1_000, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000, - 0 - ), - (0, 0) - ); - - assert_eq!( - BridgedInflationCurve::::era_payout(1, total_issuance * 1_000, 0), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000_000_000_000_000, - 0 - ), - (0, 0) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000, - total_issuance * 1_000 + amount, - 0 - ), - (commission, 0) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (commission, 0) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (commission, 0) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000, - total_issuance * 1_000 + amount, - 0 - ), - (13177472000, 192528000) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!(13177472000 + 192528000, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (13177568884, 192431116) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!(13177568884 + 192431116, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (13177568884, 192431116) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!(13177568884 + 192431116, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000 + amount, - 0 - ), - (92386700, 13277613300) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!(92386700 + 13277613300, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (92253000, 13277747000) - ); - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - assert_eq!(92253000 + 13277747000, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (92253000, 13277747000) - ); - - assert_eq!(92253000 + 13277747000, commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000, - total_issuance * 1_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000, - total_issuance * 1_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_not_ideal * 1_000_000_000_000_000_000_000_000, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - assert_eq!( - BridgedInflationCurve::::era_payout( - 1, - total_issuance * 1_000_000_000_000_000_000_000_000 + amount, - 0 - ), - (0, 0) - ); - }); -} - -#[test] -fn bridged_inflation_era_payout_clears_storage() { - ExtBuilder::build().execute_with(|| { - let amount_full: u128 = 1337 * 1_000_000_000; - let commission: u128 = amount_full / 100; // 1% commission - let amount: u128 = amount_full - commission; - - let total_staked_ideal: u128 = 69; - let total_issuance: u128 = 100; - - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - - let bridged_adjustment = BridgeAdjustment { - bridged_in: amount, - bridged_out: 0, - }; - assert_eq!(BridgedImbalance::::get(), bridged_adjustment); - assert_eq!(AccumulatedCommission::::get(), commission); - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal * 1_000, - total_issuance * 1_000 + amount, - 0 - ), - (commission, 0) - ); - assert_eq!(BridgedImbalance::::get(), Default::default()); - assert_eq!(AccumulatedCommission::::get(), Default::default()); + let state = NetworkImbalance::::get(); + assert_eq!(state.incoming, Default::default()); + assert_eq!(state.outgoing, Default::default()); + assert_eq!(state.curve_share, Default::default()); }); } @@ -1669,105 +1180,10 @@ fn check_substrate_guarantees_not_to_overflow_u32() { }); } -#[test] -fn check_bridged_inflation_curve_for_overflow() { - ExtBuilder::build().execute_with(|| { - let amount_full: u128 = 1337 * 1_000_000_000; - let commission: u128 = amount_full / 100; // 1% commission - let amount: u128 = amount_full - commission; - - let precomputed_payout: u128 = 13177568884; - let precomputed_rest: u128 = 192431116; - assert_eq!(precomputed_payout + precomputed_rest, commission); - - let mut total_staked_ideal: u128 = 69_000; - let mut total_staked_not_ideal: u128 = 68_000; - let mut total_issuance: u128 = 100_000; - - loop { - assert_ok!(GhostNetworks::accumulate_commission(&commission)); - assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount)); - - total_staked_ideal = match total_staked_ideal.checked_mul(1_000) { - Some(value) => value, - None => break, - }; - total_staked_not_ideal = match total_staked_not_ideal.checked_mul(1_000) { - Some(value) => value, - None => break, - }; - total_issuance = match total_issuance.checked_mul(1_000) { - Some(value) => value, - None => break, - }; - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal, - total_issuance + amount, - 0 - ), - (commission, 0) - ); - } - }); -} - -#[test] -fn check_bridged_inflation_curve_for_big_commissions() { - ExtBuilder::build().execute_with(|| { - let mut amount_full: u128 = 1337; - - let total_staked_ideal: u128 = 69_000_000; - let total_staked_gt_ideal: u128 = 100_000_000; - let total_staked_lt_ideal: u128 = 3_000_000; - let total_issuance: u128 = 100_000_000; - - loop { - amount_full = match amount_full.checked_mul(1_000) { - Some(value) => value, - None => break, - }; - let commission: u128 = amount_full / 100; // 1% commission - let amount: u128 = amount_full - commission; - - AccumulatedCommission::::set(commission); - BridgedImbalance::::set(BridgeAdjustment { - bridged_in: amount, - bridged_out: 0, - }); - - assert_eq!( - BridgedInflationCurve::::era_payout( - total_staked_ideal, - total_issuance + amount, - 0 - ), - (commission, 0) - ); - - let (payout, rest) = BridgedInflationCurve::::era_payout( - total_staked_gt_ideal, - total_issuance + amount, - 0, - ); - assert!(payout < commission); - assert!(rest < commission); - - let (payout, rest) = BridgedInflationCurve::::era_payout( - total_staked_lt_ideal, - total_issuance + amount, - 0, - ); - assert!(payout < commission); - assert!(rest < commission); - } - }); -} - #[test] fn error_on_max_networks_overflow() { ExtBuilder::build().execute_with(|| { - let (chain_id, network) = prepare_network_data(); + let (chain_id, network) = prepare_network_data(None, None); let max_networks = MaxNetworks::get(); for index in 0..max_networks { @@ -1789,3 +1205,102 @@ fn error_on_max_networks_overflow() { ); }); } + +#[test] +fn bridge_inbound_and_outbound_flows() { + ExtBuilder::build().execute_with(|| { + let in_share = Some(50_000_000); // 5% + let out_share = Some(100_000_000); // 10% + + let (chain_id, network) = prepare_network_data(in_share, out_share); + register_and_check_network(chain_id, network); + + let inbound_amount: u128 = 10_000; + let expected_in_curve: u128 = 500; + let expected_in_final: u128 = 9500; + + let final_amount = Pallet::::register_incoming( + &chain_id, + inbound_amount, + ); + assert_ok!(final_amount, expected_in_final); + + let imbalance = NetworkImbalance::::get(); + assert_eq!(imbalance.incoming, expected_in_final); + assert_eq!(imbalance.curve_share, expected_in_curve); + assert_eq!(imbalance.outgoing, 0); + + let gatekeeper_balance = GatekeeperAmounts::::get(&chain_id); + assert_eq!(gatekeeper_balance, inbound_amount); + + let outbound_amount: u128 = 5_000; + let expected_out_curve: u128 = 500; + let expected_out_final: u128 = 4500; + + let final_amount = Pallet::::register_outgoing(&chain_id, outbound_amount); + assert_ok!(final_amount, expected_out_final); + + let current_imbalance = NetworkImbalance::::get(); + assert_eq!(current_imbalance.incoming, expected_in_final); + assert_eq!(current_imbalance.outgoing, expected_out_final); + assert_eq!(current_imbalance.curve_share, 1000); + + assert_eq!(current_imbalance.curve_share, expected_in_curve + expected_out_curve); + + let final_gatekeeper_balance = GatekeeperAmounts::::get(&chain_id); + assert_eq!(final_gatekeeper_balance, 5_000); + + let evil_outbound_amount: u128 = 6_000; + let final_amount_result = Pallet::::register_outgoing(&chain_id, evil_outbound_amount); + assert!(final_amount_result.is_err()); + + let post_fail_imbalance = NetworkImbalance::::get(); + assert_eq!(post_fail_imbalance.curve_share, 1000); + assert_eq!(post_fail_imbalance.outgoing, expected_out_final); + + let total_accumulated_shares = 1000; + assert_eq!(current_imbalance.curve_share, total_accumulated_shares); + + let total_staked_ideal: u128 = 69_000_000; + let total_issuance: u128 = 100_000_000; + + let (payout, rest_payout) = BridgedInflationCurve::::era_payout( + total_staked_ideal, + total_issuance, + 0, + ); + + assert_eq!(payout + rest_payout, total_accumulated_shares); + + let post_era_imbalance = NetworkImbalance::::get(); + assert_eq!(post_era_imbalance.incoming, 0); + assert_eq!(post_era_imbalance.outgoing, 0); + assert_eq!(post_era_imbalance.curve_share, 0); + }); +} + +#[test] +fn test_extreme_overflow_protection() { + ExtBuilder::build().execute_with(|| { + let in_share = Some(50_000_000); // 5% + let out_share = Some(100_000_000); // 10% + + let (chain_id, network) = prepare_network_data(in_share, out_share); + register_and_check_network(chain_id, network); + + let near_max = u128::MAX - 1000; + NetworkImbalance::::put(NetworkImbalanceState { + outgoing: 0, + incoming: near_max, + curve_share: 0, + }); + + let massive_amount = 100_000_000; + let result = Pallet::::register_incoming(&chain_id, massive_amount); + assert!(result.is_err()); + + GatekeeperAmounts::::insert(&chain_id, 420); + let result = Pallet::::register_outgoing(&chain_id, massive_amount); + assert!(result.is_err()); + }); +} diff --git a/pallets/networks/src/weights.rs b/pallets/networks/src/weights.rs index 26ad07a..ecdbc7b 100644 --- a/pallets/networks/src/weights.rs +++ b/pallets/networks/src/weights.rs @@ -58,8 +58,8 @@ pub trait WeightInfo { fn update_network_curve() -> Weight; fn update_network_gatekeeper() -> Weight; fn update_network_topic_name() -> Weight; - fn update_incoming_network_fee() -> Weight; - fn update_outgoing_network_fee() -> Weight; + fn update_incoming_network_share() -> Weight; + fn update_outgoing_network_share() -> Weight; fn update_avg_block_speed() -> Weight; fn remove_network() -> Weight; } @@ -191,7 +191,7 @@ impl WeightInfo for () { } /// Storage: `GhostNetworks::Networks` (r:1 w:1) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn update_incoming_network_fee() -> Weight { + fn update_incoming_network_share() -> Weight { // Proof Size summary in bytes: // Measured: `339` // Estimated: `3804` @@ -203,7 +203,7 @@ impl WeightInfo for () { } /// Storage: `GhostNetworks::Networks` (r:1 w:1) /// Proof: `GhostNetworks::Networks` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn update_outgoing_network_fee() -> Weight { + fn update_outgoing_network_share() -> Weight { // Proof Size summary in bytes: // Measured: `339` // Estimated: `3804` diff --git a/pallets/traits/Cargo.toml b/pallets/traits/Cargo.toml index 9ec61a5..3b70db4 100644 --- a/pallets/traits/Cargo.toml +++ b/pallets/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-traits" -version = "0.4.2" +version = "0.4.3" description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more." license.workspace = true authors.workspace = true diff --git a/pallets/traits/src/networks.rs b/pallets/traits/src/networks.rs index 5500f4b..73257b0 100644 --- a/pallets/traits/src/networks.rs +++ b/pallets/traits/src/networks.rs @@ -47,18 +47,8 @@ pub trait NetworkDataMutateHandler: NetworkDataInspectHandler< fn register(chain_id: Self::NetworkId, network: Network) -> DispatchResult; fn remove(chain_id: Self::NetworkId) -> DispatchResult; - fn increase_gatekeeper_amount( - chain_id: &Self::NetworkId, - amount: &Balance, - ) -> Result; - fn decrease_gatekeeper_amount( - chain_id: &Self::NetworkId, - amount: &Balance, - ) -> Result; - - fn accumulate_outgoing_imbalance(amount: &Balance) -> Result; - fn accumulate_incoming_imbalance(amount: &Balance) -> Result; - fn accumulate_commission(commission: &Balance) -> Result; + fn register_incoming(chain_id: &Self::NetworkId, amount: Balance) -> Result; + fn register_outgoing(chain_id: &Self::NetworkId, amount: Balance) -> Result; } pub trait NetworkRpcResolver { diff --git a/pallets/weaver/Cargo.toml b/pallets/weaver/Cargo.toml index e6e19aa..3a0ec19 100644 --- a/pallets/weaver/Cargo.toml +++ b/pallets/weaver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-weaver" -version = "0.0.1" +version = "0.0.2" description = "Weaving a secure cryptographic tapestry across different external chains." license.workspace = true authors.workspace = true @@ -76,4 +76,4 @@ try-runtime = [ "frame-system/try-runtime", "ghost-networks/try-runtime", "pallet-balances/try-runtime", -] \ No newline at end of file +] diff --git a/pallets/weaver/src/lib.rs b/pallets/weaver/src/lib.rs index f7b15e3..131903f 100644 --- a/pallets/weaver/src/lib.rs +++ b/pallets/weaver/src/lib.rs @@ -19,8 +19,8 @@ use sp_runtime::{ storage::StorageValueRef, storage_lock::{StorageLock, Time}, }, - traits::{BlockNumberProvider, Saturating, UniqueSaturatedInto}, - Perbill, RuntimeAppPublic, + traits::{BlockNumberProvider, UniqueSaturatedInto}, + RuntimeAppPublic, }; use ghost_helpers::{ @@ -179,9 +179,7 @@ pub mod pallet { ThreadAlreadyPulled, InvalidMerkleProof, InvalidReceiverAddress, - CouldNotAccumulateIncomingImbalance, - CouldNotIncreaseGatekeeperAmount, - CouldNotAccumulateCommission, + CouldNotRegisterIncoming, } #[pallet::storage] @@ -446,24 +444,14 @@ pub mod pallet { .verify_proof(root_hash, &network_data.gatekeeper) .ok_or(Error::::InvalidMerkleProof)?; - let amount = thread_proof.amount().clone(); let receiver_bytes: &[u8; 32] = receiver_account.as_ref(); let receiver = T::AccountId::decode(&mut &receiver_bytes[..]) .map_err(|_| Error::::InvalidReceiverAddress)?; - let commission = Perbill::from_parts(network_data.incoming_fee).mul_ceil(amount); - let pure_amount = amount.saturating_sub(commission); - - let _ = T::NetworkDataHandler::accumulate_incoming_imbalance(&pure_amount) - .map_err(|_| Error::::CouldNotAccumulateIncomingImbalance) - .and_then(|_| { - T::NetworkDataHandler::increase_gatekeeper_amount(&network_id, &amount) - .map_err(|_| Error::::CouldNotIncreaseGatekeeperAmount) - }) - .and_then(|_| { - T::NetworkDataHandler::accumulate_commission(&commission) - .map_err(|_| Error::::CouldNotAccumulateCommission) - })?; + let amount = thread_proof.amount(); + let pure_amount = + T::NetworkDataHandler::register_incoming(&network_id, amount) + .map_err(|_| Error::::CouldNotRegisterIncoming)?; let _ = T::Currency::deposit_creating(&receiver, pure_amount); PulledThreads::::insert(pulled_thread_key, ());