Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
188 changes: 94 additions & 94 deletions Cargo.lock

Large diffs are not rendered by default.

114 changes: 57 additions & 57 deletions pallets/bfc-staking/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,10 @@ impl<T: Config> Pallet<T> {
}

/// Verifies if the given account has already requested for controller account update
pub fn is_controller_set_requested(controller: T::AccountId) -> bool {
pub fn is_controller_set_requested(controller: &T::AccountId) -> bool {
let round = Self::round();
let controller_sets = Self::delayed_controller_sets(round.current_round_index);
if controller_sets.is_empty() {
return false;
}
return controller_sets.into_iter().any(|c| c.old == controller);
controller_sets.into_iter().any(|c| c.old == *controller)
}

/// Verifies if the given account has already requested for commission rate update
Expand Down Expand Up @@ -426,48 +423,46 @@ impl<T: Config> Pallet<T> {
pub fn handle_delayed_controller_sets(now: RoundIndex) {
let delayed_round = now - 1;
let controller_sets = <DelayedControllerSets<T>>::take(delayed_round);
if !controller_sets.is_empty() {
controller_sets.into_iter().for_each(|c| {
if let Some(candidate) = Self::candidate_info(&c.old) {
// replace `CandidateInfo`
<CandidateInfo<T>>::remove(&c.old);
<CandidateInfo<T>>::insert(&c.new, candidate.clone());
// replace `BondedStash`
<BondedStash<T>>::insert(&c.stash, c.new.clone());
// replace `CandidatePool`
Self::replace_from_candidate_pool(&c.old, &c.new);
// replace `SelectedCandidates`
if candidate.is_selected {
Self::replace_from_selected_candidates(&c.old, &c.new, candidate.tier);
T::RelayManager::replace_bonded_controller(c.old.clone(), c.new.clone());
}
// replace `TopNominations`
if let Some(top_nominations) = <TopNominations<T>>::take(&c.old) {
Self::replace_nominator_nominations(
&top_nominations.nominators(),
&c.old,
&c.new,
);
<TopNominations<T>>::insert(&c.new, top_nominations);
}
// replace `BottomNominations`
if let Some(bottom_nominations) = <BottomNominations<T>>::take(&c.old) {
Self::replace_nominator_nominations(
&bottom_nominations.nominators(),
&c.old,
&c.new,
);
<BottomNominations<T>>::insert(&c.new, bottom_nominations);
}
// replace `AwardedPts`
let points = <AwardedPts<T>>::take(now, &c.old);
<AwardedPts<T>>::insert(now, &c.new, points);
// replace `AtStake`
let at_stake = <AtStake<T>>::take(now, &c.old);
<AtStake<T>>::insert(now, &c.new, at_stake);
controller_sets.into_iter().for_each(|c| {
if let Some(candidate) = Self::candidate_info(&c.old) {
// replace `CandidateInfo`
<CandidateInfo<T>>::remove(&c.old);
<CandidateInfo<T>>::insert(&c.new, candidate.clone());
// replace `BondedStash`
<BondedStash<T>>::insert(&c.stash, c.new.clone());
// replace `CandidatePool`
Self::replace_from_candidate_pool(&c.old, &c.new);
// replace `SelectedCandidates`
if candidate.is_selected {
Self::replace_from_selected_candidates(&c.old, &c.new, candidate.tier);
T::RelayManager::replace_bonded_controller(c.old.clone(), c.new.clone());
}
});
}
// replace `TopNominations`
if let Some(top_nominations) = <TopNominations<T>>::take(&c.old) {
Self::replace_nominator_nominations(
&top_nominations.nominators(),
&c.old,
&c.new,
);
<TopNominations<T>>::insert(&c.new, top_nominations);
}
// replace `BottomNominations`
if let Some(bottom_nominations) = <BottomNominations<T>>::take(&c.old) {
Self::replace_nominator_nominations(
&bottom_nominations.nominators(),
&c.old,
&c.new,
);
<BottomNominations<T>>::insert(&c.new, bottom_nominations);
}
// replace `AwardedPts`
let points = <AwardedPts<T>>::take(now, &c.old);
<AwardedPts<T>>::insert(now, &c.new, points);
// replace `AtStake`
let at_stake = <AtStake<T>>::take(now, &c.old);
<AtStake<T>>::insert(now, &c.new, at_stake);
}
});
}

/// Mints exactly `amount` native tokens to the `to` account.
Expand Down Expand Up @@ -924,30 +919,35 @@ impl<T: Config> Pallet<T> {
// update round
let mut round = Self::round();
round.update_round::<T>(now);
let current_round = round.current_round_index;
let now = round.current_round_index;
// handle delayed relayer update requests
// this must be executed in advance, bc initial and current state should be matched at this moment
T::RelayManager::refresh_round(now);
T::RelayManager::handle_delayed_relayer_sets(now);
// reset candidate states
Pallet::<T>::reset_candidate_states();
// pay all stakers for T::RewardPaymentDelay rounds ago
Self::prepare_staking_payouts(current_round);
Self::prepare_staking_payouts(now);
// select top validator candidates for the next round
let (validator_count, _, total_staked) =
Self::update_top_candidates(current_round, full_validators, basic_validators);
Self::update_top_candidates(now, full_validators, basic_validators);
// start next round
<Round<T>>::put(round);
// refresh majority
Self::refresh_majority(current_round);
T::RelayManager::refresh_majority(current_round);
Self::refresh_majority(now);
T::RelayManager::refresh_majority(now);
// refresh productivity rate per block
Self::refresh_productivity_per_block(validator_count, round.round_length);
// snapshot total stake and storage state
<Staked<T>>::insert(current_round, Self::total());
<TotalAtStake<T>>::remove(current_round - 1);
// handle delayed set requests
Self::handle_delayed_controller_sets(current_round);
Self::handle_delayed_commission_sets(current_round);
<Staked<T>>::insert(now, Self::total());
<TotalAtStake<T>>::remove(now - 1);
// handle delayed controller update requests
Self::handle_delayed_controller_sets(now);
Self::handle_delayed_commission_sets(now);

Self::deposit_event(Event::NewRound {
starting_block: round.first_round_block,
round: current_round,
round: now,
selected_validators_number: validator_count,
total_balance: total_staked,
});
Expand Down
25 changes: 19 additions & 6 deletions pallets/bfc-staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ pub mod pallet {
CannotGoOnlineIfLeaving,
/// The given candidate cannot leave due to its offline state.
CannotLeaveIfOffline,
/// The given candidate cannot leave if controller set requested. It must be cancelled.
CannotLeaveIfControllerSetRequested,
/// The given candidate cannot leave if commission set requested. It must be cancelled.
CannotLeaveIfCommissionSetRequested,
/// The given nominator exceeds the maximum limit of nominations.
ExceedMaxNominationsPerNominator,
/// The given nominator already nominated the candidate.
Expand Down Expand Up @@ -817,6 +821,7 @@ pub mod pallet {
T::DefaultBlocksPerSession::get(),
);
<Round<T>>::put(round);
T::RelayManager::refresh_round(1u32);
// Set productivity rate per block
let blocks_per_validator = {
if v_count == 0 {
Expand Down Expand Up @@ -1056,6 +1061,7 @@ pub mod pallet {
!Self::is_commission_set_requested(&controller),
Error::<T>::AlreadyCommissionSetRequested,
);
ensure!(!state.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
Self::add_to_commission_sets(&controller, old, new)?;
Self::deposit_event(Event::ValidatorCommissionSet { candidate: controller, old, new });
Ok(().into())
Expand Down Expand Up @@ -1247,7 +1253,14 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
let controller = ensure_signed(origin)?;
let mut state = <CandidateInfo<T>>::get(&controller).ok_or(Error::<T>::CandidateDNE)?;
let (now, when) = state.schedule_leave::<T>()?;
ensure!(
!Self::is_controller_set_requested(&controller),
Error::<T>::CannotLeaveIfControllerSetRequested,
);
ensure!(
!Self::is_commission_set_requested(&controller),
Error::<T>::CannotLeaveIfCommissionSetRequested,
);
let candidates = <CandidatePool<T>>::get();
ensure!(
candidate_count >= candidates.len() as u32,
Expand All @@ -1257,6 +1270,7 @@ pub mod pallet {
Self::remove_from_candidate_pool(&controller),
Error::<T>::CannotLeaveIfOffline,
);
let (now, when) = state.schedule_leave::<T>()?;
<CandidateInfo<T>>::insert(&controller, state);
Self::deposit_event(Event::CandidateScheduledExit {
exit_allowed_round: now,
Expand Down Expand Up @@ -1379,10 +1393,12 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
let stash = ensure_signed(origin)?;
let old = Self::bonded_stash(&stash).ok_or(Error::<T>::StashDNE)?;
let state = <CandidateInfo<T>>::get(&old).ok_or(Error::<T>::CandidateDNE)?;
ensure!(new != old, Error::<T>::NoWritingSameValue);
ensure!(!Self::is_candidate(&new, TierType::All), Error::<T>::AlreadyPaired);
ensure!(!state.is_leaving(), Error::<T>::CandidateAlreadyLeaving);
ensure!(
!Self::is_controller_set_requested(old.clone()),
!Self::is_controller_set_requested(&old),
Error::<T>::AlreadyControllerSetRequested
);
Self::add_to_controller_sets(stash, old.clone(), new.clone())?;
Expand All @@ -1397,10 +1413,7 @@ pub mod pallet {
pub fn cancel_controller_set(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let controller = ensure_signed(origin)?;
ensure!(Self::is_candidate(&controller, TierType::All), Error::<T>::CandidateDNE);
ensure!(
Self::is_controller_set_requested(controller.clone()),
Error::<T>::ControllerSetDNE
);
ensure!(Self::is_controller_set_requested(&controller), Error::<T>::ControllerSetDNE);
Self::remove_controller_set(&controller)?;
Self::deposit_event(Event::ControllerSetCancelled { candidate: controller });
Ok(().into())
Expand Down
15 changes: 15 additions & 0 deletions pallets/relay-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,18 @@ impl<Offender: Clone, T: pallet::pallet::Config> Offence<Offender>
<HeartbeatSlashFraction<T>>::get()
}
}

#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
/// Information of the single-round delayed relayer address update request
pub struct DelayedRelayerSet<AccountId> {
/// The current relayer address.
pub old: AccountId,
/// The requested relayer address.
pub new: AccountId,
}

impl<AccountId: PartialEq + Clone> DelayedRelayerSet<AccountId> {
pub fn new(old: AccountId, new: AccountId) -> Self {
DelayedRelayerSet { old, new }
}
}
39 changes: 38 additions & 1 deletion pallets/relay-manager/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ where
Ok(())
}

fn refresh_round(now: RoundIndex) {
<Round<T>>::put(now);
}

fn refresh_relayer_pool() {
let pool = Self::relayer_pool();
pool.iter().for_each(|r| {
Expand Down Expand Up @@ -59,7 +63,6 @@ where
});
}
}
<Round<T>>::put(round);
<SelectedRelayers<T>>::put(selected_relayers.clone());
<InitialSelectedRelayers<T>>::put(selected_relayers.clone());
Self::refresh_cached_selected_relayers(round, selected_relayers);
Expand Down Expand Up @@ -187,6 +190,14 @@ where
Self::deposit_event(Event::<T>::SomeOffline { offline: offenders.clone() });
}
}

fn handle_delayed_relayer_sets(now: RoundIndex) {
let delayed_round = now - 1;
let relayer_sets = <DelayedRelayerSets<T>>::take(delayed_round);
relayer_sets.into_iter().for_each(|r| {
Self::replace_bonded_relayer(&r.old, &r.new).expect("Replacement must success");
});
}
}

impl<T: Config> Pallet<T> {
Expand All @@ -208,6 +219,13 @@ impl<T: Config> Pallet<T> {
}
}

/// Verifies if the given account has already requested for relayer account update
pub fn is_relayer_set_requested(relayer: T::AccountId) -> bool {
let round = Self::round();
let relayer_sets = Self::delayed_relayer_sets(round);
relayer_sets.into_iter().any(|r| r.old == relayer)
}

/// Compute majority based on the current selected relayers
fn compute_majority() -> u32 {
((Self::selected_relayers().len() as u32) / 2) + 1
Expand Down Expand Up @@ -256,6 +274,25 @@ impl<T: Config> Pallet<T> {
})
}

/// Adds a new relayer address update request. The state reflection will be applied in the next round.
pub fn add_to_relayer_sets(old: T::AccountId, new: T::AccountId) -> DispatchResult {
let round = Self::round();
<DelayedRelayerSets<T>>::try_mutate(round, |relayer_sets| -> DispatchResult {
Ok(relayer_sets
.try_push(DelayedRelayerSet::new(old, new))
.map_err(|_| <Error<T>>::TooManyDelayedRelayers)?)
})
}

/// Remove the given `who` from the `DelayedRelayerSets` of the current round.
pub fn remove_relayer_set(who: &T::AccountId) -> DispatchResult {
let round = Self::round();
<DelayedRelayerSets<T>>::mutate(round, |relayer_set| {
relayer_set.retain(|r| r.old != *who);
});
Ok(())
}

/// Refresh the latest rounds cached selected relayers to the current state
fn refresh_latest_cached_relayers() {
let round = Self::round();
Expand Down
Loading