// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::{Config, Kind, OffenceDetails, Pallet, Perbill, SessionIndex, LOG_TARGET}; use frame_support::{ pallet_prelude::ValueQuery, storage_alias, traits::{Get, GetStorageVersion, OnRuntimeUpgrade}, weights::Weight, Twox64Concat, }; use sp_staking::offence::OnOffenceHandler; use sp_std::vec::Vec; #[cfg(feature = "try-runtime")] use frame_support::ensure; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; mod v0 { use super::*; #[storage_alias] pub type ReportsByKindIndex = StorageMap< Pallet, Twox64Concat, Kind, Vec, // (O::TimeSlot, ReportIdOf) ValueQuery, >; } pub mod v1 { use frame_support::traits::StorageVersion; use super::*; pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { log::info!( target: LOG_TARGET, "Number of reports to refund and delete: {}", v0::ReportsByKindIndex::::iter_keys().count() ); Ok(Vec::new()) } fn on_runtime_upgrade() -> Weight { if Pallet::::on_chain_storage_version() > 0 { log::info!(target: LOG_TARGET, "pallet_offences::MigrateToV1 should be removed"); return T::DbWeight::get().reads(1) } let keys_removed = v0::ReportsByKindIndex::::clear(u32::MAX, None).unique as u64; StorageVersion::new(1).put::>(); // + 1 for reading/writing the new storage version T::DbWeight::get().reads_writes(keys_removed + 1, keys_removed + 1) } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { let onchain = Pallet::::on_chain_storage_version(); ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run"); ensure!( v0::ReportsByKindIndex::::iter_keys().count() == 0, "there are some dangling reports that need to be destroyed and refunded" ); Ok(()) } } } /// Type of data stored as a deferred offence type DeferredOffenceOf = ( Vec::AccountId, ::IdentificationTuple>>, Vec, SessionIndex, ); // Deferred reports that have been rejected by the offence handler and need to be submitted // at a later time. #[storage_alias] type DeferredOffences = StorageValue, Vec>, ValueQuery>; pub fn remove_deferred_storage() -> Weight { let mut weight = T::DbWeight::get().reads_writes(1, 1); let deferred = >::take(); log::info!(target: LOG_TARGET, "have {} deferred offences, applying.", deferred.len()); for (offences, perbill, session) in deferred.iter() { let consumed = T::OnOffenceHandler::on_offence(offences, perbill, *session); weight = weight.saturating_add(consumed); } weight } #[cfg(test)] mod test { use super::*; use crate::mock::{new_test_ext, with_on_offence_fractions, Runtime as T, KIND}; use codec::Encode; use sp_runtime::Perbill; use sp_staking::offence::OffenceDetails; #[test] fn migration_to_v1_works() { let mut ext = new_test_ext(); ext.execute_with(|| { >::insert(KIND, 2u32.encode()); assert!(>::iter_values().count() > 0); }); ext.commit_all().unwrap(); ext.execute_with(|| { assert_eq!( v1::MigrateToV1::::on_runtime_upgrade(), ::DbWeight::get().reads_writes(2, 2), ); assert!(>::iter_values().count() == 0); }) } #[test] fn should_resubmit_deferred_offences() { new_test_ext().execute_with(|| { // given assert_eq!(>::get().len(), 0); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![]); }); let offence_details = OffenceDetails::< ::AccountId, ::IdentificationTuple, > { offender: 5, reporters: vec![], }; // push deferred offence >::append(( vec![offence_details], vec![Perbill::from_percent(5 + 1 * 100 / 5)], 1, )); // when assert_eq!( remove_deferred_storage::(), ::DbWeight::get().reads_writes(1, 1), ); // then assert!(!>::exists()); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(5 + 1 * 100 / 5)]); }); }) } }