Skip to content

Commit 7dfdfda

Browse files
authored
Start an additional session to accelerate usage of the new committee (#1038)
1 parent e378d55 commit 7dfdfda

File tree

6 files changed

+107
-38
lines changed

6 files changed

+107
-38
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1.
77
## Changed
88

99
* Updated partner-chains-smart-contracts (raw-scripts) dependency to v8.2.0. Not breaking.
10+
* `SessionManager` and `ShouldEndSession` implementations of `pallet-session-validator-management` rotate one additional
11+
session in order to make `pallet_session` use authorities with less delay.
1012

1113
## Removed
1214

demo/runtime/src/lib.rs

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,49 +1201,46 @@ mod tests {
12011201
// Needs to be run to initialize first slot and epoch numbers;
12021202
advance_block();
12031203

1204-
// Scheduled committee goes into effect after a 2-epoch delay
1204+
// Committee goes into effect 1-epoch and 1-block after selection
12051205
set_committee_through_inherent_data(&[alice()]);
12061206
until_epoch_after_finalizing(1, &|| {
1207-
assert_current_epoch!(0);
12081207
assert_grandpa_weights();
12091208
assert_grandpa_authorities!([alice(), bob()]);
12101209
});
1211-
1210+
for_next_n_blocks_after_finalizing(1, &|| {
1211+
assert_grandpa_weights();
1212+
assert_grandpa_authorities!([alice(), bob()]);
1213+
});
12121214
set_committee_through_inherent_data(&[bob()]);
12131215
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1214-
assert_current_epoch!(1);
12151216
assert_grandpa_weights();
1216-
assert_grandpa_authorities!([alice(), bob()]);
1217+
assert_grandpa_authorities!([alice()]);
12171218
});
12181219
set_committee_through_inherent_data(&[alice()]);
12191220
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1220-
assert_current_epoch!(2);
12211221
assert_grandpa_weights();
1222-
assert_grandpa_authorities!([alice()]);
1222+
assert_grandpa_authorities!([bob()]);
12231223
});
12241224
set_committee_through_inherent_data(&[alice(), bob()]);
12251225
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1226-
assert_current_epoch!(3);
12271226
assert_grandpa_weights();
1228-
assert_grandpa_authorities!([bob()]);
1227+
assert_grandpa_authorities!([alice()]);
12291228
});
12301229
set_committee_through_inherent_data(&[bob(), alice()]);
12311230
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1232-
assert_current_epoch!(4);
12331231
assert_grandpa_weights();
1234-
assert_grandpa_authorities!([alice()]);
1232+
assert_grandpa_authorities!([alice(), bob()]);
12351233
});
12361234
set_committee_through_inherent_data(&[alice()]);
12371235
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1238-
assert_current_epoch!(5);
12391236
assert_grandpa_weights();
1240-
assert_grandpa_authorities!([alice(), bob()]);
1237+
assert_grandpa_authorities!([bob(), alice()]);
12411238
});
12421239

12431240
// When there's no new committees being scheduled, the last committee stays in power
12441241
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH * 3, &|| {
12451242
assert_grandpa_weights();
1246-
assert_grandpa_authorities!([bob(), alice()]);
1243+
assert_grandpa_authorities!([alice()]);
12471244
});
12481245
});
12491246

@@ -1260,39 +1257,33 @@ mod tests {
12601257
new_test_ext().execute_with(|| {
12611258
// Needs to be run to initialize first slot and epoch numbers;
12621259
advance_block();
1263-
1264-
// Scheduled committee goes into effect after a 2-epoch delay
1260+
// Committee goes into effect 1-epoch and 1-block after selection
12651261
set_committee_through_inherent_data(&[alice()]);
12661262
until_epoch_after_finalizing(1, &|| {
1267-
assert_current_epoch!(0);
12681263
assert_aura_authorities!([alice(), bob()]);
12691264
});
1270-
1271-
set_committee_through_inherent_data(&[bob()]);
1272-
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1273-
assert_current_epoch!(1);
1265+
for_next_n_blocks_after_finalizing(1, &|| {
12741266
assert_aura_authorities!([alice(), bob()]);
12751267
});
1276-
set_committee_through_inherent_data(&[alice()]);
1268+
set_committee_through_inherent_data(&[bob()]);
12771269
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1278-
assert_current_epoch!(2);
12791270
assert_aura_authorities!([alice()]);
12801271
});
1281-
set_committee_through_inherent_data(&[alice(), bob()]);
1272+
set_committee_through_inherent_data(&[alice()]);
12821273
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1283-
assert_current_epoch!(3);
12841274
assert_aura_authorities!([bob()]);
12851275
});
1286-
set_committee_through_inherent_data(&[bob(), alice()]);
1276+
set_committee_through_inherent_data(&[alice(), bob()]);
12871277
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1288-
assert_current_epoch!(4);
12891278
assert_aura_authorities!([alice()]);
12901279
});
1291-
set_committee_through_inherent_data(&[alice()]);
1280+
set_committee_through_inherent_data(&[bob(), alice()]);
12921281
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1293-
assert_current_epoch!(5);
12941282
assert_aura_authorities!([alice(), bob()]);
12951283
});
1284+
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1285+
assert_aura_authorities!([bob(), alice()]);
1286+
});
12961287

12971288
// When there's no new committees being scheduled, the last committee stays in power
12981289
for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH * 3, &|| {
@@ -1326,9 +1317,7 @@ mod tests {
13261317
});
13271318
}
13281319

1329-
pub fn set_committee_through_inherent_data(
1330-
expected_authorities: &[TestKeys],
1331-
) -> PostDispatchInfo {
1320+
fn set_committee_through_inherent_data(expected_authorities: &[TestKeys]) -> PostDispatchInfo {
13321321
let epoch = Sidechain::current_epoch_number();
13331322
let slot = *pallet_aura::CurrentSlot::<Test>::get();
13341323
println!(

e2e-tests/tests/committee/test_committee.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ def test_authorities_matching_committee(self, api: BlockchainApi, config: ApiCon
486486

487487
# There's a delay between selecting a committee and it being in power, which means the current
488488
# committee was selected based on inputs from the previous epoch
489-
committee = api.get_epoch_committee(current_epoch - 1).result['committee']
489+
committee = api.get_epoch_committee(current_epoch).result['committee']
490490
authorities = api.get_authorities()
491491

492492
if api.get_pc_epoch() > current_epoch:

toolkit/committee-selection/pallet/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ mod tests;
3333

3434
pub mod weights;
3535

36+
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
37+
use scale_info::TypeInfo;
3638
pub use sp_session_validator_management::CommitteeMember;
3739
pub use weights::WeightInfo;
3840

@@ -165,6 +167,12 @@ pub mod pallet {
165167
OptionQuery,
166168
>;
167169

170+
/// Stores the stage of handling the inputs change. Used by session manager, to decide
171+
/// if the session should be ended quickly, to speed up using the newly selected committee.
172+
#[pallet::storage]
173+
pub type CommitteeRotationStage<T: Config> =
174+
StorageValue<_, CommitteeRotationStages, ValueQuery>;
175+
168176
#[pallet::storage]
169177
pub type MainChainScriptsConfiguration<T: Config> =
170178
StorageValue<_, MainChainScripts, ValueQuery>;
@@ -448,3 +456,15 @@ pub mod pallet {
448456
}
449457
}
450458
}
459+
460+
/// For session state machine
461+
#[derive(Encode, Decode, Default, Debug, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
462+
pub enum CommitteeRotationStages {
463+
/// No action is required until the current committee becomes obsolete
464+
#[default]
465+
AwaitEpochChange,
466+
/// Session ended because of epoch change
467+
NewSessionDueEpochChange,
468+
/// Session ended to accelerate use of validators queued in the previous block
469+
AdditionalSession,
470+
}

toolkit/committee-selection/pallet/src/mock.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,18 @@ pub(crate) fn start_session(session_index: u32) {
280280

281281
assert_eq!(Session::current_index(), session_index);
282282
}
283+
284+
pub(crate) fn advance_one_block() {
285+
let block_number = System::block_number();
286+
System::on_finalize(block_number);
287+
Session::on_finalize(block_number);
288+
let parent_hash =
289+
if block_number > 1 { System::finalize().hash() } else { System::parent_hash() };
290+
System::reset_events();
291+
let next_block_number = block_number as u64 + 1;
292+
System::initialize(&next_block_number, &parent_hash, &Default::default());
293+
System::set_block_number(next_block_number.into());
294+
295+
System::on_initialize(next_block_number);
296+
Session::on_initialize(next_block_number);
297+
}

toolkit/committee-selection/pallet/src/pallet_session_support.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! This implementation has lag of one additional PC epoch when applying committees to sessions.
44
//!
55
//! To use it, wire [crate::Pallet] in runtime configuration of [`pallet_session`].
6-
use crate::CommitteeMember;
6+
use crate::{CommitteeMember, CommitteeRotationStage, CommitteeRotationStages};
77
use frame_support::traits::UnfilteredDispatchable;
88
use frame_system::RawOrigin;
99
use frame_system::pallet_prelude::BlockNumberFor;
@@ -32,7 +32,14 @@ where
3232
/// Updates the session index of [`pallet_session`].
3333
// Instead of Some((*).expect) we could just use (*). However, we rather panic in presence of important programming errors.
3434
fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
35-
info!("💼 Session manager: new_session {new_index}");
35+
if CommitteeRotationStage::<T>::get() == CommitteeRotationStages::AdditionalSession {
36+
info!("💼 Session manager: new additional session {new_index}");
37+
CommitteeRotationStage::<T>::put(CommitteeRotationStages::AwaitEpochChange);
38+
let committee = crate::Pallet::<T>::current_committee_storage().committee;
39+
return Some(committee.iter().map(|member| member.authority_id().into()).collect());
40+
}
41+
42+
info!("💼 Session manager: new_session {new_index}, rotating the committee");
3643
let new_committee = crate::Pallet::<T>::rotate_committee_to_next_epoch().expect(
3744
"Session should never end without current epoch validators defined. \
3845
Check ShouldEndSession implementation or if it is used before starting new session",
@@ -130,6 +137,7 @@ where
130137
if current_epoch_number > current_committee_epoch {
131138
if next_committee_is_defined {
132139
info!("Session manager: should_end_session({n:?}) = true");
140+
CommitteeRotationStage::<T>::put(CommitteeRotationStages::NewSessionDueEpochChange);
133141
true
134142
} else {
135143
warn!(
@@ -138,16 +146,23 @@ where
138146
false
139147
}
140148
} else {
141-
debug!("Session manager: should_end_session({n:?}) = false");
142-
false
149+
let stage = CommitteeRotationStage::<T>::get();
150+
if stage == CommitteeRotationStages::NewSessionDueEpochChange {
151+
CommitteeRotationStage::<T>::put(CommitteeRotationStages::AdditionalSession);
152+
info!("Session manager: should_end_session({n:?}) to force the new committee");
153+
true
154+
} else {
155+
false
156+
}
143157
}
144158
}
145159
}
146160

147161
#[cfg(test)]
148162
mod tests {
149163
use crate::{
150-
CommitteeInfo, CurrentCommittee, NextCommittee, mock::mock_pallet::CurrentEpoch, mock::*,
164+
CommitteeInfo, CurrentCommittee, NextCommittee,
165+
mock::{mock_pallet::CurrentEpoch, *},
151166
tests::increment_epoch,
152167
};
153168
use pallet_session::ShouldEndSession;
@@ -200,4 +215,32 @@ mod tests {
200215
);
201216
});
202217
}
218+
219+
#[test]
220+
fn ends_two_sessions_and_rotates_once_when_committee_changes() {
221+
new_test_ext().execute_with(|| {
222+
assert_eq!(Session::current_index(), 0);
223+
assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 0);
224+
increment_epoch();
225+
set_validators_directly(&[CHARLIE, DAVE], 1).unwrap();
226+
227+
advance_one_block();
228+
assert_eq!(Session::current_index(), 1);
229+
// pallet_session needs additional session to apply CHARLIE and DAVE as validators
230+
assert_eq!(Session::validators(), Vec::<u64>::new());
231+
assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
232+
233+
advance_one_block();
234+
assert_eq!(Session::current_index(), 2);
235+
assert_eq!(Session::validators(), vec![CHARLIE.authority_id, DAVE.authority_id]);
236+
assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
237+
238+
for _i in 0..10 {
239+
advance_one_block();
240+
assert_eq!(Session::current_index(), 2);
241+
assert_eq!(Session::validators(), vec![CHARLIE.authority_id, DAVE.authority_id]);
242+
assert_eq!(SessionCommitteeManagement::current_committee_storage().epoch, 1);
243+
}
244+
});
245+
}
203246
}

0 commit comments

Comments
 (0)