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

Add necessary reward event #89

Merged
merged 9 commits into from
Jun 9, 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
12 changes: 12 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 Down
21 changes: 15 additions & 6 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);
<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};
164 changes: 104 additions & 60 deletions modules/fee-market/src/s2s/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,61 +89,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 @@ -152,31 +145,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 @@ -202,29 +193,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(),
));
Comment on lines +205 to +209
Copy link
Contributor

@hackfisher hackfisher May 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we should deposit event in this function(slash_and_calculate_rewards) or at the end of pay_relayers_rewards?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If deposit this event at the end of pay_relayers_rewards, you may need to remove the RewardBook and iterate the whole RewardItems. I prefer the current way.


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 @@ -290,10 +278,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);
}
}
Comment on lines +320 to +342
Copy link
Contributor

@hackfisher hackfisher May 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way would be directly iterate though RewardItem and calculate the value wanted. RewardBook can be removed then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do this. Store the reward item in something like HashMap and iterate it in the pay_relayers_rewards. But I think the reward book is easier to read and understand.

Copy link
Contributor

@hackfisher hackfisher Jun 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feelings from my point as a reviewer, RewardBook is unnecessary concept and removing it will be simpler.

I'm going to merge this and review this discussion in separate issue because its blocking other release tasks.

}
Loading