diff --git a/Cargo.lock b/Cargo.lock index 85a8769942..69e395fba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6246,6 +6246,7 @@ dependencies = [ "darwinia-staking", "darwinia-support", "dp-asset", + "dp-fee", "dp-s2s", "frame-benchmarking", "frame-executive", diff --git a/frame/fee-market/src/benchmarking.rs b/frame/fee-market/src/benchmarking.rs index 71c62e5bed..e92751878c 100644 --- a/frame/fee-market/src/benchmarking.rs +++ b/frame/fee-market/src/benchmarking.rs @@ -24,6 +24,7 @@ use crate::Pallet as FeeMarket; use frame_benchmarking::{account, benchmarks}; use frame_support::assert_ok; use frame_system::RawOrigin; +use sp_runtime::traits::Saturating; const SEED: u32 = 0; @@ -32,7 +33,7 @@ fn fee_market_ready() { let caller1: T::AccountId = account("source", 1, SEED); let caller2: T::AccountId = account("source", 2, SEED); let caller3: T::AccountId = account("source", 3, SEED); - let collateral = T::MiniumLockCollateral::get(); + let collateral = T::CollateralPerOrder::get(); T::RingCurrency::make_free_balance_be(&caller0, collateral.saturating_mul(10u32.into())); T::RingCurrency::make_free_balance_be(&caller1, collateral.saturating_mul(10u32.into())); T::RingCurrency::make_free_balance_be(&caller2, collateral.saturating_mul(10u32.into())); @@ -68,8 +69,8 @@ benchmarks! { enroll_and_lock_collateral { fee_market_ready::(); let relayer: T::AccountId = account("source", 100, SEED); - T::RingCurrency::make_free_balance_be(&relayer, T::MiniumLockCollateral::get().saturating_mul(10u32.into())); - let lock_collateral = T::MiniumLockCollateral::get().saturating_mul(5u32.into()); + T::RingCurrency::make_free_balance_be(&relayer, T::CollateralPerOrder::get().saturating_mul(10u32.into())); + let lock_collateral = T::CollateralPerOrder::get().saturating_mul(5u32.into()); }: enroll_and_lock_collateral(RawOrigin::Signed(relayer.clone()), lock_collateral, None) verify { assert!(>::is_enrolled(&relayer)); @@ -79,21 +80,21 @@ benchmarks! { update_locked_collateral { fee_market_ready::(); let caller3: T::AccountId = account("source", 3, SEED); - let new_collateral = T::MiniumLockCollateral::get().saturating_mul(5u32.into()); + let new_collateral = T::CollateralPerOrder::get().saturating_mul(5u32.into()); }: update_locked_collateral(RawOrigin::Signed(caller3.clone()), new_collateral) verify { - let relayer = >::get_relayer(&caller3); - assert_eq!(relayer.collateral, T::MiniumLockCollateral::get().saturating_mul(5u32.into())); + let relayer = >::relayer(&caller3); + assert_eq!(relayer.collateral, T::CollateralPerOrder::get().saturating_mul(5u32.into())); } update_relay_fee { fee_market_ready::(); let caller3: T::AccountId = account("source", 3, SEED); - let new_fee = T::MinimumRelayFee::get().saturating_mul(10u32.into()); + let new_fee = T::CollateralPerOrder::get().saturating_mul(10u32.into()); }: update_relay_fee(RawOrigin::Signed(caller3.clone()), new_fee) verify { - let relayer = >::get_relayer(&caller3); - assert_eq!(relayer.fee, T::MinimumRelayFee::get().saturating_mul(10u32.into())); + let relayer = >::relayer(&caller3); + assert_eq!(relayer.fee, T::CollateralPerOrder::get().saturating_mul(10u32.into())); } cancel_enrollment { @@ -105,4 +106,6 @@ benchmarks! { assert_eq!(>::relayers().len(), 3); } + set_slash_protect { + }:set_slash_protect(RawOrigin::Root, T::CollateralPerOrder::get().saturating_mul(1u32.into())) } diff --git a/frame/fee-market/src/lib.rs b/frame/fee-market/src/lib.rs index 3d74b7b097..c83d6a47f9 100644 --- a/frame/fee-market/src/lib.rs +++ b/frame/fee-market/src/lib.rs @@ -39,10 +39,8 @@ use frame_support::{ transactional, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::*}; -use sp_runtime::{ - traits::{Saturating, UniqueSaturatedInto}, - Permill, -}; +use num_traits::Zero; +use sp_runtime::{traits::Saturating, Permill, SaturatedConversion}; use sp_std::{default::Default, vec::Vec}; // --- darwinia-network --- use darwinia_support::{ @@ -52,9 +50,8 @@ use darwinia_support::{ use dp_fee::{Order, Relayer}; pub type RingBalance = <::RingCurrency as Currency>>::Balance; -pub type Fee = RingBalance; - pub use pallet::*; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -68,14 +65,16 @@ pub mod pallet { type TreasuryPalletId: Get; #[pallet::constant] type LockId: Get; - /// The minimum locked collateral for a fee market relayer, also represented as the maximum value for slash. - #[pallet::constant] - type MiniumLockCollateral: Get>; + /// The minimum fee for relaying. #[pallet::constant] - type MinimumRelayFee: Get>; + type MinimumRelayFee: Get>; + /// The assigned relayers number for each order. #[pallet::constant] type AssignedRelayersNumber: Get; + /// The collateral relayer need to lock for each order. + #[pallet::constant] + type CollateralPerOrder: Get>; /// The slot times set #[pallet::constant] type Slot: Get; @@ -101,42 +100,41 @@ pub mod pallet { #[pallet::metadata( T::AccountId = "AccountId", RingBalance = "RingBalance", - Fee = "Fee" )] pub enum Event { - /// Relayer enrollment - EnrollAndLockCollateral(T::AccountId, RingBalance, Fee), - /// Update relayer locked collateral + /// Relayer enrollment. \[account_id, locked_collateral, relay_fee\] + Enroll(T::AccountId, RingBalance, RingBalance), + /// Update relayer locked collateral. \[account_id, new_collateral\] UpdateLockedCollateral(T::AccountId, RingBalance), - /// Update relayer fee - UpdateRelayFee(T::AccountId, Fee), - /// Relayer cancel enrollment + /// Update relayer fee. \[account_id, new_fee\] + UpdateRelayFee(T::AccountId, RingBalance), + /// Relayer cancel enrollment. \[account_id\] CancelEnrollment(T::AccountId), + /// Update collateral slash protect value. \[slash_protect_value\] + UpdateCollateralSlashProtect(RingBalance), } #[pallet::error] pub enum Error { - /// Insufficient balance + /// Insufficient balance. InsufficientBalance, - /// The locked collateral is lower than MiniumLockLimit - LockCollateralTooLow, - /// The relayer has been enrolled + /// The relayer has been enrolled. AlreadyEnrolled, - /// This relayer doesn't enroll ever + /// This relayer doesn't enroll ever. NotEnrolled, - /// Only increase lock collateral is allowed when update_locked_balance - OnlyIncreaseLockedCollateralAllowed, - /// The fee is lower than MinimumRelayFee + /// Update locked collateral is not allow since some orders are not confirm. + StillHasOrdersNotConfirmed, + /// The fee is lower than MinimumRelayFee. RelayFeeTooLow, - /// The enrolled relayers less than MIN_RELAYERS_NUMBER - TooFewEnrolledRelayers, /// The relayer is occupied, and can't cancel enrollment now. OccupiedRelayer, + /// Extend lock failed. + ExtendLockFailed, } // Enrolled relayers storage #[pallet::storage] - #[pallet::getter(fn get_relayer)] + #[pallet::getter(fn relayer)] pub type RelayersMap = StorageMap< _, Blake2_128Concat, @@ -151,22 +149,27 @@ pub mod pallet { // Priority relayers storage #[pallet::storage] #[pallet::getter(fn assigned_relayers)] - pub type AssignedRelayersStorage = + pub type AssignedRelayers = StorageValue<_, Vec>>, OptionQuery>; // Order storage #[pallet::storage] + #[pallet::getter(fn order)] pub type Orders = StorageMap< _, Blake2_128Concat, (LaneId, MessageNonce), - Order>, + Order>, OptionQuery, >; #[pallet::storage] pub type ConfirmedMessagesThisBlock = StorageValue<_, Vec<(LaneId, MessageNonce)>, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn collateral_slash_protect)] + pub type CollateralSlashProtect = StorageValue<_, RingBalance, OptionQuery>; + #[pallet::pallet] pub struct Pallet(_); #[pallet::hooks] @@ -183,30 +186,27 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Any accounts can enroll to be a relayer by lock collateral. The relay fee is optional, - /// the default value is MinimumRelayFee in runtime. + /// the default value is MinimumRelayFee in runtime. (Update market needed) /// Note: One account can enroll only once. #[pallet::weight(::WeightInfo::enroll_and_lock_collateral())] #[transactional] pub fn enroll_and_lock_collateral( origin: OriginFor, lock_collateral: RingBalance, - relay_fee: Option>, + relay_fee: Option>, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!( - lock_collateral >= T::MiniumLockCollateral::get(), - >::LockCollateralTooLow - ); + ensure!(!Self::is_enrolled(&who), >::AlreadyEnrolled); + ensure!( T::RingCurrency::free_balance(&who) >= lock_collateral, >::InsufficientBalance ); - ensure!(!Self::is_enrolled(&who), >::AlreadyEnrolled); if let Some(fee) = relay_fee { ensure!(fee >= T::MinimumRelayFee::get(), >::RelayFeeTooLow); } - let fee = relay_fee.unwrap_or_else(T::MinimumRelayFee::get); + T::RingCurrency::set_lock( T::LockId::get(), &who, @@ -215,20 +215,16 @@ pub mod pallet { }, WithdrawReasons::all(), ); - + // Store enrollment detail information. >::insert(&who, Relayer::new(who.clone(), lock_collateral, fee)); - >::append(who.clone()); + >::append(&who); Self::update_market(); - Self::deposit_event(Event::::EnrollAndLockCollateral( - who, - lock_collateral, - fee, - )); + Self::deposit_event(Event::::Enroll(who, lock_collateral, fee)); Ok(().into()) } - /// Update locked collateral for enrolled relayer, only supporting lock more. + /// Update locked collateral for enrolled relayer, only supporting lock more. (Update market needed) #[pallet::weight(::WeightInfo::update_locked_collateral())] #[transactional] pub fn update_locked_collateral( @@ -241,53 +237,89 @@ pub mod pallet { T::RingCurrency::free_balance(&who) >= new_collateral, >::InsufficientBalance ); - ensure!( - new_collateral > Self::get_relayer(&who).collateral, - >::OnlyIncreaseLockedCollateralAllowed - ); - Self::update_collateral(&who, new_collateral); - Self::deposit_event(Event::::UpdateLockedCollateral(who, new_collateral)); - Ok(().into()) - } - - /// Cancel enrolled relayer - #[pallet::weight(::WeightInfo::cancel_enrollment())] - #[transactional] - pub fn cancel_enrollment(origin: OriginFor) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - ensure!(Self::is_enrolled(&who), >::NotEnrolled); - ensure!( - >::get().len() > T::AssignedRelayersNumber::get() as usize, - >::TooFewEnrolledRelayers - ); - ensure!(!Self::is_occupied(&who), >::OccupiedRelayer); + // Increase the locked collateral + if new_collateral >= Self::relayer(&who).collateral { + let _ = T::RingCurrency::extend_lock( + T::LockId::get(), + &who, + new_collateral, + WithdrawReasons::all(), + ) + .map_err(|_| >::ExtendLockFailed); + } else { + // Decrease the locked collateral + if let Some((_, orders_locked_collateral)) = Self::occupied(&who) { + ensure!( + new_collateral >= orders_locked_collateral, + >::StillHasOrdersNotConfirmed + ); + + T::RingCurrency::remove_lock(T::LockId::get(), &who); + T::RingCurrency::set_lock( + T::LockId::get(), + &who, + LockFor::Common { + amount: new_collateral, + }, + WithdrawReasons::all(), + ); + } + } - Self::remove_enrolled_relayer(&who); - Self::deposit_event(Event::::CancelEnrollment(who)); + >::mutate(who.clone(), |relayer| { + relayer.collateral = new_collateral; + }); + Self::update_market(); + Self::deposit_event(Event::::UpdateLockedCollateral(who, new_collateral)); Ok(().into()) } - /// Update relay fee for enrolled relayer + /// Update relay fee for enrolled relayer. (Update market needed) #[pallet::weight(::WeightInfo::update_relay_fee())] #[transactional] pub fn update_relay_fee( origin: OriginFor, - relay_fee: Fee, + new_fee: RingBalance, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; ensure!(Self::is_enrolled(&who), >::NotEnrolled); ensure!( - relay_fee >= T::MinimumRelayFee::get(), + new_fee >= T::MinimumRelayFee::get(), >::RelayFeeTooLow ); >::mutate(who.clone(), |relayer| { - relayer.fee = relay_fee; + relayer.fee = new_fee; }); Self::update_market(); - Self::deposit_event(Event::::UpdateRelayFee(who, relay_fee)); + Self::deposit_event(Event::::UpdateRelayFee(who, new_fee)); + Ok(().into()) + } + + /// Cancel enrolled relayer(Update market needed) + #[pallet::weight(::WeightInfo::cancel_enrollment())] + #[transactional] + pub fn cancel_enrollment(origin: OriginFor) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!(Self::is_enrolled(&who), >::NotEnrolled); + ensure!(Self::occupied(&who).is_none(), >::OccupiedRelayer); + + Self::remove_enrolled_relayer(&who); + Self::deposit_event(Event::::CancelEnrollment(who)); + Ok(().into()) + } + + #[pallet::weight(::WeightInfo::set_slash_protect())] + #[transactional] + pub fn set_slash_protect( + origin: OriginFor, + slash_protect: RingBalance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + CollateralSlashProtect::::put(slash_protect); + Self::deposit_event(Event::::UpdateCollateralSlashProtect(slash_protect)); Ok(().into()) } } @@ -296,79 +328,70 @@ pub mod pallet { impl Pallet { /// An important update in this pallet, need to update market information in the following cases: /// - /// - When new relayer enroll - /// - When enrolled relayer wants to update relaying fee - /// - When enrolled relayer wants to cancel enrollment - /// - When some enrolled relayer's collateral below MiniumLockCollateral, might trigger market update - pub fn update_market() { - // Sort all enrolled relayers firstly. + /// - New relayer enroll. + /// - The enrolled relayer wants to update fee or order capacity. + /// - The enrolled relayer wants to cancel enrollment. + /// - The order didn't confirm in-time, slash occurred. + pub(crate) fn update_market() { + // Sort all enrolled relayers who are able to accept orders. let mut relayers: Vec>> = >::get() .iter() .map(RelayersMap::::get) + .filter(|r| Self::usable_order_capacity(&r.id) >= 1) .collect(); - relayers.sort(); // Select the first `AssignedRelayersNumber` relayers as AssignedRelayer. - // Only when total relayer's number greater than `AssignedRelayersNumber`, selection happens. let assigned_relayers_len = T::AssignedRelayersNumber::get() as usize; if relayers.len() >= assigned_relayers_len { - let mut assigned_relayers = Vec::with_capacity(assigned_relayers_len); - for i in 0..assigned_relayers_len { - if let Some(r) = relayers.get(i) { - assigned_relayers.push(r); - } - } - >::put(assigned_relayers); + relayers.sort(); + + let assigned_relayers: Vec<_> = relayers.iter().take(assigned_relayers_len).collect(); + >::put(assigned_relayers); + } else { + // The market fee comes from the last item in AssignedRelayers, + // It's would be essential to wipe this storage if relayers not enough. + >::kill(); } } - /// Update relayer locked collateral, then update market fee, this will changes RelayersMap storage. - pub fn update_collateral(who: &T::AccountId, new_collateral: RingBalance) { - // Ensure always at least `AssignedRelayersNumber` relayers are able to provide market fee. - if new_collateral < T::MiniumLockCollateral::get() - && >::get().len() > T::AssignedRelayersNumber::get() as usize - { - Self::remove_enrolled_relayer(who); - return; - } - - let _ = T::RingCurrency::extend_lock( + /// Update relayer after slash occurred, this will changes RelayersMap storage. (Update market needed) + pub(crate) fn update_relayer_after_slash(who: &T::AccountId, new_collateral: RingBalance) { + T::RingCurrency::set_lock( T::LockId::get(), - who, - new_collateral, + &who, + LockFor::Common { + amount: new_collateral, + }, WithdrawReasons::all(), ); >::mutate(who.clone(), |relayer| { relayer.collateral = new_collateral; }); + Self::update_market(); } - /// Remove enrolled relayer, then update market fee. - pub fn remove_enrolled_relayer(who: &T::AccountId) { + /// Remove enrolled relayer, then update market fee. (Update market needed) + pub(crate) fn remove_enrolled_relayer(who: &T::AccountId) { T::RingCurrency::remove_lock(T::LockId::get(), who); + >::remove(who.clone()); >::mutate(|relayers| relayers.retain(|x| x != who)); + >::mutate(|assigned_relayers| { + if let Some(relayers) = assigned_relayers { + relayers.retain(|x| x.id != *who); + } + }); Self::update_market(); } /// Whether the relayer has enrolled - pub fn is_enrolled(who: &T::AccountId) -> bool { + pub(crate) fn is_enrolled(who: &T::AccountId) -> bool { >::get().iter().any(|r| *r == *who) } - /// Get relayer fee - pub fn relayer_fee(who: &T::AccountId) -> Fee { - Self::get_relayer(who).fee - } - - /// Get relayer locked collateral - pub fn relayer_locked_collateral(who: &T::AccountId) -> RingBalance { - Self::get_relayer(who).collateral - } - - /// Get market fee(P3), If the enrolled relayers less then MIN_RELAYERS_NUMBER, return NONE. - pub fn market_fee() -> Option> { + /// Get market fee, If there is not enough relayers have order capacity to accept new order, return None. + pub fn market_fee() -> Option> { Self::assigned_relayers().and_then(|relayers| relayers.last().map(|r| r.fee)) } @@ -377,46 +400,42 @@ impl Pallet { Orders::::iter().map(|(k, _v)| k).collect() } - /// Get order info - pub fn order( - lane_id: &LaneId, - message: &MessageNonce, - ) -> Option>> { - >::get((lane_id, message)) - } - /// Whether the enrolled relayer is occupied(Responsible for order relaying) - pub fn is_occupied(who: &T::AccountId) -> bool { + /// Whether the enrolled relayer is occupied, If occupied, return the number of orders and orders locked collateral, otherwise, return None. + pub(crate) fn occupied(who: &T::AccountId) -> Option<(u32, RingBalance)> { + let mut count = 0u32; + let mut orders_locked_collateral = RingBalance::::zero(); for (_, order) in >::iter() { if order.relayers_slice().iter().any(|r| r.id == *who) && !order.is_confirmed() { - return true; + count += 1; + orders_locked_collateral = + orders_locked_collateral.saturating_add(order.locked_collateral); } } - false - } -} - -pub trait Slasher { - fn slash(base: RingBalance, _timeout: T::BlockNumber) -> RingBalance; -} -impl Slasher for () { - // The slash result = base(p3 fee) + slash_each_block * timeout - // Note: The maximum slash result is the MiniumLockCollateral. We mush ensures that all enrolled - // relayers have ability to pay this slash result. - fn slash(base: Fee, timeout: T::BlockNumber) -> RingBalance { - // Slash 20 RING for each delay block until the maximum slash value - let slash_each_block = 20_000_000_000u128; - let timeout_u128: u128 = timeout.unique_saturated_into(); - let mut slash = base.saturating_add( - timeout_u128 - .saturating_mul(slash_each_block) - .unique_saturated_into(), - ); + if count == 0 { + return None; + } + Some((count, orders_locked_collateral)) + } - if slash >= T::MiniumLockCollateral::get() { - slash = T::MiniumLockCollateral::get(); + /// The relayer collateral is composed of two part: fee_collateral and orders_locked_collateral. + /// Calculate the order capacity with fee_collateral + pub(crate) fn usable_order_capacity(who: &T::AccountId) -> u32 { + if let Some((_, orders_locked_collateral)) = Self::occupied(&who) { + let free_collateral = Self::relayer(who) + .collateral + .saturating_sub(orders_locked_collateral); + return Self::collateral_to_order_capacity(free_collateral); } - slash + Self::collateral_to_order_capacity(Self::relayer(who).collateral) } + + fn collateral_to_order_capacity(collateral: RingBalance) -> u32 { + (collateral / T::CollateralPerOrder::get()).saturated_into::() + } +} + +pub trait Slasher { + fn slash(locked_collateral: RingBalance, timeout: T::BlockNumber) -> RingBalance; } diff --git a/frame/fee-market/src/s2s/callbacks.rs b/frame/fee-market/src/s2s/callbacks.rs index 4af504c408..e4b3b1c3f5 100644 --- a/frame/fee-market/src/s2s/callbacks.rs +++ b/frame/fee-market/src/s2s/callbacks.rs @@ -27,21 +27,23 @@ pub struct FeeMarketMessageAcceptedHandler(PhantomData); impl OnMessageAccepted for FeeMarketMessageAcceptedHandler { // Called when the message is accepted by message pallet fn on_messages_accepted(lane: &LaneId, message: &MessageNonce) -> Weight { - let mut reads = 0; - let mut writes = 0; - // Create a new order based on the latest block, assign relayers which have priority to relaying let now = frame_system::Pallet::::block_number(); if let Some(assigned_relayers) = >::assigned_relayers() { - reads += 1; - let order = Order::new(*lane, *message, now, assigned_relayers, T::Slot::get()); - + let order = Order::new( + *lane, + *message, + now, + T::CollateralPerOrder::get(), + assigned_relayers, + T::Slot::get(), + ); // Store the create order - >::insert((order.lane, order.message), order); - writes += 1; + >::insert((order.lane, order.message), order.clone()); } - ::DbWeight::get().reads_writes(reads, writes) + // TODO: The returned weight should be more accurately. See: https://github.com/darwinia-network/darwinia-common/issues/911 + ::DbWeight::get().reads_writes(1, 1) } } @@ -62,6 +64,7 @@ impl OnDeliveryConfirmed for FeeMarketMessageConfirmedHandler { } } + // TODO: The returned weight should be more accurately. See: https://github.com/darwinia-network/darwinia-common/issues/911 ::DbWeight::get().reads_writes(1, 1) } } diff --git a/frame/fee-market/src/s2s/payment.rs b/frame/fee-market/src/s2s/payment.rs index f436a7def1..b9bd5f651f 100644 --- a/frame/fee-market/src/s2s/payment.rs +++ b/frame/fee-market/src/s2s/payment.rs @@ -28,7 +28,7 @@ use sp_std::{ ops::RangeInclusive, }; // --- darwinia-network --- -use crate::{Config, ConfirmedMessagesThisBlock, Orders, *}; +use crate::{Config, ConfirmedMessagesThisBlock, Orders, Pallet, *}; // --- std --- use num_traits::Zero; @@ -87,21 +87,21 @@ where } = slash_and_calculate_rewards::(messages_relayers, relayer_fund_account); // Pay confirmation relayer rewards - transfer_and_print_logs_on_error::( + do_reward::( relayer_fund_account, confirmation_relayer, confirmation_relayer_rewards, ); // Pay messages relayers rewards for (relayer, reward) in messages_relayers_rewards { - transfer_and_print_logs_on_error::(relayer_fund_account, &relayer, reward); + do_reward::(relayer_fund_account, &relayer, reward); } // Pay assign relayer reward for (relayer, reward) in assigned_relayers_rewards { - transfer_and_print_logs_on_error::(relayer_fund_account, &relayer, reward); + do_reward::(relayer_fund_account, &relayer, reward); } // Pay treasury reward - transfer_and_print_logs_on_error::( + do_reward::( relayer_fund_account, &T::TreasuryPalletId::get().into_account(), treasury_total_rewards, @@ -139,47 +139,76 @@ where break; } } - let lowest_fee = order.first_and_last_fee().0.unwrap_or_default(); - let message_fee = order.first_and_last_fee().1.unwrap_or_default(); - let message_reward; - let confirm_reward; - if let Some(who) = order.required_delivery_relayer_for_time(order_confirm_time) { - // message fee - lowest fee => treasury - let treasury_reward = message_fee.saturating_sub(lowest_fee); - treasury_total_rewards = treasury_total_rewards.saturating_add(treasury_reward); + match order.lowest_and_highest_fee() { + (Some(lowest_fee), Some(message_fee)) => { + let message_reward; + let confirm_reward; - // 60% * lowest fee => assigned_relayers_rewards - let assigned_relayers_reward = T::AssignedRelayersRewardRatio::get() * lowest_fee; - assigned_relayers_rewards - .entry(who) - .and_modify(|r| *r = r.saturating_add(assigned_relayers_reward)) - .or_insert(assigned_relayers_reward); + if let Some(who) = order.required_delivery_relayer_for_time(order_confirm_time) + { + // message fee - lowest fee => treasury + let treasury_reward = message_fee.saturating_sub(lowest_fee); + treasury_total_rewards = + treasury_total_rewards.saturating_add(treasury_reward); - let bridger_relayers_reward = lowest_fee.saturating_sub(assigned_relayers_reward); - // 80% * (1 - 60%) * lowest_fee => message relayer - message_reward = T::MessageRelayersRewardRatio::get() * bridger_relayers_reward; - // 20% * (1 - 60%) * lowest_fee => confirm relayer - confirm_reward = T::ConfirmRelayersRewardRatio::get() * bridger_relayers_reward; - } else { - // The message is delivered by common relayer instead of order assigned relayers, all assigned relayers of this order should be punished. - let slashed_reward = slash_assigned_relayers::(order, relayer_fund_account); + // 60% * lowest fee => assigned_relayers_rewards + let assigned_relayers_reward = + T::AssignedRelayersRewardRatio::get() * lowest_fee; + assigned_relayers_rewards + .entry(who) + .and_modify(|r| *r = r.saturating_add(assigned_relayers_reward)) + .or_insert(assigned_relayers_reward); - // 80% total slash => confirm relayer - message_reward = T::MessageRelayersRewardRatio::get() * slashed_reward; - // 20% total slash => confirm relayer - confirm_reward = T::ConfirmRelayersRewardRatio::get() * slashed_reward; - } + let bridger_relayers_reward = + lowest_fee.saturating_sub(assigned_relayers_reward); + // 80% * (1 - 60%) * lowest_fee => message relayer + message_reward = + T::MessageRelayersRewardRatio::get() * bridger_relayers_reward; + // 20% * (1 - 60%) * lowest_fee => confirm relayer + confirm_reward = + T::ConfirmRelayersRewardRatio::get() * bridger_relayers_reward; + } else { + // The order delivery is delay + let mut total_slash = message_fee; + + // calculate slash amount + let mut amount: RingBalance = T::Slasher::slash( + order.locked_collateral, + order.delivery_delay().unwrap_or_default(), + ); + if let Some(slash_protect) = Pallet::::collateral_slash_protect() { + amount = sp_std::cmp::min(amount, slash_protect); + } + + // Slash order's assigned relayers + let mut assigned_relayers_slash = RingBalance::::zero(); + for assigned_relayer in order.relayers_slice() { + let slashed = + do_slash::(&assigned_relayer.id, relayer_fund_account, amount); + assigned_relayers_slash += slashed; + } + total_slash += assigned_relayers_slash; + + // 80% total slash => confirm relayer + message_reward = T::MessageRelayersRewardRatio::get() * total_slash; + // 20% total slash => confirm relayer + confirm_reward = T::ConfirmRelayersRewardRatio::get() * total_slash; + } - // Update confirmation relayer total rewards - confirmation_rewards = confirmation_rewards.saturating_add(confirm_reward); - // Update message relayers total rewards - messages_rewards - .entry(message_relayer) - .and_modify(|r| *r = r.saturating_add(message_reward)) - .or_insert(message_reward); + // Update confirmation relayer total rewards + confirmation_rewards = confirmation_rewards.saturating_add(confirm_reward); + // Update message relayers total rewards + messages_rewards + .entry(message_relayer) + .and_modify(|r| *r = r.saturating_add(message_reward)) + .or_insert(message_reward); + } + _ => {} + } } } + RewardsBook { messages_relayers_rewards: messages_rewards, confirmation_relayer_rewards: confirmation_rewards, @@ -188,61 +217,45 @@ where } } -/// Slash order assigned relayers -pub fn slash_assigned_relayers( - order: Order>, - relayer_fund_account: &T::AccountId, +/// Do slash for absent assigned relayers +pub(crate) fn do_slash( + who: &T::AccountId, + fund_account: &T::AccountId, + amount: RingBalance, ) -> RingBalance { - let mut total_slash = RingBalance::::zero(); - match (order.confirm_time, order.range_end()) { - (Some(confirm_time), Some(end_time)) if confirm_time >= end_time => { - let timeout = confirm_time - end_time; - let message_fee = order.first_and_last_fee().1.unwrap_or_default(); - let slash_max = T::Slasher::slash(message_fee, timeout); - debug_assert!( - slash_max <= T::MiniumLockCollateral::get(), - "The maximum slash value returned from Slasher is MiniumLockCollateral" - ); + let locked_collateral = Pallet::::relayer(&who).collateral; + T::RingCurrency::remove_lock(T::LockId::get(), &who); + debug_assert!( + locked_collateral >= amount, + "The locked collateral must alway greater than slash max" + ); - for assigned_relayer in order.relayers_slice() { - let slashed_asset = - do_slash::(&assigned_relayer.id, relayer_fund_account, slash_max); - total_slash += slashed_asset; - } + let pay_result = ::RingCurrency::transfer( + who, + fund_account, + amount, + ExistenceRequirement::AllowDeath, + ); + match pay_result { + Ok(_) => { + crate::Pallet::::update_relayer_after_slash( + &who, + locked_collateral.saturating_sub(amount), + ); + log::trace!("Slash {:?} amount: {:?}", who, amount); + return amount; + } + Err(e) => { + crate::Pallet::::update_relayer_after_slash(&who, locked_collateral); + log::error!("Slash {:?} amount {:?}, err {:?}", who, amount, e) } - _ => {} } - total_slash -} -/// Do slash for absent assigned relayers -pub fn do_slash( - slash_account: &T::AccountId, - fund_account: &T::AccountId, - slash_max: RingBalance, -) -> RingBalance { - let slashed; - let locked_collateral = crate::Pallet::::relayer_locked_collateral(&slash_account); - T::RingCurrency::remove_lock(T::LockId::get(), &slash_account); - if locked_collateral >= slash_max { - slashed = slash_max; - let locked_reserved = locked_collateral.saturating_sub(slashed); - transfer_and_print_logs_on_error::(slash_account, fund_account, slashed); - crate::Pallet::::update_collateral(&slash_account, locked_reserved); - } else { - slashed = locked_collateral; - transfer_and_print_logs_on_error::(slash_account, fund_account, slashed); - crate::Pallet::::update_collateral(&slash_account, RingBalance::::zero()); - } - slashed + RingBalance::::zero() } -/// Do transfer -fn transfer_and_print_logs_on_error( - from: &T::AccountId, - to: &T::AccountId, - reward: RingBalance, -) { +/// Do reward +pub(crate) fn do_reward(from: &T::AccountId, to: &T::AccountId, reward: RingBalance) { if reward.is_zero() { return; } @@ -256,19 +269,13 @@ fn transfer_and_print_logs_on_error( ); match pay_result { - Ok(_) => log::trace!( - "Pay reward, from {:?} to {:?} reward: {:?}", - from, - to, - reward, - ), - - Err(error) => log::error!( - "Failed to pay reward, from {:?} to {:?} reward {:?}: {:?}", + Ok(_) => log::trace!("Reward, from {:?} to {:?} reward: {:?}", from, to, reward), + Err(e) => log::error!( + "Reward, from {:?} to {:?} reward {:?}: {:?}", from, to, reward, - error, + e, ), } } diff --git a/frame/fee-market/src/tests.rs b/frame/fee-market/src/tests.rs index bc5501ce33..066dce2b5f 100644 --- a/frame/fee-market/src/tests.rs +++ b/frame/fee-market/src/tests.rs @@ -38,7 +38,7 @@ use frame_system::mocking::*; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, UniqueSaturatedInto}, FixedU128, Permill, RuntimeDebug, }; // --- std --- @@ -48,7 +48,7 @@ use std::{collections::VecDeque, ops::RangeInclusive}; use crate::{ self as darwinia_fee_market, s2s::{ - payment::{slash_and_calculate_rewards, slash_assigned_relayers, RewardsBook}, + payment::{slash_and_calculate_rewards, RewardsBook}, FeeMarketMessageAcceptedHandler, FeeMarketMessageConfirmedHandler, }, *, @@ -420,7 +420,7 @@ frame_support::parameter_types! { pub const TreasuryPalletId: PalletId = PalletId(*b"da/trsry"); pub const FeeMarketLockId: LockIdentifier = *b"da/feelf"; pub const MinimumRelayFee: Balance = 30; - pub const MiniumLockCollateral: Balance = 100; + pub const CollateralPerOrder: Balance = 100; pub const AssignedRelayersNumber: u64 = 3; pub const Slot: u64 = 50; @@ -430,21 +430,16 @@ frame_support::parameter_types! { pub const TreasuryPalletAccount: u64 = 666; } -pub struct MockSlasher; -impl Slasher for MockSlasher { - fn slash(base: Fee, timeout: T::BlockNumber) -> RingBalance { - let slash_each_block = 2u128; - let timeout_u128: u128 = timeout.unique_saturated_into(); - let mut slash = base.saturating_add( - timeout_u128 - .saturating_mul(slash_each_block) - .unique_saturated_into(), - ); - - if slash >= T::MiniumLockCollateral::get() { - slash = T::MiniumLockCollateral::get(); - } - slash +pub struct TestSlasher; +impl Slasher for TestSlasher { + fn slash(locked_collateral: RingBalance, timeout: T::BlockNumber) -> RingBalance { + let slash_each_block = 2; + let slash_value = UniqueSaturatedInto::::unique_saturated_into(timeout) + .saturating_mul(UniqueSaturatedInto::::unique_saturated_into( + slash_each_block, + )) + .unique_saturated_into(); + sp_std::cmp::min(locked_collateral, slash_value) } } @@ -452,7 +447,7 @@ impl Config for Test { type PalletId = FeeMarketPalletId; type TreasuryPalletId = TreasuryPalletId; type LockId = FeeMarketLockId; - type MiniumLockCollateral = MiniumLockCollateral; + type CollateralPerOrder = CollateralPerOrder; type MinimumRelayFee = MinimumRelayFee; type AssignedRelayersNumber = AssignedRelayersNumber; type Slot = Slot; @@ -460,8 +455,8 @@ impl Config for Test { type AssignedRelayersRewardRatio = AssignedRelayersRewardRatio; type MessageRelayersRewardRatio = MessageRelayersRewardRatio; type ConfirmRelayersRewardRatio = ConfirmRelayersRewardRatio; - type Slasher = MockSlasher; + type Slasher = TestSlasher; type RingCurrency = Ring; type Event = Event; type WeightInfo = (); @@ -485,7 +480,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .build_storage::() .unwrap(); darwinia_balances::GenesisConfig:: { - balances: vec![(1, 150), (2, 200), (3, 350), (4, 220), (5, 350), (12, 400)], + balances: vec![ + (1, 150), + (2, 200), + (3, 350), + (4, 220), + (5, 350), + (6, 500), + (7, 500), + (8, 500), + (12, 2000), + (13, 2000), + (14, 2000), + ], } .assimilate_storage(&mut t) .unwrap(); @@ -525,13 +532,9 @@ pub fn unrewarded_relayer( } #[test] -fn test_single_relayer_registration_workflow_works() { +fn test_call_relayer_enroll_works() { new_test_ext().execute_with(|| { assert_eq!(Ring::free_balance(1), 150); - assert_err!( - FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 1, None), - >::LockCollateralTooLow - ); assert_err!( FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 200, None), >::InsufficientBalance @@ -546,122 +549,115 @@ fn test_single_relayer_registration_workflow_works() { assert_eq!(FeeMarket::relayers().len(), 1); assert_eq!(Ring::free_balance(1), 150); assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 100); + assert_eq!(FeeMarket::relayer(&1).collateral, 100); assert_eq!(FeeMarket::market_fee(), None); - assert_err!( FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, None), >::AlreadyEnrolled ); + + assert_ok!(FeeMarket::enroll_and_lock_collateral( + Origin::signed(3), + 250, + None + )); + + assert_ok!(FeeMarket::enroll_and_lock_collateral( + Origin::signed(4), + 0, + None + ),); }); } + #[test] -fn test_single_relayer_update_lock_collateral() { +fn test_call_relayer_increase_lock_collateral_works() { new_test_ext().execute_with(|| { assert_err!( - FeeMarket::update_locked_collateral(Origin::signed(1), 120), + FeeMarket::update_locked_collateral(Origin::signed(12), 100), >::NotEnrolled ); - assert_ok!(FeeMarket::enroll_and_lock_collateral( - Origin::signed(1), - 100, - None - )); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 100); - // Increase locked balance - assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(1), 120)); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 120); - // Decrease locked balance - assert_err!( - FeeMarket::update_locked_collateral(Origin::signed(1), 100), - >::OnlyIncreaseLockedCollateralAllowed - ); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(12), 200, None); + assert_eq!(FeeMarket::relayer(&12).collateral, 200); + + // Increase locked balance from 200 to 500 + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(12), 500)); + assert_eq!(FeeMarket::relayer(&12).collateral, 500); + + // Increase locked balance from 20 to 200 + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(13), 20, None); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(13), 200)); + + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(14), 300, None); + let market_fee = FeeMarket::market_fee().unwrap(); + let _ = send_regular_message(market_fee); + let _ = send_regular_message(market_fee); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(12), 800)); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(13), 800)); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(14), 800)); + assert_eq!(FeeMarket::relayer(&12).collateral, 800); + assert_eq!(FeeMarket::relayer(&13).collateral, 800); + assert_eq!(FeeMarket::relayer(&14).collateral, 800); }); } #[test] -fn test_single_relayer_cancel_registration() { +fn test_call_relayer_decrease_lock_collateral_works() { new_test_ext().execute_with(|| { - assert_err!( - FeeMarket::cancel_enrollment(Origin::signed(1)), - >::NotEnrolled - ); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(12), 800, None); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(13), 800, None); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(14), 800, None); + let market_fee = FeeMarket::market_fee().unwrap(); + let _ = send_regular_message(market_fee); + let _ = send_regular_message(market_fee); + let _ = send_regular_message(market_fee); + let _ = send_regular_message(market_fee); - assert_ok!(FeeMarket::enroll_and_lock_collateral( - Origin::signed(1), - 100, - None - )); - assert!(FeeMarket::is_enrolled(&1)); assert_err!( - FeeMarket::cancel_enrollment(Origin::signed(1)), - >::TooFewEnrolledRelayers + FeeMarket::update_locked_collateral(Origin::signed(12), 300), + >::StillHasOrdersNotConfirmed ); - assert!(FeeMarket::is_enrolled(&1)); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(12), 400)); + assert_eq!(FeeMarket::relayer(&12).collateral, 400); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(13), 500)); + assert_eq!(FeeMarket::relayer(&13).collateral, 500); + assert_ok!(FeeMarket::update_locked_collateral(Origin::signed(14), 700)); + assert_eq!(FeeMarket::relayer(&14).collateral, 700); }); } #[test] -fn test_single_relayer_update_fee_works() { +fn test_call_relayer_cancel_registration_works() { new_test_ext().execute_with(|| { assert_err!( - FeeMarket::update_relay_fee(Origin::signed(1), 1), + FeeMarket::cancel_enrollment(Origin::signed(1)), >::NotEnrolled ); + assert_ok!(FeeMarket::enroll_and_lock_collateral( Origin::signed(1), 100, None )); - assert_err!( - FeeMarket::update_relay_fee(Origin::signed(1), 1), - >::RelayFeeTooLow - ); - - assert_eq!(FeeMarket::relayer_fee(&1), 30); - assert_ok!(FeeMarket::update_relay_fee(Origin::signed(1), 40)); - assert_eq!(FeeMarket::relayer_fee(&1), 40); - }); -} - -#[test] -fn test_multiple_relayers_registration_with_same_lock_value_and_default_fee() { - new_test_ext().execute_with(|| { - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 100, None); - - assert_eq!(FeeMarket::relayers(), vec![1, 2, 3, 4]); - assert_eq!(FeeMarket::market_fee().unwrap(), 30); - }); -} + assert!(FeeMarket::is_enrolled(&1)); + assert_ok!(FeeMarket::cancel_enrollment(Origin::signed(1))); + assert!(!FeeMarket::is_enrolled(&1)); -#[test] -fn test_multiple_relayers_cancel_registration() { - new_test_ext().execute_with(|| { + System::set_block_number(2); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 100, None); - assert_eq!(FeeMarket::relayers(), vec![1, 2, 3, 4, 5]); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 100); - assert_eq!(FeeMarket::relayer_locked_collateral(&2), 100); - assert_eq!(FeeMarket::market_fee().unwrap(), 30); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(100)); + assert_eq!(FeeMarket::relayers(), vec![1, 2, 3]); assert_eq!( FeeMarket::assigned_relayers().unwrap(), vec![ Relayer::::new(1, 100, 30), - Relayer::::new(2, 100, 30), - Relayer::::new(3, 100, 30), + Relayer::::new(2, 110, 50), + Relayer::::new(3, 120, 100), ] ); - - // Test enrolled relayer not allowed to cancel cancel_enrollment when occupied - let market_fee = FeeMarket::market_fee().unwrap(); - let (_, _) = send_regular_message(market_fee); + let _ = send_regular_message(FeeMarket::market_fee().unwrap()); assert_err!( FeeMarket::cancel_enrollment(Origin::signed(1)), >::OccupiedRelayer @@ -675,83 +671,77 @@ fn test_multiple_relayers_cancel_registration() { >::OccupiedRelayer ); - assert_ok!(FeeMarket::cancel_enrollment(Origin::signed(5))); - assert!(!FeeMarket::is_enrolled(&5)); - assert_eq!(FeeMarket::relayer_locked_collateral(&5), 0); - assert_eq!(FeeMarket::relayers(), vec![1, 2, 3, 4]); - assert_eq!(FeeMarket::market_fee().unwrap(), 30); - - assert_ok!(FeeMarket::cancel_enrollment(Origin::signed(4))); - assert_err!( - FeeMarket::cancel_enrollment(Origin::signed(2)), - >::TooFewEnrolledRelayers - ); + // clean order info, then 3 is able to cancel enrollment. + System::set_block_number(3); + assert_ok!(Messages::receive_messages_delivery_proof( + Origin::signed(5), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + )); + assert_ok!(FeeMarket::cancel_enrollment(Origin::signed(3))); + assert_eq!(FeeMarket::relayers(), vec![1, 2]); + assert!(FeeMarket::assigned_relayers().is_none()); + assert!(FeeMarket::market_fee().is_none()); }); } #[test] -fn test_multiple_relayers_choose_assigned_relayers_with_same_default_fee() { +fn test_call_relayer_update_fee_works() { new_test_ext().execute_with(|| { + assert_err!( + FeeMarket::update_relay_fee(Origin::signed(1), 1), + >::NotEnrolled + ); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 130, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 140, None); - - assert_eq!(FeeMarket::relayers().len(), 5); - assert_eq!( - FeeMarket::assigned_relayers().unwrap(), - vec![ - Relayer::::new(5, 140, 30), - Relayer::::new(4, 130, 30), - Relayer::::new(3, 120, 30), - ] + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(100)); + assert_eq!(FeeMarket::market_fee(), Some(100)); + assert_err!( + FeeMarket::update_relay_fee(Origin::signed(1), 1), + >::RelayFeeTooLow ); - assert_eq!(FeeMarket::market_fee().unwrap(), 30); - }); -} -#[test] -fn test_multiple_relayers_choose_assigned_relayers_with_same_lock_balance() { - new_test_ext().execute_with(|| { - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, Some(40)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 100, Some(50)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 100, Some(60)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 100, Some(70)); + assert_eq!(FeeMarket::relayer(&1).fee, 30); + assert_ok!(FeeMarket::update_relay_fee(Origin::signed(1), 40)); + assert_eq!(FeeMarket::relayer(&1).fee, 40); - assert_eq!(FeeMarket::relayers().len(), 5); - assert_eq!( - FeeMarket::assigned_relayers().unwrap(), - vec![ - Relayer::::new(1, 100, 30), - Relayer::::new(2, 100, 40), - Relayer::::new(3, 100, 50), - ] - ); - assert_eq!(FeeMarket::market_fee().unwrap(), 50); + assert_ok!(FeeMarket::update_relay_fee(Origin::signed(3), 150)); + assert_eq!(FeeMarket::relayer(&3).fee, 150); + assert_eq!(FeeMarket::market_fee(), Some(150)); }); } #[test] -#[test] -fn test_multiple_relayers_choose_assigned_relayers_with_diff_lock_and_fee() { +fn test_rpc_market_fee_works() { new_test_ext().execute_with(|| { - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, Some(40)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(50)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 100, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, None); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(40)); + assert!(FeeMarket::market_fee().is_none()); - assert_eq!(FeeMarket::relayers().len(), 4); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 200, Some(40)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 120, Some(40)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 150, Some(50)); + assert_eq!(FeeMarket::market_fee(), Some(40)); assert_eq!( FeeMarket::assigned_relayers().unwrap(), vec![ Relayer::::new(1, 100, 30), - Relayer::::new(2, 100, 40), - Relayer::::new(3, 120, 50), + Relayer::::new(3, 200, 40), + Relayer::::new(4, 120, 40), ] ); - assert_eq!(FeeMarket::market_fee().unwrap(), 50); }); } @@ -791,7 +781,7 @@ fn receive_messages_delivery_proof() { } #[test] -fn test_order_creation_when_bridged_pallet_accept_message() { +fn test_callback_order_creation() { new_test_ext().execute_with(|| { let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); @@ -801,7 +791,10 @@ fn test_order_creation_when_bridged_pallet_accept_message() { let assigned_relayers = FeeMarket::assigned_relayers().unwrap(); let market_fee = FeeMarket::market_fee().unwrap(); let (lane, message_nonce) = send_regular_message(market_fee); - let order = FeeMarket::order(&lane, &message_nonce).unwrap(); + assert!(FeeMarket::market_fee().is_some()); + assert!(FeeMarket::assigned_relayers().is_some()); + + let order = FeeMarket::order((&lane, &message_nonce)).unwrap(); let relayers = order.relayers_slice(); assert_eq!(relayers[0].id, assigned_relayers.get(0).unwrap().id); assert_eq!(relayers[1].id, assigned_relayers.get(1).unwrap().id); @@ -811,7 +804,7 @@ fn test_order_creation_when_bridged_pallet_accept_message() { } #[test] -fn test_no_order_created_after_send_message_when_fee_market_not_ready() { +fn test_callback_no_order_created_when_fee_market_not_ready() { new_test_ext().execute_with(|| { let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); @@ -830,7 +823,7 @@ fn test_no_order_created_after_send_message_when_fee_market_not_ready() { } #[test] -fn test_order_confirm_time_set_when_bridged_pallet_confirmed_message() { +fn test_callback_order_confirm() { new_test_ext().execute_with(|| { System::set_block_number(2); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); @@ -838,19 +831,21 @@ fn test_order_confirm_time_set_when_bridged_pallet_confirmed_message() { let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(100)); let market_fee = FeeMarket::market_fee().unwrap(); let (lane, message_nonce) = send_regular_message(market_fee); - let order = FeeMarket::order(&lane, &message_nonce).unwrap(); + let order = FeeMarket::order((&lane, &message_nonce)).unwrap(); assert_eq!(order.confirm_time, None); System::set_block_number(4); receive_messages_delivery_proof(); - let order = FeeMarket::order(&lane, &message_nonce).unwrap(); + let order = FeeMarket::order((&lane, &message_nonce)).unwrap(); assert_eq!(order.confirm_time, Some(4)); assert_eq!(>::get().len(), 1); + assert!(FeeMarket::market_fee().is_some()); + assert!(FeeMarket::assigned_relayers().is_some()); }); } #[test] -fn test_payment_reward_calculation_assigned_relayer_finish_delivery_single_message() { +fn test_payment_cal_reward_normally_single_message() { new_test_ext().execute_with(|| { // Send message System::set_block_number(2); @@ -897,12 +892,12 @@ fn test_payment_reward_calculation_assigned_relayer_finish_delivery_single_messa } #[test] -fn test_payment_reward_calculation_assigned_relayer_finish_delivery_with_multiple_messages() { +fn test_payment_cal_reward_normally_multi_message() { new_test_ext().execute_with(|| { System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(300)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(500)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(1000)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 300, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 300, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 300, Some(100)); // Send message let market_fee = FeeMarket::market_fee().unwrap(); @@ -913,7 +908,7 @@ fn test_payment_reward_calculation_assigned_relayer_finish_delivery_with_multipl // Receive delivery message proof System::set_block_number(4); assert_ok!(Messages::receive_messages_delivery_proof( - Origin::signed(5), + Origin::signed(1), TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { @@ -935,26 +930,23 @@ fn test_payment_reward_calculation_assigned_relayer_finish_delivery_with_multipl let t: AccountId = ::TreasuryPalletId::get().into_account(); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( - t, 1400 + t, 140 )); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( - 1, 360 - )); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 48)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 4)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 36)); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( TEST_RELAYER_A, - 96 + 10 )); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( TEST_RELAYER_B, - 96 + 10 )); }); } #[test] -fn test_payment_reward_calculation_assigned_relayer_single_message_with_multiple_duplicated_delivery_proof( -) { +fn test_payment_cal_reward_with_duplicated_delivery_proof() { new_test_ext().execute_with(|| { System::set_block_number(2); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); @@ -1016,97 +1008,65 @@ fn test_payment_reward_calculation_assigned_relayer_single_message_with_multiple } #[test] -fn test_assigned_relayers_slash_calculation() { - new_test_ext().execute_with(|| { - System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(70)); - let market_fee = FeeMarket::market_fee().unwrap(); - let (lane, message_nonce) = send_regular_message(market_fee); - let mut order = FeeMarket::order(&lane, &message_nonce).unwrap(); - assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(Ring::usable_balance(&2), 90); - assert_eq!(Ring::usable_balance(&3), 230); - - order.confirm_time = order.range_end(); - assert_eq!(slash_assigned_relayers::(order.clone(), &0), 210); - assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(Ring::usable_balance(&2), 90); - assert_eq!(Ring::usable_balance(&3), 230); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 30); - assert_eq!(FeeMarket::relayer_locked_collateral(&2), 40); - assert_eq!(FeeMarket::relayer_locked_collateral(&3), 50); - assert_eq!(FeeMarket::assigned_relayers().unwrap().len(), 3); - - order.confirm_time = Some(order.range_end().unwrap() + 5); - assert_eq!(slash_assigned_relayers::(order, &0), 120); - assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(Ring::usable_balance(&2), 90); - assert_eq!(Ring::usable_balance(&3), 230); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 0); - assert_eq!(FeeMarket::relayer_locked_collateral(&2), 0); - assert_eq!(FeeMarket::relayer_locked_collateral(&3), 0); - assert_eq!(FeeMarket::assigned_relayers().unwrap().len(), 3); - }); -} - -#[test] -fn test_assigned_relayers_slash_kick_out() { +fn test_payment_with_slash_and_reduce_order_capacity() { new_test_ext().execute_with(|| { + // Send message System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(70)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 120, Some(80)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 400, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 400, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(8), 400, Some(100)); + assert_eq!(FeeMarket::relayer(&6).collateral, 400); let market_fee = FeeMarket::market_fee().unwrap(); - let (lane, message_nonce) = send_regular_message(market_fee); - let mut order = FeeMarket::order(&lane, &message_nonce).unwrap(); - assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(Ring::usable_balance(&2), 90); - assert_eq!(Ring::usable_balance(&3), 230); - assert_eq!( - FeeMarket::assigned_relayers().unwrap(), - vec![ - Relayer::::new(1, 100, 30), - Relayer::::new(2, 110, 50), - Relayer::::new(3, 120, 70), - ] - ); + let (_, _) = send_regular_message(market_fee); - order.confirm_time = Some(order.range_end().unwrap() + 50); - assert_eq!(slash_assigned_relayers::(order.clone(), &0), 300); - assert_eq!(Ring::usable_balance(&1), 50); - assert_eq!(Ring::usable_balance(&2), 90); - assert_eq!(Ring::usable_balance(&3), 230); - assert_eq!(FeeMarket::relayer_locked_collateral(&1), 0); - assert_eq!(FeeMarket::relayer_locked_collateral(&2), 10); - assert_eq!(FeeMarket::relayer_locked_collateral(&3), 20); - assert_eq!( - FeeMarket::assigned_relayers().unwrap(), - vec![ - Relayer::::new(2, 10, 50), - Relayer::::new(3, 20, 70), - Relayer::::new(4, 120, 80), - ] - ); - assert!(!FeeMarket::is_enrolled(&1)); + // Receive delivery message proof + System::set_block_number(2000); + assert_ok!(Messages::receive_messages_delivery_proof( + Origin::signed(5), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + )); + assert!(FeeMarket::is_enrolled(&6)); + assert!(FeeMarket::is_enrolled(&6)); + assert!(FeeMarket::is_enrolled(&6)); + assert_eq!(FeeMarket::relayer(&6).collateral, 300); + assert_eq!(FeeMarket::relayer(&7).collateral, 300); + assert_eq!(FeeMarket::relayer(&8).collateral, 300); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 80)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( + TEST_RELAYER_A, + 320 + )); }); } #[test] -fn test_payment_reward_calculation_assigned_relayers_absent_with_single_message() { +fn test_payment_slash_with_protect() { new_test_ext().execute_with(|| { // Send message System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(100)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 400, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 400, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(8), 400, Some(100)); + assert_eq!(FeeMarket::relayer(&6).collateral, 400); let market_fee = FeeMarket::market_fee().unwrap(); let (_, _) = send_regular_message(market_fee); + assert_ok!(FeeMarket::set_slash_protect(Origin::root(), 50)); // Receive delivery message proof - System::set_block_number(200); + System::set_block_number(2000); assert_ok!(Messages::receive_messages_delivery_proof( Origin::signed(5), TestMessagesDeliveryProof(Ok(( @@ -1124,21 +1084,27 @@ fn test_payment_reward_calculation_assigned_relayers_absent_with_single_message( ..Default::default() }, )); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 60)); + assert!(FeeMarket::is_enrolled(&6)); + assert!(FeeMarket::is_enrolled(&6)); + assert!(FeeMarket::is_enrolled(&6)); + assert_eq!(FeeMarket::relayer(&6).collateral, 350); + assert_eq!(FeeMarket::relayer(&7).collateral, 350); + assert_eq!(FeeMarket::relayer(&8).collateral, 350); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 50)); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( TEST_RELAYER_A, - 240 + 200 )); }); } #[test] -fn test_payment_reward_calculation_assigned_relayers_absent_with_multiple_message() { +fn test_payment_cal_slash_with_multiple_message() { new_test_ext().execute_with(|| { System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(300)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(500)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(1000)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 400, Some(300)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 400, Some(500)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(8), 400, Some(1000)); // Send message let market_fee = FeeMarket::market_fee().unwrap(); @@ -1147,7 +1113,7 @@ fn test_payment_reward_calculation_assigned_relayers_absent_with_multiple_messag assert_eq!(message_nonce1 + 1, message_nonce2); // Receive delivery message proof - System::set_block_number(200); + System::set_block_number(2000); assert_ok!(Messages::receive_messages_delivery_proof( Origin::signed(5), TestMessagesDeliveryProof(Ok(( @@ -1168,14 +1134,16 @@ fn test_payment_reward_calculation_assigned_relayers_absent_with_multiple_messag ..Default::default() }, )); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 66)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( + 5, 520 + )); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( TEST_RELAYER_A, - 240 + 1040 )); assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid( TEST_RELAYER_B, - 24 + 1040 )); }); } @@ -1184,9 +1152,9 @@ fn test_payment_reward_calculation_assigned_relayers_absent_with_multiple_messag fn test_clean_order_state_at_the_end_of_block() { new_test_ext().execute_with(|| { System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(300)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(500)); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 120, Some(1000)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 400, Some(300)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 400, Some(500)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(8), 400, Some(1000)); let market_fee = FeeMarket::market_fee().unwrap(); let (lane1, nonce1) = send_regular_message(market_fee); let (lane2, nonce2) = send_regular_message(market_fee); @@ -1216,24 +1184,24 @@ fn test_clean_order_state_at_the_end_of_block() { }, )); assert_eq!(ConfirmedMessagesThisBlock::::get().len(), 4); - assert!(FeeMarket::order(&lane1, &nonce1).is_some()); - assert!(FeeMarket::order(&lane2, &nonce2).is_some()); - assert!(FeeMarket::order(&lane3, &nonce3).is_some()); - assert!(FeeMarket::order(&lane4, &nonce4).is_some()); + assert!(FeeMarket::order((&lane1, &nonce1)).is_some()); + assert!(FeeMarket::order((&lane2, &nonce2)).is_some()); + assert!(FeeMarket::order((&lane3, &nonce3)).is_some()); + assert!(FeeMarket::order((&lane4, &nonce4)).is_some()); // Check in next block FeeMarket::on_finalize(10); System::set_block_number(1); assert_eq!(ConfirmedMessagesThisBlock::::get().len(), 0); - assert!(FeeMarket::order(&lane1, &nonce1).is_none()); - assert!(FeeMarket::order(&lane2, &nonce2).is_none()); - assert!(FeeMarket::order(&lane3, &nonce3).is_none()); - assert!(FeeMarket::order(&lane4, &nonce4).is_none()); + assert!(FeeMarket::order((&lane1, &nonce1)).is_none()); + assert!(FeeMarket::order((&lane2, &nonce2)).is_none()); + assert!(FeeMarket::order((&lane3, &nonce3)).is_none()); + assert!(FeeMarket::order((&lane4, &nonce4)).is_none()); }); } #[test] -fn test_check_submitted_fee_with_fee_market() { +fn test_fee_verification_when_send_message() { new_test_ext().execute_with(|| { let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, Some(40)); @@ -1269,3 +1237,26 @@ fn test_check_submitted_fee_with_fee_market() { ),); }); } + +#[test] +fn test_relayer_is_occupied() { + new_test_ext().execute_with(|| { + System::set_block_number(2); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 300, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 300, Some(50)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 300, Some(100)); + + // Send message + let market_fee = FeeMarket::market_fee().unwrap(); + let _ = send_regular_message(market_fee); + let _ = send_regular_message(market_fee); + + assert_eq!(FeeMarket::occupied(&5), Some((2, 200))); + assert_eq!(FeeMarket::occupied(&6), Some((2, 200))); + assert_eq!(FeeMarket::occupied(&7), Some((2, 200))); + receive_messages_delivery_proof(); + assert_eq!(FeeMarket::occupied(&5), Some((1, 100))); + assert_eq!(FeeMarket::occupied(&6), Some((1, 100))); + assert_eq!(FeeMarket::occupied(&7), Some((1, 100))); + }); +} diff --git a/frame/fee-market/src/weight.rs b/frame/fee-market/src/weight.rs index c3c5582134..31eb2315f1 100644 --- a/frame/fee-market/src/weight.rs +++ b/frame/fee-market/src/weight.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for darwinia_fee_market //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-09-29, STEPS: [10, ], REPEAT: 50, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-11-12, STEPS: [100, ], REPEAT: 50, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -32,9 +32,10 @@ // darwinia_fee_market // --execution // wasm -// --extrinsic=* +// --extrinsic +// * // --steps -// 10 +// 100 // --repeat // 50 // --raw @@ -57,53 +58,64 @@ pub trait WeightInfo { fn update_locked_collateral() -> Weight; fn update_relay_fee() -> Weight; fn cancel_enrollment() -> Weight; + fn set_slash_protect() -> Weight; } /// Weights for darwinia_fee_market using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn enroll_and_lock_collateral() -> Weight { - (93_604_000 as Weight) - .saturating_add(T::DbWeight::get().reads(11 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + (125_404_000 as Weight) + .saturating_add(T::DbWeight::get().reads(12 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn update_locked_collateral() -> Weight { - (86_772_000 as Weight) - .saturating_add(T::DbWeight::get().reads(11 as Weight)) - .saturating_add(T::DbWeight::get().writes(6 as Weight)) + (111_939_000 as Weight) + .saturating_add(T::DbWeight::get().reads(12 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) } fn update_relay_fee() -> Weight { - (62_427_000 as Weight) - .saturating_add(T::DbWeight::get().reads(9 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) + (89_316_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn cancel_enrollment() -> Weight { - (83_676_000 as Weight) - .saturating_add(T::DbWeight::get().reads(10 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + (110_125_000 as Weight) + .saturating_add(T::DbWeight::get().reads(12 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) + } + fn set_slash_protect() -> Weight { + (17_924_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } } // For backwards compatibility and tests impl WeightInfo for () { fn enroll_and_lock_collateral() -> Weight { - (93_604_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(11 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + (125_404_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(12 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } fn update_locked_collateral() -> Weight { - (86_772_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(11 as Weight)) - .saturating_add(RocksDbWeight::get().writes(6 as Weight)) + (111_939_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(12 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } fn update_relay_fee() -> Weight { - (62_427_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(9 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + (89_316_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(10 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn cancel_enrollment() -> Weight { - (83_676_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(10 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + (110_125_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(12 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) + } + fn set_slash_protect() -> Weight { + (17_924_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } } diff --git a/node/runtime/pangolin/src/lib.rs b/node/runtime/pangolin/src/lib.rs index c6ade84ef8..5c6b87031d 100644 --- a/node/runtime/pangolin/src/lib.rs +++ b/node/runtime/pangolin/src/lib.rs @@ -800,23 +800,94 @@ impl dvm_rpc_runtime_api::ConvertTransaction for TransactionCon #[allow(unused)] fn migrate() -> Weight { + use dp_fee::Relayer; // TODO: Move to S2S // const CrabBackingPalletId: PalletId = PalletId(*b"da/crabk"); // const CrabIssuingPalletId: PalletId = PalletId(*b"da/crais"); + use bp_messages::{LaneId, MessageNonce}; + use dp_fee::{Order, PriorRelayer}; + use frame_support::{Blake2_128Concat, StorageHasher}; + + log::info!("===> Start migrate all storage items in AssignedRelayersStorage"); + if let Some(value) = migration::take_storage_value::>>( + b"FeeMarket", + b"AssignedRelayersStorage", + &[], + ) { + log::info!("the migrate content {:?}", value); + migration::put_storage_value(b"FeeMarket", b"AssignedRelayers", &[], value); + } + log::info!("===> End migrate all storage items in AssignedRelayersStorage"); + + log::info!("===> Start migrate all storage items in Orders"); + #[derive(Encode, Decode, Debug, Clone)] + struct OldOrder { + lane: LaneId, + message: MessageNonce, + sent_time: BlockNumber, + confirm_time: Option, + relayers: Vec>, + } + for (index, order) in migration::storage_iter::(b"FeeMarket", b"Orders").drain() { + let new_order = Order { + lane: order.lane, + message: order.message, + sent_time: order.sent_time, + confirm_time: order.confirm_time, + locked_collateral: 100 * COIN, + relayers: order.relayers, + }; + log::info!("The new order: order {:?}", new_order); + let hash = Blake2_128Concat::hash(&(new_order.lane, new_order.message).encode()); + migration::put_storage_value(b"FeeMarket", b"Orders", &hash, new_order); + } + log::info!("===> End migrate all storage items in Orders"); + log::info!("===> All Migrates finished"); - 0 - // RuntimeBlockWeights::get().max_block + // 0 + RuntimeBlockWeights::get().max_block } pub struct CustomOnRuntimeUpgrade; impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result<(), &'static str> { + assert!(migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayersStorage", + &[] + )); + assert!(!migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayers", + &[] + )); Ok(()) } #[cfg(feature = "try-runtime")] fn post_upgrade() -> Result<(), &'static str> { + use dp_fee::Order; + assert!(!migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayersStorage", + &[] + )); + assert!(migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayers", + &[] + )); + + for (_index, new_order) in + migration::storage_iter::>( + b"FeeMarket", + b"Orders", + ) + .drain() + { + assert_eq!(new_order.locked_collateral, 100 * COIN); + } Ok(()) } diff --git a/node/runtime/pangolin/src/pallets/fee_market.rs b/node/runtime/pangolin/src/pallets/fee_market.rs index ea6046efc4..2f501ca4f8 100644 --- a/node/runtime/pangolin/src/pallets/fee_market.rs +++ b/node/runtime/pangolin/src/pallets/fee_market.rs @@ -1,9 +1,9 @@ // --- substrate --- use frame_support::{traits::LockIdentifier, PalletId}; +use sp_runtime::{traits::UniqueSaturatedInto, Permill}; // --- darwinia --- use crate::*; -use darwinia_fee_market::Config; -use sp_runtime::Permill; +use darwinia_fee_market::{Config, RingBalance, Slasher}; frame_support::parameter_types! { pub const FeeMarketPalletId: PalletId = PalletId(*b"da/feemk"); @@ -11,9 +11,9 @@ frame_support::parameter_types! { pub const AssignedRelayersNumber: u64 = 1; pub const FeeMarketLockId: LockIdentifier = *b"da/feelf"; - pub const MiniumLockCollateral: Balance = 3000 * COIN; pub const MinimumRelayFee: Balance = 15 * COIN; pub const Slot: BlockNumber = 50; + pub const CollateralPerOrder: Balance = 100 * COIN; pub const AssignedRelayersRewardRatio: Permill = Permill::from_percent(60); pub const MessageRelayersRewardRatio: Permill = Permill::from_percent(80); @@ -26,16 +26,29 @@ impl Config for Runtime { type LockId = FeeMarketLockId; type AssignedRelayersNumber = AssignedRelayersNumber; - type MiniumLockCollateral = MiniumLockCollateral; type MinimumRelayFee = MinimumRelayFee; + type CollateralPerOrder = CollateralPerOrder; type Slot = Slot; type AssignedRelayersRewardRatio = AssignedRelayersRewardRatio; type MessageRelayersRewardRatio = MessageRelayersRewardRatio; type ConfirmRelayersRewardRatio = ConfirmRelayersRewardRatio; - type Slasher = (); + type Slasher = FeeMarketSlasher; type RingCurrency = Ring; type Event = Event; type WeightInfo = (); } + +pub struct FeeMarketSlasher; +impl Slasher for FeeMarketSlasher { + fn slash(locked_collateral: RingBalance, timeout: T::BlockNumber) -> RingBalance { + let slash_each_block = 2 * COIN; + let slash_value = UniqueSaturatedInto::::unique_saturated_into(timeout) + .saturating_mul(UniqueSaturatedInto::::unique_saturated_into( + slash_each_block, + )) + .unique_saturated_into(); + sp_std::cmp::min(locked_collateral, slash_value) + } +} diff --git a/node/runtime/pangolin/types.json b/node/runtime/pangolin/types.json index 188a9664d3..790a519627 100644 --- a/node/runtime/pangolin/types.json +++ b/node/runtime/pangolin/types.json @@ -258,12 +258,12 @@ "collateral": "Balance", "fee": "Balance" }, - "Fee": "Balance", "Order": { "lane": "LaneId", "message": "MessageNonce", "sent_time": "BlockNumber", "confirm_time": "BlockNumber", + "locked_collateral": "Balance", "assigned_relayers": "Vec" }, "PriorRelayer": { diff --git a/node/runtime/pangoro/Cargo.toml b/node/runtime/pangoro/Cargo.toml index 908d43183d..23c4adbf94 100644 --- a/node/runtime/pangoro/Cargo.toml +++ b/node/runtime/pangoro/Cargo.toml @@ -24,6 +24,7 @@ darwinia-fee-market-rpc-runtime-api = { default-features = false, path = "../../ darwinia-staking = { default-features = false, path = "../../../frame/staking" } darwinia-support = { default-features = false, path = "../../../frame/support" } dp-asset = { default-features = false, path = "../../../primitives/asset" } +dp-fee = { default-features = false, path = "../../../primitives/fee-market" } dp-s2s = { default-features = false, path = "../../../primitives/s2s" } module-transaction-pause = { default-features = false, path = "../../../frame/transaction-pause" } pangolin-constants = { default-features = false, path = "../pangolin/src/constants" } @@ -100,6 +101,7 @@ std = [ "darwinia-staking/std", "darwinia-support/std", "dp-asset/std", + "dp-fee/std", "dp-s2s/std", "module-transaction-pause/std", "pangolin-constants/std", diff --git a/node/runtime/pangoro/src/lib.rs b/node/runtime/pangoro/src/lib.rs index c5489c1f1e..c46e3a5bab 100644 --- a/node/runtime/pangoro/src/lib.rs +++ b/node/runtime/pangoro/src/lib.rs @@ -72,6 +72,8 @@ use codec::{Decode, Encode}; use bridge_runtime_common::messages::{ source::estimate_message_dispatch_and_delivery_fee, MessageBridge, }; +#[allow(unused)] +use frame_support::migration; use frame_support::{ traits::{KeyOwnerProofSystem, OnRuntimeUpgrade}, weights::Weight, @@ -545,22 +547,90 @@ sp_api::impl_runtime_apis! { #[allow(unused)] fn migrate() -> Weight { // --- paritytech --- - #[allow(unused)] - use frame_support::migration; + use bp_messages::{LaneId, MessageNonce}; + use dp_fee::{Order, PriorRelayer, Relayer}; + use frame_support::{Blake2_128Concat, StorageHasher}; + + log::info!("===> Start migrate all storage items in AssignedRelayersStorage"); + if let Some(value) = migration::take_storage_value::>>( + b"FeeMarket", + b"AssignedRelayersStorage", + &[], + ) { + log::info!("the migrate content {:?}", value); + migration::put_storage_value(b"FeeMarket", b"AssignedRelayers", &[], value); + } + log::info!("===> End migrate all storage items in AssignedRelayersStorage"); + + log::info!("===> Start migrate all storage items in Orders"); + #[derive(Encode, Decode, Debug, Clone)] + struct OldOrder { + lane: LaneId, + message: MessageNonce, + sent_time: BlockNumber, + confirm_time: Option, + relayers: Vec>, + } + for (index, order) in migration::storage_iter::(b"FeeMarket", b"Orders").drain() { + let new_order = Order { + lane: order.lane, + message: order.message, + sent_time: order.sent_time, + confirm_time: order.confirm_time, + locked_collateral: 100 * COIN, + relayers: order.relayers, + }; + log::info!("The new order: order {:?}", new_order); + let hash = Blake2_128Concat::hash(&(new_order.lane, new_order.message).encode()); + migration::put_storage_value(b"FeeMarket", b"Orders", &hash, new_order); + } + log::info!("===> End migrate all storage items in Orders"); + log::info!("===> All Migrates finished"); - 0 - // RuntimeBlockWeights::get().max_block + // 0 + RuntimeBlockWeights::get().max_block } pub struct CustomOnRuntimeUpgrade; impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result<(), &'static str> { + assert!(migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayersStorage", + &[] + )); + assert!(!migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayers", + &[] + )); Ok(()) } #[cfg(feature = "try-runtime")] fn post_upgrade() -> Result<(), &'static str> { + use dp_fee::Order; + assert!(!migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayersStorage", + &[] + )); + assert!(migration::have_storage_value( + b"FeeMarket", + b"AssignedRelayers", + &[] + )); + + for (_index, new_order) in + migration::storage_iter::>( + b"FeeMarket", + b"Orders", + ) + .drain() + { + assert_eq!(new_order.locked_collateral, 100 * COIN); + } Ok(()) } diff --git a/node/runtime/pangoro/src/pallets/fee_market.rs b/node/runtime/pangoro/src/pallets/fee_market.rs index 351035ee7d..1599de3c5d 100644 --- a/node/runtime/pangoro/src/pallets/fee_market.rs +++ b/node/runtime/pangoro/src/pallets/fee_market.rs @@ -1,9 +1,9 @@ // --- substrate --- use frame_support::{traits::LockIdentifier, PalletId}; +use sp_runtime::{traits::UniqueSaturatedInto, Permill}; // --- darwinia --- use crate::*; -use darwinia_fee_market::Config; -use sp_runtime::Permill; +use darwinia_fee_market::{Config, RingBalance, Slasher}; frame_support::parameter_types! { pub const FeeMarketPalletId: PalletId = PalletId(*b"da/feemk"); @@ -11,8 +11,9 @@ frame_support::parameter_types! { pub const FeeMarketLockId: LockIdentifier = *b"da/feelf"; pub const AssignedRelayersNumber: u64 = 3; - pub const MiniumLockCollateral: Balance = 3000 * COIN; pub const MinimumRelayFee: Balance = 15 * COIN; + pub const CollateralPerOrder: Balance = 100 * COIN; + pub const SlashPerBlock: Balance = 2 * COIN; pub const Slot: BlockNumber = 50; pub const AssignedRelayersRewardRatio: Permill = Permill::from_percent(60); @@ -26,16 +27,29 @@ impl Config for Runtime { type LockId = FeeMarketLockId; type AssignedRelayersNumber = AssignedRelayersNumber; - type MiniumLockCollateral = MiniumLockCollateral; type MinimumRelayFee = MinimumRelayFee; + type CollateralPerOrder = CollateralPerOrder; type Slot = Slot; type AssignedRelayersRewardRatio = AssignedRelayersRewardRatio; type MessageRelayersRewardRatio = MessageRelayersRewardRatio; type ConfirmRelayersRewardRatio = ConfirmRelayersRewardRatio; - type Slasher = (); + type Slasher = FeeMarketSlasher; type RingCurrency = Ring; type Event = Event; type WeightInfo = (); } + +pub struct FeeMarketSlasher; +impl Slasher for FeeMarketSlasher { + fn slash(locked_collateral: RingBalance, timeout: T::BlockNumber) -> RingBalance { + let slash_each_block = 2 * COIN; + let slash_value = UniqueSaturatedInto::::unique_saturated_into(timeout) + .saturating_mul(UniqueSaturatedInto::::unique_saturated_into( + slash_each_block, + )) + .unique_saturated_into(); + sp_std::cmp::min(locked_collateral, slash_value) + } +} diff --git a/primitives/fee-market/src/lib.rs b/primitives/fee-market/src/lib.rs index 16c1e9438a..f8ae5c900a 100644 --- a/primitives/fee-market/src/lib.rs +++ b/primitives/fee-market/src/lib.rs @@ -22,11 +22,11 @@ use frame_support::Parameter; use sp_std::{ cmp::{Ord, Ordering, PartialEq}, default::Default, - ops::{Add, AddAssign, Range}, + ops::{Add, AddAssign, Range, Sub}, vec::Vec, }; /// Relayer who has enrolled the fee market -#[derive(Encode, Decode, Clone, Eq, Debug, Copy)] +#[derive(Encode, Decode, Clone, Eq, Debug)] pub struct Relayer { pub id: AccountId, pub collateral: Balance, @@ -78,18 +78,20 @@ impl Default for Relayer { pub lane: LaneId, pub message: MessageNonce, pub sent_time: BlockNumber, pub confirm_time: Option, + pub locked_collateral: Balance, pub relayers: Vec>, } impl Order where - BlockNumber: Add + Copy + AddAssign + PartialOrd, + BlockNumber: + Add + Copy + AddAssign + PartialOrd + Sub, Balance: Copy + PartialOrd, AccountId: Clone + PartialEq, { @@ -97,6 +99,7 @@ where lane: LaneId, message: MessageNonce, sent_time: BlockNumber, + locked_collateral: Balance, assigned_relayers: Vec>, slot: BlockNumber, ) -> Self { @@ -119,6 +122,7 @@ where message, sent_time, confirm_time: None, + locked_collateral, relayers, } } @@ -131,10 +135,10 @@ where self.relayers.as_ref() } - pub fn first_and_last_fee(&self) -> (Option, Option) { - let first = self.relayers.iter().nth(0).map(|r| r.fee); - let last = self.relayers.iter().last().map(|r| r.fee); - (first, last) + pub fn lowest_and_highest_fee(&self) -> (Option, Option) { + let lowest = self.relayers.iter().nth(0).map(|r| r.fee); + let highest = self.relayers.iter().last().map(|r| r.fee); + (lowest, highest) } pub fn is_confirmed(&self) -> bool { @@ -145,6 +149,15 @@ where self.relayers.iter().last().map(|r| r.valid_range.end) } + pub fn delivery_delay(&self) -> Option { + if let (Some(confirm_time), Some(range_end)) = (self.confirm_time, self.range_end()) { + if confirm_time > range_end { + return Some(confirm_time - range_end); + } + } + None + } + pub fn required_delivery_relayer_for_time( &self, message_confirm_time: BlockNumber, @@ -170,7 +183,7 @@ where /// Relayers selected by the fee market. Each prior relayer has a valid slot, if the order can finished in time, /// will be rewarded with more percentage. PriorRelayer are responsible for the messages relay in most time. -#[derive(Clone, Encode, Decode, Default)] +#[derive(Clone, Encode, Decode, Default, Debug)] pub struct PriorRelayer { pub id: AccountId, pub fee: Balance, @@ -222,7 +235,14 @@ mod test { fn test_assign_order_relayers_one() { let mut assigned_relayers = Vec::new(); assigned_relayers.push(Relayer::::new(1, 100, 30)); - let order = Order::new(TEST_LANE_ID, TEST_MESSAGE_NONCE, 100, assigned_relayers, 50); + let order = Order::new( + TEST_LANE_ID, + TEST_MESSAGE_NONCE, + 100, + 100, + assigned_relayers, + 50, + ); assert_eq!(order.relayer_valid_range(1).unwrap(), (100..150)); } @@ -231,7 +251,14 @@ mod test { let mut assigned_relayers = Vec::new(); assigned_relayers.push(Relayer::::new(1, 100, 30)); assigned_relayers.push(Relayer::::new(2, 100, 30)); - let order = Order::new(TEST_LANE_ID, TEST_MESSAGE_NONCE, 100, assigned_relayers, 50); + let order = Order::new( + TEST_LANE_ID, + TEST_MESSAGE_NONCE, + 100, + 100, + assigned_relayers, + 50, + ); assert_eq!(order.relayer_valid_range(1).unwrap(), (100..150)); assert_eq!(order.relayer_valid_range(2).unwrap(), (150..200)); } @@ -242,12 +269,19 @@ mod test { assigned_relayers.push(Relayer::::new(1, 100, 30)); assigned_relayers.push(Relayer::::new(2, 100, 40)); assigned_relayers.push(Relayer::::new(3, 100, 80)); - let order = Order::new(TEST_LANE_ID, TEST_MESSAGE_NONCE, 100, assigned_relayers, 50); + let order = Order::new( + TEST_LANE_ID, + TEST_MESSAGE_NONCE, + 100, + 100, + assigned_relayers, + 50, + ); assert_eq!(order.relayer_valid_range(1).unwrap(), (100..150)); assert_eq!(order.relayer_valid_range(2).unwrap(), (150..200)); assert_eq!(order.relayer_valid_range(3).unwrap(), (200..250)); assert_eq!(order.range_end(), Some(250)); - assert_eq!(order.first_and_last_fee(), (Some(30), Some(80))); + assert_eq!(order.lowest_and_highest_fee(), (Some(30), Some(80))); } #[test] @@ -257,7 +291,14 @@ mod test { assigned_relayers.push(Relayer::::new(2, 100, 30)); assigned_relayers.push(Relayer::::new(3, 100, 30)); assigned_relayers.push(Relayer::::new(4, 100, 30)); - let order = Order::new(TEST_LANE_ID, TEST_MESSAGE_NONCE, 100, assigned_relayers, 50); + let order = Order::new( + TEST_LANE_ID, + TEST_MESSAGE_NONCE, + 100, + 100, + assigned_relayers, + 50, + ); assert_eq!(order.relayer_valid_range(1).unwrap(), (100..150)); assert_eq!(order.relayer_valid_range(2).unwrap(), (150..200)); assert_eq!(order.relayer_valid_range(3).unwrap(), (200..250));