Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 162ba81

Browse files
andresilvabkchrmelekes
authored andcommitted
grandpa: cleanup stale entries in set id session mapping (#13237)
* grandpa: cleanup stale entries in set id session mapping * Update frame/grandpa/src/migrations.rs Co-authored-by: Bastian Köcher <git@kchr.de> * grandpa: remove unused import * grandpa: migration off-by-one * Update frame/grandpa/src/lib.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Update frame/grandpa/src/lib.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * grandpa: MaxSetIdSessionEntries as u64 * node-template: fix MaxSetIdSessionEntries type --------- Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: Anton <anton.kalyaev@gmail.com>
1 parent c61e4f7 commit 162ba81

File tree

6 files changed

+107
-4
lines changed

6 files changed

+107
-4
lines changed

bin/node-template/runtime/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ impl pallet_grandpa::Config for Runtime {
230230

231231
type WeightInfo = ();
232232
type MaxAuthorities = ConstU32<32>;
233+
type MaxSetIdSessionEntries = ConstU64<0>;
233234
}
234235

235236
impl pallet_timestamp::Config for Runtime {

bin/node/runtime/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,10 @@ impl pallet_authority_discovery::Config for Runtime {
13101310
type MaxAuthorities = MaxAuthorities;
13111311
}
13121312

1313+
parameter_types! {
1314+
pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get();
1315+
}
1316+
13131317
impl pallet_grandpa::Config for Runtime {
13141318
type RuntimeEvent = RuntimeEvent;
13151319

@@ -1331,6 +1335,7 @@ impl pallet_grandpa::Config for Runtime {
13311335

13321336
type WeightInfo = ();
13331337
type MaxAuthorities = MaxAuthorities;
1338+
type MaxSetIdSessionEntries = MaxSetIdSessionEntries;
13341339
}
13351340

13361341
parameter_types! {

frame/grandpa/src/lib.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ pub mod pallet {
121121
/// Max Authorities in use
122122
#[pallet::constant]
123123
type MaxAuthorities: Get<u32>;
124+
125+
/// The maximum number of entries to keep in the set id to session index mapping.
126+
///
127+
/// Since the `SetIdSession` map is only used for validating equivocations this
128+
/// value should relate to the bonding duration of whatever staking system is
129+
/// being used (if any). If equivocation handling is not enabled then this value
130+
/// can be zero.
131+
#[pallet::constant]
132+
type MaxSetIdSessionEntries: Get<u64>;
124133
}
125134

126135
#[pallet::hooks]
@@ -323,6 +332,12 @@ pub mod pallet {
323332
/// A mapping from grandpa set ID to the index of the *most recent* session for which its
324333
/// members were responsible.
325334
///
335+
/// This is only used for validating equivocation proofs. An equivocation proof must
336+
/// contains a key-ownership proof for a given session, therefore we need a way to tie
337+
/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
338+
/// was the owner of a given key on a given session, and what the active set ID was
339+
/// during that session.
340+
///
326341
/// TWOX-NOTE: `SetId` is not under user control.
327342
#[pallet::storage]
328343
#[pallet::getter(fn session_for_set)]
@@ -643,10 +658,17 @@ where
643658
};
644659

645660
if res.is_ok() {
646-
CurrentSetId::<T>::mutate(|s| {
661+
let current_set_id = CurrentSetId::<T>::mutate(|s| {
647662
*s += 1;
648663
*s
649-
})
664+
});
665+
666+
let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
667+
if current_set_id >= max_set_id_session_entries {
668+
SetIdSession::<T>::remove(current_set_id - max_set_id_session_entries);
669+
}
670+
671+
current_set_id
650672
} else {
651673
// either the session module signalled that the validators have changed
652674
// or the set was stalled. but since we didn't successfully schedule
@@ -659,8 +681,8 @@ where
659681
Self::current_set_id()
660682
};
661683

662-
// if we didn't issue a change, we update the mapping to note that the current
663-
// set corresponds to the latest equivalent session (i.e. now).
684+
// update the mapping to note that the current set corresponds to the
685+
// latest equivalent session (i.e. now).
664686
let session_index = <pallet_session::Pallet<T>>::current_index();
665687
SetIdSession::<T>::insert(current_set_id, &session_index);
666688
}

frame/grandpa/src/migrations.rs

+46
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,51 @@
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
1717

18+
use frame_support::{
19+
traits::{Get, OnRuntimeUpgrade},
20+
weights::Weight,
21+
};
22+
23+
use crate::{Config, CurrentSetId, SetIdSession, LOG_TARGET};
24+
1825
/// Version 4.
1926
pub mod v4;
27+
28+
/// This migration will clean up all stale set id -> session entries from the
29+
/// `SetIdSession` storage map, only the latest `max_set_id_session_entries`
30+
/// will be kept.
31+
///
32+
/// This migration should be added with a runtime upgrade that introduces the
33+
/// `MaxSetIdSessionEntries` constant to the pallet (although it could also be
34+
/// done later on).
35+
pub struct CleanupSetIdSessionMap<T>(sp_std::marker::PhantomData<T>);
36+
impl<T: Config> OnRuntimeUpgrade for CleanupSetIdSessionMap<T> {
37+
fn on_runtime_upgrade() -> Weight {
38+
// NOTE: since this migration will loop over all stale entries in the
39+
// map we need to set some cutoff value, otherwise the migration might
40+
// take too long to run. for scenarios where there are that many entries
41+
// to cleanup a multiblock migration will be needed instead.
42+
if CurrentSetId::<T>::get() > 25_000 {
43+
log::warn!(
44+
target: LOG_TARGET,
45+
"CleanupSetIdSessionMap migration was aborted since there are too many entries to cleanup."
46+
);
47+
48+
return T::DbWeight::get().reads(1)
49+
}
50+
51+
cleanup_set_id_sesion_map::<T>()
52+
}
53+
}
54+
55+
fn cleanup_set_id_sesion_map<T: Config>() -> Weight {
56+
let until_set_id = CurrentSetId::<T>::get().saturating_sub(T::MaxSetIdSessionEntries::get());
57+
58+
for set_id in 0..=until_set_id {
59+
SetIdSession::<T>::remove(set_id);
60+
}
61+
62+
T::DbWeight::get()
63+
.reads(1)
64+
.saturating_add(T::DbWeight::get().writes(until_set_id + 1))
65+
}

frame/grandpa/src/mock.rs

+2
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ impl pallet_offences::Config for Test {
219219
parameter_types! {
220220
pub const ReportLongevity: u64 =
221221
BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get();
222+
pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get();
222223
}
223224

224225
impl Config for Test {
@@ -239,6 +240,7 @@ impl Config for Test {
239240

240241
type WeightInfo = ();
241242
type MaxAuthorities = ConstU32<100>;
243+
type MaxSetIdSessionEntries = MaxSetIdSessionEntries;
242244
}
243245

244246
pub fn grandpa_log(log: ConsensusLog<u64>) -> DigestItem {

frame/grandpa/src/tests.rs

+27
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,33 @@ fn on_new_session_doesnt_start_new_set_if_schedule_change_failed() {
781781
});
782782
}
783783

784+
#[test]
785+
fn cleans_up_old_set_id_session_mappings() {
786+
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
787+
let max_set_id_session_entries = MaxSetIdSessionEntries::get();
788+
789+
start_era(max_set_id_session_entries);
790+
791+
// we should have a session id mapping for all the set ids from
792+
// `max_set_id_session_entries` eras we have observed
793+
for i in 1..=max_set_id_session_entries {
794+
assert!(Grandpa::session_for_set(i as u64).is_some());
795+
}
796+
797+
start_era(max_set_id_session_entries * 2);
798+
799+
// we should keep tracking the new mappings for new eras
800+
for i in max_set_id_session_entries + 1..=max_set_id_session_entries * 2 {
801+
assert!(Grandpa::session_for_set(i as u64).is_some());
802+
}
803+
804+
// but the old ones should have been pruned by now
805+
for i in 1..=max_set_id_session_entries {
806+
assert!(Grandpa::session_for_set(i as u64).is_none());
807+
}
808+
});
809+
}
810+
784811
#[test]
785812
fn always_schedules_a_change_on_new_session_when_stalled() {
786813
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {

0 commit comments

Comments
 (0)