Introduce bridge relayers pallet (#1513)

* introduce relayers pallet

* add MessageDeliveryAndDispatchPaymentAdapter

* plug in pallet into test runtimes

* tests prototype

* tests for the relayers pallet

* tests for payment adapter

* mint_reward_payment_procedure_actually_mints_tokens

* benchmarks

* remove irrelevant todo

* remove redundant clone
This commit is contained in:
Svyatoslav Nikolsky
2022-07-20 13:10:59 +03:00
committed by Bastian Köcher
parent 1e0c2a6e02
commit 7590abd1a3
17 changed files with 862 additions and 43 deletions
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// 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 <http://www.gnu.org/licenses/>.
//! Runtime module that is used to store relayer rewards and (in the future) to
//! coordinate relations between relayers.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
use bp_relayers::PaymentProcedure;
use sp_arithmetic::traits::AtLeast32BitUnsigned;
use sp_std::marker::PhantomData;
use weights::WeightInfo;
pub use pallet::*;
pub use payment_adapter::MessageDeliveryAndDispatchPaymentAdapter;
mod benchmarking;
mod mock;
mod payment_adapter;
pub mod weights;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Type of relayer reward.
type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen;
/// Pay rewards adapter.
type PaymentProcedure: PaymentProcedure<Self::AccountId, Self::Reward>;
/// Pallet call weights.
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Claim accumulated rewards.
#[pallet::weight(T::WeightInfo::claim_rewards())]
pub fn claim_rewards(origin: OriginFor<T>) -> DispatchResult {
let relayer = ensure_signed(origin)?;
RelayerRewards::<T>::try_mutate_exists(&relayer, |maybe_reward| -> DispatchResult {
let reward = maybe_reward.take().ok_or(Error::<T>::NoRewardForRelayer)?;
T::PaymentProcedure::pay_reward(&relayer, reward).map_err(|e| {
log::trace!(
target: "runtime::bridge-relayers",
"Failed to pay rewards to {:?}: {:?}",
relayer,
e,
);
Error::<T>::FailedToPayReward
})?;
Self::deposit_event(Event::<T>::RewardPaid { relayer: relayer.clone(), reward });
Ok(())
})
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Reward has been paid to the relayer.
RewardPaid {
/// Relayer account that has been rewarded.
relayer: T::AccountId,
/// Reward amount.
reward: T::Reward,
},
}
#[pallet::error]
pub enum Error<T> {
/// No reward can be claimed by given relayer.
NoRewardForRelayer,
/// Reward payment procedure has failed.
FailedToPayReward,
}
/// Map of the relayer => accumulated reward.
#[pallet::storage]
pub type RelayerRewards<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, T::Reward, OptionQuery>;
}
#[cfg(test)]
mod tests {
use super::*;
use mock::*;
use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect};
use sp_runtime::DispatchError;
#[test]
fn root_cant_claim_anything() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::root()),
DispatchError::BadOrigin,
);
});
}
#[test]
fn relayer_cant_claim_if_no_reward_exists() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::signed(REGULAR_RELAYER)),
Error::<TestRuntime>::NoRewardForRelayer,
);
});
}
#[test]
fn relayer_cant_claim_if_payment_procedure_fails() {
run_test(|| {
RelayerRewards::<TestRuntime>::insert(FAILING_RELAYER, 100);
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::signed(FAILING_RELAYER)),
Error::<TestRuntime>::FailedToPayReward,
);
});
}
#[test]
fn relayer_can_claim_reward() {
run_test(|| {
RelayerRewards::<TestRuntime>::insert(REGULAR_RELAYER, 100);
assert_ok!(Pallet::<TestRuntime>::claim_rewards(Origin::signed(REGULAR_RELAYER)));
assert_eq!(RelayerRewards::<TestRuntime>::get(REGULAR_RELAYER), None);
});
}
#[test]
fn mint_reward_payment_procedure_actually_mints_tokens() {
type Balances = pallet_balances::Pallet<TestRuntime>;
run_test(|| {
assert_eq!(Balances::balance(&1), 0);
assert_eq!(Balances::total_issuance(), 0);
bp_relayers::MintReward::<Balances, AccountId>::pay_reward(&1, 100).unwrap();
assert_eq!(Balances::balance(&1), 100);
assert_eq!(Balances::total_issuance(), 100);
});
}
}