@@ -12,9 +12,10 @@ use crate::pallet::{
1212} ;
1313use crate :: staking_epoch:: { mint_funds, mint_into_treasury} ;
1414use crate :: {
15- BalanceOf , Config , DepositOnHold , DeregisteredOperators , DomainBlockNumberFor ,
16- DomainHashingFor , Event , ExecutionReceiptOf , HoldIdentifier , InvalidBundleAuthors , NominatorId ,
17- OperatorEpochSharePrice , OperatorHighestSlot , Pallet , ReceiptHashFor , SlashedReason ,
15+ AllowedDefaultSharePriceEpoch , BalanceOf , Config , DepositOnHold , DeregisteredOperators ,
16+ DomainBlockNumberFor , DomainHashingFor , Event , ExecutionReceiptOf , HoldIdentifier ,
17+ InvalidBundleAuthors , NominatorId , OperatorEpochSharePrice , OperatorHighestSlot , Pallet ,
18+ ReceiptHashFor , SlashedReason ,
1819} ;
1920use frame_support:: traits:: fungible:: { Inspect , MutateHold } ;
2021use frame_support:: traits:: tokens:: { Fortitude , Precision , Preservation } ;
@@ -423,6 +424,7 @@ pub fn do_register_operator<T: Config>(
423424 current_domain_epoch,
424425 new_deposit,
425426 None ,
427+ None ,
426428 ) ?;
427429
428430 Ok ( ( operator_id, domain_stake_summary. current_epoch_index ) )
@@ -439,10 +441,16 @@ pub(crate) fn do_calculate_previous_epoch_deposit_shares_and_add_new_deposit<T:
439441 current_domain_epoch : DomainEpoch ,
440442 new_deposit : NewDeposit < BalanceOf < T > > ,
441443 required_minimum_nominator_stake : Option < BalanceOf < T > > ,
444+ maybe_share_price : Option < SharePrice > ,
442445) -> Result < ( ) , Error > {
443446 Deposits :: < T > :: try_mutate ( operator_id, nominator_id, |maybe_deposit| {
444447 let mut deposit = maybe_deposit. take ( ) . unwrap_or_default ( ) ;
445- do_convert_previous_epoch_deposits :: < T > ( operator_id, & mut deposit, current_domain_epoch. 1 ) ?;
448+ do_convert_previous_epoch_deposits :: < T > (
449+ operator_id,
450+ & mut deposit,
451+ current_domain_epoch. 1 ,
452+ maybe_share_price,
453+ ) ?;
446454
447455 // add or create new pending deposit
448456 let pending_deposit = match deposit. pending {
@@ -483,19 +491,27 @@ pub(crate) fn do_convert_previous_epoch_deposits<T: Config>(
483491 operator_id : OperatorId ,
484492 deposit : & mut Deposit < T :: Share , BalanceOf < T > > ,
485493 current_domain_epoch_index : EpochIndex ,
494+ maybe_share_price : Option < SharePrice > ,
486495) -> Result < ( ) , Error > {
487496 // if it is one of the previous domain epoch, then calculate shares for the epoch and update known deposit
488497 let epoch_share_price = match deposit. pending {
489498 None => return Ok ( ( ) ) ,
490499 Some ( pending_deposit) => {
500+ if pending_deposit. effective_domain_epoch . 1 >= current_domain_epoch_index {
501+ return Ok ( ( ) ) ;
502+ }
503+
491504 match get_share_price :: < T > ( operator_id, pending_deposit. effective_domain_epoch ) {
492505 Some ( p) => p,
493506 None => {
494- ensure ! (
495- pending_deposit. effective_domain_epoch. 1 >= current_domain_epoch_index,
496- Error :: MissingOperatorEpochSharePrice
497- ) ;
498- return Ok ( ( ) ) ;
507+ match allowed_default_share_price :: < T > (
508+ operator_id,
509+ pending_deposit. effective_domain_epoch . 1 ,
510+ maybe_share_price,
511+ ) {
512+ Some ( p) => p,
513+ None => return Err ( Error :: MissingOperatorEpochSharePrice ) ,
514+ }
499515 }
500516 }
501517 }
@@ -523,6 +539,25 @@ pub(crate) fn do_convert_previous_epoch_deposits<T: Config>(
523539 Ok ( ( ) )
524540}
525541
542+ pub ( crate ) fn allowed_default_share_price < T : Config > (
543+ operator_id : OperatorId ,
544+ domain_epoch_index : EpochIndex ,
545+ maybe_share_price : Option < SharePrice > ,
546+ ) -> Option < SharePrice > {
547+ let DomainEpoch ( allowed_domain_id, allowed_epoch_index) =
548+ AllowedDefaultSharePriceEpoch :: < T > :: get ( ) ;
549+ let operator = Operators :: < T > :: get ( operator_id) ?;
550+
551+ // The default share price is only allowed if the requested `domain_epoch_index` is happen
552+ // before (or is the same as) the `AllowedDefaultSharePriceEpoch`
553+ if allowed_domain_id == operator. current_domain_id && allowed_epoch_index >= domain_epoch_index
554+ {
555+ maybe_share_price
556+ } else {
557+ None
558+ }
559+ }
560+
526561/// Converts any epoch withdrawals into balance using the operator epoch share price.
527562///
528563/// If there is withdrawal happened in the current epoch (thus share price is unavailable),
@@ -532,6 +567,7 @@ pub(crate) fn do_convert_previous_epoch_withdrawal<T: Config>(
532567 operator_id : OperatorId ,
533568 withdrawal : & mut Withdrawal < BalanceOf < T > , T :: Share , DomainBlockNumberFor < T > > ,
534569 current_domain_epoch_index : EpochIndex ,
570+ maybe_share_price : Option < SharePrice > ,
535571) -> Result < ( ) , Error > {
536572 let epoch_share_price = match withdrawal. withdrawal_in_shares . as_ref ( ) {
537573 None => return Ok ( ( ) ) ,
@@ -543,7 +579,16 @@ pub(crate) fn do_convert_previous_epoch_withdrawal<T: Config>(
543579
544580 match get_share_price :: < T > ( operator_id, withdraw. domain_epoch ) {
545581 Some ( p) => p,
546- None => return Err ( Error :: MissingOperatorEpochSharePrice ) ,
582+ None => {
583+ match allowed_default_share_price :: < T > (
584+ operator_id,
585+ withdraw. domain_epoch . 1 ,
586+ maybe_share_price,
587+ ) {
588+ Some ( p) => p,
589+ None => return Err ( Error :: MissingOperatorEpochSharePrice ) ,
590+ }
591+ }
547592 }
548593 }
549594 } ;
@@ -595,6 +640,8 @@ pub(crate) fn do_nominate_operator<T: Config>(
595640 let domain_stake_summary = DomainStakingSummary :: < T > :: get ( operator. current_domain_id )
596641 . ok_or ( Error :: DomainNotInitialized ) ?;
597642
643+ let share_price = current_share_price :: < T > ( operator_id, operator, & domain_stake_summary) ?;
644+
598645 // Reserve for the bundle storage fund
599646 let new_deposit = deposit_reserve_for_storage_fund :: < T > ( operator_id, & nominator_id, amount)
600647 . map_err ( Error :: BundleStorageFund ) ?;
@@ -630,6 +677,7 @@ pub(crate) fn do_nominate_operator<T: Config>(
630677 current_domain_epoch,
631678 new_deposit,
632679 Some ( operator. minimum_nominator_stake ) ,
680+ Some ( share_price) ,
633681 ) ?;
634682
635683 Ok ( ( ) )
@@ -770,13 +818,15 @@ pub(crate) fn do_withdraw_stake<T: Config>(
770818 )
771819 . into ( ) ;
772820
821+ let share_price = current_share_price :: < T > ( operator_id, operator, & domain_stake_summary) ?;
773822 let known_shares =
774823 Deposits :: < T > :: try_mutate ( operator_id, nominator_id. clone ( ) , |maybe_deposit| {
775824 let deposit = maybe_deposit. as_mut ( ) . ok_or ( Error :: UnknownNominator ) ?;
776825 do_convert_previous_epoch_deposits :: < T > (
777826 operator_id,
778827 deposit,
779828 domain_stake_summary. current_epoch_index ,
829+ Some ( share_price. clone ( ) ) ,
780830 ) ?;
781831 Ok ( deposit. known . shares )
782832 } ) ?;
@@ -787,6 +837,7 @@ pub(crate) fn do_withdraw_stake<T: Config>(
787837 operator_id,
788838 withdrawal,
789839 domain_stake_summary. current_epoch_index ,
840+ Some ( share_price. clone ( ) ) ,
790841 ) ?;
791842 if withdrawal. withdrawals . len ( ) as u32 >= T :: WithdrawalLimit :: get ( ) {
792843 return Err ( Error :: TooManyWithdrawals ) ;
@@ -816,9 +867,6 @@ pub(crate) fn do_withdraw_stake<T: Config>(
816867
817868 ( remaining_shares, to_withdraw)
818869 } else {
819- let share_price =
820- current_share_price :: < T > ( operator_id, operator, & domain_stake_summary) ?;
821-
822870 let remaining_storage_fee =
823871 Perquintill :: from_rational ( remaining_shares. into ( ) , known_shares. into ( ) )
824872 . mul_floor ( deposit. known . storage_fee_deposit ) ;
@@ -948,16 +996,18 @@ pub(crate) fn do_unlock_funds<T: Config>(
948996 Error :: OperatorNotRegistered
949997 ) ;
950998
951- let current_domain_epoch_index = DomainStakingSummary :: < T > :: get ( operator. current_domain_id )
952- . ok_or ( Error :: DomainNotInitialized ) ?
953- . current_epoch_index ;
999+ let stake_summary = DomainStakingSummary :: < T > :: get ( operator. current_domain_id )
1000+ . ok_or ( Error :: DomainNotInitialized ) ?;
1001+ let current_domain_epoch_index = stake_summary. current_epoch_index ;
1002+ let share_price = current_share_price :: < T > ( operator_id, & operator, & stake_summary) ?;
9541003
9551004 Withdrawals :: < T > :: try_mutate_exists ( operator_id, nominator_id. clone ( ) , |maybe_withdrawal| {
9561005 let withdrawal = maybe_withdrawal. as_mut ( ) . ok_or ( Error :: MissingWithdrawal ) ?;
9571006 do_convert_previous_epoch_withdrawal :: < T > (
9581007 operator_id,
9591008 withdrawal,
9601009 current_domain_epoch_index,
1010+ Some ( share_price) ,
9611011 ) ?;
9621012
9631013 ensure ! ( !withdrawal. withdrawals. is_empty( ) , Error :: MissingWithdrawal ) ;
@@ -1145,6 +1195,7 @@ pub(crate) fn do_unlock_nominator<T: Config>(
11451195 operator_id,
11461196 & mut deposit,
11471197 current_domain_epoch_index,
1198+ Some ( share_price. clone ( ) ) ,
11481199 ) ?;
11491200
11501201 // if there are any withdrawals from this operator, account for them
@@ -1163,6 +1214,7 @@ pub(crate) fn do_unlock_nominator<T: Config>(
11631214 operator_id,
11641215 & mut withdrawal,
11651216 current_domain_epoch_index,
1217+ Some ( share_price. clone ( ) ) ,
11661218 ) ?;
11671219 Ok ( (
11681220 withdrawal. total_withdrawal_amount ,
@@ -1569,8 +1621,8 @@ pub(crate) mod tests {
15691621 use crate :: staking_epoch:: { do_finalize_domain_current_epoch, do_slash_operator} ;
15701622 use crate :: tests:: { ExistentialDeposit , MinOperatorStake , RuntimeOrigin , Test , new_test_ext} ;
15711623 use crate :: {
1572- BalanceOf , Error , MAX_NOMINATORS_TO_SLASH , NominatorId , OperatorEpochSharePrice ,
1573- SlashedReason , bundle_storage_fund,
1624+ AllowedDefaultSharePriceEpoch , BalanceOf , Error , MAX_NOMINATORS_TO_SLASH , NominatorId ,
1625+ OperatorEpochSharePrice , SlashedReason , bundle_storage_fund,
15741626 } ;
15751627 use frame_support:: traits:: Currency ;
15761628 use frame_support:: traits:: fungible:: Mutate ;
@@ -2318,6 +2370,9 @@ pub(crate) mod tests {
23182370 }
23192371
23202372 // Export the macro for use in other modules (and earlier in this file).
2373+ use crate :: staking_epoch:: tests:: {
2374+ get_current_epoch, operator_share_price_does_not_exist, operator_share_price_exists,
2375+ } ;
23212376 pub ( crate ) use prop_assert_approx;
23222377
23232378 /// Rounding down factor for property tests to account for arithmetic precision errors.
@@ -3310,6 +3365,7 @@ pub(crate) mod tests {
33103365 operator_id,
33113366 withdrawal,
33123367 domain_stake_summary. current_epoch_index ,
3368+ None ,
33133369 )
33143370 . unwrap ( ) ;
33153371 assert_eq ! (
@@ -3427,6 +3483,7 @@ pub(crate) mod tests {
34273483 operator_id,
34283484 maybe_withdrawal. as_mut ( ) . unwrap ( ) ,
34293485 domain_stake_summary. current_epoch_index ,
3486+ None ,
34303487 )
34313488 } )
34323489 . unwrap ( ) ;
@@ -3560,8 +3617,13 @@ pub(crate) mod tests {
35603617 Default :: default ( ) ,
35613618 BTreeMap :: from_iter ( nominators) ,
35623619 ) ;
3620+ assert_eq ! ( get_current_epoch( domain_id) , 1 ) ;
3621+ operator_share_price_exists ( domain_id, vec ! [ 0 ] , 0 ) ;
35633622
35643623 do_finalize_domain_current_epoch :: < Test > ( domain_id) . unwrap ( ) ;
3624+ assert_eq ! ( get_current_epoch( domain_id) , 2 ) ;
3625+ operator_share_price_exists ( domain_id, vec ! [ 0 ] , 1 ) ;
3626+
35653627 let domain_stake_summary = DomainStakingSummary :: < Test > :: get ( domain_id) . unwrap ( ) ;
35663628 assert_eq ! ( domain_stake_summary. current_total_stake, init_total_stake) ;
35673629
@@ -3585,6 +3647,8 @@ pub(crate) mod tests {
35853647 )
35863648 . unwrap ( ) ;
35873649 do_finalize_domain_current_epoch :: < Test > ( domain_id) . unwrap ( ) ;
3650+ assert_eq ! ( get_current_epoch( domain_id) , 3 ) ;
3651+ operator_share_price_exists ( domain_id, vec ! [ 0 ] , 2 ) ;
35883652
35893653 // Manually convert previous withdrawal in share to balance
35903654 let domain_stake_summary = DomainStakingSummary :: < Test > :: get ( domain_id) . unwrap ( ) ;
@@ -3594,6 +3658,7 @@ pub(crate) mod tests {
35943658 operator_id,
35953659 maybe_withdrawal. as_mut ( ) . unwrap ( ) ,
35963660 domain_stake_summary. current_epoch_index ,
3661+ None ,
35973662 )
35983663 } )
35993664 . unwrap ( ) ;
@@ -3649,6 +3714,8 @@ pub(crate) mod tests {
36493714 . unwrap ( ) ;
36503715
36513716 do_finalize_domain_current_epoch :: < Test > ( domain_id) . unwrap ( ) ;
3717+ assert_eq ! ( get_current_epoch( domain_id) , 4 ) ;
3718+ operator_share_price_does_not_exist ( domain_id, vec ! [ 0 ] , 3 ) ;
36523719
36533720 let domain_stake_summary = DomainStakingSummary :: < Test > :: get ( domain_id) . unwrap ( ) ;
36543721 assert ! ( !domain_stake_summary. next_operators. contains( & operator_id) ) ;
@@ -3996,6 +4063,7 @@ pub(crate) mod tests {
39964063
39974064 let mut ext = new_test_ext ( ) ;
39984065 ext. execute_with ( || {
4066+ AllowedDefaultSharePriceEpoch :: < Test > :: set ( ( domain_id, 0 ) . into ( ) ) ;
39994067 let ( operator_id, _) = register_operator (
40004068 domain_id,
40014069 operator_account,
0 commit comments