Skip to content

Commit

Permalink
Introduce ActiveLaneRelayersSet and NextLaneRelayersSet (#2627)
Browse files Browse the repository at this point in the history
* intreoduce ActiveLaneRelayersSet and NextLaneRelayersSet

* more tests

* fix runtimes

* CI

* CI 2

* CI 3

* apply review suggestions

* return Option<> from try_remove

* RelayerAndReward -> LaneRegistration

* separate BTreeSet -> boolean flag
  • Loading branch information
svyatonik authored and bkontur committed Jun 7, 2024
1 parent 7c05ef6 commit d938c84
Show file tree
Hide file tree
Showing 6 changed files with 717 additions and 27 deletions.
3 changes: 2 additions & 1 deletion bridges/bin/runtime-common/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ impl pallet_bridge_relayers::Config for TestRuntime {
type Reward = ThisChainBalance;
type PaymentProcedure = TestPaymentProcedure;
type StakeAndSlash = TestStakeAndSlash;
type MaxRelayersPerLane = ConstU32<16>;
type MaxActiveRelayersPerLane = ConstU32<4>;
type MaxNextRelayersPerLane = ConstU32<16>;
type SlotLength = SlotLength;
type PriorityBoostPerItem = ConstU64<1>;
type PriorityBoostForActiveLaneRelayer = PriorityBoostForActiveLaneRelayer;
Expand Down
42 changes: 25 additions & 17 deletions bridges/modules/relayers/src/extension/priority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use frame_system::{pallet_prelude::BlockNumberFor, Pallet as SystemPallet};
use sp_runtime::{
traits::{One, Zero},
transaction_validity::TransactionPriority,
Saturating,
};

// reexport everything from `integrity_tests` module
Expand Down Expand Up @@ -74,8 +75,9 @@ where
{
// if there are no relayers, explicitly registered at this lane, noone gets additional
// priority boost
let lane_relayers = RelayersPallet::<R>::lane_relayers(lane_id);
let lane_relayers_len: BlockNumberFor<R> = (lane_relayers.len() as u32).into();
let lane_relayers = RelayersPallet::<R>::active_lane_relayers(lane_id);
let active_lane_relayers = lane_relayers.relayers();
let lane_relayers_len: BlockNumberFor<R> = (active_lane_relayers.len() as u32).into();
if lane_relayers_len.is_zero() {
return 0
}
Expand All @@ -88,17 +90,17 @@ where

// let's compute current slot number
let current_block_number = SystemPallet::<R>::block_number();
let slot = current_block_number / slot_length;
let slot = current_block_number.saturating_sub(*lane_relayers.enacted_at()) / slot_length;

// and then get the relayer for that slot
let slot_relayer = match usize::try_from(slot % lane_relayers_len) {
Ok(slot_relayer_index) => &lane_relayers[slot_relayer_index],
Ok(slot_relayer_index) => &active_lane_relayers[slot_relayer_index],
Err(_) => return 0,
};

// if message delivery transaction is submitted by the relayer, assigned to the current
// slot, let's boost the transaction priority
if relayer != slot_relayer {
if relayer != slot_relayer.relayer() {
return 0
}

Expand Down Expand Up @@ -482,24 +484,30 @@ mod integrity_tests {
#[cfg(test)]
mod tests {
use super::*;
use crate::{mock::*, LaneRelayers};
use crate::{mock::*, ActiveLaneRelayers};
use bp_relayers::{ActiveLaneRelayersSet, NextLaneRelayersSet};
use sp_runtime::traits::ConstU32;

#[test]
fn compute_per_lane_priority_boost_works() {
run_test(|| {
// insert 3 relayers to the queue
let lane_id = LaneId::new(1, 2);
let relayer1 = 1_000;
let relayer2 = 2_000;
let relayer3 = 3_000;
LaneRelayers::<TestRuntime>::insert(
lane_id,
sp_runtime::BoundedVec::try_from(vec![relayer1, relayer2, relayer3]).unwrap(),
);

// at blocks 1..=SlotLength relayer1 gets the boost
System::set_block_number(0);
for _ in 1..SlotLength::get() {
let relayer1 = 100;
let relayer2 = 200;
let relayer3 = 300;
let mut next_set: NextLaneRelayersSet<_, _, ConstU32<3>> =
NextLaneRelayersSet::empty(5);
assert!(next_set.try_insert(relayer1, 0));
assert!(next_set.try_insert(relayer2, 0));
assert!(next_set.try_insert(relayer3, 0));
let mut active_set = ActiveLaneRelayersSet::default();
active_set.activate_next_set(7, next_set, |_| true);
ActiveLaneRelayers::<TestRuntime>::insert(lane_id, active_set);

// at blocks 7..=7+SlotLength relayer1 gets the boost
System::set_block_number(6);
for _ in 7..SlotLength::get() + 7 {
System::set_block_number(System::block_number() + 1);
assert_eq!(
compute_per_lane_priority_boost::<TestRuntime>(lane_id, &relayer1),
Expand Down
48 changes: 40 additions & 8 deletions bridges/modules/relayers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

use bp_messages::LaneId;
use bp_relayers::{
ExplicitOrAccountParams, PaymentProcedure, Registration, RelayerRewardsKeyProvider,
RewardsAccountParams, StakeAndSlash,
ActiveLaneRelayersSet, ExplicitOrAccountParams, NextLaneRelayersSet, PaymentProcedure,
Registration, RelayerRewardsKeyProvider, RewardsAccountParams, StakeAndSlash,
};
use bp_runtime::StorageDoubleMapKeyProvider;
use frame_support::fail;
Expand Down Expand Up @@ -71,9 +71,28 @@ pub mod pallet {
/// Stake and slash scheme.
type StakeAndSlash: StakeAndSlash<Self::AccountId, BlockNumberFor<Self>, Self::Reward>;

/// Maximal number of relayers that can register themselves on a single lane.
/// Maximal number of relayers that can reside in the active lane relayers set on a single
/// lane.
///
/// Lowering this value leads to additional concurrency between relayers, potentially
/// making messages cheaper. So it shall not be too large.
#[pallet::constant]
type MaxActiveRelayersPerLane: Get<u32>;
/// Maximal number of relayers that can reside in the next lane relayers set on a single
/// lane.
///
/// Relayers set is a bounded priority queue, where relayers with lower expected reward are
/// prioritized over greedier relayers. At the end of epoch, we select top
/// `MaxActiveRelayersPerLane` relayers from the next set and move them to the next set. To
/// alleviate possible spam attacks, where relayers are registering at lane with zero reward
/// (pushing out actual relayers with larger expected reward) and then `deregistering`
/// themselves right before epoch end, we make the next relayers set larger than the active
/// set. It would make it more expensive for attackers to fill the whole next set.
///
/// This value must be larger than or equal to the [`Self::MaxActiveRelayersPerLane`].
#[pallet::constant]
type MaxRelayersPerLane: Get<u32>;
type MaxNextRelayersPerLane: Get<u32>;

/// Length of slots in chain blocks.
///
/// Registered relayer may explicitly register himself at some lane to get priority boost
Expand Down Expand Up @@ -499,20 +518,33 @@ pub mod pallet {
OptionQuery,
>;

/// A set of relayers that have explicitly registered themselves at a given lane.
/// An active set of relayers that have explicitly registered themselves at a given lane.
///
/// Every relayer inside this set receives additional priority boost when it submits
/// message delivers messages at given lane. The boost only happens inside the slot,
/// assigned to relayer.
#[pallet::storage]
#[pallet::getter(fn lane_relayers)]
pub type LaneRelayers<T: Config> = StorageMap<
#[pallet::getter(fn active_lane_relayers)]
pub type ActiveLaneRelayers<T: Config> = StorageMap<
_,
Identity,
LaneId,
BoundedVec<T::AccountId, T::MaxRelayersPerLane>,
ActiveLaneRelayersSet<T::AccountId, BlockNumberFor<T>, T::MaxActiveRelayersPerLane>,
ValueQuery,
>;

/// A next set of relayers that have explicitly registered themselves at a given lane.
///
/// This set may replace the [`ActiveLaneRelayers`] after current epoch ends.
#[pallet::storage]
#[pallet::getter(fn next_lane_relayers)]
pub type NextLaneRelayers<T: Config> = StorageMap<
_,
Identity,
LaneId,
NextLaneRelayersSet<T::AccountId, BlockNumberFor<T>, T::MaxNextRelayersPerLane>,
OptionQuery,
>;
}

#[cfg(test)]
Expand Down
5 changes: 4 additions & 1 deletion bridges/modules/relayers/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ parameter_types! {
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128);
pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value();
pub MaxActiveRelayersPerLane: u32 = 4;
pub MaxNextRelayersPerLane: u32 = 16;
pub SlotLength: u32 = 16;
pub PriorityBoostForActiveLaneRelayer: TransactionPriority = 1;
}
Expand Down Expand Up @@ -288,7 +290,8 @@ impl pallet_bridge_relayers::Config for TestRuntime {
type Reward = ThisChainBalance;
type PaymentProcedure = TestPaymentProcedure;
type StakeAndSlash = TestStakeAndSlash;
type MaxRelayersPerLane = ConstU32<16>;
type MaxActiveRelayersPerLane = MaxActiveRelayersPerLane;
type MaxNextRelayersPerLane = MaxNextRelayersPerLane;
type SlotLength = SlotLength;
type PriorityBoostPerItem = ConstU64<1>;
type PriorityBoostForActiveLaneRelayer = PriorityBoostForActiveLaneRelayer;
Expand Down
Loading

0 comments on commit d938c84

Please sign in to comment.