11//! Staking epoch transition for domain
22use crate :: bundle_storage_fund:: deposit_reserve_for_storage_fund;
33use crate :: pallet:: {
4- AccumulatedTreasuryFunds , Deposits , DomainStakingSummary , LastEpochStakingDistribution ,
5- OperatorIdOwner , Operators , PendingSlashes , PendingStakingOperationCount , Withdrawals ,
4+ AccumulatedTreasuryFunds , Deposits , DomainRegistry , DomainStakingSummary ,
5+ LastEpochStakingDistribution , OperatorIdOwner , Operators , PendingSlashes ,
6+ PendingStakingOperationCount , Withdrawals ,
67} ;
78use crate :: staking:: {
89 DomainEpoch , Error as TransitionError , OperatorStatus , SharePrice , WithdrawalInShares ,
910 do_cleanup_operator, do_convert_previous_epoch_deposits, do_convert_previous_epoch_withdrawal,
1011} ;
1112use crate :: {
12- BalanceOf , Config , DeactivatedOperators , DepositOnHold , DeregisteredOperators ,
13- DomainChainRewards , ElectionVerificationParams , Event , HoldIdentifier , InvalidBundleAuthors ,
14- OperatorEpochSharePrice , Pallet , bundle_storage_fund,
13+ BalanceOf , BlockSlot , Config , DeactivatedOperators , DepositOnHold , DeregisteredOperators ,
14+ DomainChainRewards , ElectionVerificationParams , EpochStartSlot , Event , HoldIdentifier ,
15+ InvalidBundleAuthors , OperatorBundleCountInEpoch , OperatorEpochSharePrice , Pallet ,
16+ bundle_storage_fund,
1517} ;
1618use frame_support:: traits:: fungible:: { Inspect , Mutate , MutateHold } ;
1719use frame_support:: traits:: tokens:: {
@@ -21,9 +23,12 @@ use frame_support::{PalletError, StorageDoubleMap};
2123use parity_scale_codec:: { Decode , Encode } ;
2224use scale_info:: TypeInfo ;
2325use sp_core:: Get ;
26+ use sp_domains:: offline_operators:: {
27+ E_BASE , LN_1_OVER_TAU_1_PERCENT , operator_expected_bundles_in_epoch,
28+ } ;
2429use sp_domains:: { DomainId , EpochIndex , OperatorId , OperatorRewardSource } ;
2530use sp_runtime:: traits:: { CheckedAdd , CheckedSub , One , Zero } ;
26- use sp_runtime:: { Perquintill , Saturating } ;
31+ use sp_runtime:: { Perquintill , SaturatedConversion , Saturating } ;
2732use sp_std:: collections:: btree_map:: BTreeMap ;
2833use sp_std:: collections:: btree_set:: BTreeSet ;
2934
@@ -237,6 +242,17 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
237242 let mut total_domain_stake = BalanceOf :: < T > :: zero ( ) ;
238243 let mut current_operators = BTreeMap :: new ( ) ;
239244 let mut next_operators = BTreeSet :: new ( ) ;
245+ let current_slot = T :: BlockSlot :: current_slot ( ) ;
246+ let total_epoch_slots = EpochStartSlot :: < T > :: get ( )
247+ . map ( |start_slot| current_slot - start_slot + 1 )
248+ . unwrap_or_default ( ) ;
249+ let epoch_total_stake = stake_summary. current_total_stake ;
250+ let operators_total_bundle_count =
251+ OperatorBundleCountInEpoch :: < T > :: drain ( ) . collect :: < BTreeMap < _ , _ > > ( ) ;
252+ let bundle_slot_probability = DomainRegistry :: < T > :: get ( domain_id)
253+ . ok_or ( TransitionError :: DomainNotInitialized ) ?
254+ . domain_config
255+ . bundle_slot_probability ;
240256 for next_operator_id in & stake_summary. next_operators {
241257 // If an operator is pending to slash then similar to the slashed operator it should not be added
242258 // into the `next_operators/current_operators` and we should not `do_finalize_operator_epoch_staking`
@@ -245,6 +261,32 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
245261 continue ;
246262 }
247263
264+ // check operator performance in the previous epoch if the operator was part of the previous epoch set
265+ // if they are not part of the previous epoch set, their performance will be checked in the next epoch.
266+ if let Some ( operator_stake) = stake_summary. current_operators . get ( next_operator_id)
267+ && let Some ( bundle_count) = operators_total_bundle_count. get ( next_operator_id)
268+ {
269+ let maybe_operator_epoch_expectations = operator_expected_bundles_in_epoch (
270+ total_epoch_slots. into ( ) ,
271+ ( * operator_stake) . saturated_into ( ) ,
272+ epoch_total_stake. saturated_into ( ) ,
273+ bundle_slot_probability,
274+ LN_1_OVER_TAU_1_PERCENT ,
275+ E_BASE ,
276+ ) ;
277+
278+ if let Some ( operator_epoch_expectations) = maybe_operator_epoch_expectations
279+ && bundle_count < & operator_epoch_expectations. min_required_bundles
280+ {
281+ Pallet :: < T > :: deposit_event ( Event :: OperatorOffline {
282+ operator_id : * next_operator_id,
283+ domain_id,
284+ submitted_bundles : * bundle_count,
285+ expectations : operator_epoch_expectations,
286+ } ) ;
287+ } ;
288+ }
289+
248290 let ( operator_stake, stake_changed) = do_finalize_operator_epoch_staking :: < T > (
249291 domain_id,
250292 * next_operator_id,
@@ -263,6 +305,9 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
263305 }
264306 }
265307
308+ // set epoch start slot
309+ EpochStartSlot :: < T > :: put ( current_slot) ;
310+
266311 // we finalized the operators who are in the next operator set.
267312 // But there will be deposits/withdrawals for operators who are not part of the next operator set.
268313 // So they need to have share price for the previous epoch
0 commit comments