diff --git a/modules/fee-market/src/lib.rs b/modules/fee-market/src/lib.rs index 310570917..4638b09c7 100644 --- a/modules/fee-market/src/lib.rs +++ b/modules/fee-market/src/lib.rs @@ -47,7 +47,6 @@ use sp_runtime::{ }; use sp_std::vec::Vec; // --- darwinia-network --- -use s2s::RewardItem; use types::{Order, Relayer, SlashReport}; pub type AccountId = ::AccountId; @@ -117,8 +116,16 @@ pub mod pallet { Vec, Option, ), - /// Reward distribute of the order. \[lane_id, message_nonce, rewards\] - OrderReward(LaneId, MessageNonce, RewardItem>), + /// Reward distribute of the order. \[lane_id, message_nonce, to_treasury, to_slot_relayer, + /// to_message_relayer, to_confirm_relayer\] + OrderReward( + LaneId, + MessageNonce, + Option>, + Option<(T::AccountId, BalanceOf)>, + (T::AccountId, BalanceOf), + BalanceOf, + ), } #[pallet::error] diff --git a/modules/fee-market/src/s2s/mod.rs b/modules/fee-market/src/s2s/mod.rs index 5321950d1..1aaa10195 100644 --- a/modules/fee-market/src/s2s/mod.rs +++ b/modules/fee-market/src/s2s/mod.rs @@ -17,7 +17,7 @@ // along with Darwinia. If not, see . pub mod payment; -pub use payment::{FeeMarketPayment, RewardItem}; +pub use payment::FeeMarketPayment; pub mod callbacks; pub use callbacks::{FeeMarketMessageAcceptedHandler, FeeMarketMessageConfirmedHandler}; diff --git a/modules/fee-market/src/s2s/payment.rs b/modules/fee-market/src/s2s/payment.rs index 179cc6b33..2f5eadb8e 100644 --- a/modules/fee-market/src/s2s/payment.rs +++ b/modules/fee-market/src/s2s/payment.rs @@ -89,31 +89,33 @@ where received_range: &RangeInclusive, relayer_fund_account: &T::AccountId, ) { - let RewardsBook { deliver_sum, confirm_sum, assigned_relayers_sum, treasury_sum } = + let (treasury_sum, slot_relayers_sum, messages_relayers_sum, confirmation_sum) = slash_and_calculate_rewards::( lane_id, messages_relayers, - confirmation_relayer.clone(), received_range, relayer_fund_account, ); - // Pay confirmation relayer rewards - do_reward::(relayer_fund_account, confirmation_relayer, confirm_sum); - // Pay messages relayers rewards - for (relayer, reward) in deliver_sum { - do_reward::(relayer_fund_account, &relayer, reward); - } - // Pay assign relayer reward - for (relayer, reward) in assigned_relayers_sum { - do_reward::(relayer_fund_account, &relayer, reward); - } // Pay treasury_sum reward do_reward::( relayer_fund_account, &T::TreasuryPalletId::get().into_account(), treasury_sum, ); + + // Pay assign relayer reward + for (relayer, reward) in slot_relayers_sum { + do_reward::(relayer_fund_account, &relayer, reward); + } + + // Pay messages relayers rewards + for (relayer, reward) in messages_relayers_sum { + do_reward::(relayer_fund_account, &relayer, reward); + } + + // Pay confirmation relayer rewards + do_reward::(relayer_fund_account, confirmation_relayer, confirmation_sum); } } @@ -122,97 +124,145 @@ where pub fn slash_and_calculate_rewards( lane_id: LaneId, messages_relayers: VecDeque>, - confirm_relayer: T::AccountId, received_range: &RangeInclusive, relayer_fund_account: &T::AccountId, -) -> RewardsBook +) -> ( + BalanceOf, + BTreeMap>, + BTreeMap>, + BalanceOf, +) where T: frame_system::Config + Config, I: 'static, { - let mut rewards_book = RewardsBook::new(); - for entry in messages_relayers { - let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); - let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); + let mut treasury_sum = BalanceOf::::zero(); + let mut slot_relayers_sum = BTreeMap::new(); + let mut messages_relayers_sum = BTreeMap::new(); + let mut confirmation_sum = BalanceOf::::zero(); + + for messages_relayer in messages_relayers { + let nonce_begin = + sp_std::cmp::max(messages_relayer.messages.begin, *received_range.start()); + let nonce_end = sp_std::cmp::min(messages_relayer.messages.end, *received_range.end()); for message_nonce in nonce_begin..nonce_end + 1 { // The order created when message was accepted, so we can always get the order info. if let Some(order) = >::get(&(lane_id, message_nonce)) { - // The confirm_time of the order is set in the `OnDeliveryConfirmed` callback. And - // the callback function was called as source chain received message delivery proof, - // before the reward payment. - let order_confirm_time = - order.confirm_time.unwrap_or_else(|| frame_system::Pallet::::block_number()); - let message_fee = order.fee(); - - let mut reward_item = RewardItem::new(); - let message_reward; - let confirm_reward; - - if let Some((who, base_fee)) = - order.required_delivery_relayer_for_time(order_confirm_time) - { - // message fee - base fee => treasury_sum - reward_item.to_treasury = Some(message_fee.saturating_sub(base_fee)); - - // AssignedRelayersRewardRatio * base fee => slot relayer - let slot_relayer_reward = T::AssignedRelayersRewardRatio::get() * base_fee; - reward_item.to_slot_relayer = Some((who, slot_relayer_reward)); - - let bridger_relayers_reward = base_fee.saturating_sub(slot_relayer_reward); - // MessageRelayersRewardRatio * (1 - AssignedRelayersRewardRatio) * base_fee => - // message relayer - message_reward = T::MessageRelayersRewardRatio::get() * bridger_relayers_reward; - // ConfirmRelayersRewardRatio * (1 - AssignedRelayersRewardRatio) * base_fee => - // confirm relayer - confirm_reward = T::ConfirmRelayersRewardRatio::get() * bridger_relayers_reward; - } else { - // The order delivery is delay, slash occurs. - let mut total_slash = message_fee; - - // calculate slash amount - let mut amount: BalanceOf = 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 = BalanceOf::::zero(); - for assigned_relayer in order.relayers_slice() { - let report = SlashReport::new(&order, assigned_relayer.id.clone(), amount); - let slashed = do_slash::( - &assigned_relayer.id, - relayer_fund_account, - amount, - report, - ); - assigned_relayers_slash += slashed; - } - total_slash += assigned_relayers_slash; - - // MessageRelayersRewardRatio total slash => message relayer - message_reward = T::MessageRelayersRewardRatio::get() * total_slash; - // ConfirmRelayersRewardRatio total slash => confirm relayer - confirm_reward = T::ConfirmRelayersRewardRatio::get() * total_slash; + // 1. Calc rewards of this order + let ( + to_treasury, + to_slot_relayer, + to_messages_relayer, + to_confirmation_relayer + ) = slash_and_calc_rewards_for_order::(relayer_fund_account, &order); + + // 2. Aggregate by category + if let Some(to_treasury) = to_treasury { + treasury_sum = treasury_sum.saturating_add(to_treasury); + } + + if let Some((who, slot_relayer_reward)) = to_slot_relayer.clone() { + slot_relayers_sum + .entry(who) + .and_modify(|r: &mut BalanceOf| { + *r = r.saturating_add(slot_relayer_reward) + }) + .or_insert(slot_relayer_reward); } - reward_item.to_message_relayer = Some((entry.clone().relayer, message_reward)); - reward_item.to_confirm_relayer = Some((confirm_relayer.clone(), confirm_reward)); + messages_relayers_sum + .entry(messages_relayer.clone().relayer) + .and_modify(|r: &mut BalanceOf| *r = r.saturating_add(to_messages_relayer)) + .or_insert(to_messages_relayer); + confirmation_sum = confirmation_sum.saturating_add(to_confirmation_relayer); + + // 3. Emit a OrderReward event Pallet::::deposit_event(Event::OrderReward( lane_id, message_nonce, - reward_item.clone(), + to_treasury, + to_slot_relayer, + (messages_relayer.clone().relayer, to_messages_relayer), + to_confirmation_relayer, )); - - rewards_book.add_reward_item(reward_item); } } } - rewards_book + + (treasury_sum, slot_relayers_sum, messages_relayers_sum, confirmation_sum) +} + +fn slash_and_calc_rewards_for_order( + relayer_fund_account: &T::AccountId, + order: &Order>, +) -> (Option>, Option<(T::AccountId, BalanceOf)>, BalanceOf, BalanceOf) +where + T: frame_system::Config + Config, + I: 'static, +{ + let mut to_treasury = None; + let mut to_slot_relayer = None; + let to_messages_relayer; + let to_confirmation_relayer; + + if let Some((who, base_fee)) = + order.confirmed_by_prior_relayer_on_time(frame_system::Pallet::::block_number()) + { + // message fee - base fee => treasury_sum + to_treasury = Some(order.fee().saturating_sub(base_fee)); + + // AssignedRelayersRewardRatio * base fee => slot relayer + let slot_relayer_reward = T::AssignedRelayersRewardRatio::get() * base_fee; + to_slot_relayer = Some((who.clone(), slot_relayer_reward)); + + let bridger_relayers_reward = base_fee.saturating_sub(slot_relayer_reward); + // MessageRelayersRewardRatio * (1 - AssignedRelayersRewardRatio) * base_fee => + // message relayer + to_messages_relayer = T::MessageRelayersRewardRatio::get() * bridger_relayers_reward; + + // ConfirmRelayersRewardRatio * (1 - AssignedRelayersRewardRatio) * base_fee => + // confirm relayer + to_confirmation_relayer = T::ConfirmRelayersRewardRatio::get() * bridger_relayers_reward; + } else { + let total_slash = slash::(relayer_fund_account, &order); + + // MessageRelayersRewardRatio total slash => message relayer + to_messages_relayer = T::MessageRelayersRewardRatio::get() * total_slash; + // ConfirmRelayersRewardRatio total slash => confirm relayer + to_confirmation_relayer = T::ConfirmRelayersRewardRatio::get() * total_slash; + } + (to_treasury, to_slot_relayer, to_messages_relayer, to_confirmation_relayer) +} + +fn slash( + relayer_fund_account: &T::AccountId, + order: &Order>, +) -> BalanceOf +where + T: Config, + I: 'static, +{ + // The order delivery is delay, slash occurs. + let mut total_slash = order.fee(); + + // calculate slash amount + let mut amount: BalanceOf = + 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 = BalanceOf::::zero(); + for assigned_relayer in order.relayers_slice() { + let report = SlashReport::new(&order, assigned_relayer.id.clone(), amount); + let slashed = do_slash::(&assigned_relayer.id, relayer_fund_account, amount, report); + assigned_relayers_slash += slashed; + } + total_slash += assigned_relayers_slash; + total_slash } /// Do slash for absent assigned relayers @@ -277,67 +327,3 @@ pub(crate) fn do_reward, I: 'static>( Err(e) => log::error!("Reward, from {:?} to {:?} reward {:?}: {:?}", from, to, reward, e,), } } - -/// Record the concrete reward distribution of certain order -#[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, TypeInfo)] -pub struct RewardItem { - pub to_slot_relayer: Option<(AccountId, Balance)>, - pub to_treasury: Option, - pub to_message_relayer: Option<(AccountId, Balance)>, - pub to_confirm_relayer: Option<(AccountId, Balance)>, -} - -impl RewardItem { - fn new() -> Self { - Self { - to_slot_relayer: None, - to_treasury: None, - to_message_relayer: None, - to_confirm_relayer: None, - } - } -} - -/// Record the calculation rewards result -#[derive(Clone, Debug, Eq, PartialEq, TypeInfo)] -pub struct RewardsBook, I: 'static> { - pub deliver_sum: BTreeMap>, - pub confirm_sum: BalanceOf, - pub assigned_relayers_sum: BTreeMap>, - pub treasury_sum: BalanceOf, -} - -impl, I: 'static> RewardsBook { - fn new() -> Self { - Self { - deliver_sum: BTreeMap::new(), - confirm_sum: BalanceOf::::zero(), - assigned_relayers_sum: BTreeMap::new(), - treasury_sum: BalanceOf::::zero(), - } - } - - fn add_reward_item(&mut self, item: RewardItem>) { - if let Some((id, reward)) = item.to_slot_relayer { - self.assigned_relayers_sum - .entry(id) - .and_modify(|r| *r = r.saturating_add(reward)) - .or_insert(reward); - } - - if let Some(reward) = item.to_treasury { - self.treasury_sum = self.treasury_sum.saturating_add(reward); - } - - if let Some((id, reward)) = item.to_message_relayer { - self.deliver_sum - .entry(id) - .and_modify(|r| *r = r.saturating_add(reward)) - .or_insert(reward); - } - - if let Some((_id, reward)) = item.to_confirm_relayer { - self.confirm_sum = self.confirm_sum.saturating_add(reward); - } - } -} diff --git a/modules/fee-market/src/tests.rs b/modules/fee-market/src/tests.rs index 400346a0f..044e3d695 100644 --- a/modules/fee-market/src/tests.rs +++ b/modules/fee-market/src/tests.rs @@ -51,8 +51,8 @@ use sp_runtime::{ use crate::{ self as darwinia_fee_market, s2s::{ - payment::{slash_and_calculate_rewards, RewardsBook}, - FeeMarketMessageAcceptedHandler, FeeMarketMessageConfirmedHandler, + payment::slash_and_calculate_rewards, FeeMarketMessageAcceptedHandler, + FeeMarketMessageConfirmedHandler, }, *, }; @@ -289,11 +289,10 @@ impl MessageDeliveryAndDispatchPayment received_range: &RangeInclusive, relayer_fund_account: &AccountId, ) { - let RewardsBook { deliver_sum, confirm_sum, assigned_relayers_sum, treasury_sum } = + let (treasury_sum, assigned_relayers_sum, deliver_sum, confirm_sum) = slash_and_calculate_rewards::( lane_id, message_relayers, - confirmation_relayer.clone(), received_range, relayer_fund_account, ); @@ -842,12 +841,10 @@ fn test_payment_cal_reward_normally_single_message() { System::assert_has_event(Event::FeeMarket(crate::Event::OrderReward( lane, message_nonce, - RewardItem { - to_slot_relayer: Some((1, 18)), - to_treasury: Some(70), - to_message_relayer: Some((100, 10)), - to_confirm_relayer: Some((5, 2)), - }, + Some(70), + Some((1, 18)), + (100, 10), + 2, ))); }); } diff --git a/modules/fee-market/src/types.rs b/modules/fee-market/src/types.rs index 281b6e250..9412485af 100644 --- a/modules/fee-market/src/types.rs +++ b/modules/fee-market/src/types.rs @@ -139,12 +139,18 @@ where None } - pub fn required_delivery_relayer_for_time( + /// One of P1, P2, P3 do the job in time + pub fn confirmed_by_prior_relayer_on_time( &self, - message_confirm_time: BlockNumber, + latest_block_number: BlockNumber, ) -> Option<(AccountId, Balance)> { + // The confirm_time of the order is set in the `OnDeliveryConfirmed` callback. And + // the callback function was called as source chain received message delivery proof, + // before the reward payment. + let order_confirmed_time = self.confirm_time.unwrap_or_else(|| latest_block_number); + for prior_relayer in self.relayers.iter() { - if prior_relayer.valid_range.contains(&message_confirm_time) { + if prior_relayer.valid_range.contains(&order_confirmed_time) { return Some((prior_relayer.id.clone(), prior_relayer.fee)); } }