diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 45244e4a9275f640524a87f8d09362b162f5e40d..4a2c808770a81ecc9e795dc10110fbb057342f50 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -788,6 +788,10 @@ pub mod pallet { IdtyNotFound, /// Invalid payload signature. InvalidSignature, + /// Key used as validator. + OwnerKeyUsedAsValidator, + /// Key in bound period. + OwnerKeyInBound, /// Invalid revocation key. InvalidRevocationKey, /// Issuer is not member and can not perform this action. diff --git a/pallets/smith-members/src/lib.rs b/pallets/smith-members/src/lib.rs index d9246b0cc506c6f644cacd2048aef11a6d6d104c..66cab0d70924e09ba8bea09b3c81738c3141cf80 100644 --- a/pallets/smith-members/src/lib.rs +++ b/pallets/smith-members/src/lib.rs @@ -59,7 +59,10 @@ use frame_support::{ ensure, pallet_prelude::{Get, RuntimeDebug, Weight}, }; -use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, +}; use scale_info::{ prelude::{collections::BTreeMap, fmt::Debug, vec, vec::Vec}, TypeInfo, @@ -239,6 +242,7 @@ pub mod pallet { }, issued_certs: vec![], received_certs: issuers_, + last_online: None, }, ); // if smith is offline, schedule expire @@ -264,8 +268,13 @@ pub mod pallet { /// The Smith metadata for each identity. #[pallet::storage] #[pallet::getter(fn smiths)] - pub type Smiths<T: Config> = - StorageMap<_, Twox64Concat, T::IdtyIndex, SmithMeta<T::IdtyIndex>, OptionQuery>; + pub type Smiths<T: Config> = StorageMap< + _, + Twox64Concat, + T::IdtyIndex, + SmithMeta<T::IdtyIndex, BlockNumberFor<T>>, + OptionQuery, + >; /// The indexes of Smith to remove at a given session. #[pallet::storage] @@ -586,7 +595,7 @@ impl<T: Config> Pallet<T> { if let Some(smith_meta) = maybe_smith_meta { // As long as the smith is online, it cannot expire smith_meta.expires_on = None; - // FIXME: unschedule old expiry (#182) + smith_meta.last_online = None; } }); } @@ -605,6 +614,8 @@ impl<T: Config> Pallet<T> { let new_expires_on = CurrentSession::<T>::get() + T::SmithInactivityMaxDuration::get(); smith_meta.expires_on = Some(new_expires_on); + let block_number = frame_system::pallet::Pallet::<T>::block_number(); + smith_meta.last_online = Some(block_number); ExpiresOn::<T>::append(new_expires_on, idty_index); } }); diff --git a/pallets/smith-members/src/tests.rs b/pallets/smith-members/src/tests.rs index c92bffb680caedd2e006ad2b7c8378ce04e529d6..068821a4eb8a88a093f40c690964ea761671d744 100644 --- a/pallets/smith-members/src/tests.rs +++ b/pallets/smith-members/src/tests.rs @@ -62,6 +62,7 @@ fn process_to_become_a_smith_and_lose_it() { expires_on: Some(5), issued_certs: vec![], received_certs: vec![], + last_online: None, } ); // Then certification 1/2 @@ -80,6 +81,7 @@ fn process_to_become_a_smith_and_lose_it() { expires_on: Some(5), issued_certs: vec![], received_certs: vec![1], + last_online: None, } ); // Then certification 2/2 @@ -101,6 +103,7 @@ fn process_to_become_a_smith_and_lose_it() { expires_on: Some(5), issued_certs: vec![], received_certs: vec![1, 2], + last_online: None, } ); // Go online to be able to invite+certify @@ -161,7 +164,8 @@ fn process_to_become_a_smith_and_lose_it() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: Some(1), }) ); assert_eq!( @@ -170,7 +174,8 @@ fn process_to_become_a_smith_and_lose_it() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: Some(1), }) ); assert_eq!( @@ -179,7 +184,8 @@ fn process_to_become_a_smith_and_lose_it() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: None, }) ); }); @@ -252,6 +258,7 @@ fn should_have_checks_on_certify() { expires_on: None, issued_certs: vec![4], received_certs: vec![2, 3, 4], + last_online: None, } ); assert_eq!( @@ -261,6 +268,7 @@ fn should_have_checks_on_certify() { expires_on: Some(5), issued_certs: vec![1, 4], received_certs: vec![3, 4], + last_online: None, } ); assert_eq!( @@ -270,6 +278,7 @@ fn should_have_checks_on_certify() { expires_on: Some(5), issued_certs: vec![1, 2], received_certs: vec![4], + last_online: None, } ); assert_eq!( @@ -279,6 +288,7 @@ fn should_have_checks_on_certify() { expires_on: Some(5), issued_certs: vec![1, 2, 3], received_certs: vec![1, 2], + last_online: None, } ); @@ -312,6 +322,7 @@ fn should_have_checks_on_certify() { expires_on: Some(5), issued_certs: vec![1, 2], received_certs: vec![4], + last_online: None, } ); // Try to certify #3 @@ -327,6 +338,7 @@ fn should_have_checks_on_certify() { expires_on: Some(5), issued_certs: vec![1, 2], received_certs: vec![1, 4], + last_online: None, } ); }); @@ -359,7 +371,8 @@ fn smith_activity_postpones_expiration() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: None, }) ); // issued_certs is empty because #1 was excluded @@ -370,6 +383,7 @@ fn smith_activity_postpones_expiration() { expires_on: None, issued_certs: vec![], received_certs: vec![3, 4], + last_online: None, }) ); @@ -383,6 +397,7 @@ fn smith_activity_postpones_expiration() { expires_on: Some(11), issued_certs: vec![], received_certs: vec![3, 4], + last_online: Some(0), }) ); // Still not expired on session 10 @@ -394,6 +409,7 @@ fn smith_activity_postpones_expiration() { expires_on: Some(11), issued_certs: vec![], received_certs: vec![3, 4], + last_online: Some(0), }) ); // But expired on session 11 @@ -404,7 +420,8 @@ fn smith_activity_postpones_expiration() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: None, }) ); assert_eq!( @@ -413,7 +430,8 @@ fn smith_activity_postpones_expiration() { status: SmithStatus::Excluded, expires_on: None, issued_certs: vec![], - received_certs: vec![] + received_certs: vec![], + last_online: Some(0), }) ); }); @@ -443,7 +461,8 @@ fn smith_coming_back_recovers_its_issued_certs() { status: Excluded, expires_on: None, issued_certs: vec![1], - received_certs: vec![] + received_certs: vec![], + last_online: None, }) ); // Smith #2 comes back @@ -466,7 +485,8 @@ fn smith_coming_back_recovers_its_issued_certs() { status: Smith, expires_on: Some(10), issued_certs: vec![1], - received_certs: vec![1, 3] + received_certs: vec![1, 3], + last_online: None, }) ); Pallet::<Runtime>::on_smith_goes_online(2); @@ -590,7 +610,8 @@ fn certifying_an_online_smith() { status: Smith, expires_on: Some(8), issued_certs: vec![], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: None, }) ); assert_eq!(ExpiresOn::<Runtime>::get(7), Some(vec![5])); @@ -604,7 +625,8 @@ fn certifying_an_online_smith() { status: Smith, expires_on: None, issued_certs: vec![], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: None, }) ); // ExpiresOn is not unscheduled, but as expires_on has switched to None it's not a problem @@ -622,7 +644,8 @@ fn certifying_an_online_smith() { status: Smith, expires_on: None, issued_certs: vec![], - received_certs: vec![1, 2, 3] + received_certs: vec![1, 2, 3], + last_online: None, }) ); }); @@ -648,7 +671,8 @@ fn expires_on_cleans_up() { status: Smith, expires_on: Some(5), issued_certs: vec![2, 3], - received_certs: vec![2, 3] + received_certs: vec![2, 3], + last_online: Some(0), }) ); // It is also present in ExpiresOn schedule @@ -662,7 +686,8 @@ fn expires_on_cleans_up() { status: Excluded, expires_on: None, issued_certs: vec![2, 3], - received_certs: vec![] + received_certs: vec![], + last_online: Some(0), }) ); // ExpiresOn is clean @@ -713,6 +738,7 @@ fn losing_wot_membership_cascades_to_smith_members() { expires_on: Some(5), issued_certs: vec![3], received_certs: vec![2, 3, 4], + last_online: None, }) ); assert_eq!( @@ -742,6 +768,7 @@ fn losing_wot_membership_cascades_to_smith_members() { expires_on: None, issued_certs: vec![3], received_certs: vec![], + last_online: None, }) ); // Issued certifications updated for certifiers of 1 diff --git a/pallets/smith-members/src/types.rs b/pallets/smith-members/src/types.rs index 8399226517f42337ebc36b031d07d76c39dbdd0f..396e0334181616e3dbee0ce8aa039bf12c227d81 100644 --- a/pallets/smith-members/src/types.rs +++ b/pallets/smith-members/src/types.rs @@ -24,7 +24,7 @@ use sp_staking::SessionIndex; /// Represents a certification metadata attached to a Smith identity. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct SmithMeta<IdtyIndex> { +pub struct SmithMeta<IdtyIndex, BlockNumber> { /// Current status of the Smith. pub status: SmithStatus, /// The session at which the Smith will expire (for lack of validation activity). @@ -33,16 +33,19 @@ pub struct SmithMeta<IdtyIndex> { pub issued_certs: Vec<IdtyIndex>, /// Certifications received from other Smiths. pub received_certs: Vec<IdtyIndex>, + /// Last online time. + pub last_online: Option<BlockNumber>, } /// By default, a smith has the least possible privileges -impl<IdtyIndex> Default for SmithMeta<IdtyIndex> { +impl<IdtyIndex, BlockNumber> Default for SmithMeta<IdtyIndex, BlockNumber> { fn default() -> Self { Self { status: SmithStatus::Excluded, expires_on: None, issued_certs: Vec::<IdtyIndex>::new(), received_certs: Vec::<IdtyIndex>::new(), + last_online: Default::default(), } } } diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs index ba0d0f296b7af1d88ff6f10a5bb9008751bc0aaa..66ac960d7712c65514fe6745e224f9fe8a7eccbb 100644 --- a/runtime/common/src/handlers.rs +++ b/runtime/common/src/handlers.rs @@ -19,6 +19,7 @@ use frame_support::{ pallet_prelude::Weight, traits::{Imbalance, UnfilteredDispatchable}, }; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_smith_members::SmithRemovalReason; use sp_core::Get; @@ -171,19 +172,48 @@ where } /// Runtime handler OwnerKeyChangePermission. -pub struct KeyChangeHandler<Runtime>(core::marker::PhantomData<Runtime>); +pub struct KeyChangeHandler<Runtime, ReportLongevity>( + core::marker::PhantomData<Runtime>, + core::marker::PhantomData<ReportLongevity>, +); impl< Runtime: frame_system::Config<AccountId = AccountId> + pallet_identity::Config<IdtyIndex = IdtyIndex> - + pallet_authority_members::Config<MemberId = IdtyIndex>, - > pallet_identity::traits::KeyChange<Runtime> for KeyChangeHandler<Runtime> + + pallet_authority_members::Config<MemberId = IdtyIndex> + + pallet_smith_members::Config<IdtyIndex = IdtyIndex>, + ReportLongevity: Get<BlockNumberFor<Runtime>>, + > pallet_identity::traits::KeyChange<Runtime> for KeyChangeHandler<Runtime, ReportLongevity> { + /// Handles the event when an identity's owner key is changed. + /// + /// # Errors + /// * Returns `OwnerKeyInBound` if the smith was a validator and the bond period is not finished, meaning it can still be punished for past actions. + /// * Returns `OwnerKeyUsedAsValidator` if the owner key is currently used as a validator. + /// + /// # Behavior + /// * If the smith is online, the operation is rejected. + /// * If the smith was a validator and is still within the bond period, the operation is rejected. It means they can still be punished for past actions. + /// * If the smith is neither online nor within the bond period, the owner key is changed successfully and the change is reflected in the validator member data if available. fn on_changed( idty_index: IdtyIndex, account_id: AccountId, ) -> Result<(), sp_runtime::DispatchError> { - pallet_authority_members::Pallet::<Runtime>::change_owner_key(idty_index, account_id) - .map_err(|e| e.error)?; + if let Some(smith) = pallet_smith_members::Pallet::<Runtime>::smiths(&idty_index) { + if let Some(last_online) = smith.last_online { + if last_online + ReportLongevity::get() + > frame_system::pallet::Pallet::<Runtime>::block_number() + { + return Err(pallet_identity::Error::<Runtime>::OwnerKeyInBound.into()); + } else { + pallet_authority_members::Pallet::<Runtime>::change_owner_key( + idty_index, account_id, + ) + .map_err(|e| e.error)?; + } + } else { + return Err(pallet_identity::Error::<Runtime>::OwnerKeyUsedAsValidator.into()); + } + } Ok(()) } } diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index af8bdf59609726a3884b42075e68284193cfc4a6..8cc6b927dfcdcbe253bd7cf59857489b03bf5ba8 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -444,7 +444,7 @@ macro_rules! pallets_config { type IdtyData = IdtyData; type IdtyIndex = IdtyIndex; type IdtyNameValidator = IdtyNameValidatorImpl; - type OnKeyChange = KeyChangeHandler<Runtime>; + type OnKeyChange = KeyChangeHandler<Runtime, ReportLongevity>; type OnNewIdty = OnNewIdtyHandler<Runtime>; type OnRemoveIdty = OnRemoveIdtyHandler<Runtime>; type RuntimeEvent = RuntimeEvent; diff --git a/runtime/g1/src/parameters.rs b/runtime/g1/src/parameters.rs index d787eb5bf38e58c37ed0bc6d078f449c9617d983..5fe8b963cebf556a7be2e5f0ab9ad5b6ed4c1b8e 100644 --- a/runtime/g1/src/parameters.rs +++ b/runtime/g1/src/parameters.rs @@ -58,7 +58,7 @@ pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = 4 * HOURS; parameter_types! { pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS as u64; pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; - pub const ReportLongevity: u64 = 168 * EpochDuration::get(); + pub const ReportLongevity: BlockNumber = 168 * EPOCH_DURATION_IN_SLOTS; } // ImOnline diff --git a/runtime/gdev/src/parameters.rs b/runtime/gdev/src/parameters.rs index bcca8e7d2315b7d331a026fc53174199fbe31571..7fc7e9ba55d2e4c95044972f003def9421b80751 100644 --- a/runtime/gdev/src/parameters.rs +++ b/runtime/gdev/src/parameters.rs @@ -58,9 +58,8 @@ parameter_types! { // Babe parameter_types! { pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; - pub const ReportLongevity: u64 = 168 * HOURS as u64; + pub const ReportLongevity: BlockNumber = 168 * HOURS; } - // ImOnline parameter_types! { pub const ImOnlineUnsignedPriority: TransactionPriority = TransactionPriority::MAX; diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 32dd489f27875fd1f3b3dbce2b25a2bbacd73654..bc3ccd87ecd5945f08faaad402dfb971761e3dd6 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -1020,6 +1020,7 @@ fn test_smith_process() { expires_on: Some(48), issued_certs: vec![], received_certs: vec![1, 2, 3], + last_online: None, }) ); @@ -1053,6 +1054,7 @@ fn test_expired_smith_has_null_expires_on() { expires_on: None, // because online issued_certs: vec![1, 3], received_certs: vec![1, 3], + last_online: None, }) ); @@ -1085,6 +1087,7 @@ fn test_expired_smith_has_null_expires_on() { expires_on: None, // because excluded, no expiry is scheduled issued_certs: vec![1, 3], received_certs: vec![], // received certs are deleted + last_online: None, }) ); // Alice smith cert to Bob has been deleted @@ -1095,6 +1098,7 @@ fn test_expired_smith_has_null_expires_on() { expires_on: None, // because online issued_certs: vec![3], // cert to Bob has been deleted received_certs: vec![2, 3], + last_online: None, }) ); @@ -1116,6 +1120,7 @@ fn test_expired_smith_has_null_expires_on() { expires_on: None, // should be still None issued_certs: vec![1, 3], received_certs: vec![], + last_online: None, }) ); @@ -1516,7 +1521,7 @@ fn test_link_account() { }) } -/// test change owner key +/// test change owner key was validator is online #[test] fn test_change_owner_key_validator_online() { ExtBuilder::new(1, 3, 4) @@ -1538,23 +1543,104 @@ fn test_change_owner_key_validator_online() { alice ); + // As an online validator she cannot change key + assert_noop!( + Identity::change_owner_key( + RuntimeOrigin::signed(alice.clone()), + ferdie.clone(), + signature.into() + ), + pallet_identity::Error::<gdev_runtime::Runtime>::OwnerKeyUsedAsValidator + ); + }) +} + +/// test change owner key between set_key and go online +#[test] +#[ignore = "long to go to ReportLongevity"] +fn test_change_owner_key_offline() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Ferdie.to_account_id(), 8888)]) + .build() + .execute_with(|| { + let genesis_hash = System::block_hash(0); + let charlie = AccountKeyring::Charlie.to_account_id(); + let ferdie = AccountKeyring::Ferdie.to_account_id(); + let payload = (b"icok", genesis_hash, 3u32, charlie.clone()).encode(); + let signature = AccountKeyring::Ferdie.sign(&payload); + + // Charlie is an offline smith + SmithMembers::on_smith_goes_offline(3); + assert_eq!( + SmithMembers::smiths(3), + Some(SmithMeta { + status: SmithStatus::Smith, + expires_on: Some(48), + issued_certs: vec![1, 2], + received_certs: vec![1, 2], + last_online: Some(1), + }) + ); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&charlie).linked_idty, + Some(3) + ); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, + None + ); + + // We run after the bound period + // Keeping members intact + for i in 1..4 { + let expiration = pallet_membership::Membership::<Runtime>::get(i) + .unwrap() + .expire_on; + pallet_membership::MembershipsExpireOn::<Runtime>::take(expiration); + pallet_smith_members::Smiths::<Runtime>::mutate(i, |data| { + if let Some(ref mut data) = data { + data.expires_on = None; + } + }); + } + run_to_block(ReportLongevity::get() + 1); + + // Charlie can set its session_keys + assert_ok!(AuthorityMembers::set_session_keys( + RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()), + create_dummy_session_keys() + )); + assert_eq!( + pallet_authority_members::Members::<Runtime>::get(3) + .unwrap() + .owner_key, + charlie.clone() + ); + + // Charlie can change his owner key to Ferdie's a valid account + // with providers and balance assert_ok!(Identity::change_owner_key( - RuntimeOrigin::signed(alice.clone()), + RuntimeOrigin::signed(charlie.clone()), ferdie.clone(), signature.into() - ),); - + )); + // The change is reflected on the authority member data assert_eq!( - pallet_authority_members::Members::<Runtime>::get(1) + pallet_authority_members::Members::<Runtime>::get(3) .unwrap() .owner_key, - ferdie + ferdie.clone() + ); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, + Some(3) ); }) } /// test change owner key #[test] +#[ignore = "long to go to ReportLongevity"] fn test_change_owner_key() { ExtBuilder::new(1, 3, 4) .with_initial_balances(vec![(AccountKeyring::Ferdie.to_account_id(), 8888)]) @@ -1574,7 +1660,8 @@ fn test_change_owner_key() { status: SmithStatus::Smith, expires_on: Some(48), issued_certs: vec![1, 2], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: Some(1), }) ); @@ -1586,7 +1673,36 @@ fn test_change_owner_key() { frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, None ); - // Dave can change his owner key to Ferdie's + + run_to_block(5); + + // Charlie cannot change his owner key because he is in bound period + // and can be punished for past actions + assert_noop!( + Identity::change_owner_key( + RuntimeOrigin::signed(charlie.clone()), + ferdie.clone(), + signature.into() + ), + pallet_identity::Error::<gdev_runtime::Runtime>::OwnerKeyInBound + ); + + // We run after the bound period + // Keeping members intact + for i in 1..4 { + let expiration = pallet_membership::Membership::<Runtime>::get(i) + .unwrap() + .expire_on; + pallet_membership::MembershipsExpireOn::<Runtime>::take(expiration); + pallet_smith_members::Smiths::<Runtime>::mutate(i, |data| { + if let Some(ref mut data) = data { + data.expires_on = Some(ReportLongevity::get() * 2); + } + }); + } + run_to_block(ReportLongevity::get() + 1); + + // Charlie can change his owner key to Ferdie's assert_ok!(Identity::change_owner_key( RuntimeOrigin::signed(charlie.clone()), ferdie.clone(), @@ -1602,9 +1718,10 @@ fn test_change_owner_key() { SmithMembers::smiths(3), Some(SmithMeta { status: SmithStatus::Smith, - expires_on: Some(48), + expires_on: Some(ReportLongevity::get() * 2), issued_certs: vec![1, 2], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: Some(1), }) ); @@ -1623,20 +1740,18 @@ fn test_change_owner_key() { SmithMembers::smiths(3), Some(SmithMeta { status: SmithStatus::Smith, - expires_on: Some(48), + expires_on: Some(ReportLongevity::get() * 2), issued_certs: vec![1, 2], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: Some(1), }) ); - run_to_block(25); + run_to_block(((ReportLongevity::get() + 1 + 24) / 25) * 25); System::assert_has_event(RuntimeEvent::AuthorityMembers( pallet_authority_members::Event::IncomingAuthorities { members: vec![3] }, )); - System::assert_has_event(RuntimeEvent::AuthorityMembers( - pallet_authority_members::Event::OutgoingAuthorities { members: vec![1] }, - )); // "Charlie" (idty 3) is now online because its identity is mapped to Ferdies's key assert_eq!( @@ -1645,7 +1760,8 @@ fn test_change_owner_key() { status: SmithStatus::Smith, expires_on: None, issued_certs: vec![1, 2], - received_certs: vec![1, 2] + received_certs: vec![1, 2], + last_online: None, }) ); }) diff --git a/runtime/gtest/src/parameters.rs b/runtime/gtest/src/parameters.rs index 307f59b19733e1ad99bfb62bb656e9dc51bb70f1..8ac8aeb0aae0a874137ea2660a56b7e2f20dbcde 100644 --- a/runtime/gtest/src/parameters.rs +++ b/runtime/gtest/src/parameters.rs @@ -58,7 +58,7 @@ pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = HOURS; parameter_types! { pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS as u64; pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; - pub const ReportLongevity: u64 = 168 * EpochDuration::get(); + pub const ReportLongevity: BlockNumber = 168 * EPOCH_DURATION_IN_SLOTS; } // ImOnline