pallet_session/historical/
mod.rs1pub mod offchain;
30pub mod onchain;
31mod shared;
32
33use alloc::vec::Vec;
34use codec::{Decode, Encode};
35use core::fmt::Debug;
36use sp_runtime::{
37 traits::{Convert, OpaqueKeys},
38 KeyTypeId,
39};
40use sp_session::{MembershipProof, ValidatorCount};
41use sp_staking::SessionIndex;
42use sp_trie::{
43 trie_types::{TrieDBBuilder, TrieDBMutBuilderV0},
44 LayoutV0, MemoryDB, RandomState, Recorder, StorageProof, Trie, TrieMut, TrieRecorder,
45};
46
47use frame_support::{
48 traits::{KeyOwnerProofSystem, ValidatorSet, ValidatorSetWithIdentification},
49 Parameter,
50};
51
52const LOG_TARGET: &'static str = "runtime::historical";
53
54use crate::{self as pallet_session, Pallet as Session};
55
56pub use pallet::*;
57use sp_trie::{accessed_nodes_tracker::AccessedNodesTracker, recorder_ext::RecorderExt};
58
59#[frame_support::pallet]
60pub mod pallet {
61 use super::*;
62 use frame_support::pallet_prelude::*;
63
64 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
66
67 #[pallet::pallet]
68 #[pallet::storage_version(STORAGE_VERSION)]
69 pub struct Pallet<T>(_);
70
71 #[pallet::config]
73 pub trait Config: pallet_session::Config + frame_system::Config {
74 #[allow(deprecated)]
76 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
77
78 type FullIdentification: Parameter;
80
81 type FullIdentificationOf: Convert<Self::ValidatorId, Option<Self::FullIdentification>>;
89 }
90
91 #[pallet::storage]
93 #[pallet::getter(fn historical_root)]
94 pub type HistoricalSessions<T: Config> =
95 StorageMap<_, Twox64Concat, SessionIndex, (T::Hash, ValidatorCount), OptionQuery>;
96
97 #[pallet::storage]
99 pub type StoredRange<T> = StorageValue<_, (SessionIndex, SessionIndex), OptionQuery>;
100
101 #[pallet::event]
102 #[pallet::generate_deposit(pub(super) fn deposit_event)]
103 pub enum Event<T> {
104 RootStored { index: SessionIndex },
106 RootsPruned { up_to: SessionIndex },
108 }
109}
110
111impl<T: Config> Pallet<T> {
112 pub fn prune_up_to(up_to: SessionIndex) {
115 StoredRange::<T>::mutate(|range| {
116 let (start, end) = match *range {
117 Some(range) => range,
118 None => return, };
120
121 let up_to = core::cmp::min(up_to, end);
122
123 if up_to < start {
124 return; }
126
127 (start..up_to).for_each(HistoricalSessions::<T>::remove);
128
129 let new_start = up_to;
130 *range = if new_start == end {
131 None } else {
133 Some((new_start, end))
134 }
135 });
136
137 Self::deposit_event(Event::<T>::RootsPruned { up_to });
138 }
139
140 fn full_id_validators() -> Vec<(T::ValidatorId, T::FullIdentification)> {
141 <Session<T>>::validators()
142 .into_iter()
143 .filter_map(|validator| {
144 T::FullIdentificationOf::convert(validator.clone())
145 .map(|full_id| (validator, full_id))
146 })
147 .collect::<Vec<_>>()
148 }
149}
150
151impl<T: Config> ValidatorSet<T::AccountId> for Pallet<T> {
152 type ValidatorId = T::ValidatorId;
153 type ValidatorIdOf = T::ValidatorIdOf;
154
155 fn session_index() -> sp_staking::SessionIndex {
156 super::Pallet::<T>::current_index()
157 }
158
159 fn validators() -> Vec<Self::ValidatorId> {
160 super::Pallet::<T>::validators()
161 }
162}
163
164impl<T: Config> ValidatorSetWithIdentification<T::AccountId> for Pallet<T> {
165 type Identification = T::FullIdentification;
166 type IdentificationOf = T::FullIdentificationOf;
167}
168
169pub trait SessionManager<ValidatorId, FullIdentification>:
172 pallet_session::SessionManager<ValidatorId>
173{
174 fn new_session(new_index: SessionIndex) -> Option<Vec<(ValidatorId, FullIdentification)>>;
177 fn new_session_genesis(
178 new_index: SessionIndex,
179 ) -> Option<Vec<(ValidatorId, FullIdentification)>> {
180 <Self as SessionManager<_, _>>::new_session(new_index)
181 }
182 fn start_session(start_index: SessionIndex);
183 fn end_session(end_index: SessionIndex);
184}
185
186pub struct NoteHistoricalRoot<T, I>(core::marker::PhantomData<(T, I)>);
189
190impl<T: Config, I: SessionManager<T::ValidatorId, T::FullIdentification>> NoteHistoricalRoot<T, I> {
191 fn do_new_session(new_index: SessionIndex, is_genesis: bool) -> Option<Vec<T::ValidatorId>> {
192 <StoredRange<T>>::mutate(|range| {
193 range.get_or_insert_with(|| (new_index, new_index)).1 = new_index + 1;
194 });
195
196 let new_validators_and_id = if is_genesis {
197 <I as SessionManager<_, _>>::new_session_genesis(new_index)
198 } else {
199 <I as SessionManager<_, _>>::new_session(new_index)
200 };
201 let new_validators_opt = new_validators_and_id
202 .as_ref()
203 .map(|new_validators| new_validators.iter().map(|(v, _id)| v.clone()).collect());
204
205 if let Some(new_validators) = new_validators_and_id {
206 let count = new_validators.len() as ValidatorCount;
207 match ProvingTrie::<T>::generate_for(new_validators) {
208 Ok(trie) => {
209 <HistoricalSessions<T>>::insert(new_index, &(trie.root, count));
210 Pallet::<T>::deposit_event(Event::RootStored { index: new_index });
211 },
212 Err(reason) => {
213 log::error!(
214 target: LOG_TARGET,
215 "Failed to generate historical ancestry-inclusion proof: {:?}",
216 reason,
217 );
218 },
219 };
220 } else {
221 let previous_index = new_index.saturating_sub(1);
222 if let Some(previous_session) = <HistoricalSessions<T>>::get(previous_index) {
223 <HistoricalSessions<T>>::insert(new_index, previous_session);
224 Pallet::<T>::deposit_event(Event::RootStored { index: new_index });
225 }
226 }
227
228 new_validators_opt
229 }
230}
231
232impl<T: Config, I> pallet_session::SessionManager<T::ValidatorId> for NoteHistoricalRoot<T, I>
233where
234 I: SessionManager<T::ValidatorId, T::FullIdentification>,
235{
236 fn new_session(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
237 Self::do_new_session(new_index, false)
238 }
239
240 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
241 Self::do_new_session(new_index, true)
242 }
243
244 fn start_session(start_index: SessionIndex) {
245 <I as SessionManager<_, _>>::start_session(start_index)
246 }
247
248 fn end_session(end_index: SessionIndex) {
249 onchain::store_session_validator_set_to_offchain::<T>(end_index);
250 <I as SessionManager<_, _>>::end_session(end_index)
251 }
252}
253
254pub type IdentificationTuple<T> =
256 (<T as pallet_session::Config>::ValidatorId, <T as Config>::FullIdentification);
257
258pub struct ProvingTrie<T: Config> {
260 db: MemoryDB<T::Hashing>,
261 root: T::Hash,
262}
263
264impl<T: Config> ProvingTrie<T> {
265 fn generate_for<I>(validators: I) -> Result<Self, &'static str>
266 where
267 I: IntoIterator<Item = (T::ValidatorId, T::FullIdentification)>,
268 {
269 let mut db = MemoryDB::with_hasher(RandomState::default());
270 let mut root = Default::default();
271
272 {
273 let mut trie = TrieDBMutBuilderV0::new(&mut db, &mut root).build();
274 for (i, (validator, full_id)) in validators.into_iter().enumerate() {
275 let i = i as u32;
276 let keys = match <Session<T>>::load_keys(&validator) {
277 None => continue,
278 Some(k) => k,
279 };
280
281 let id_tuple = (validator, full_id);
282
283 for key_id in T::Keys::key_ids() {
285 let key = keys.get_raw(*key_id);
286 let res =
287 (key_id, key).using_encoded(|k| i.using_encoded(|v| trie.insert(k, v)));
288
289 res.map_err(|_| "failed to insert into trie")?;
290 }
291
292 i.using_encoded(|k| id_tuple.using_encoded(|v| trie.insert(k, v)))
294 .map_err(|_| "failed to insert into trie")?;
295 }
296 }
297
298 Ok(ProvingTrie { db, root })
299 }
300
301 fn from_proof(root: T::Hash, proof: StorageProof) -> Self {
302 ProvingTrie { db: proof.into_memory_db(), root }
303 }
304
305 pub fn prove(&self, key_id: KeyTypeId, key_data: &[u8]) -> Option<Vec<Vec<u8>>> {
307 let mut recorder = Recorder::<LayoutV0<T::Hashing>>::new();
308 self.query(key_id, key_data, Some(&mut recorder));
309
310 Some(recorder.into_raw_storage_proof())
311 }
312
313 pub fn root(&self) -> &T::Hash {
315 &self.root
316 }
317
318 fn query(
320 &self,
321 key_id: KeyTypeId,
322 key_data: &[u8],
323 recorder: Option<&mut dyn TrieRecorder<T::Hash>>,
324 ) -> Option<IdentificationTuple<T>> {
325 let trie = TrieDBBuilder::new(&self.db, &self.root)
326 .with_optional_recorder(recorder)
327 .build();
328
329 let val_idx = (key_id, key_data)
330 .using_encoded(|s| trie.get(s))
331 .ok()?
332 .and_then(|raw| u32::decode(&mut &*raw).ok())?;
333
334 val_idx
335 .using_encoded(|s| trie.get(s))
336 .ok()?
337 .and_then(|raw| <IdentificationTuple<T>>::decode(&mut &*raw).ok())
338 }
339}
340
341impl<T: Config, D: AsRef<[u8]>> KeyOwnerProofSystem<(KeyTypeId, D)> for Pallet<T> {
342 type Proof = MembershipProof;
343 type IdentificationTuple = IdentificationTuple<T>;
344
345 fn prove(key: (KeyTypeId, D)) -> Option<Self::Proof> {
346 let session = <Session<T>>::current_index();
347 let validators = Self::full_id_validators();
348
349 let count = validators.len() as ValidatorCount;
350
351 let trie = ProvingTrie::<T>::generate_for(validators).ok()?;
352
353 let (id, data) = key;
354 trie.prove(id, data.as_ref()).map(|trie_nodes| MembershipProof {
355 session,
356 trie_nodes,
357 validator_count: count,
358 })
359 }
360
361 fn check_proof(key: (KeyTypeId, D), proof: Self::Proof) -> Option<IdentificationTuple<T>> {
362 fn print_error<E: Debug>(e: E) {
363 log::error!(
364 target: LOG_TARGET,
365 "Rejecting equivocation report because of key ownership proof error: {:?}", e
366 );
367 }
368
369 let (id, data) = key;
370 let (root, count) = if proof.session == <Session<T>>::current_index() {
371 let validators = Self::full_id_validators();
372 let count = validators.len() as ValidatorCount;
373 let trie = ProvingTrie::<T>::generate_for(validators).map_err(print_error).ok()?;
374 (trie.root, count)
375 } else {
376 <HistoricalSessions<T>>::get(&proof.session)?
377 };
378
379 if count != proof.validator_count {
380 print_error("InvalidCount");
381 return None;
382 }
383
384 let proof = StorageProof::new_with_duplicate_nodes_check(proof.trie_nodes)
385 .map_err(print_error)
386 .ok()?;
387 let mut accessed_nodes_tracker = AccessedNodesTracker::<T::Hash>::new(proof.len());
388 let trie = ProvingTrie::<T>::from_proof(root, proof);
389 let res = trie.query(id, data.as_ref(), Some(&mut accessed_nodes_tracker))?;
390 accessed_nodes_tracker.ensure_no_unused_nodes().map_err(print_error).ok()?;
391 Some(res)
392 }
393}
394
395#[cfg(test)]
396pub(crate) mod tests {
397 use super::*;
398 use crate::mock::{
399 force_new_session, set_next_validators, NextValidators, Session, System, Test,
400 };
401 use alloc::vec;
402
403 use sp_runtime::{key_types::DUMMY, testing::UintAuthorityId, BuildStorage};
404 use sp_state_machine::BasicExternalities;
405
406 use frame_support::traits::{KeyOwnerProofSystem, OnInitialize};
407
408 type Historical = Pallet<Test>;
409
410 pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
411 let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
412 let keys: Vec<_> = NextValidators::get()
413 .iter()
414 .cloned()
415 .map(|i| (i, i, UintAuthorityId(i).into()))
416 .collect();
417 BasicExternalities::execute_with_storage(&mut t, || {
418 for (ref k, ..) in &keys {
419 frame_system::Pallet::<Test>::inc_providers(k);
420 }
421 });
422 pallet_session::GenesisConfig::<Test> { keys, ..Default::default() }
423 .assimilate_storage(&mut t)
424 .unwrap();
425 sp_io::TestExternalities::new(t)
426 }
427
428 #[test]
429 fn generated_proof_is_good() {
430 new_test_ext().execute_with(|| {
431 set_next_validators(vec![1, 2]);
432 force_new_session();
433
434 System::set_block_number(1);
435 Session::on_initialize(1);
436
437 let encoded_key_1 = UintAuthorityId(1).encode();
438 let proof = Historical::prove((DUMMY, &encoded_key_1[..])).unwrap();
439
440 assert!(Historical::check_proof((DUMMY, &encoded_key_1[..]), proof.clone()).is_some());
442
443 set_next_validators(vec![1, 2, 4]);
444 force_new_session();
445
446 System::set_block_number(2);
447 Session::on_initialize(2);
448
449 assert!(Historical::historical_root(proof.session).is_some());
450 assert!(Session::current_index() > proof.session);
451
452 assert!(Historical::check_proof((DUMMY, &encoded_key_1[..]), proof.clone()).is_some());
454
455 set_next_validators(vec![1, 2, 5]);
456
457 force_new_session();
458 System::set_block_number(3);
459 Session::on_initialize(3);
460 });
461 }
462
463 #[test]
464 fn prune_up_to_works() {
465 new_test_ext().execute_with(|| {
466 for i in 1..99u64 {
467 set_next_validators(vec![i]);
468 force_new_session();
469
470 System::set_block_number(i);
471 Session::on_initialize(i);
472 }
473
474 assert_eq!(<StoredRange<Test>>::get(), Some((0, 100)));
475
476 for i in 0..100 {
477 assert!(Historical::historical_root(i).is_some())
478 }
479
480 Historical::prune_up_to(10);
481 assert_eq!(<StoredRange<Test>>::get(), Some((10, 100)));
482
483 Historical::prune_up_to(9);
484 assert_eq!(<StoredRange<Test>>::get(), Some((10, 100)));
485
486 for i in 10..100 {
487 assert!(Historical::historical_root(i).is_some())
488 }
489
490 Historical::prune_up_to(99);
491 assert_eq!(<StoredRange<Test>>::get(), Some((99, 100)));
492
493 Historical::prune_up_to(100);
494 assert_eq!(<StoredRange<Test>>::get(), None);
495
496 for i in 99..199u64 {
497 set_next_validators(vec![i]);
498 force_new_session();
499
500 System::set_block_number(i);
501 Session::on_initialize(i);
502 }
503
504 assert_eq!(<StoredRange<Test>>::get(), Some((100, 200)));
505
506 for i in 100..200 {
507 assert!(Historical::historical_root(i).is_some())
508 }
509
510 Historical::prune_up_to(9999);
511 assert_eq!(<StoredRange<Test>>::get(), None);
512
513 for i in 100..200 {
514 assert!(Historical::historical_root(i).is_none())
515 }
516 });
517 }
518}