Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Companions fee market for darwinia-v0.12.3 #120

Merged
merged 1 commit into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions modules/fee-market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use sp_runtime::{
};
use sp_std::vec::Vec;
// --- darwinia-network ---
use s2s::RewardItem;
use types::{Order, Relayer, SlashReport};

pub type AccountId<T> = <T as frame_system::Config>::AccountId;
Expand Down Expand Up @@ -107,6 +108,17 @@ pub mod pallet {
UpdateAssignedRelayersNumber(u32),
/// Slash report
FeeMarketSlash(SlashReport<T::AccountId, T::BlockNumber, BalanceOf<T, I>>),
/// Create new order. \[lane_id, message_nonce, order_fee, assigned_relayers,
/// out_of_slots_time\]
OrderCreated(
LaneId,
MessageNonce,
BalanceOf<T, I>,
Vec<T::AccountId>,
Option<T::BlockNumber>,
),
/// Reward distribute of the order. \[lane_id, message_nonce, rewards\]
OrderReward(LaneId, MessageNonce, RewardItem<T::AccountId, BalanceOf<T, I>>),
}

#[pallet::error]
Expand All @@ -117,6 +129,8 @@ pub mod pallet {
AlreadyEnrolled,
/// This relayer doesn't enroll ever.
NotEnrolled,
/// Locked collateral is too low to cover one order.
CollateralTooLow,
/// Update locked collateral is not allow since some orders are not confirm.
StillHasOrdersNotConfirmed,
/// The fee is lower than MinimumRelayFee.
Expand Down Expand Up @@ -206,6 +220,12 @@ pub mod pallet {
T::Currency::free_balance(&who) >= lock_collateral,
<Error<T, I>>::InsufficientBalance
);

ensure!(
Self::collateral_to_order_capacity(lock_collateral) > 0,
<Error<T, I>>::CollateralTooLow
);

if let Some(fee) = relay_fee {
ensure!(fee >= T::MinimumRelayFee::get(), <Error<T, I>>::RelayFeeTooLow);
}
Expand Down
19 changes: 14 additions & 5 deletions modules/fee-market/src/s2s/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,25 @@ impl<T: Config<I>, I: 'static> OnMessageAccepted for FeeMarketMessageAcceptedHan
*message,
now,
T::CollateralPerOrder::get(),
assigned_relayers,
assigned_relayers.clone(),
T::Slot::get(),
);
// Store the create order
<Orders<T, I>>::insert((order.lane, order.message), order.clone());

let ids: Vec<T::AccountId> = assigned_relayers.iter().map(|r| r.id.clone()).collect();
Pallet::<T, I>::deposit_event(Event::OrderCreated(
order.lane,
order.message,
order.fee(),
ids,
order.range_end(),
));
}

// one read for assigned relayers
// one write for store order
// Storage: FeeMarket AssignedRelayers (r:1 w:0)
// Storage: FeeMarket Orders (r:0 w:1)
// Storage: System Events (r:0 w:1)
<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1)
}
}
Expand All @@ -66,8 +76,7 @@ impl<T: Config<I>, I: 'static> OnDeliveryConfirmed for FeeMarketMessageConfirmed
}
}

// one db read for get order
// one db write for update order confirm time
// Storage: FeeMarket Orders (r:1 w:1)
<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1)
}
}
2 changes: 1 addition & 1 deletion modules/fee-market/src/s2s/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// along with Darwinia. If not, see <https://www.gnu.org/licenses/>.

pub mod payment;
pub use payment::FeeMarketPayment;
pub use payment::{FeeMarketPayment, RewardItem};

pub mod callbacks;
pub use callbacks::{FeeMarketMessageAcceptedHandler, FeeMarketMessageConfirmedHandler};
165 changes: 105 additions & 60 deletions modules/fee-market/src/s2s/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use sp_std::{
collections::{btree_map::BTreeMap, vec_deque::VecDeque},
ops::RangeInclusive,
};
use scale_info::TypeInfo;
// --- darwinia-network ---
use crate::{Config, Orders, Pallet, *};

Expand Down Expand Up @@ -77,61 +78,54 @@ where
received_range: &RangeInclusive<MessageNonce>,
relayer_fund_account: &T::AccountId,
) {
let RewardsBook {
messages_relayers_rewards,
confirmation_relayer_rewards,
assigned_relayers_rewards,
treasury_total_rewards,
} = slash_and_calculate_rewards::<T, I>(
lane_id,
messages_relayers,
received_range,
relayer_fund_account,
);
let RewardsBook { deliver_sum, confirm_sum, assigned_relayers_sum, treasury_sum } =
slash_and_calculate_rewards::<T, I>(
lane_id,
messages_relayers,
confirmation_relayer.clone(),
received_range,
relayer_fund_account,
);

// Pay confirmation relayer rewards
do_reward::<T, I>(relayer_fund_account, confirmation_relayer, confirmation_relayer_rewards);
do_reward::<T, I>(relayer_fund_account, confirmation_relayer, confirm_sum);
// Pay messages relayers rewards
for (relayer, reward) in messages_relayers_rewards {
for (relayer, reward) in deliver_sum {
do_reward::<T, I>(relayer_fund_account, &relayer, reward);
}
// Pay assign relayer reward
for (relayer, reward) in assigned_relayers_rewards {
for (relayer, reward) in assigned_relayers_sum {
do_reward::<T, I>(relayer_fund_account, &relayer, reward);
}
// Pay treasury reward
// Pay treasury_sum reward
do_reward::<T, I>(
relayer_fund_account,
&T::TreasuryPalletId::get().into_account(),
treasury_total_rewards,
treasury_sum,
);
}
}

/// Slash and calculate rewards for messages_relayers, confirmation relayers, treasury,
/// Slash and calculate rewards for messages_relayers, confirmation relayers, treasury_sum,
/// assigned_relayers
pub fn slash_and_calculate_rewards<T, I>(
lane_id: LaneId,
messages_relayers: VecDeque<UnrewardedRelayer<T::AccountId>>,
confirm_relayer: T::AccountId,
received_range: &RangeInclusive<MessageNonce>,
relayer_fund_account: &T::AccountId,
) -> RewardsBook<T::AccountId, BalanceOf<T, I>>
) -> RewardsBook<T, I>
where
T: frame_system::Config + Config<I>,
I: 'static,
{
let mut confirmation_rewards = BalanceOf::<T, I>::zero();
let mut messages_rewards = BTreeMap::<T::AccountId, BalanceOf<T, I>>::new();
let mut assigned_relayers_rewards = BTreeMap::<T::AccountId, BalanceOf<T, I>>::new();
let mut treasury_total_rewards = BalanceOf::<T, I>::zero();

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());

for message_nonce in nonce_begin..nonce_end + 1 {
// The order created when message was accepted, so we can always get the order info
// below.
// The order created when message was accepted, so we can always get the order info.
if let Some(order) = <Orders<T, I>>::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,
Expand All @@ -140,31 +134,29 @@ where
order.confirm_time.unwrap_or_else(|| frame_system::Pallet::<T>::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
let treasury_reward = message_fee.saturating_sub(base_fee);
treasury_total_rewards = treasury_total_rewards.saturating_add(treasury_reward);
// message fee - base fee => treasury_sum
reward_item.to_treasury = Some(message_fee.saturating_sub(base_fee));

// 60% * base fee => assigned_relayers_rewards
let assigned_relayers_reward = T::AssignedRelayersRewardRatio::get() * base_fee;
assigned_relayers_rewards
.entry(who)
.and_modify(|r| *r = r.saturating_add(assigned_relayers_reward))
.or_insert(assigned_relayers_reward);
// 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(assigned_relayers_reward);

// 80% * (1 - 60%) * base_fee => message relayer
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;
// 20% * (1 - 60%) * base_fee => confirm relayer
// ConfirmRelayersRewardRatio * (1 - AssignedRelayersRewardRatio) * base_fee =>
// confirm relayer
confirm_reward = T::ConfirmRelayersRewardRatio::get() * bridger_relayers_reward;
} else {
// The order delivery is delay
// The order delivery is delay, slash occurs.
let mut total_slash = message_fee;

// calculate slash amount
Expand All @@ -190,29 +182,26 @@ where
}
total_slash += assigned_relayers_slash;

// 80% total slash => confirm relayer
// MessageRelayersRewardRatio total slash => message relayer
message_reward = T::MessageRelayersRewardRatio::get() * total_slash;
// 20% total slash => confirm relayer
// ConfirmRelayersRewardRatio 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(entry.relayer.clone())
.and_modify(|r| *r = r.saturating_add(message_reward))
.or_insert(message_reward);
reward_item.to_message_relayer = Some((entry.clone().relayer, message_reward));
reward_item.to_confirm_relayer = Some((confirm_relayer.clone(), confirm_reward));

Pallet::<T, I>::deposit_event(Event::OrderReward(
lane_id,
message_nonce,
reward_item.clone(),
));

rewards_book.add_reward_item(reward_item);
}
}
}

RewardsBook {
messages_relayers_rewards: messages_rewards,
confirmation_relayer_rewards: confirmation_rewards,
assigned_relayers_rewards,
treasury_total_rewards,
}
rewards_book
}

/// Do slash for absent assigned relayers
Expand Down Expand Up @@ -278,10 +267,66 @@ pub(crate) fn do_reward<T: Config<I>, I: 'static>(
}
}

/// Record the concrete reward distribution of certain order
#[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, TypeInfo)]
pub struct RewardItem<AccountId, Balance> {
pub to_slot_relayer: Option<(AccountId, Balance)>,
pub to_treasury: Option<Balance>,
pub to_message_relayer: Option<(AccountId, Balance)>,
pub to_confirm_relayer: Option<(AccountId, Balance)>,
}

impl<AccountId, Balance> RewardItem<AccountId, Balance> {
fn new() -> Self {
Self {
to_slot_relayer: None,
to_treasury: None,
to_message_relayer: None,
to_confirm_relayer: None,
}
}
}

/// Record the calculation rewards result
pub struct RewardsBook<AccountId, Balance> {
pub messages_relayers_rewards: BTreeMap<AccountId, Balance>,
pub confirmation_relayer_rewards: Balance,
pub assigned_relayers_rewards: BTreeMap<AccountId, Balance>,
pub treasury_total_rewards: Balance,
#[derive(Clone, Debug, Eq, PartialEq, TypeInfo)]
pub struct RewardsBook<T: Config<I>, I: 'static> {
pub deliver_sum: BTreeMap<T::AccountId, BalanceOf<T, I>>,
pub confirm_sum: BalanceOf<T, I>,
pub assigned_relayers_sum: BTreeMap<T::AccountId, BalanceOf<T, I>>,
pub treasury_sum: BalanceOf<T, I>,
}

impl<T: Config<I>, I: 'static> RewardsBook<T, I> {
fn new() -> Self {
Self {
deliver_sum: BTreeMap::new(),
confirm_sum: BalanceOf::<T, I>::zero(),
assigned_relayers_sum: BTreeMap::new(),
treasury_sum: BalanceOf::<T, I>::zero(),
}
}

fn add_reward_item(&mut self, item: RewardItem<T::AccountId, BalanceOf<T, I>>) {
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);
}
}
}
Loading