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