// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Code that allows relayers pezpallet to be used as a payment mechanism for //! the `pezpallet-bridge-messages` pezpallet using `RewardsAccountParams`. use crate::{Config, Pezpallet}; use alloc::collections::vec_deque::VecDeque; use core::{marker::PhantomData, ops::RangeInclusive}; use pezbp_messages::{ source_chain::{DeliveryConfirmationPayments, RelayersRewards}, MessageNonce, }; pub use pezbp_relayers::PayRewardFromAccount; use pezbp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use pezbp_runtime::Chain; use pezframe_support::{pezsp_runtime::SaturatedConversion, traits::Get}; use pezpallet_bridge_messages::LaneIdOf; use pezsp_arithmetic::traits::{Saturating, Zero}; /// Adapter that allows relayers pezpallet to be used as a delivery+dispatch payment mechanism /// for the `pezpallet-bridge-messages` pezpallet and using `RewardsAccountParams`. pub struct DeliveryConfirmationPaymentsAdapter( PhantomData<(T, MI, RI, DeliveryReward)>, ); impl DeliveryConfirmationPayments> for DeliveryConfirmationPaymentsAdapter where T: Config + pezpallet_bridge_messages::Config, MI: 'static, RI: 'static, DeliveryReward: Get, >::Reward: From>>, { type Error = &'static str; fn pay_reward( lane_id: LaneIdOf, pez_messages_relayers: VecDeque>, confirmation_relayer: &T::AccountId, received_range: &RangeInclusive, ) -> MessageNonce { let relayers_rewards = pezbp_messages::calc_relayers_rewards::( pez_messages_relayers, received_range, ); let rewarded_relayers = relayers_rewards.len(); register_relayers_rewards::( confirmation_relayer, relayers_rewards, RewardsAccountParams::new( lane_id, T::BridgedChain::ID, RewardsAccountOwner::BridgedChain, ), DeliveryReward::get(), ); rewarded_relayers as _ } } // Update rewards to given relayers, optionally rewarding confirmation relayer. fn register_relayers_rewards< T: Config + pezpallet_bridge_messages::Config, RI: 'static, MI: 'static, >( confirmation_relayer: &T::AccountId, relayers_rewards: RelayersRewards, lane_id: RewardsAccountParams>, delivery_fee: T::RewardBalance, ) where >::Reward: From>>, { // reward every relayer except `confirmation_relayer` let mut confirmation_relayer_reward = T::RewardBalance::zero(); for (relayer, messages) in relayers_rewards { // sane runtime configurations guarantee that the number of messages will be below // `u32::MAX` let relayer_reward = T::RewardBalance::saturated_from(messages).saturating_mul(delivery_fee); if relayer != *confirmation_relayer { Pezpallet::::register_relayer_reward(lane_id.into(), &relayer, relayer_reward); } else { confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(relayer_reward); } } // finally - pay reward to confirmation relayer Pezpallet::::register_relayer_reward( lane_id.into(), confirmation_relayer, confirmation_relayer_reward, ); } #[cfg(test)] mod tests { use super::*; use crate::{mock::*, RelayerRewards}; use pezbp_messages::LaneIdType; use pezbp_relayers::PaymentProcedure; use pezframe_support::{ assert_ok, traits::fungible::{Inspect, Mutate}, }; const RELAYER_1: ThisChainAccountId = 1; const RELAYER_2: ThisChainAccountId = 2; const RELAYER_3: ThisChainAccountId = 3; fn relayers_rewards() -> RelayersRewards { vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() } #[test] fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { run_test(|| { register_relayers_rewards::( &RELAYER_2, relayers_rewards(), test_reward_account_param(), 50, ); assert_eq!( RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); }); } #[test] fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { run_test(|| { register_relayers_rewards::( &RELAYER_3, relayers_rewards(), test_reward_account_param(), 50, ); assert_eq!( RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); assert_eq!( RelayerRewards::::get(RELAYER_3, test_reward_account_param()), None ); }); } #[test] fn pay_reward_from_account_actually_pays_reward() { type Balances = pezpallet_balances::Pezpallet; type PayLaneRewardFromAccount = PayRewardFromAccount; run_test(|| { let in_lane_0 = RewardsAccountParams::new( TestLaneIdType::try_new(1, 2).unwrap(), *b"test", RewardsAccountOwner::ThisChain, ); let out_lane_1 = RewardsAccountParams::new( TestLaneIdType::try_new(1, 3).unwrap(), *b"test", RewardsAccountOwner::BridgedChain, ); let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); assert_ok!(Balances::mint_into(&in_lane0_rewards_account, 200)); assert_ok!(Balances::mint_into(&out_lane1_rewards_account, 100)); assert_eq!(Balances::balance(&in_lane0_rewards_account), 200); assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 0); assert_eq!(Balances::balance(&2), 0); assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, 1_u64)); assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 100); assert_eq!(Balances::balance(&2), 0); assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100, 1_u64)); assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); assert_eq!(Balances::balance(&1), 200); assert_eq!(Balances::balance(&2), 0); assert_ok!(PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100, 2_u64)); assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); assert_eq!(Balances::balance(&1), 200); assert_eq!(Balances::balance(&2), 100); }); } }