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 codec::Decode;
21use frame_benchmarking::v2::*;
22use frame_support::traits::{OnFinalize, OnInitialize};
23use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
24use pallet_staking::testing_utils::create_validators;
25use polkadot_primitives::{Hash, PARACHAIN_KEY_TYPE_ID};
26use sp_runtime::traits::{One, OpaqueKeys, StaticLookup};
27use sp_session::MembershipProof;
28
29// Candidate hash of the disputed candidate.
30const CANDIDATE_HASH: CandidateHash = CandidateHash(Hash::zero());
31
32// Simplify getting the value in the benchmark
33pub const fn max_validators_for<T: super::Config>() -> u32 {
34	<<T>::BenchmarkingConfig as BenchmarkingConfiguration>::MAX_VALIDATORS
35}
36
37pub trait Config:
38	pallet_session::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 (n, who) in create_validators::<T>(n, balance_factor).unwrap().into_iter().enumerate() {
56		use rand::{RngCore, SeedableRng};
57
58		let validator = T::Lookup::lookup(who).unwrap();
59		let controller = pallet_staking::Pallet::<T>::bonded(&validator).unwrap();
60
61		let keys = {
62			const SESSION_KEY_LEN: usize = 32;
63			let key_ids = T::Keys::key_ids();
64			let mut keys_len = key_ids.len() * SESSION_KEY_LEN;
65			if key_ids.contains(&sp_core::crypto::key_types::BEEFY) {
66				// BEEFY key is 33 bytes long, not 32.
67				keys_len += 1;
68			}
69			let mut keys = vec![0u8; keys_len];
70			let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(n as u64);
71			rng.fill_bytes(&mut keys);
72			keys
73		};
74
75		let keys: T::Keys = Decode::decode(&mut &keys[..]).expect("wrong number of session keys?");
76		let proof: Vec<u8> = vec![];
77
78		whitelist_account!(controller);
79		pallet_session::Pallet::<T>::ensure_can_pay_key_deposit(&controller).unwrap();
80		pallet_session::Pallet::<T>::set_keys(RawOrigin::Signed(controller).into(), keys, proof)
81			.expect("session::set_keys should work");
82	}
83
84	pallet_session::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
85	initializer::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
86
87	// signal to `pallet-staking`'s `ElectionProvider` to be ready asap.
88	use frame_election_provider_support::ElectionProvider;
89	<<T as pallet_staking::Config>::ElectionProvider as ElectionProvider>::asap();
90
91	// skip sessions until the new validator set is enacted
92	while pallet_session::Pallet::<T>::validators().len() < n as usize {
93		pallet_session::Pallet::<T>::rotate_session();
94	}
95	initializer::Pallet::<T>::on_finalize(BlockNumberFor::<T>::one());
96
97	let session_index = crate::shared::CurrentSessionIndex::<T>::get();
98	let session_info = crate::session_info::Sessions::<T>::get(session_index);
99	let session_info = session_info.unwrap();
100	let validator_id = session_info.validators.get(ValidatorIndex::from(0)).unwrap().clone();
101	let key = (PARACHAIN_KEY_TYPE_ID, validator_id.clone());
102	let key_owner_proof = pallet_session::historical::Pallet::<T>::prove(key).unwrap();
103
104	// rotate a session to make sure `key_owner_proof` is historical
105	initializer::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
106	pallet_session::Pallet::<T>::rotate_session();
107	initializer::Pallet::<T>::on_finalize(BlockNumberFor::<T>::one());
108
109	let idx = crate::shared::CurrentSessionIndex::<T>::get();
110	assert!(
111		idx > session_index,
112		"session rotation should work for parachain pallets: {} <= {}",
113		idx,
114		session_index,
115	);
116
117	(session_index, key_owner_proof, validator_id)
118}
119
120/// Submits a single `ForInvalid` dispute.
121fn setup_dispute<T>(session_index: SessionIndex, validator_id: ValidatorId) -> DisputeProof
122where
123	T: Config,
124{
125	let current_session = T::ValidatorSet::session_index();
126	assert_ne!(session_index, current_session);
127
128	let validator_index = ValidatorIndex(0);
129	let losers = [validator_index].into_iter();
130	let backers = losers.clone();
131
132	T::SlashingHandler::punish_for_invalid(session_index, CANDIDATE_HASH, losers, backers);
133
134	let unapplied = <UnappliedSlashes<T>>::get(session_index, CANDIDATE_HASH);
135	assert_eq!(unapplied.unwrap().keys.len(), 1);
136
137	dispute_proof(session_index, validator_id, validator_index)
138}
139
140/// Creates a `ForInvalid` dispute proof.
141fn dispute_proof(
142	session_index: SessionIndex,
143	validator_id: ValidatorId,
144	validator_index: ValidatorIndex,
145) -> DisputeProof {
146	let kind = DisputeOffenceKind::ForInvalidBacked;
147	let time_slot = DisputesTimeSlot::new(session_index, CANDIDATE_HASH);
148
149	DisputeProof { time_slot, kind, validator_index, validator_id }
150}
151
152#[benchmarks(where T: Config<KeyOwnerProof = MembershipProof>)]
153mod benchmarks {
154	use super::*;
155
156	#[benchmark]
157	fn report_dispute_lost_unsigned(n: Linear<4, { max_validators_for::<T>() }>) {
158		let (session_index, key_owner_proof, validator_id) = setup_validator_set::<T>(n);
159
160		// submit a single `ForInvalid` dispute for a past session.
161		let dispute_proof = setup_dispute::<T>(session_index, validator_id);
162
163		#[extrinsic_call]
164		_(RawOrigin::None, Box::new(dispute_proof), key_owner_proof);
165
166		let unapplied = <UnappliedSlashes<T>>::get(session_index, CANDIDATE_HASH);
167		assert!(unapplied.is_none());
168	}
169}