referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/disputes/slashing/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use super::*;
18
19use crate::{disputes::SlashingHandler, initializer, shared};
20use frame_benchmarking::v2::*;
21use frame_support::traits::{OnFinalize, OnInitialize};
22use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
23use pallet_staking::testing_utils::create_validators;
24use polkadot_primitives::{Hash, PARACHAIN_KEY_TYPE_ID};
25use sp_runtime::traits::{One, StaticLookup};
26use sp_session::MembershipProof;
27
28// Candidate hash of the disputed candidate.
29const CANDIDATE_HASH: CandidateHash = CandidateHash(Hash::zero());
30
31// Simplify getting the value in the benchmark
32pub const fn max_validators_for<T: super::Config>() -> u32 {
33	<<T>::BenchmarkingConfig as BenchmarkingConfiguration>::MAX_VALIDATORS
34}
35
36pub trait Config:
37	pallet_session::Config
38	+ pallet_session_benchmarking::Config
39	+ pallet_session::historical::Config
40	+ pallet_staking::Config
41	+ super::Config
42	+ shared::Config
43	+ initializer::Config
44{
45}
46
47fn setup_validator_set<T>(n: u32) -> (SessionIndex, MembershipProof, ValidatorId)
48where
49	T: Config,
50{
51	pallet_staking::ValidatorCount::<T>::put(n);
52
53	let balance_factor = 1000;
54	// Create validators and set random session keys
55	for who in create_validators::<T>(n, balance_factor).unwrap().into_iter() {
56		let validator = T::Lookup::lookup(who).unwrap();
57		let controller = pallet_staking::Pallet::<T>::bonded(&validator).unwrap();
58
59		let (keys, proof) = T::generate_session_keys_and_proof(controller.clone());
60
61		whitelist_account!(controller);
62		pallet_session::Pallet::<T>::ensure_can_pay_key_deposit(&controller).unwrap();
63		pallet_session::Pallet::<T>::set_keys(RawOrigin::Signed(controller).into(), keys, proof)
64			.expect("session::set_keys should work");
65	}
66
67	pallet_session::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
68	initializer::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
69
70	// signal to `pallet-staking`'s `ElectionProvider` to be ready asap.
71	use frame_election_provider_support::ElectionProvider;
72	<<T as pallet_staking::Config>::ElectionProvider as ElectionProvider>::asap();
73
74	// skip sessions until the new validator set is enacted
75	while pallet_session::Pallet::<T>::validators().len() < n as usize {
76		pallet_session::Pallet::<T>::rotate_session();
77	}
78	initializer::Pallet::<T>::on_finalize(BlockNumberFor::<T>::one());
79
80	let session_index = crate::shared::CurrentSessionIndex::<T>::get();
81	let session_info = crate::session_info::Sessions::<T>::get(session_index);
82	let session_info = session_info.unwrap();
83	let validator_id = session_info.validators.get(ValidatorIndex::from(0)).unwrap().clone();
84	let key = (PARACHAIN_KEY_TYPE_ID, validator_id.clone());
85	let key_owner_proof = pallet_session::historical::Pallet::<T>::prove(key).unwrap();
86
87	// rotate a session to make sure `key_owner_proof` is historical
88	initializer::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
89	pallet_session::Pallet::<T>::rotate_session();
90	initializer::Pallet::<T>::on_finalize(BlockNumberFor::<T>::one());
91
92	let idx = crate::shared::CurrentSessionIndex::<T>::get();
93	assert!(
94		idx > session_index,
95		"session rotation should work for parachain pallets: {} <= {}",
96		idx,
97		session_index,
98	);
99
100	(session_index, key_owner_proof, validator_id)
101}
102
103/// Submits a single `ForInvalid` dispute.
104fn setup_dispute<T>(session_index: SessionIndex, validator_id: ValidatorId) -> DisputeProof
105where
106	T: Config,
107{
108	let current_session = T::ValidatorSet::session_index();
109	assert_ne!(session_index, current_session);
110
111	let validator_index = ValidatorIndex(0);
112	let losers = [validator_index].into_iter();
113	let backers = losers.clone();
114
115	T::SlashingHandler::punish_for_invalid(session_index, CANDIDATE_HASH, losers, backers);
116
117	let unapplied = <UnappliedSlashes<T>>::get(session_index, CANDIDATE_HASH);
118	assert_eq!(unapplied.unwrap().keys.len(), 1);
119
120	dispute_proof(session_index, validator_id, validator_index)
121}
122
123/// Creates a `ForInvalid` dispute proof.
124fn dispute_proof(
125	session_index: SessionIndex,
126	validator_id: ValidatorId,
127	validator_index: ValidatorIndex,
128) -> DisputeProof {
129	let kind = DisputeOffenceKind::ForInvalidBacked;
130	let time_slot = DisputesTimeSlot::new(session_index, CANDIDATE_HASH);
131
132	DisputeProof { time_slot, kind, validator_index, validator_id }
133}
134
135#[benchmarks(where T: Config<KeyOwnerProof = MembershipProof>)]
136mod benchmarks {
137	use super::*;
138
139	#[benchmark]
140	fn report_dispute_lost_unsigned(n: Linear<4, { max_validators_for::<T>() }>) {
141		let (session_index, key_owner_proof, validator_id) = setup_validator_set::<T>(n);
142
143		// submit a single `ForInvalid` dispute for a past session.
144		let dispute_proof = setup_dispute::<T>(session_index, validator_id);
145
146		#[extrinsic_call]
147		_(RawOrigin::None, Box::new(dispute_proof), key_owner_proof);
148
149		let unapplied = <UnappliedSlashes<T>>::get(session_index, CANDIDATE_HASH);
150		assert!(unapplied.is_none());
151	}
152}