Skip to content

Commit f993518

Browse files
authored
add an upgrade_keys method for pallet-session (#7688)
* add an upgrade_keys method for pallet-session * test the upgrade_keys function
1 parent b839b31 commit f993518

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

src/lib.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,55 @@ impl<T: Config> Module<T> {
683683
Self::validators().iter().position(|i| i == c).map(Self::disable_index).ok_or(())
684684
}
685685

686+
/// Upgrade the key type from some old type to a new type. Supports adding
687+
/// and removing key types.
688+
///
689+
/// This function should be used with extreme care and only during an
690+
/// `on_runtime_upgrade` block. Misuse of this function can put your blockchain
691+
/// into an unrecoverable state.
692+
///
693+
/// Care should be taken that the raw versions of the
694+
/// added keys are unique for every `ValidatorId, KeyTypeId` combination.
695+
/// This is an invariant that the session module typically maintains internally.
696+
///
697+
/// As the actual values of the keys are typically not known at runtime upgrade,
698+
/// it's recommended to initialize the keys to a (unique) dummy value with the expectation
699+
/// that all validators should invoke `set_keys` before those keys are actually
700+
/// required.
701+
pub fn upgrade_keys<Old, F>(upgrade: F) where
702+
Old: OpaqueKeys + Member + Decode,
703+
F: Fn(T::ValidatorId, Old) -> T::Keys,
704+
{
705+
let old_ids = Old::key_ids();
706+
let new_ids = T::Keys::key_ids();
707+
708+
// Translate NextKeys, and key ownership relations at the same time.
709+
<NextKeys<T>>::translate::<Old, _>(|val, old_keys| {
710+
// Clear all key ownership relations. Typically the overlap should
711+
// stay the same, but no guarantees by the upgrade function.
712+
for i in old_ids.iter() {
713+
Self::clear_key_owner(*i, old_keys.get_raw(*i));
714+
}
715+
716+
let new_keys = upgrade(val.clone(), old_keys);
717+
718+
// And now set the new ones.
719+
for i in new_ids.iter() {
720+
Self::put_key_owner(*i, new_keys.get_raw(*i), &val);
721+
}
722+
723+
Some(new_keys)
724+
});
725+
726+
let _ = <QueuedKeys<T>>::translate::<Vec<(T::ValidatorId, Old)>, _>(
727+
|k| {
728+
k.map(|k| k.into_iter()
729+
.map(|(val, old_keys)| (val.clone(), upgrade(val, old_keys)))
730+
.collect::<Vec<_>>())
731+
}
732+
);
733+
}
734+
686735
/// Perform the set_key operation, checking for duplicates. Does not set `Changed`.
687736
///
688737
/// This ensures that the reference counter in system is incremented appropriately and as such

src/mock.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ impl From<UintAuthorityId> for MockSessionKeys {
4040
}
4141
}
4242

43+
pub const KEY_ID_A: KeyTypeId = KeyTypeId([4; 4]);
44+
pub const KEY_ID_B: KeyTypeId = KeyTypeId([9; 4]);
45+
46+
#[derive(Debug, Clone, codec::Encode, codec::Decode, PartialEq, Eq)]
47+
pub struct PreUpgradeMockSessionKeys {
48+
pub a: [u8; 32],
49+
pub b: [u8; 64],
50+
}
51+
52+
impl OpaqueKeys for PreUpgradeMockSessionKeys {
53+
type KeyTypeIdProviders = ();
54+
55+
fn key_ids() -> &'static [KeyTypeId] {
56+
&[KEY_ID_A, KEY_ID_B]
57+
}
58+
59+
fn get_raw(&self, i: KeyTypeId) -> &[u8] {
60+
match i {
61+
i if i == KEY_ID_A => &self.a[..],
62+
i if i == KEY_ID_B => &self.b[..],
63+
_ => &[],
64+
}
65+
}
66+
}
67+
4368
impl_outer_origin! {
4469
pub enum Origin for Test where system = frame_system {}
4570
}

src/tests.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use mock::{
2525
SESSION_CHANGED, TEST_SESSION_CHANGED, authorities, force_new_session,
2626
set_next_validators, set_session_length, session_changed, Origin, System, Session,
2727
reset_before_session_end_called, before_session_end_called, new_test_ext,
28+
PreUpgradeMockSessionKeys,
2829
};
2930

3031
fn initialize_block(block: u64) {
@@ -308,3 +309,97 @@ fn return_true_if_more_than_third_is_disabled() {
308309
assert_eq!(Session::disable_index(3), true);
309310
});
310311
}
312+
313+
#[test]
314+
fn upgrade_keys() {
315+
use frame_support::storage;
316+
use mock::Test;
317+
use sp_core::crypto::key_types::DUMMY;
318+
319+
// This test assumes certain mocks.
320+
assert_eq!(mock::NEXT_VALIDATORS.with(|l| l.borrow().clone()), vec![1, 2, 3]);
321+
assert_eq!(mock::VALIDATORS.with(|l| l.borrow().clone()), vec![1, 2, 3]);
322+
323+
new_test_ext().execute_with(|| {
324+
let pre_one = PreUpgradeMockSessionKeys {
325+
a: [1u8; 32],
326+
b: [1u8; 64],
327+
};
328+
329+
let pre_two = PreUpgradeMockSessionKeys {
330+
a: [2u8; 32],
331+
b: [2u8; 64],
332+
};
333+
334+
let pre_three = PreUpgradeMockSessionKeys {
335+
a: [3u8; 32],
336+
b: [3u8; 64],
337+
};
338+
339+
let val_keys = vec![
340+
(1u64, pre_one),
341+
(2u64, pre_two),
342+
(3u64, pre_three),
343+
];
344+
345+
// Set `QueuedKeys`.
346+
{
347+
let storage_key = <super::QueuedKeys<Test>>::hashed_key();
348+
assert!(storage::unhashed::exists(&storage_key));
349+
storage::unhashed::put(&storage_key, &val_keys);
350+
}
351+
352+
// Set `NextKeys`.
353+
{
354+
for &(i, ref keys) in val_keys.iter() {
355+
let storage_key = <super::NextKeys<Test>>::hashed_key_for(i);
356+
assert!(storage::unhashed::exists(&storage_key));
357+
storage::unhashed::put(&storage_key, keys);
358+
}
359+
}
360+
361+
// Set `KeyOwner`.
362+
{
363+
for &(i, ref keys) in val_keys.iter() {
364+
// clear key owner for `UintAuthorityId` keys set in genesis.
365+
let presumed = UintAuthorityId(i);
366+
let raw_prev = presumed.as_ref();
367+
368+
assert_eq!(Session::key_owner(DUMMY, raw_prev), Some(i));
369+
Session::clear_key_owner(DUMMY, raw_prev);
370+
371+
Session::put_key_owner(mock::KEY_ID_A, keys.get_raw(mock::KEY_ID_A), &i);
372+
Session::put_key_owner(mock::KEY_ID_B, keys.get_raw(mock::KEY_ID_B), &i);
373+
}
374+
}
375+
376+
// Do the upgrade and check sanity.
377+
let mock_keys_for = |val| mock::MockSessionKeys { dummy: UintAuthorityId(val) };
378+
Session::upgrade_keys::<PreUpgradeMockSessionKeys, _>(
379+
|val, _old_keys| mock_keys_for(val),
380+
);
381+
382+
// Check key ownership.
383+
for (i, ref keys) in val_keys.iter() {
384+
assert!(Session::key_owner(mock::KEY_ID_A, keys.get_raw(mock::KEY_ID_A)).is_none());
385+
assert!(Session::key_owner(mock::KEY_ID_B, keys.get_raw(mock::KEY_ID_B)).is_none());
386+
387+
let migrated_key = UintAuthorityId(*i);
388+
assert_eq!(Session::key_owner(DUMMY, migrated_key.as_ref()), Some(*i));
389+
}
390+
391+
// Check queued keys.
392+
assert_eq!(
393+
Session::queued_keys(),
394+
vec![
395+
(1, mock_keys_for(1)),
396+
(2, mock_keys_for(2)),
397+
(3, mock_keys_for(3)),
398+
],
399+
);
400+
401+
for i in 1u64..4 {
402+
assert_eq!(<super::NextKeys<Test>>::get(&i), Some(mock_keys_for(i)));
403+
}
404+
})
405+
}

0 commit comments

Comments
 (0)