#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
pub use bp_relayers::RewardLedger;
use bp_relayers::{PaymentProcedure, Registration, RelayerRewardsKeyProvider, StakeAndSlash};
use bp_runtime::StorageDoubleMapKeyProvider;
use core::marker::PhantomData;
use frame_support::{fail, traits::tokens::Balance};
use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero};
use sp_runtime::{
traits::{CheckedSub, IdentifyAccount},
Saturating,
};
pub use pallet::*;
pub use payment_adapter::{DeliveryConfirmationPaymentsAdapter, PayRewardFromAccount};
pub use stake_adapter::StakeAndSlashNamed;
pub use weights::WeightInfo;
pub use weights_ext::WeightInfoExt;
mod mock;
mod payment_adapter;
mod stake_adapter;
mod weights_ext;
pub mod benchmarking;
pub mod extension;
pub mod migration;
pub mod weights;
pub const LOG_TARGET: &str = "runtime::bridge-relayers";
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
type RelayerRewardsKeyProviderOf<T, I> = RelayerRewardsKeyProvider<
<T as frame_system::Config>::AccountId,
<T as Config<I>>::Reward,
<T as Config<I>>::RewardBalance,
>;
pub type BeneficiaryOf<T, I> = <<T as Config<I>>::PaymentProcedure as PaymentProcedure<
<T as frame_system::Config>::AccountId,
<T as Config<I>>::Reward,
<T as Config<I>>::RewardBalance,
>>::Beneficiary;
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
type RewardBalance: AtLeast32BitUnsigned + Copy + Member + Parameter + MaxEncodedLen;
type Reward: Parameter + MaxEncodedLen + Send + Sync + Copy + Clone;
type PaymentProcedure: PaymentProcedure<Self::AccountId, Self::Reward, Self::RewardBalance>;
type StakeAndSlash: StakeAndSlash<Self::AccountId, BlockNumberFor<Self>, Self::Balance>;
type Balance: Balance;
type WeightInfo: WeightInfoExt;
}
#[pallet::pallet]
#[pallet::storage_version(migration::STORAGE_VERSION)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I>
where
BeneficiaryOf<T, I>: From<<T as frame_system::Config>::AccountId>,
{
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::claim_rewards())]
pub fn claim_rewards(origin: OriginFor<T>, reward_kind: T::Reward) -> DispatchResult {
let relayer = ensure_signed(origin)?;
Self::do_claim_rewards(relayer.clone(), reward_kind, relayer.into())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::register())]
pub fn register(origin: OriginFor<T>, valid_till: BlockNumberFor<T>) -> DispatchResult {
let relayer = ensure_signed(origin)?;
let lease = valid_till.saturating_sub(frame_system::Pallet::<T>::block_number());
ensure!(
lease > Self::required_registration_lease(),
Error::<T, I>::InvalidRegistrationLease
);
RegisteredRelayers::<T, I>::try_mutate(
&relayer,
|maybe_registration| -> DispatchResult {
let mut registration = maybe_registration
.unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() });
ensure!(
valid_till >= registration.valid_till,
Error::<T, I>::CannotReduceRegistrationLease,
);
registration.valid_till = valid_till;
let required_stake = Self::required_stake();
if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) {
Self::do_unreserve(&relayer, to_unreserve)?;
} else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake)
{
T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| {
log::trace!(
target: LOG_TARGET,
"Failed to reserve {:?} on relayer {:?} account: {:?}",
to_reserve,
relayer,
e,
);
Error::<T, I>::FailedToReserve
})?;
}
registration.stake = required_stake;
log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer);
Self::deposit_event(Event::<T, I>::RegistrationUpdated {
relayer: relayer.clone(),
registration,
});
*maybe_registration = Some(registration);
Ok(())
},
)
}
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::deregister())]
pub fn deregister(origin: OriginFor<T>) -> DispatchResult {
let relayer = ensure_signed(origin)?;
RegisteredRelayers::<T, I>::try_mutate(
&relayer,
|maybe_registration| -> DispatchResult {
let registration = match maybe_registration.take() {
Some(registration) => registration,
None => fail!(Error::<T, I>::NotRegistered),
};
ensure!(
registration.valid_till < frame_system::Pallet::<T>::block_number(),
Error::<T, I>::RegistrationIsStillActive,
);
if !registration.stake.is_zero() {
Self::do_unreserve(&relayer, registration.stake)?;
}
log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer);
Self::deposit_event(Event::<T, I>::Deregistered { relayer: relayer.clone() });
*maybe_registration = None;
Ok(())
},
)
}
#[pallet::call_index(3)]
#[pallet::weight(T::WeightInfo::claim_rewards_to())]
pub fn claim_rewards_to(
origin: OriginFor<T>,
reward_kind: T::Reward,
beneficiary: BeneficiaryOf<T, I>,
) -> DispatchResult {
let relayer = ensure_signed(origin)?;
Self::do_claim_rewards(relayer, reward_kind, beneficiary)
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn registered_relayer(
relayer: &T::AccountId,
) -> Option<Registration<BlockNumberFor<T>, T::Balance>> {
RegisteredRelayers::<T, I>::get(relayer)
}
pub fn relayer_reward<EncodeLikeAccountId, EncodeLikeReward>(
key1: EncodeLikeAccountId,
key2: EncodeLikeReward,
) -> Option<<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Value>
where
EncodeLikeAccountId: codec::EncodeLike<
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key1,
>,
EncodeLikeReward: codec::EncodeLike<
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key2,
>,
{
RelayerRewards::<T, I>::get(key1, key2)
}
fn do_claim_rewards(
relayer: T::AccountId,
reward_kind: T::Reward,
beneficiary: BeneficiaryOf<T, I>,
) -> DispatchResult {
RelayerRewards::<T, I>::try_mutate_exists(
&relayer,
reward_kind,
|maybe_reward| -> DispatchResult {
let reward_balance =
maybe_reward.take().ok_or(Error::<T, I>::NoRewardForRelayer)?;
T::PaymentProcedure::pay_reward(
&relayer,
reward_kind,
reward_balance,
beneficiary.clone(),
)
.map_err(|e| {
log::error!(
target: LOG_TARGET,
"Failed to pay ({:?} / {:?}) rewards to {:?}(beneficiary: {:?}), error: {:?}",
reward_kind,
reward_balance,
relayer,
beneficiary,
e,
);
Error::<T, I>::FailedToPayReward
})?;
Self::deposit_event(Event::<T, I>::RewardPaid {
relayer: relayer.clone(),
reward_kind,
reward_balance,
beneficiary,
});
Ok(())
},
)
}
pub fn is_registration_active(relayer: &T::AccountId) -> bool {
let registration = match Self::registered_relayer(relayer) {
Some(registration) => registration,
None => return false,
};
if registration.stake < Self::required_stake() {
return false;
}
let remaining_lease = registration
.valid_till
.saturating_sub(frame_system::Pallet::<T>::block_number());
if remaining_lease <= Self::required_registration_lease() {
return false;
}
true
}
pub fn slash_and_deregister(
relayer: &T::AccountId,
slash_destination: impl IdentifyAccount<AccountId = T::AccountId>,
) {
let registration = match RegisteredRelayers::<T, I>::take(relayer) {
Some(registration) => registration,
None => {
log::trace!(
target: crate::LOG_TARGET,
"Cannot slash unregistered relayer {:?}",
relayer,
);
return;
},
};
let slash_destination = slash_destination.into_account();
match T::StakeAndSlash::repatriate_reserved(
relayer,
&slash_destination,
registration.stake,
) {
Ok(failed_to_slash) if failed_to_slash.is_zero() => {
log::trace!(
target: crate::LOG_TARGET,
"Relayer account {:?} has been slashed for {:?}. Funds were deposited to {:?}",
relayer,
registration.stake,
slash_destination,
);
},
Ok(failed_to_slash) => {
log::trace!(
target: crate::LOG_TARGET,
"Relayer account {:?} has been partially slashed for {:?}. Funds were deposited to {:?}. \
Failed to slash: {:?}",
relayer,
registration.stake,
slash_destination,
failed_to_slash,
);
},
Err(e) => {
log::debug!(
target: crate::LOG_TARGET,
"Failed to slash relayer account {:?}: {:?}. Maybe beneficiary account doesn't exist? \
Beneficiary: {:?}, amount: {:?}, failed to slash: {:?}",
relayer,
e,
slash_destination,
registration.stake,
registration.stake,
);
},
}
Self::deposit_event(Event::<T, I>::SlashedAndDeregistered {
relayer: relayer.clone(),
registration,
});
}
pub(crate) fn register_relayer_reward(
reward_kind: T::Reward,
relayer: &T::AccountId,
reward_balance: T::RewardBalance,
) {
if reward_balance.is_zero() {
return;
}
RelayerRewards::<T, I>::mutate(
relayer,
reward_kind,
|old_reward: &mut Option<T::RewardBalance>| {
let new_reward =
old_reward.unwrap_or_else(Zero::zero).saturating_add(reward_balance);
*old_reward = Some(new_reward);
log::trace!(
target: crate::LOG_TARGET,
"Relayer {:?} can now claim reward for serving payer {:?}: {:?}",
relayer,
reward_kind,
new_reward,
);
Self::deposit_event(Event::<T, I>::RewardRegistered {
relayer: relayer.clone(),
reward_kind,
reward_balance,
});
},
);
}
pub(crate) fn required_registration_lease() -> BlockNumberFor<T> {
<T::StakeAndSlash as StakeAndSlash<
T::AccountId,
BlockNumberFor<T>,
T::Balance,
>>::RequiredRegistrationLease::get()
}
pub(crate) fn required_stake() -> T::Balance {
<T::StakeAndSlash as StakeAndSlash<
T::AccountId,
BlockNumberFor<T>,
T::Balance,
>>::RequiredStake::get()
}
fn do_unreserve(relayer: &T::AccountId, amount: T::Balance) -> DispatchResult {
let failed_to_unreserve = T::StakeAndSlash::unreserve(relayer, amount);
if !failed_to_unreserve.is_zero() {
log::trace!(
target: LOG_TARGET,
"Failed to unreserve {:?}/{:?} on relayer {:?} account",
failed_to_unreserve,
amount,
relayer,
);
fail!(Error::<T, I>::FailedToUnreserve)
}
Ok(())
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
RewardRegistered {
relayer: T::AccountId,
reward_kind: T::Reward,
reward_balance: T::RewardBalance,
},
RewardPaid {
relayer: T::AccountId,
reward_kind: T::Reward,
reward_balance: T::RewardBalance,
beneficiary: BeneficiaryOf<T, I>,
},
RegistrationUpdated {
relayer: T::AccountId,
registration: Registration<BlockNumberFor<T>, T::Balance>,
},
Deregistered {
relayer: T::AccountId,
},
SlashedAndDeregistered {
relayer: T::AccountId,
registration: Registration<BlockNumberFor<T>, T::Balance>,
},
}
#[pallet::error]
pub enum Error<T, I = ()> {
NoRewardForRelayer,
FailedToPayReward,
InvalidRegistrationLease,
CannotReduceRegistrationLease,
FailedToReserve,
FailedToUnreserve,
NotRegistered,
RegistrationIsStillActive,
}
#[pallet::storage]
pub type RelayerRewards<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher1,
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key1,
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Hasher2,
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Key2,
<RelayerRewardsKeyProviderOf<T, I> as StorageDoubleMapKeyProvider>::Value,
OptionQuery,
>;
#[pallet::storage]
pub type RegisteredRelayers<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Blake2_128Concat,
T::AccountId,
Registration<BlockNumberFor<T>, T::Balance>,
OptionQuery,
>;
}
impl<T: Config<I>, I: 'static, Reward, RewardBalance>
RewardLedger<T::AccountId, Reward, RewardBalance> for Pallet<T, I>
where
Reward: Into<T::Reward>,
RewardBalance: Into<T::RewardBalance>,
{
fn register_reward(relayer: &T::AccountId, reward: Reward, reward_balance: RewardBalance) {
Self::register_relayer_reward(reward.into(), relayer, reward_balance.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
use mock::{RuntimeEvent as TestEvent, *};
use bp_messages::{HashedLaneId, LaneIdType};
use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
use frame_support::{assert_noop, assert_ok, traits::fungible::Mutate};
use frame_system::{EventRecord, Pallet as System, Phase};
use sp_runtime::DispatchError;
fn get_ready_for_events() {
System::<TestRuntime>::set_block_number(1);
System::<TestRuntime>::reset_events();
}
#[test]
fn register_relayer_reward_emit_event() {
run_test(|| {
get_ready_for_events();
Pallet::<TestRuntime>::register_relayer_reward(
test_reward_account_param(),
®ULAR_RELAYER,
100,
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RewardRegistered {
relayer: REGULAR_RELAYER,
reward_kind: test_reward_account_param(),
reward_balance: 100
}),
topics: vec![],
}),
);
});
}
#[test]
fn slash_and_deregister_works() {
run_test(|| {
get_ready_for_events();
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150,
));
let registration =
Pallet::<TestRuntime>::registered_relayer(®ISTER_RELAYER).unwrap();
assert_eq!(registration, Registration { valid_till: 150, stake: Stake::get() });
let slash_destination = RewardsAccountParams::new(
HashedLaneId::try_new(1, 2).unwrap(),
*b"test",
RewardsAccountOwner::ThisChain,
);
let slash_destination = bp_relayers::ExplicitOrAccountParams::Params(slash_destination);
Pallet::<TestRuntime>::slash_and_deregister(®ISTER_RELAYER, slash_destination);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::SlashedAndDeregistered {
relayer: REGISTER_RELAYER,
registration,
}),
topics: vec![],
})
)
});
}
#[test]
fn root_cant_claim_anything() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(
RuntimeOrigin::root(),
test_reward_account_param()
),
DispatchError::BadOrigin,
);
});
}
#[test]
fn relayer_cant_claim_if_no_reward_exists() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(
RuntimeOrigin::signed(REGULAR_RELAYER),
test_reward_account_param()
),
Error::<TestRuntime>::NoRewardForRelayer,
);
});
}
#[test]
fn relayer_cant_claim_if_payment_procedure_fails() {
run_test(|| {
RelayerRewards::<TestRuntime>::insert(
FAILING_RELAYER,
test_reward_account_param(),
100,
);
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(
RuntimeOrigin::signed(FAILING_RELAYER),
test_reward_account_param()
),
Error::<TestRuntime>::FailedToPayReward,
);
});
}
#[test]
fn relayer_can_claim_reward() {
run_test(|| {
get_ready_for_events();
RelayerRewards::<TestRuntime>::insert(
REGULAR_RELAYER,
test_reward_account_param(),
100,
);
assert_ok!(Pallet::<TestRuntime>::claim_rewards(
RuntimeOrigin::signed(REGULAR_RELAYER),
test_reward_account_param()
));
assert_eq!(
RelayerRewards::<TestRuntime>::get(REGULAR_RELAYER, test_reward_account_param()),
None
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RewardPaid {
relayer: REGULAR_RELAYER,
reward_kind: test_reward_account_param(),
reward_balance: 100,
beneficiary: REGULAR_RELAYER,
}),
topics: vec![],
}),
);
});
}
#[test]
fn relayer_can_claim_reward_to() {
run_test(|| {
get_ready_for_events();
RelayerRewards::<TestRuntime>::insert(
REGULAR_RELAYER,
test_reward_account_param(),
100,
);
assert_ok!(Pallet::<TestRuntime>::claim_rewards_to(
RuntimeOrigin::signed(REGULAR_RELAYER),
test_reward_account_param(),
REGULAR_RELAYER2,
));
assert_eq!(
RelayerRewards::<TestRuntime>::get(REGULAR_RELAYER, test_reward_account_param()),
None
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RewardPaid {
relayer: REGULAR_RELAYER,
reward_kind: test_reward_account_param(),
reward_balance: 100,
beneficiary: REGULAR_RELAYER2,
}),
topics: vec![],
}),
);
});
}
#[test]
fn register_fails_if_valid_till_is_a_past_block() {
run_test(|| {
System::<TestRuntime>::set_block_number(100);
assert_noop!(
Pallet::<TestRuntime>::register(RuntimeOrigin::signed(REGISTER_RELAYER), 50),
Error::<TestRuntime>::InvalidRegistrationLease,
);
});
}
#[test]
fn register_fails_if_valid_till_lease_is_less_than_required() {
run_test(|| {
System::<TestRuntime>::set_block_number(100);
assert_noop!(
Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
99 + Lease::get()
),
Error::<TestRuntime>::InvalidRegistrationLease,
);
});
}
#[test]
fn register_works() {
run_test(|| {
get_ready_for_events();
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get());
assert_eq!(
Pallet::<TestRuntime>::registered_relayer(®ISTER_RELAYER),
Some(Registration { valid_till: 150, stake: Stake::get() }),
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RegistrationUpdated {
relayer: REGISTER_RELAYER,
registration: Registration { valid_till: 150, stake: Stake::get() },
}),
topics: vec![],
}),
);
});
}
#[test]
fn register_fails_if_new_valid_till_is_lesser_than_previous() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
assert_noop!(
Pallet::<TestRuntime>::register(RuntimeOrigin::signed(REGISTER_RELAYER), 125),
Error::<TestRuntime>::CannotReduceRegistrationLease,
);
});
}
#[test]
fn register_fails_if_it_cant_unreserve_some_balance_if_required_stake_decreases() {
run_test(|| {
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() + 1 },
);
assert_noop!(
Pallet::<TestRuntime>::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150),
Error::<TestRuntime>::FailedToUnreserve,
);
});
}
#[test]
fn register_unreserves_some_balance_if_required_stake_decreases() {
run_test(|| {
get_ready_for_events();
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() + 1 },
);
TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() + 1).unwrap();
assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get() + 1);
let free_balance = Balances::free_balance(REGISTER_RELAYER);
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get());
assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + 1);
assert_eq!(
Pallet::<TestRuntime>::registered_relayer(®ISTER_RELAYER),
Some(Registration { valid_till: 150, stake: Stake::get() }),
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RegistrationUpdated {
relayer: REGISTER_RELAYER,
registration: Registration { valid_till: 150, stake: Stake::get() }
}),
topics: vec![],
}),
);
});
}
#[test]
fn register_fails_if_it_cant_reserve_some_balance() {
run_test(|| {
Balances::set_balance(®ISTER_RELAYER, 0);
assert_noop!(
Pallet::<TestRuntime>::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150),
Error::<TestRuntime>::FailedToReserve,
);
});
}
#[test]
fn register_fails_if_it_cant_reserve_some_balance_if_required_stake_increases() {
run_test(|| {
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() - 1 },
);
Balances::set_balance(®ISTER_RELAYER, 0);
assert_noop!(
Pallet::<TestRuntime>::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150),
Error::<TestRuntime>::FailedToReserve,
);
});
}
#[test]
fn register_reserves_some_balance_if_required_stake_increases() {
run_test(|| {
get_ready_for_events();
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() - 1 },
);
TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() - 1).unwrap();
let free_balance = Balances::free_balance(REGISTER_RELAYER);
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get());
assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance - 1);
assert_eq!(
Pallet::<TestRuntime>::registered_relayer(®ISTER_RELAYER),
Some(Registration { valid_till: 150, stake: Stake::get() }),
);
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::RegistrationUpdated {
relayer: REGISTER_RELAYER,
registration: Registration { valid_till: 150, stake: Stake::get() }
}),
topics: vec![],
}),
);
});
}
#[test]
fn deregister_fails_if_not_registered() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)),
Error::<TestRuntime>::NotRegistered,
);
});
}
#[test]
fn deregister_fails_if_registration_is_still_active() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
System::<TestRuntime>::set_block_number(100);
assert_noop!(
Pallet::<TestRuntime>::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)),
Error::<TestRuntime>::RegistrationIsStillActive,
);
});
}
#[test]
fn deregister_works() {
run_test(|| {
get_ready_for_events();
assert_ok!(Pallet::<TestRuntime>::register(
RuntimeOrigin::signed(REGISTER_RELAYER),
150
));
System::<TestRuntime>::set_block_number(151);
let reserved_balance = Balances::reserved_balance(REGISTER_RELAYER);
let free_balance = Balances::free_balance(REGISTER_RELAYER);
assert_ok!(Pallet::<TestRuntime>::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)));
assert_eq!(
Balances::reserved_balance(REGISTER_RELAYER),
reserved_balance - Stake::get()
);
assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + Stake::get());
assert_eq!(
System::<TestRuntime>::events().last(),
Some(&EventRecord {
phase: Phase::Initialization,
event: TestEvent::BridgeRelayers(Event::Deregistered {
relayer: REGISTER_RELAYER
}),
topics: vec![],
}),
);
});
}
#[test]
fn is_registration_active_is_false_for_unregistered_relayer() {
run_test(|| {
assert!(!Pallet::<TestRuntime>::is_registration_active(®ISTER_RELAYER));
});
}
#[test]
fn is_registration_active_is_false_when_stake_is_too_low() {
run_test(|| {
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() - 1 },
);
assert!(!Pallet::<TestRuntime>::is_registration_active(®ISTER_RELAYER));
});
}
#[test]
fn is_registration_active_is_false_when_remaining_lease_is_too_low() {
run_test(|| {
System::<TestRuntime>::set_block_number(150 - Lease::get());
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 150, stake: Stake::get() },
);
assert!(!Pallet::<TestRuntime>::is_registration_active(®ISTER_RELAYER));
});
}
#[test]
fn is_registration_active_is_true_when_relayer_is_properly_registeered() {
run_test(|| {
System::<TestRuntime>::set_block_number(150 - Lease::get());
RegisteredRelayers::<TestRuntime>::insert(
REGISTER_RELAYER,
Registration { valid_till: 151, stake: Stake::get() },
);
assert!(Pallet::<TestRuntime>::is_registration_active(®ISTER_RELAYER));
});
}
}