From 78a6bbc737f219d498fa1e8b864184c2bb1e985e Mon Sep 17 00:00:00 2001 From: bear Date: Thu, 9 Jun 2022 17:50:36 +0800 Subject: [PATCH] Add necessary reward event (#89) * Refactor the total reward cal * Code Clean * Fix test * Add OrderCreated event * Self review * Update comment * Update OrderCreate event to include relayers * Fix review --- modules/fee-market/src/s2s/callbacks.rs | 9 - modules/fee-market/src/s2s/payment.rs | 307 +++++++++---------- modules/fee-market/src/tests.rs | 375 +++++------------------- 3 files changed, 207 insertions(+), 484 deletions(-) diff --git a/modules/fee-market/src/s2s/callbacks.rs b/modules/fee-market/src/s2s/callbacks.rs index 98a468d85..4ba1a470b 100644 --- a/modules/fee-market/src/s2s/callbacks.rs +++ b/modules/fee-market/src/s2s/callbacks.rs @@ -40,12 +40,8 @@ impl, I: 'static> OnMessageAccepted for FeeMarketMessageAcceptedHan assigned_relayers.clone(), T::Slot::get(), ); - // Store the create order >::insert((order.lane, order.message), order.clone()); - // Once order is created, the assigned relayers's order capacity should reduce by one. - // Thus, the whole market needs to re-sort to generate new assigned relayers set. - let _ = Pallet::::update_market(|| Ok(()), None); let ids: Vec = assigned_relayers.iter().map(|r| r.id.clone()).collect(); Pallet::::deposit_event(Event::OrderCreated( @@ -76,11 +72,6 @@ impl, I: 'static> OnDeliveryConfirmed for FeeMarketMessageConfirmed Some(order) => order.set_confirm_time(Some(now)), None => {}, }); - - // Once order is confirmed, the assigned relayers's order capacity should - // increase by one. Thus, the whole market needs to re-sort to generate new - // assigned relayers set. - let _ = Pallet::::update_market(|| Ok(()), None); } } } diff --git a/modules/fee-market/src/s2s/payment.rs b/modules/fee-market/src/s2s/payment.rs index b025c7b2d..179cc6b33 100644 --- a/modules/fee-market/src/s2s/payment.rs +++ b/modules/fee-market/src/s2s/payment.rs @@ -25,10 +25,8 @@ use frame_support::{ log, traits::{Currency as CurrencyT, ExistenceRequirement, Get}, }; -use scale_info::TypeInfo; -use sp_runtime::traits::{AccountIdConversion, CheckedDiv, Saturating, UniqueSaturatedInto, Zero}; +use sp_runtime::traits::{AccountIdConversion, Saturating, Zero}; use sp_std::{ - cmp::{max, min}, collections::{btree_map::BTreeMap, vec_deque::VecDeque}, ops::RangeInclusive, }; @@ -91,200 +89,118 @@ where received_range: &RangeInclusive, relayer_fund_account: &T::AccountId, ) { - let rewards_items = calculate_rewards::( - lane_id, - messages_relayers, - confirmation_relayer.clone(), - received_range, - relayer_fund_account, - ); - - let mut deliver_sum = BTreeMap::>::new(); - let mut confirm_sum = BalanceOf::::zero(); - let mut assigned_relayers_sum = BTreeMap::>::new(); - let mut treasury_sum = BalanceOf::::zero(); - for item in rewards_items { - for (k, v) in item.to_assigned_relayers.iter() { - assigned_relayers_sum - .entry(k.clone()) - .and_modify(|r| *r = r.saturating_add(*v)) - .or_insert(*v); - } - - if let Some(reward) = item.to_treasury { - treasury_sum = treasury_sum.saturating_add(reward); - } - - if let Some((id, reward)) = item.to_message_relayer { - deliver_sum - .entry(id) - .and_modify(|r| *r = r.saturating_add(reward)) - .or_insert(reward); - } - - if let Some((_id, reward)) = item.to_confirm_relayer { - confirm_sum = confirm_sum.saturating_add(reward); - } - } + let RewardsBook { deliver_sum, confirm_sum, assigned_relayers_sum, treasury_sum } = + slash_and_calculate_rewards::( + lane_id, + messages_relayers, + confirmation_relayer.clone(), + received_range, + relayer_fund_account, + ); - // Pay rewards to the message confirm relayer + // Pay confirmation relayer rewards do_reward::(relayer_fund_account, confirmation_relayer, confirm_sum); - // Pay rewards to the messages deliver relayers + // Pay messages relayers rewards for (relayer, reward) in deliver_sum { do_reward::(relayer_fund_account, &relayer, reward); } - // Pay rewards to the assigned relayers + // Pay assign relayer reward for (relayer, reward) in assigned_relayers_sum { do_reward::(relayer_fund_account, &relayer, reward); } - // Pay to treasury + // Pay treasury_sum reward do_reward::( relayer_fund_account, - &T::TreasuryPalletId::get().into_account_truncating(), + &T::TreasuryPalletId::get().into_account(), treasury_sum, ); } } -/// Calculate rewards for messages_relayers, confirmation relayers, treasury_sum, +/// Slash and calculate rewards for messages_relayers, confirmation relayers, treasury_sum, /// assigned_relayers -pub fn calculate_rewards( +pub fn slash_and_calculate_rewards( lane_id: LaneId, messages_relayers: VecDeque>, confirm_relayer: T::AccountId, received_range: &RangeInclusive, relayer_fund_account: &T::AccountId, -) -> Vec>> +) -> RewardsBook where T: frame_system::Config + Config, I: 'static, { - let mut rewards_items = Vec::new(); + let mut rewards_book = RewardsBook::new(); for entry in messages_relayers { - let nonce_begin = max(entry.messages.begin, *received_range.start()); - let nonce_end = min(entry.messages.end, *received_range.end()); + 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()); 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)) { - let mut reward_item = RewardItem::new(); + // 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 (message_reward, treasury_reward) = match order.confirmed_info() { - // When the order is confirmed at the first slot, no assigned relayers will be - // not slashed in this case. The total reward to the message deliver relayer and - // message confirm relayer is the confirmed slot price(first slot price), the - // duty relayers would be rewarded with the 20% of the message fee, and all - // the duty relayers share the duty_rewards equally. Finally, the - // surplus of the message fee goes to the treasury. - Some((slot_index, slot_price)) if slot_index == 0 => { - let mut message_surplus = order.fee().saturating_sub(slot_price); - let slot_duty_rewards = T::DutyRelayersRewardRatio::get() * message_surplus; - - // All assigned relayers successfully are on duty in this case, no slash - // happens, just calculate the duty relayers rewards. - let duty_relayers: Vec<_> = - order.assigned_relayers_slice().iter().map(|r| r.id.clone()).collect(); - let average_reward = slot_duty_rewards - .checked_div(&(duty_relayers.len()).unique_saturated_into()) - .unwrap_or_default(); - for id in duty_relayers { - reward_item.to_assigned_relayers.insert(id.clone(), average_reward); - message_surplus = message_surplus.saturating_sub(average_reward); - } - - (slot_price, Some(message_surplus)) - }, - // When the order is confirmed not at the first slot but within the deadline, - // some other assigned relayers will be slashed in this case. The total reward - // to the message deliver relayer and message confirm relayer is the confirmed - // slot price(first slot price) + slot_offensive_slash part, the - // duty relayers would be rewarded with the 20% of the message surplus, and all - // the duty relayers share the duty_rewards equally. Finally, the - // surplus of the message fee goes to the treasury. - Some((slot_index, slot_price)) if slot_index >= 1 => { - let mut message_surplus = order.fee().saturating_sub(slot_price); - let slot_duty_rewards = T::DutyRelayersRewardRatio::get() * message_surplus; - - // Since part of the assigned relayers are on duty, calculate the duty - // relayers slash part first. - let mut offensive_relayers: Vec<_> = - order.assigned_relayers_slice().iter().map(|r| r.id.clone()).collect(); - let duty_relayers = offensive_relayers.split_off(slot_index); - - // Calculate the assigned relayers slash part - let mut slot_offensive_slash = BalanceOf::::zero(); - for r in offensive_relayers { - let amount = slash_assigned_relayer::( - &order, - &r, - relayer_fund_account, - T::AssignedRelayerSlashRatio::get() - * Pallet::::relayer_locked_collateral(&r), - ); - slot_offensive_slash += amount; - } - - // Calculate the duty relayers rewards - let average_reward = slot_duty_rewards - .checked_div(&(duty_relayers.len()).unique_saturated_into()) - .unwrap_or_default(); - for id in duty_relayers { - reward_item.to_assigned_relayers.insert(id.clone(), average_reward); - message_surplus = message_surplus.saturating_sub(average_reward); - } - - (slot_price.saturating_add(slot_offensive_slash), Some(message_surplus)) - }, - // When the order is confirmed delayer, all assigned relayers will be slashed in - // this case. So, no confirmed slot price here. All reward will distribute to - // the message deliver relayer and message confirm relayer. No duty rewards - // and treasury reward. - _ => { - let mut slot_offensive_slash = BalanceOf::::zero(); - for r in order.assigned_relayers_slice() { - // 1. For the fixed part - let mut slash_amount = T::AssignedRelayerSlashRatio::get() - * Pallet::::relayer_locked_collateral(&r.id); - - // 2. For the dynamic part - slash_amount += T::Slasher::cal_slash_amount( - order.locked_collateral, - order.comfirm_delay().unwrap_or_default(), - ); - - // The total_slash_amount can't be greater than the slash_protect. - if let Some(slash_protect) = Pallet::::collateral_slash_protect() - { - // slash_amount = sp_std::cmp::min(slash_amount, slash_protect); - slash_amount = slash_amount.min(slash_protect); - } - - // The total_slash_amount can't be greater than the locked_collateral. - let locked_collateral = - Pallet::::relayer_locked_collateral(&r.id); - slash_amount = sp_std::cmp::min(slash_amount, locked_collateral); - - let amount = slash_assigned_relayer::( - &order, - &r.id, - relayer_fund_account, - slash_amount, - ); - slot_offensive_slash += amount; - } - - (order.fee().saturating_add(slot_offensive_slash), None) - }, - }; - - if let Some(treasury_reward) = treasury_reward { - reward_item.to_treasury = Some(treasury_reward); + 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; } - let deliver_rd = T::MessageRelayersRewardRatio::get() * message_reward; - let confirm_rd = T::ConfirmRelayersRewardRatio::get() * message_reward; - reward_item.to_message_relayer = Some((entry.relayer.clone(), deliver_rd)); - reward_item.to_confirm_relayer = Some((confirm_relayer.clone(), confirm_rd)); + reward_item.to_message_relayer = Some((entry.clone().relayer, message_reward)); + reward_item.to_confirm_relayer = Some((confirm_relayer.clone(), confirm_reward)); Pallet::::deposit_event(Event::OrderReward( lane_id, @@ -292,23 +208,19 @@ where reward_item.clone(), )); - rewards_items.push(reward_item); + rewards_book.add_reward_item(reward_item); } } } - rewards_items + rewards_book } -/// Slash the assigned relayer and emit the slash report. -/// -/// fund_account refers to the user who pays the cross-chain fee to this account when creating an -/// order. The slash part will be transferred to fund_account first, and then distributed to various -/// relayers. -pub(crate) fn slash_assigned_relayer, I: 'static>( - order: &Order>, +/// Do slash for absent assigned relayers +pub(crate) fn do_slash, I: 'static>( who: &T::AccountId, fund_account: &T::AccountId, amount: BalanceOf, + report: SlashReport>, ) -> BalanceOf { let locked_collateral = Pallet::::relayer_locked_collateral(who); T::Currency::remove_lock(T::LockId::get(), who); @@ -323,7 +235,6 @@ pub(crate) fn slash_assigned_relayer, I: 'static>( amount, ExistenceRequirement::AllowDeath, ); - let report = SlashReport::new(order, who.clone(), amount); match pay_result { Ok(_) => { crate::Pallet::::update_relayer_after_slash( @@ -370,7 +281,7 @@ pub(crate) fn do_reward, I: 'static>( /// Record the concrete reward distribution of certain order #[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, TypeInfo)] pub struct RewardItem { - pub to_assigned_relayers: BTreeMap, + 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)>, @@ -379,10 +290,54 @@ pub struct RewardItem { impl RewardItem { fn new() -> Self { Self { - to_assigned_relayers: BTreeMap::new(), + 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 1163fd524..400346a0f 100644 --- a/modules/fee-market/src/tests.rs +++ b/modules/fee-market/src/tests.rs @@ -17,10 +17,7 @@ // along with Darwinia. If not, see . // --- std --- -use std::{ - collections::{BTreeMap, VecDeque}, - ops::RangeInclusive, -}; +use std::{collections::VecDeque, ops::RangeInclusive}; // --- crates.io --- use bitvec::prelude::*; use scale_info::TypeInfo; @@ -54,8 +51,8 @@ use sp_runtime::{ use crate::{ self as darwinia_fee_market, s2s::{ - payment::calculate_rewards, FeeMarketMessageAcceptedHandler, - FeeMarketMessageConfirmedHandler, + payment::{slash_and_calculate_rewards, RewardsBook}, + FeeMarketMessageAcceptedHandler, FeeMarketMessageConfirmedHandler, }, *, }; @@ -287,46 +284,19 @@ impl MessageDeliveryAndDispatchPayment fn pay_relayers_rewards( lane_id: LaneId, - messages_relayers: VecDeque>, + message_relayers: VecDeque>, confirmation_relayer: &AccountId, received_range: &RangeInclusive, relayer_fund_account: &AccountId, ) { - let rewards_items = calculate_rewards::( - lane_id, - messages_relayers, - confirmation_relayer.clone(), - received_range, - relayer_fund_account, - ); - - let mut deliver_sum = BTreeMap::::new(); - let mut confirm_sum = Balance::zero(); - let mut assigned_relayers_sum = BTreeMap::::new(); - let mut treasury_sum = Balance::zero(); - for item in rewards_items { - for (k, v) in item.to_assigned_relayers.iter() { - assigned_relayers_sum - .entry(k.clone()) - .and_modify(|r| *r = r.saturating_add(v.clone())) - .or_insert(*v); - } - - if let Some(reward) = item.to_treasury { - treasury_sum = treasury_sum.saturating_add(reward); - } - - if let Some((id, reward)) = item.to_message_relayer { - deliver_sum - .entry(id) - .and_modify(|r| *r = r.saturating_add(reward)) - .or_insert(reward); - } - - if let Some((_id, reward)) = item.to_confirm_relayer { - confirm_sum = confirm_sum.saturating_add(reward); - } - } + let RewardsBook { deliver_sum, confirm_sum, assigned_relayers_sum, treasury_sum } = + slash_and_calculate_rewards::( + lane_id, + message_relayers, + confirmation_relayer.clone(), + received_range, + relayer_fund_account, + ); let confimation_key = (b":relayer-reward:", confirmation_relayer, confirm_sum).encode(); frame_support::storage::unhashed::put(&confimation_key, &true); @@ -341,8 +311,7 @@ impl MessageDeliveryAndDispatchPayment frame_support::storage::unhashed::put(&key, &true); } - let treasury_account: AccountId = - ::TreasuryPalletId::get().into_account_truncating(); + let treasury_account: AccountId = ::TreasuryPalletId::get().into_account(); let treasury_key = (b":relayer-reward:", &treasury_account, treasury_sum).encode(); frame_support::storage::unhashed::put(&treasury_key, &true); } @@ -375,13 +344,6 @@ impl MessageDispatch for TestMessageDispatch { } } - fn pre_dispatch( - _relayer_account: &AccountId, - _message: &DispatchMessage, - ) -> Result<(), &'static str> { - Ok(()) - } - fn dispatch( _relayer_account: &AccountId, message: DispatchMessage, @@ -450,33 +412,28 @@ frame_support::parameter_types! { pub const CollateralPerOrder: Balance = 100; pub const Slot: u64 = 50; - pub const DutyRelayersRewardRatio: Permill = Permill::from_percent(20); + pub const AssignedRelayersRewardRatio: Permill = Permill::from_percent(60); pub const MessageRelayersRewardRatio: Permill = Permill::from_percent(80); pub const ConfirmRelayersRewardRatio: Permill = Permill::from_percent(20); - pub const AssignedRelayerSlashRatio: Permill = Permill::from_percent(20); pub const TreasuryPalletAccount: u64 = 666; } pub struct TestSlasher; impl, I: 'static> Slasher for TestSlasher { - fn cal_slash_amount( - collateral_per_order: BalanceOf, - timeout: T::BlockNumber, - ) -> BalanceOf { + fn slash(locked_collateral: BalanceOf, timeout: T::BlockNumber) -> BalanceOf { 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(collateral_per_order, slash_value) + sp_std::cmp::min(locked_collateral, slash_value) } } impl Config for Test { - type AssignedRelayerSlashRatio = AssignedRelayerSlashRatio; + type AssignedRelayersRewardRatio = AssignedRelayersRewardRatio; type CollateralPerOrder = CollateralPerOrder; type ConfirmRelayersRewardRatio = ConfirmRelayersRewardRatio; type Currency = Balances; - type DutyRelayersRewardRatio = DutyRelayersRewardRatio; type Event = Event; type LockId = FeeMarketLockId; type MessageRelayersRewardRatio = MessageRelayersRewardRatio; @@ -774,9 +731,9 @@ fn receive_messages_delivery_proof() { #[test] fn test_callback_order_creation() { new_test_ext().execute_with(|| { - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 200, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(3), 210, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(4), 220, None); + 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)); System::set_block_number(2); let assigned_relayers = FeeMarket::assigned_relayers().unwrap(); @@ -786,7 +743,7 @@ fn test_callback_order_creation() { assert!(FeeMarket::assigned_relayers().is_some()); let order = FeeMarket::order((&lane, &message_nonce)).unwrap(); - let relayers = order.assigned_relayers_slice(); + 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); assert_eq!(relayers[2].id, assigned_relayers.get(2).unwrap().id); @@ -805,8 +762,8 @@ fn test_callback_order_creation() { #[test] 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, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, None); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(1), 100, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 110, Some(50)); System::set_block_number(2); assert!(FeeMarket::assigned_relayers().is_none()); @@ -814,7 +771,7 @@ fn test_callback_no_order_created_when_fee_market_not_ready() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: 2, message: Some("MessageRejectedByLaneVerifier") }) ); @@ -825,9 +782,9 @@ fn test_callback_no_order_created_when_fee_market_not_ready() { 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, 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(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 market_fee = FeeMarket::market_fee().unwrap(); let (lane, message_nonce) = send_regular_message(market_fee); let order = FeeMarket::order((&lane, &message_nonce)).unwrap(); @@ -843,7 +800,7 @@ fn test_callback_order_confirm() { } #[test] -fn test_payment_cal_rewards_normally_single_message() { +fn test_payment_cal_reward_normally_single_message() { new_test_ext().execute_with(|| { // Send message System::set_block_number(2); @@ -854,7 +811,7 @@ fn test_payment_cal_rewards_normally_single_message() { let (lane, message_nonce) = send_regular_message(market_fee); // Receive delivery message proof - System::set_block_number(4); // confirmed at block 4, the first slot + System::set_block_number(4); assert_ok!(Messages::receive_messages_delivery_proof( Origin::signed(5), TestMessagesDeliveryProof(Ok(( @@ -871,36 +828,32 @@ fn test_payment_cal_rewards_normally_single_message() { }, )); - // Rewards Analysis: - // 1. The order's assigned_relayers: [(1, 30, 2-52),(2, 50, 52-102),(3, 100, 102-152)] - // 2. The order's fee: 100 - // For message delivery relayer(id=100): 30 * 80% = 24 - // For message confirm relayer(id=5): 30 * 20% = 6 - // For each assigned_relayer(id=1, 2, 3): (100 - 30) * 20% / 3 = 4 - // For treasury: 100 - (24 + 6) - (4 * 3) = 58 - let t: AccountId = ::TreasuryPalletId::get().into_account_truncating(); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 58)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(2, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(3, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 6)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 24)); + // Reward Analysis: assigned_relayers [(1, 30, 2-52),(2, 50, 52-102),(3, 100, 102-152)] + // 1. slot relayer -> id: 1, reward = 60% * 30 = 18 + // 2. message relayer -> id: 100, reward = 40% * 30 * 80% = 9.6 ~ 10 + // 3. confirm relayer -> id: 5, reward = 40% * 30 * 20% = 2.4 ~ 2 + // 4. treasury reward -> reward = 100 - 30 = 70 + let t: AccountId = ::TreasuryPalletId::get().into_account(); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 70)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 18)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 2)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 10)); System::assert_has_event(Event::FeeMarket(crate::Event::OrderReward( lane, message_nonce, RewardItem { - to_assigned_relayers: BTreeMap::from_iter([(1, 4), (2, 4), (3, 4)]), - to_treasury: Some(58), - to_message_relayer: Some((100, 24)), - to_confirm_relayer: Some((5, 6)), + to_slot_relayer: Some((1, 18)), + to_treasury: Some(70), + to_message_relayer: Some((100, 10)), + to_confirm_relayer: Some((5, 2)), }, ))); }); } #[test] -fn test_payment_cal_rewards_normally_multi_message() { +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(5), 300, Some(30)); @@ -914,7 +867,7 @@ fn test_payment_cal_rewards_normally_multi_message() { assert_eq!(message_nonce1 + 1, message_nonce2); // Receive delivery message proof - System::set_block_number(4); // confirmed at block 4, the first slot + System::set_block_number(4); assert_ok!(Messages::receive_messages_delivery_proof( Origin::signed(1), TestMessagesDeliveryProof(Ok(( @@ -936,128 +889,12 @@ fn test_payment_cal_rewards_normally_multi_message() { }, )); - // Rewards order1 Analysis(The same with order2): - // 1. The order's assigned_relayers: [(5, 30, 2-52),(6, 50, 52-102),(7, 100, 102-152)] - // 2. The order's fee: 100 - // For message delivery relayer(id=100): 30 * 80% = 24 - // For message confirm relayer(id=1): 30 * 20% = 6 - // For each assigned_relayer(id=5, 6, 7): (100 - 30) * 20% / 3 = 4 - // For treasury: 100 - (24 + 6) - (4 * 3) = 58 - let t: AccountId = ::TreasuryPalletId::get().into_account_truncating(); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 116)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 8)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(6, 8)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(7, 8)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 12)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 24)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 24)); - }); -} - -#[test] -fn test_payment_cal_rewards_when_order_confirmed_in_second_slot() { - 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); - - assert_eq!(FeeMarket::relayer_locked_collateral(&5), 300); - assert_eq!(FeeMarket::relayer_locked_collateral(&6), 300); - assert_eq!(FeeMarket::relayer_locked_collateral(&7), 300); - - System::set_block_number(55); // confirmed at block 55, the second slot - assert_ok!(Messages::receive_messages_delivery_proof( - Origin::signed(1), - 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_eq!(FeeMarket::relayer_locked_collateral(&5), 240); - assert_eq!(FeeMarket::relayer_locked_collateral(&6), 300); - assert_eq!(FeeMarket::relayer_locked_collateral(&7), 300); - - // Rewards Analysis: - // 1. The order's assigned_relayers: [(5, 30, 2-52),(6, 50, 52-102),(7, 100, 102-152)] - // 2. The order's fee: 100 - // 3. The slash for relayer(id=5): 300 * 20% = 60 - // For message delivery relayer(id=100): (50 + 60) * 80% = 88 - // For message confirm relayer(id=1): (50 + 60) * 20% = 22 - // For each assigned_relayer(id=6, 7): (100 - 50) * 20% / 2 = 5 - // For treasury: 100 - 50 - (5 * 2) = 40 - let t: AccountId = ::TreasuryPalletId::get().into_account_truncating(); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 40)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(6, 5)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(7, 5)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 22)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 88)); - }); -} - -#[test] -fn test_payment_cal_rewards_when_order_confirmed_in_third_slot() { - 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); - - assert_eq!(FeeMarket::relayer_locked_collateral(&5), 300); - assert_eq!(FeeMarket::relayer_locked_collateral(&6), 300); - assert_eq!(FeeMarket::relayer_locked_collateral(&7), 300); - - System::set_block_number(105); // confirmed at block 55, the third slot - assert_ok!(Messages::receive_messages_delivery_proof( - Origin::signed(1), - 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_eq!(FeeMarket::relayer_locked_collateral(&5), 240); - assert_eq!(FeeMarket::relayer_locked_collateral(&6), 240); - assert_eq!(FeeMarket::relayer_locked_collateral(&7), 300); - - // Rewards Analysis: - // 1. The order's assigned_relayers: [(5, 30, 2-52),(6, 50, 52-102),(7, 100, 102-152)] - // 2. The order's fee: 100 - // 3. The slash for relayer(id=5, 6): 300 * 20% = 60 - // For message delivery relayer(id=100): (100 + 60 * 2) * 80% = 176 - // For message confirm relayer(id=1): (100 + 60 * 2) * 20% = 44 - // For each assigned_relayer(id=7): (100 - 100) * 20% = 0 - // For treasury: 100 - 100 - (0 * 2) = 0 - let t: AccountId = ::TreasuryPalletId::get().into_account_truncating(); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 0)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(7, 0)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 44)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 176)); + let t: AccountId = ::TreasuryPalletId::get().into_account(); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 140)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 4)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 36)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 10)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 10)); }); } @@ -1107,20 +944,11 @@ fn test_payment_cal_reward_with_duplicated_delivery_proof() { }, )); - // Rewards Analysis: - // 1. The order's assigned_relayers: [(1, 30, 2-52),(2, 50, 52-102),(3, 100, 102-152)] - // 2. The order's fee: 100 - // For message delivery relayer(id=100): 30 * 80% = 24 - // For message confirm relayer(id=5): 30 * 20% = 6 - // For each assigned_relayer(id=1, 2, 3): (100 - 30) * 20% / 3 = 4 - // For treasury: 100 - (24 + 6) - (4 * 3) = 58 - let t: AccountId = ::TreasuryPalletId::get().into_account_truncating(); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 58)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(2, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(3, 4)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 6)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 24)); + let t: AccountId = ::TreasuryPalletId::get().into_account(); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(t, 70)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(1, 18)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 2)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 10)); }); } @@ -1156,11 +984,11 @@ fn test_payment_with_slash_and_reduce_order_capacity() { assert!(FeeMarket::is_enrolled(&6)); assert!(FeeMarket::is_enrolled(&6)); assert!(FeeMarket::is_enrolled(&6)); - assert_eq!(FeeMarket::relayer_locked_collateral(&6), 220); - assert_eq!(FeeMarket::relayer_locked_collateral(&7), 220); - assert_eq!(FeeMarket::relayer_locked_collateral(&8), 220); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 128)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 512)); + assert_eq!(FeeMarket::relayer_locked_collateral(&6), 300); + assert_eq!(FeeMarket::relayer_locked_collateral(&7), 300); + assert_eq!(FeeMarket::relayer_locked_collateral(&8), 300); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 80)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 320)); }); } @@ -1267,7 +1095,7 @@ fn test_payment_slash_event() { } #[test] -fn test_payment_with_multiple_message_out_of_deadline() { +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(6), 400, Some(300)); @@ -1302,10 +1130,9 @@ fn test_payment_with_multiple_message_out_of_deadline() { ..Default::default() }, )); - - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 594)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1232)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1146)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(5, 520)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1040)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1040)); }); } @@ -1313,9 +1140,9 @@ fn test_payment_with_multiple_message_out_of_deadline() { 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(6), 400, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 400, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(8), 400, None); + 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); @@ -1362,8 +1189,8 @@ fn test_clean_order_state_at_the_end_of_block() { #[test] fn test_fee_verification_when_send_message() { 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(1), 100, Some(30)); + let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(2), 100, Some(40)); assert!(FeeMarket::market_fee().is_none()); // Case 1: When fee market are not ready, but somebody send messages @@ -1371,7 +1198,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: 2, message: Some("MessageRejectedByLaneVerifier") }) ); @@ -1382,7 +1209,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 49), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: 2, message: Some("MessageRejectedByLaneVerifier") }) ); @@ -1393,12 +1220,12 @@ fn test_fee_verification_when_send_message() { } #[test] -fn test_relayer_occupied_result() { +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, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 300, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 300, None); + 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(); @@ -1408,59 +1235,9 @@ fn test_relayer_occupied_result() { assert_eq!(FeeMarket::occupied(&5), Some((2, 200))); assert_eq!(FeeMarket::occupied(&6), Some((2, 200))); assert_eq!(FeeMarket::occupied(&7), Some((2, 200))); - assert_eq!(FeeMarket::usable_order_capacity(&5), 1); - assert_eq!(FeeMarket::usable_order_capacity(&6), 1); - assert_eq!(FeeMarket::usable_order_capacity(&7), 1); 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))); - assert_eq!(FeeMarket::usable_order_capacity(&5), 2); - assert_eq!(FeeMarket::usable_order_capacity(&6), 2); - assert_eq!(FeeMarket::usable_order_capacity(&7), 2); - }); -} - -#[test] -fn test_relayer_update_order_capacity() { - new_test_ext().execute_with(|| { - System::set_block_number(2); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(5), 300, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(6), 300, None); - let _ = FeeMarket::enroll_and_lock_collateral(Origin::signed(7), 300, 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); - - assert_eq!(FeeMarket::occupied(&5), Some((3, 300))); - assert_eq!(FeeMarket::usable_order_capacity(&5), 0); - assert_eq!(FeeMarket::usable_order_capacity(&6), 0); - assert_eq!(FeeMarket::usable_order_capacity(&7), 0); - assert!(FeeMarket::market_fee().is_none()); - - System::set_block_number(10); - assert_ok!(Messages::receive_messages_delivery_proof( - Origin::signed(5), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![unrewarded_relayer(1, 3, TEST_RELAYER_A),].into_iter().collect(), - ..Default::default() - } - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 3, - ..Default::default() - }, - )); - - assert_eq!(FeeMarket::occupied(&5), None); - assert_eq!(FeeMarket::usable_order_capacity(&5), 3); - assert_eq!(FeeMarket::usable_order_capacity(&6), 3); - assert_eq!(FeeMarket::usable_order_capacity(&7), 3); - assert!(FeeMarket::market_fee().is_some()); }); }