mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 11:51:01 +00:00
im-online: use EstimateNextSessionRotation to get better estimates of session progress (#8242)
* frame-support: add method to estimate current session progress * im-online: use EstimateNextSessionRotation trait to delay heartbeats * node: fix im-online pallet instantiation * frame-support: fix docs * frame: fix tests * pallet-session: last block of periodic session means 100% session progress * pallet-session: add test for periodic session progress * pallet-babe: fix epoch progress and add test * frame-support: return weight with session estimates * pallet-im-online: add test for session progress logic
This commit is contained in:
@@ -116,8 +116,10 @@ pub mod weights;
|
||||
|
||||
use sp_std::{prelude::*, marker::PhantomData, ops::{Sub, Rem}};
|
||||
use codec::Decode;
|
||||
use sp_runtime::{KeyTypeId, Perbill, RuntimeAppPublic};
|
||||
use sp_runtime::traits::{Convert, Zero, Member, OpaqueKeys, Saturating};
|
||||
use sp_runtime::{
|
||||
traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
|
||||
KeyTypeId, Perbill, Percent, RuntimeAppPublic,
|
||||
};
|
||||
use sp_staking::SessionIndex;
|
||||
use frame_support::{
|
||||
ensure, decl_module, decl_event, decl_storage, decl_error, ConsensusEngineId, Parameter,
|
||||
@@ -142,16 +144,14 @@ pub trait ShouldEndSession<BlockNumber> {
|
||||
/// The first session will have length of `Offset`, and
|
||||
/// the following sessions will have length of `Period`.
|
||||
/// This may prove nonsensical if `Offset` >= `Period`.
|
||||
pub struct PeriodicSessions<
|
||||
Period,
|
||||
Offset,
|
||||
>(PhantomData<(Period, Offset)>);
|
||||
pub struct PeriodicSessions<Period, Offset>(PhantomData<(Period, Offset)>);
|
||||
|
||||
impl<
|
||||
BlockNumber: Rem<Output=BlockNumber> + Sub<Output=BlockNumber> + Zero + PartialOrd,
|
||||
BlockNumber: Rem<Output = BlockNumber> + Sub<Output = BlockNumber> + Zero + PartialOrd,
|
||||
Period: Get<BlockNumber>,
|
||||
Offset: Get<BlockNumber>,
|
||||
> ShouldEndSession<BlockNumber> for PeriodicSessions<Period, Offset> {
|
||||
> ShouldEndSession<BlockNumber> for PeriodicSessions<Period, Offset>
|
||||
{
|
||||
fn should_end_session(now: BlockNumber) -> bool {
|
||||
let offset = Offset::get();
|
||||
now >= offset && ((now - offset) % Period::get()).is_zero()
|
||||
@@ -159,14 +159,47 @@ impl<
|
||||
}
|
||||
|
||||
impl<
|
||||
BlockNumber: Rem<Output=BlockNumber> + Sub<Output=BlockNumber> + Zero + PartialOrd + Saturating + Clone,
|
||||
BlockNumber: AtLeast32BitUnsigned + Clone,
|
||||
Period: Get<BlockNumber>,
|
||||
Offset: Get<BlockNumber>,
|
||||
> EstimateNextSessionRotation<BlockNumber> for PeriodicSessions<Period, Offset> {
|
||||
fn estimate_next_session_rotation(now: BlockNumber) -> Option<BlockNumber> {
|
||||
Offset: Get<BlockNumber>
|
||||
> EstimateNextSessionRotation<BlockNumber> for PeriodicSessions<Period, Offset>
|
||||
{
|
||||
fn average_session_length() -> BlockNumber {
|
||||
Period::get()
|
||||
}
|
||||
|
||||
fn estimate_current_session_progress(now: BlockNumber) -> (Option<Percent>, Weight) {
|
||||
let offset = Offset::get();
|
||||
let period = Period::get();
|
||||
Some(if now > offset {
|
||||
|
||||
// NOTE: we add one since we assume that the current block has already elapsed,
|
||||
// i.e. when evaluating the last block in the session the progress should be 100%
|
||||
// (0% is never returned).
|
||||
let progress = if now >= offset {
|
||||
let current = (now - offset) % period.clone() + One::one();
|
||||
Some(Percent::from_rational_approximation(
|
||||
current.clone(),
|
||||
period.clone(),
|
||||
))
|
||||
} else {
|
||||
Some(Percent::from_rational_approximation(
|
||||
now + One::one(),
|
||||
offset,
|
||||
))
|
||||
};
|
||||
|
||||
// Weight note: `estimate_current_session_progress` has no storage reads and trivial
|
||||
// computational overhead. There should be no risk to the chain having this weight value be
|
||||
// zero for now. However, this value of zero was not properly calculated, and so it would be
|
||||
// reasonable to come back here and properly calculate the weight of this function.
|
||||
(progress, Zero::zero())
|
||||
}
|
||||
|
||||
fn estimate_next_session_rotation(now: BlockNumber) -> (Option<BlockNumber>, Weight) {
|
||||
let offset = Offset::get();
|
||||
let period = Period::get();
|
||||
|
||||
let next_session = if now > offset {
|
||||
let block_after_last_session = (now.clone() - offset) % period.clone();
|
||||
if block_after_last_session > Zero::zero() {
|
||||
now.saturating_add(period.saturating_sub(block_after_last_session))
|
||||
@@ -179,19 +212,13 @@ impl<
|
||||
}
|
||||
} else {
|
||||
offset
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
fn weight(_now: BlockNumber) -> Weight {
|
||||
// Weight note: `estimate_next_session_rotation` has no storage reads and trivial
|
||||
// computational overhead. There should be no risk to the chain having this weight value be
|
||||
// zero for now. However, this value of zero was not properly calculated, and so it would be
|
||||
// reasonable to come back here and properly calculate the weight of this function.
|
||||
0
|
||||
}
|
||||
|
||||
fn average_session_length() -> BlockNumber {
|
||||
Period::get()
|
||||
(Some(next_session), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,17 +860,13 @@ impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
|
||||
}
|
||||
|
||||
impl<T: Config> EstimateNextNewSession<T::BlockNumber> for Module<T> {
|
||||
/// This session module always calls new_session and next_session at the same time, hence we
|
||||
/// do a simple proxy and pass the function to next rotation.
|
||||
fn estimate_next_new_session(now: T::BlockNumber) -> Option<T::BlockNumber> {
|
||||
T::NextSessionRotation::estimate_next_session_rotation(now)
|
||||
}
|
||||
|
||||
fn average_session_length() -> T::BlockNumber {
|
||||
T::NextSessionRotation::average_session_length()
|
||||
}
|
||||
|
||||
fn weight(now: T::BlockNumber) -> Weight {
|
||||
T::NextSessionRotation::weight(now)
|
||||
/// This session module always calls new_session and next_session at the same time, hence we
|
||||
/// do a simple proxy and pass the function to next rotation.
|
||||
fn estimate_next_new_session(now: T::BlockNumber) -> (Option<T::BlockNumber>, Weight) {
|
||||
T::NextSessionRotation::estimate_next_session_rotation(now)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +253,6 @@ fn session_changed_flag_works() {
|
||||
|
||||
#[test]
|
||||
fn periodic_session_works() {
|
||||
|
||||
frame_support::parameter_types! {
|
||||
const Period: u64 = 10;
|
||||
const Offset: u64 = 3;
|
||||
@@ -261,24 +260,67 @@ fn periodic_session_works() {
|
||||
|
||||
type P = PeriodicSessions<Period, Offset>;
|
||||
|
||||
// make sure that offset phase behaves correctly
|
||||
for i in 0u64..3 {
|
||||
assert!(!P::should_end_session(i));
|
||||
assert_eq!(P::estimate_next_session_rotation(i).unwrap(), 3);
|
||||
assert_eq!(P::estimate_next_session_rotation(i).0.unwrap(), 3);
|
||||
|
||||
// the last block of the session (i.e. the one before session rotation)
|
||||
// should have progress 100%.
|
||||
if P::estimate_next_session_rotation(i).0.unwrap() - 1 == i {
|
||||
assert_eq!(
|
||||
P::estimate_current_session_progress(i).0.unwrap(),
|
||||
Percent::from_percent(100)
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
P::estimate_current_session_progress(i).0.unwrap() < Percent::from_percent(100)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// we end the session at block #3 and we consider this block the first one
|
||||
// from the next session. since we're past the offset phase it represents
|
||||
// 1/10 of progress.
|
||||
assert!(P::should_end_session(3u64));
|
||||
assert_eq!(P::estimate_next_session_rotation(3u64).unwrap(), 3);
|
||||
assert_eq!(P::estimate_next_session_rotation(3u64).0.unwrap(), 3);
|
||||
assert_eq!(
|
||||
P::estimate_current_session_progress(3u64).0.unwrap(),
|
||||
Percent::from_percent(10),
|
||||
);
|
||||
|
||||
for i in (1u64..10).map(|i| 3 + i) {
|
||||
assert!(!P::should_end_session(i));
|
||||
assert_eq!(P::estimate_next_session_rotation(i).unwrap(), 13);
|
||||
assert_eq!(P::estimate_next_session_rotation(i).0.unwrap(), 13);
|
||||
|
||||
// as with the offset phase the last block of the session must have 100%
|
||||
// progress.
|
||||
if P::estimate_next_session_rotation(i).0.unwrap() - 1 == i {
|
||||
assert_eq!(
|
||||
P::estimate_current_session_progress(i).0.unwrap(),
|
||||
Percent::from_percent(100)
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
P::estimate_current_session_progress(i).0.unwrap() < Percent::from_percent(100)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// the new session starts and we proceed in 1/10 increments.
|
||||
assert!(P::should_end_session(13u64));
|
||||
assert_eq!(P::estimate_next_session_rotation(13u64).unwrap(), 23);
|
||||
assert_eq!(P::estimate_next_session_rotation(13u64).0.unwrap(), 23);
|
||||
assert_eq!(
|
||||
P::estimate_current_session_progress(13u64).0.unwrap(),
|
||||
Percent::from_percent(10)
|
||||
);
|
||||
|
||||
assert!(!P::should_end_session(14u64));
|
||||
assert_eq!(P::estimate_next_session_rotation(14u64).unwrap(), 23);
|
||||
assert_eq!(P::estimate_next_session_rotation(14u64).0.unwrap(), 23);
|
||||
assert_eq!(
|
||||
P::estimate_current_session_progress(14u64).0.unwrap(),
|
||||
Percent::from_percent(20)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user