Rococo/Westend Coretime Runtime

New runtimes for the Coretime Chain (a.k.a. "Broker Chain") described in
RFC-1.

Replaces https://github.com/paritytech/cumulus/pull/2889


- [x] Add Agile Coretime pallet
https://github.com/paritytech/substrate/pull/14568
- [x] Generate chain specs for local and testnets
- [x] Deploy parachain on Rococo - Done:
[rococo-coretime-rpc.polkadot.io](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-coretime-rpc.polkadot.io#/explorer)

DevOps issue for Aura keygen:
https://github.com/paritytech/devops/issues/2725

Edit (Dónal): This PR is mainly for Rococo, the Westend runtime is a
shell with no `Broker` pallet. The Rococo runtime has the broker calls
filtered for initial deployment.

---------

Co-authored-by: Dónal Murray <donal.murray@parity.io>
Co-authored-by: 0xmovses <r.v.melkonian@gmail.com>
Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Marcin S. <marcin@realemail.net>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: command-bot <>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
This commit is contained in:
joe petrowski
2023-12-19 15:12:24 +01:00
committed by GitHub
parent 166ae5ae12
commit 2e70dd3bbe
72 changed files with 9856 additions and 111 deletions
+62 -28
View File
@@ -31,7 +31,7 @@ use frame_support::{
use frame_system::{Pallet as System, RawOrigin};
use sp_arithmetic::{traits::Zero, Perbill};
use sp_core::Get;
use sp_runtime::Saturating;
use sp_runtime::{traits::BlockNumberProvider, Saturating};
use sp_std::{vec, vec::Vec};
const SEED: u32 = 0;
@@ -82,6 +82,10 @@ fn setup_leases<T: Config>(n: u32, task: u32, until: u32) {
fn advance_to<T: Config>(b: u32) {
while System::<T>::block_number() < b.into() {
System::<T>::set_block_number(System::<T>::block_number().saturating_add(1u32.into()));
let block_number: u32 = System::<T>::block_number().try_into().ok().unwrap();
RCBlockNumberProviderOf::<T::Coretime>::set_block_number(block_number.into());
Broker::<T>::on_initialize(System::<T>::block_number());
}
}
@@ -182,7 +186,8 @@ mod benches {
#[benchmark]
fn start_sales(n: Linear<0, { MAX_CORE_COUNT.into() }>) -> Result<(), BenchmarkError> {
Configuration::<T>::put(new_config_record::<T>());
let config = new_config_record::<T>();
Configuration::<T>::put(config.clone());
// Assume Reservations to be filled for worst case
setup_reservations::<T>(T::MaxReservedCores::get());
@@ -190,6 +195,8 @@ mod benches {
// Assume Leases to be filled for worst case
setup_leases::<T>(T::MaxLeasedCores::get(), 1, 10);
let latest_region_begin = Broker::<T>::latest_timeslice_ready_to_commit(&config);
let initial_price = 10u32.into();
let origin =
@@ -205,8 +212,8 @@ mod benches {
leadin_length: 1u32.into(),
start_price: 20u32.into(),
regular_price: 10u32.into(),
region_begin: 4,
region_end: 7,
region_begin: latest_region_begin + config.region_length,
region_end: latest_region_begin + config.region_length * 2,
ideal_cores_sold: 0,
cores_offered: n
.saturating_sub(T::MaxReservedCores::get())
@@ -239,7 +246,11 @@ mod benches {
assert_last_event::<T>(
Event::Purchased {
who: caller,
region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
region_id: RegionId {
begin: SaleInfo::<T>::get().unwrap().region_begin,
core,
mask: CoreMask::complete(),
},
price: 10u32.into(),
duration: 3u32.into(),
}
@@ -252,6 +263,7 @@ mod benches {
#[benchmark]
fn renew() -> Result<(), BenchmarkError> {
setup_and_start_sale::<T>()?;
let region_len = Configuration::<T>::get().unwrap().region_length;
advance_to::<T>(2);
@@ -267,12 +279,12 @@ mod benches {
Broker::<T>::do_assign(region, None, 1001, Final)
.map_err(|_| BenchmarkError::Weightless)?;
advance_to::<T>(6);
advance_to::<T>((T::TimeslicePeriod::get() * region_len.into()).try_into().ok().unwrap());
#[extrinsic_call]
_(RawOrigin::Signed(caller), region.core);
let id = AllowedRenewalId { core: region.core, when: 10 };
let id = AllowedRenewalId { core: region.core, when: region.begin + region_len * 2 };
assert!(AllowedRenewals::<T>::get(id).is_some());
Ok(())
@@ -331,10 +343,10 @@ mod benches {
assert_last_event::<T>(
Event::Partitioned {
old_region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
old_region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
new_region_ids: (
RegionId { begin: 4, core, mask: CoreMask::complete() },
RegionId { begin: 6, core, mask: CoreMask::complete() },
RegionId { begin: region.begin, core, mask: CoreMask::complete() },
RegionId { begin: region.begin + 2, core, mask: CoreMask::complete() },
),
}
.into(),
@@ -363,11 +375,11 @@ mod benches {
assert_last_event::<T>(
Event::Interlaced {
old_region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
old_region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
new_region_ids: (
RegionId { begin: 4, core, mask: 0x00000_fffff_fffff_00000.into() },
RegionId { begin: region.begin, core, mask: 0x00000_fffff_fffff_00000.into() },
RegionId {
begin: 4,
begin: region.begin,
core,
mask: CoreMask::complete() ^ 0x00000_fffff_fffff_00000.into(),
},
@@ -404,7 +416,7 @@ mod benches {
assert_last_event::<T>(
Event::Assigned {
region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
task: 1000,
duration: 3u32.into(),
}
@@ -439,7 +451,7 @@ mod benches {
assert_last_event::<T>(
Event::Pooled {
region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
duration: 3u32.into(),
}
.into(),
@@ -494,7 +506,11 @@ mod benches {
who: recipient,
amount: 200u32.into(),
next: if m < new_config_record::<T>().region_length {
Some(RegionId { begin: 4.saturating_add(m), core, mask: CoreMask::complete() })
Some(RegionId {
begin: region.begin.saturating_add(m),
core,
mask: CoreMask::complete(),
})
} else {
None
},
@@ -541,6 +557,7 @@ mod benches {
#[benchmark]
fn drop_region() -> Result<(), BenchmarkError> {
let core = setup_and_start_sale::<T>()?;
let region_len = Configuration::<T>::get().unwrap().region_length;
advance_to::<T>(2);
@@ -553,14 +570,16 @@ mod benches {
let region = Broker::<T>::do_purchase(caller.clone(), 10u32.into())
.map_err(|_| BenchmarkError::Weightless)?;
advance_to::<T>(12);
advance_to::<T>(
(T::TimeslicePeriod::get() * (region_len * 4).into()).try_into().ok().unwrap(),
);
#[extrinsic_call]
_(RawOrigin::Signed(caller), region);
assert_last_event::<T>(
Event::RegionDropped {
region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
duration: 3u32.into(),
}
.into(),
@@ -572,6 +591,7 @@ mod benches {
#[benchmark]
fn drop_contribution() -> Result<(), BenchmarkError> {
let core = setup_and_start_sale::<T>()?;
let region_len = Configuration::<T>::get().unwrap().region_length;
advance_to::<T>(2);
@@ -589,14 +609,16 @@ mod benches {
Broker::<T>::do_pool(region, None, recipient, Final)
.map_err(|_| BenchmarkError::Weightless)?;
advance_to::<T>(26);
advance_to::<T>(
(T::TimeslicePeriod::get() * (region_len * 8).into()).try_into().ok().unwrap(),
);
#[extrinsic_call]
_(RawOrigin::Signed(caller), region);
assert_last_event::<T>(
Event::ContributionDropped {
region_id: RegionId { begin: 4, core, mask: CoreMask::complete() },
region_id: RegionId { begin: region.begin, core, mask: CoreMask::complete() },
}
.into(),
);
@@ -609,8 +631,11 @@ mod benches {
setup_and_start_sale::<T>()?;
let when = 5u32.into();
let revenue = 10u32.into();
let region_len = Configuration::<T>::get().unwrap().region_length;
advance_to::<T>(25);
advance_to::<T>(
(T::TimeslicePeriod::get() * (region_len * 8).into()).try_into().ok().unwrap(),
);
let caller: T::AccountId = whitelisted_caller();
InstaPoolHistory::<T>::insert(
@@ -635,8 +660,11 @@ mod benches {
fn drop_renewal() -> Result<(), BenchmarkError> {
let core = setup_and_start_sale::<T>()?;
let when = 5u32.into();
let region_len = Configuration::<T>::get().unwrap().region_length;
advance_to::<T>(10);
advance_to::<T>(
(T::TimeslicePeriod::get() * (region_len * 3).into()).try_into().ok().unwrap(),
);
let id = AllowedRenewalId { core, when };
let record = AllowedRenewalRecord {
@@ -704,10 +732,16 @@ mod benches {
);
T::Currency::set_balance(&Broker::<T>::account_id(), T::Currency::minimum_balance());
<T::Coretime as CoretimeInterface>::ensure_notify_revenue_info(10u32.into(), 10u32.into());
let timeslice_period: u32 = T::TimeslicePeriod::get().try_into().ok().unwrap();
let multiplicator = 5;
<T::Coretime as CoretimeInterface>::ensure_notify_revenue_info(
(timeslice_period * multiplicator).into(),
10u32.into(),
);
let timeslice = multiplicator - 1;
InstaPoolHistory::<T>::insert(
4u32,
timeslice,
InstaPoolHistoryRecord {
private_contributions: 1u32.into(),
system_contributions: 9u32.into(),
@@ -722,7 +756,7 @@ mod benches {
assert_last_event::<T>(
Event::ClaimsReady {
when: 4u32.into(),
when: timeslice.into(),
system_payout: 9u32.into(),
private_payout: 1u32.into(),
}
@@ -769,7 +803,7 @@ mod benches {
#[block]
{
Broker::<T>::rotate_sale(sale, &config, &status);
Broker::<T>::rotate_sale(sale.clone(), &config, &status);
}
assert!(SaleInfo::<T>::get().is_some());
@@ -779,8 +813,8 @@ mod benches {
leadin_length: 1u32.into(),
start_price: 20u32.into(),
regular_price: 10u32.into(),
region_begin: 4,
region_end: 7,
region_begin: sale.region_begin + config.region_length,
region_end: sale.region_end + config.region_length,
ideal_cores_sold: 0,
cores_offered: n
.saturating_sub(T::MaxReservedCores::get())
@@ -22,7 +22,8 @@ use frame_support::Parameter;
use scale_info::TypeInfo;
use sp_arithmetic::traits::AtLeast32BitUnsigned;
use sp_core::RuntimeDebug;
use sp_std::{fmt::Debug, vec::Vec};
use sp_runtime::traits::BlockNumberProvider;
use sp_std::vec::Vec;
/// Index of a Polkadot Core.
pub type CoreIndex = u16;
@@ -46,6 +47,12 @@ pub enum CoreAssignment {
Task(TaskId),
}
/// Relay chain block number of `T` that implements [`CoretimeInterface`].
pub type RCBlockNumberOf<T> = <RCBlockNumberProviderOf<T> as BlockNumberProvider>::BlockNumber;
/// Relay chain block number provider of `T` that implements [`CoretimeInterface`].
pub type RCBlockNumberProviderOf<T> = <T as CoretimeInterface>::RealyChainBlockNumberProvider;
/// Type able to accept Coretime scheduling instructions and provide certain usage information.
/// Generally implemented by the Relay-chain or some means of communicating with it.
///
@@ -57,17 +64,8 @@ pub trait CoretimeInterface {
/// A (Relay-chain-side) balance.
type Balance: AtLeast32BitUnsigned;
/// A (Relay-chain-side) block number.
type BlockNumber: AtLeast32BitUnsigned
+ Copy
+ TypeInfo
+ Encode
+ Decode
+ MaxEncodedLen
+ Debug;
/// Return the latest block number on the Relay-chain.
fn latest() -> Self::BlockNumber;
/// A provider for the relay chain block number.
type RealyChainBlockNumberProvider: BlockNumberProvider;
/// Requests the Relay-chain to alter the number of schedulable cores to `count`. Under normal
/// operation, the Relay-chain SHOULD send a `notify_core_count(count)` message back.
@@ -81,7 +79,7 @@ pub trait CoretimeInterface {
/// should be understood on a channel outside of this proposal. In the case that the request
/// cannot be serviced because `when` is too old a block then a `notify_revenue` message must
/// still be returned, but its `revenue` field may be `None`.
fn request_revenue_info_at(when: Self::BlockNumber);
fn request_revenue_info_at(when: RCBlockNumberOf<Self>);
/// Instructs the Relay-chain to add the `amount` of DOT to the Instantaneous Coretime Market
/// Credit account of `who`.
@@ -104,9 +102,9 @@ pub trait CoretimeInterface {
/// remain unchanged regardless of the `end_hint` value.
fn assign_core(
core: CoreIndex,
begin: Self::BlockNumber,
begin: RCBlockNumberOf<Self>,
assignment: Vec<(CoreAssignment, PartsOf57600)>,
end_hint: Option<Self::BlockNumber>,
end_hint: Option<RCBlockNumberOf<Self>>,
);
/// Indicate that from this block onwards, the range of acceptable values of the `core`
@@ -123,7 +121,7 @@ pub trait CoretimeInterface {
/// This explicitly disregards the possibility of multiple parachains requesting and being
/// notified of revenue information. The Relay-chain must be configured to ensure that only a
/// single revenue information destination exists.
fn check_notify_revenue_info() -> Option<(Self::BlockNumber, Self::Balance)>;
fn check_notify_revenue_info() -> Option<(RCBlockNumberOf<Self>, Self::Balance)>;
/// Ensure that core count is updated to the provided value.
///
@@ -135,34 +133,32 @@ pub trait CoretimeInterface {
///
/// This is only used for benchmarking.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_notify_revenue_info(when: Self::BlockNumber, revenue: Self::Balance);
fn ensure_notify_revenue_info(when: RCBlockNumberOf<Self>, revenue: Self::Balance);
}
impl CoretimeInterface for () {
type AccountId = ();
type Balance = u64;
type BlockNumber = u32;
fn latest() -> Self::BlockNumber {
0
}
type RealyChainBlockNumberProvider = ();
fn request_core_count(_count: CoreIndex) {}
fn request_revenue_info_at(_when: Self::BlockNumber) {}
fn request_revenue_info_at(_when: RCBlockNumberOf<Self>) {}
fn credit_account(_who: Self::AccountId, _amount: Self::Balance) {}
fn assign_core(
_core: CoreIndex,
_begin: Self::BlockNumber,
_begin: RCBlockNumberOf<Self>,
_assignment: Vec<(CoreAssignment, PartsOf57600)>,
_end_hint: Option<Self::BlockNumber>,
_end_hint: Option<RCBlockNumberOf<Self>>,
) {
}
fn check_notify_core_count() -> Option<u16> {
None
}
fn check_notify_revenue_info() -> Option<(Self::BlockNumber, Self::Balance)> {
fn check_notify_revenue_info() -> Option<(RCBlockNumberOf<Self>, Self::Balance)> {
None
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_notify_core_count(_count: u16) {}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_notify_revenue_info(_when: Self::BlockNumber, _revenue: Self::Balance) {}
fn ensure_notify_revenue_info(_when: RCBlockNumberOf<Self>, _revenue: Self::Balance) {}
}
+35 -20
View File
@@ -30,7 +30,10 @@ use frame_support::{
use frame_system::{EnsureRoot, EnsureSignedBy};
use sp_arithmetic::Perbill;
use sp_core::{ConstU32, ConstU64};
use sp_runtime::{traits::Identity, BuildStorage, Saturating};
use sp_runtime::{
traits::{BlockNumberProvider, Identity},
BuildStorage, Saturating,
};
use sp_std::collections::btree_map::BTreeMap;
type Block = frame_system::mocking::MockBlock<Test>;
@@ -75,18 +78,20 @@ pub struct TestCoretimeProvider;
impl CoretimeInterface for TestCoretimeProvider {
type AccountId = u64;
type Balance = u64;
type BlockNumber = u32;
fn latest() -> Self::BlockNumber {
System::block_number() as u32
}
type RealyChainBlockNumberProvider = System;
fn request_core_count(count: CoreIndex) {
NotifyCoreCount::mutate(|s| s.insert(0, count));
}
fn request_revenue_info_at(when: Self::BlockNumber) {
if when > Self::latest() {
panic!("Asking for revenue info in the future {:?} {:?}", when, Self::latest());
fn request_revenue_info_at(when: RCBlockNumberOf<Self>) {
if when > RCBlockNumberProviderOf::<Self>::current_block_number() {
panic!(
"Asking for revenue info in the future {:?} {:?}",
when,
RCBlockNumberProviderOf::<Self>::current_block_number()
);
}
let when = when as u32;
let mut total = 0;
CoretimeSpending::mutate(|s| {
s.retain(|(n, a)| {
@@ -105,27 +110,35 @@ impl CoretimeInterface for TestCoretimeProvider {
}
fn assign_core(
core: CoreIndex,
begin: Self::BlockNumber,
begin: RCBlockNumberOf<Self>,
assignment: Vec<(CoreAssignment, PartsOf57600)>,
end_hint: Option<Self::BlockNumber>,
end_hint: Option<RCBlockNumberOf<Self>>,
) {
CoretimeWorkplan::mutate(|p| p.insert((begin, core), assignment.clone()));
let item = (Self::latest(), AssignCore { core, begin, assignment, end_hint });
CoretimeWorkplan::mutate(|p| p.insert((begin as u32, core), assignment.clone()));
let item = (
RCBlockNumberProviderOf::<Self>::current_block_number() as u32,
AssignCore {
core,
begin: begin as u32,
assignment,
end_hint: end_hint.map(|v| v as u32),
},
);
CoretimeTrace::mutate(|v| v.push(item));
}
fn check_notify_core_count() -> Option<u16> {
NotifyCoreCount::mutate(|s| s.pop())
}
fn check_notify_revenue_info() -> Option<(Self::BlockNumber, Self::Balance)> {
NotifyRevenueInfo::mutate(|s| s.pop())
fn check_notify_revenue_info() -> Option<(RCBlockNumberOf<Self>, Self::Balance)> {
NotifyRevenueInfo::mutate(|s| s.pop()).map(|v| (v.0 as _, v.1))
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_notify_core_count(count: u16) {
NotifyCoreCount::mutate(|s| s.insert(0, count));
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_notify_revenue_info(when: Self::BlockNumber, revenue: Self::Balance) {
NotifyRevenueInfo::mutate(|s| s.push((when, revenue)));
fn ensure_notify_revenue_info(when: RCBlockNumberOf<Self>, revenue: Self::Balance) {
NotifyRevenueInfo::mutate(|s| s.push((when as u32, revenue)));
}
}
impl TestCoretimeProvider {
@@ -134,14 +147,16 @@ impl TestCoretimeProvider {
ensure!(CoretimeInPool::get() > 0, ());
c.insert(who, c.get(&who).ok_or(())?.checked_sub(price).ok_or(())?);
CoretimeCredit::set(c);
CoretimeSpending::mutate(|v| v.push((Self::latest(), price)));
CoretimeSpending::mutate(|v| {
v.push((RCBlockNumberProviderOf::<Self>::current_block_number() as u32, price))
});
Ok(())
}
pub fn bump() {
let mut pool_size = CoretimeInPool::get();
let mut workplan = CoretimeWorkplan::get();
let mut usage = CoretimeUsage::get();
let now = Self::latest();
let now = RCBlockNumberProviderOf::<Self>::current_block_number() as u32;
workplan.retain(|(when, core), assignment| {
if *when <= now {
if let Some(old_assignment) = usage.get(core) {
@@ -184,7 +199,7 @@ impl crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Currency = ItemOf<TestFungibles<(), u64, (), ConstU64<0>, ()>, (), u64>;
type OnRevenue = IntoZero;
type TimeslicePeriod = ConstU32<2>;
type TimeslicePeriod = ConstU64<2>;
type MaxLeasedCores = ConstU32<5>;
type MaxReservedCores = ConstU32<5>;
type Coretime = TestCoretimeProvider;
@@ -240,7 +255,7 @@ impl TestExt {
}
pub fn advance_notice(mut self, advance_notice: Timeslice) -> Self {
self.0.advance_notice = advance_notice;
self.0.advance_notice = advance_notice as u64;
self
}
+10 -4
View File
@@ -112,10 +112,16 @@ impl<T: Config> Pallet<T> {
}
// Payout system InstaPool Cores.
let total_contrib = r.system_contributions.saturating_add(r.private_contributions);
let system_payout =
revenue.saturating_mul(r.system_contributions.into()) / total_contrib.into();
let _ = Self::charge(&Self::account_id(), system_payout);
revenue.saturating_reduce(system_payout);
let system_payout = if !total_contrib.is_zero() {
let system_payout =
revenue.saturating_mul(r.system_contributions.into()) / total_contrib.into();
let _ = Self::charge(&Self::account_id(), system_payout);
revenue.saturating_reduce(system_payout);
system_payout
} else {
Zero::zero()
};
if !revenue.is_zero() && r.private_contributions > 0 {
r.maybe_payout = Some(revenue);
+3 -2
View File
@@ -16,7 +16,8 @@
// limitations under the License.
use crate::{
Config, CoreAssignment, CoreIndex, CoreMask, CoretimeInterface, TaskId, CORE_MASK_BITS,
Config, CoreAssignment, CoreIndex, CoreMask, CoretimeInterface, RCBlockNumberOf, TaskId,
CORE_MASK_BITS,
};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::traits::fungible::Inspect;
@@ -28,7 +29,7 @@ use sp_runtime::BoundedVec;
pub type BalanceOf<T> = <<T as Config>::Currency as Inspect<<T as SConfig>::AccountId>>::Balance;
pub type RelayBalanceOf<T> = <<T as Config>::Coretime as CoretimeInterface>::Balance;
pub type RelayBlockNumberOf<T> = <<T as Config>::Coretime as CoretimeInterface>::BlockNumber;
pub type RelayBlockNumberOf<T> = RCBlockNumberOf<<T as Config>::Coretime>;
pub type RelayAccountIdOf<T> = <<T as Config>::Coretime as CoretimeInterface>::AccountId;
/// Relay-chain block number with a fixed divisor of Config::TimeslicePeriod.
+3 -3
View File
@@ -29,17 +29,17 @@ use sp_arithmetic::{
traits::{SaturatedConversion, Saturating},
FixedPointNumber, FixedU64,
};
use sp_runtime::traits::AccountIdConversion;
use sp_runtime::traits::{AccountIdConversion, BlockNumberProvider};
impl<T: Config> Pallet<T> {
pub fn current_timeslice() -> Timeslice {
let latest = T::Coretime::latest();
let latest = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
let timeslice_period = T::TimeslicePeriod::get();
(latest / timeslice_period).saturated_into()
}
pub fn latest_timeslice_ready_to_commit(config: &ConfigRecordOf<T>) -> Timeslice {
let latest = T::Coretime::latest();
let latest = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
let advanced = latest.saturating_add(config.advance_notice);
let timeslice_period = T::TimeslicePeriod::get();
(advanced / timeslice_period).saturated_into()