referrerpolicy=no-referrer-when-downgrade

pallet_offences/
migration.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use super::{Config, Kind, OffenceDetails, Pallet, Perbill, SessionIndex, LOG_TARGET};
19use alloc::vec::Vec;
20use frame_support::{
21	pallet_prelude::ValueQuery,
22	storage_alias,
23	traits::{Get, GetStorageVersion, OnRuntimeUpgrade},
24	weights::Weight,
25	Twox64Concat,
26};
27use sp_staking::offence::OnOffenceHandler;
28
29#[cfg(feature = "try-runtime")]
30use frame_support::ensure;
31#[cfg(feature = "try-runtime")]
32use sp_runtime::TryRuntimeError;
33
34mod v0 {
35	use super::*;
36
37	#[storage_alias]
38	pub type ReportsByKindIndex<T: Config> = StorageMap<
39		Pallet<T>,
40		Twox64Concat,
41		Kind,
42		Vec<u8>, // (O::TimeSlot, ReportIdOf<T>)
43		ValueQuery,
44	>;
45}
46
47pub mod v1 {
48	use frame_support::traits::StorageVersion;
49
50	use super::*;
51
52	pub struct MigrateToV1<T>(core::marker::PhantomData<T>);
53	impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
54		#[cfg(feature = "try-runtime")]
55		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
56			log::info!(
57				target: LOG_TARGET,
58				"Number of reports to refund and delete: {}",
59				v0::ReportsByKindIndex::<T>::iter_keys().count()
60			);
61
62			Ok(Vec::new())
63		}
64
65		fn on_runtime_upgrade() -> Weight {
66			if Pallet::<T>::on_chain_storage_version() > 0 {
67				log::info!(target: LOG_TARGET, "pallet_offences::MigrateToV1 should be removed");
68				return T::DbWeight::get().reads(1)
69			}
70
71			let keys_removed = v0::ReportsByKindIndex::<T>::clear(u32::MAX, None).unique as u64;
72			StorageVersion::new(1).put::<Pallet<T>>();
73
74			// + 1 for reading/writing the new storage version
75			T::DbWeight::get().reads_writes(keys_removed + 1, keys_removed + 1)
76		}
77
78		#[cfg(feature = "try-runtime")]
79		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
80			let onchain = Pallet::<T>::on_chain_storage_version();
81			ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run");
82			ensure!(
83				v0::ReportsByKindIndex::<T>::iter_keys().count() == 0,
84				"there are some dangling reports that need to be destroyed and refunded"
85			);
86			Ok(())
87		}
88	}
89}
90
91/// Type of data stored as a deferred offence
92type DeferredOffenceOf<T> = (
93	Vec<OffenceDetails<<T as frame_system::Config>::AccountId, <T as Config>::IdentificationTuple>>,
94	Vec<Perbill>,
95	SessionIndex,
96);
97
98// Deferred reports that have been rejected by the offence handler and need to be submitted
99// at a later time.
100#[storage_alias]
101type DeferredOffences<T: Config> =
102	StorageValue<crate::Pallet<T>, Vec<DeferredOffenceOf<T>>, ValueQuery>;
103
104pub fn remove_deferred_storage<T: Config>() -> Weight {
105	let mut weight = T::DbWeight::get().reads_writes(1, 1);
106	let deferred = <DeferredOffences<T>>::take();
107	log::info!(target: LOG_TARGET, "have {} deferred offences, applying.", deferred.len());
108	for (offences, perbill, session) in deferred.iter() {
109		let consumed = T::OnOffenceHandler::on_offence(offences, perbill, *session);
110		weight = weight.saturating_add(consumed);
111	}
112
113	weight
114}
115
116#[cfg(test)]
117mod test {
118	use super::*;
119	use crate::mock::{new_test_ext, with_on_offence_fractions, Runtime as T, KIND};
120	use codec::Encode;
121	use sp_runtime::Perbill;
122	use sp_staking::offence::OffenceDetails;
123
124	#[test]
125	fn migration_to_v1_works() {
126		let mut ext = new_test_ext();
127
128		ext.execute_with(|| {
129			<v0::ReportsByKindIndex<T>>::insert(KIND, 2u32.encode());
130			assert!(<v0::ReportsByKindIndex<T>>::iter_values().count() > 0);
131		});
132
133		ext.commit_all().unwrap();
134
135		ext.execute_with(|| {
136			assert_eq!(
137				v1::MigrateToV1::<T>::on_runtime_upgrade(),
138				<T as frame_system::Config>::DbWeight::get().reads_writes(2, 2),
139			);
140
141			assert!(<v0::ReportsByKindIndex<T>>::iter_values().count() == 0);
142		})
143	}
144
145	#[test]
146	fn should_resubmit_deferred_offences() {
147		new_test_ext().execute_with(|| {
148			// given
149			assert_eq!(<DeferredOffences<T>>::get().len(), 0);
150			with_on_offence_fractions(|f| {
151				assert_eq!(f.clone(), vec![]);
152			});
153
154			let offence_details = OffenceDetails::<
155				<T as frame_system::Config>::AccountId,
156				<T as Config>::IdentificationTuple,
157			> {
158				offender: 5,
159				reporters: vec![],
160			};
161
162			// push deferred offence
163			<DeferredOffences<T>>::append((
164				vec![offence_details],
165				vec![Perbill::from_percent(5 + 1 * 100 / 5)],
166				1,
167			));
168
169			// when
170			assert_eq!(
171				remove_deferred_storage::<T>(),
172				<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1),
173			);
174
175			// then
176			assert!(!<DeferredOffences<T>>::exists());
177			with_on_offence_fractions(|f| {
178				assert_eq!(f.clone(), vec![Perbill::from_percent(5 + 1 * 100 / 5)]);
179			});
180		})
181	}
182}