referrerpolicy=no-referrer-when-downgrade

pallet_collective/
benchmarking.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
18//! Staking pallet benchmarking.
19
20use super::*;
21use crate::Pallet as Collective;
22
23use core::mem::size_of;
24use sp_runtime::traits::Bounded;
25
26use frame_benchmarking::{
27	v1::{account, whitelisted_caller},
28	v2::*,
29};
30use frame_system::{
31	pallet_prelude::BlockNumberFor, Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin,
32};
33
34const SEED: u32 = 0;
35
36const MAX_BYTES: u32 = 1_024;
37
38fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
39	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
40}
41
42fn assert_has_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
43	frame_system::Pallet::<T>::assert_has_event(generic_event.into());
44}
45
46fn id_to_remark_data(id: u32, length: usize) -> Vec<u8> {
47	id.to_le_bytes().into_iter().cycle().take(length).collect()
48}
49
50#[instance_benchmarks(where T: Config<I>, I: 'static)]
51mod benchmarks {
52	use super::*;
53
54	#[benchmark]
55	fn set_members(
56		m: Linear<0, { T::MaxMembers::get() }>,
57		n: Linear<0, { T::MaxMembers::get() }>,
58		p: Linear<0, { T::MaxProposals::get() }>,
59	) -> Result<(), BenchmarkError> {
60		// Set old members.
61		// We compute the difference of old and new members, so it should influence timing.
62		let mut old_members = vec![];
63		for i in 0..m {
64			let old_member = account::<T::AccountId>("old member", i, SEED);
65			old_members.push(old_member);
66		}
67
68		Collective::<T, I>::set_members(
69			SystemOrigin::Root.into(),
70			old_members.clone(),
71			old_members.last().cloned(),
72			T::MaxMembers::get(),
73		)?;
74
75		// If there were any old members generate a bunch of proposals.
76		if m > 0 {
77			let caller = old_members.last().unwrap().clone();
78			// Set a high threshold for proposals passing so that they stay around.
79			let threshold = m.max(2);
80			// Length of the proposals should be irrelevant to `set_members`.
81			let length = 100;
82			for i in 0..p {
83				T::Consideration::ensure_successful(&caller, i);
84				// Proposals should be different so that different proposal hashes are generated
85				let proposal: T::Proposal =
86					SystemCall::<T>::remark { remark: id_to_remark_data(i, length) }.into();
87				Collective::<T, I>::propose(
88					SystemOrigin::Signed(caller.clone()).into(),
89					threshold,
90					Box::new(proposal.clone()),
91					MAX_BYTES,
92				)?;
93				let hash = T::Hashing::hash_of(&proposal);
94				// Vote on the proposal to increase state relevant for `set_members`.
95				// Not voting for last old member because they proposed and not voting for the first
96				// member to keep the proposal from passing.
97				for j in 2..m - 1 {
98					let voter = &old_members[j as usize];
99					let approve = true;
100					Collective::<T, I>::vote(
101						SystemOrigin::Signed(voter.clone()).into(),
102						hash,
103						i,
104						approve,
105					)?;
106				}
107			}
108		}
109
110		// Construct `new_members`.
111		// It should influence timing since it will sort this vector.
112		let mut new_members = vec![];
113		for i in 0..n {
114			let member = account::<T::AccountId>("member", i, SEED);
115			new_members.push(member);
116		}
117		#[extrinsic_call]
118		_(
119			SystemOrigin::Root,
120			new_members.clone(),
121			new_members.last().cloned(),
122			T::MaxMembers::get(),
123		);
124
125		new_members.sort();
126		assert_eq!(Members::<T, I>::get(), new_members);
127		Ok(())
128	}
129
130	#[benchmark]
131	fn execute(
132		b: Linear<2, MAX_BYTES>,
133		m: Linear<1, { T::MaxMembers::get() }>,
134	) -> Result<(), BenchmarkError> {
135		let bytes_in_storage = b + size_of::<u32>() as u32;
136
137		// Construct `members`.
138		let mut members = vec![];
139		for i in 0..m - 1 {
140			let member = account::<T::AccountId>("member", i, SEED);
141			members.push(member);
142		}
143
144		let caller: T::AccountId = whitelisted_caller();
145		members.push(caller.clone());
146
147		Collective::<T, I>::set_members(
148			SystemOrigin::Root.into(),
149			members,
150			None,
151			T::MaxMembers::get(),
152		)?;
153
154		let proposal: T::Proposal =
155			SystemCall::<T>::remark { remark: id_to_remark_data(1, b as usize) }.into();
156
157		#[extrinsic_call]
158		_(SystemOrigin::Signed(caller), Box::new(proposal.clone()), bytes_in_storage);
159
160		let proposal_hash = T::Hashing::hash_of(&proposal);
161		// Note that execution fails due to mis-matched origin
162		assert_last_event::<T, I>(Event::MemberExecuted { proposal_hash, result: Ok(()) }.into());
163		Ok(())
164	}
165
166	// This tests when execution would happen immediately after proposal
167	#[benchmark]
168	fn propose_execute(
169		b: Linear<2, MAX_BYTES>,
170		m: Linear<1, { T::MaxMembers::get() }>,
171	) -> Result<(), BenchmarkError> {
172		let bytes_in_storage = b + size_of::<u32>() as u32;
173
174		// Construct `members`.
175		let mut members = vec![];
176		for i in 0..m - 1 {
177			let member = account::<T::AccountId>("member", i, SEED);
178			members.push(member);
179		}
180
181		let caller: T::AccountId = whitelisted_caller();
182		members.push(caller.clone());
183
184		Collective::<T, I>::set_members(
185			SystemOrigin::Root.into(),
186			members,
187			None,
188			T::MaxMembers::get(),
189		)?;
190
191		let proposal: T::Proposal =
192			SystemCall::<T>::remark { remark: id_to_remark_data(1, b as usize) }.into();
193		let threshold = 1;
194
195		#[extrinsic_call]
196		propose(
197			SystemOrigin::Signed(caller),
198			threshold,
199			Box::new(proposal.clone()),
200			bytes_in_storage,
201		);
202
203		let proposal_hash = T::Hashing::hash_of(&proposal);
204		// Note that execution fails due to mis-matched origin
205		assert_last_event::<T, I>(Event::Executed { proposal_hash, result: Ok(()) }.into());
206		Ok(())
207	}
208
209	// This tests when proposal is created and queued as "proposed"
210	#[benchmark]
211	fn propose_proposed(
212		b: Linear<2, MAX_BYTES>,
213		m: Linear<2, { T::MaxMembers::get() }>,
214		p: Linear<1, { T::MaxProposals::get() }>,
215	) -> Result<(), BenchmarkError> {
216		let bytes_in_storage = b + size_of::<u32>() as u32;
217
218		// Construct `members`.
219		let mut members = vec![];
220		for i in 0..m - 1 {
221			let member = account::<T::AccountId>("member", i, SEED);
222			members.push(member);
223		}
224		let caller: T::AccountId = whitelisted_caller();
225		members.push(caller.clone());
226		Collective::<T, I>::set_members(
227			SystemOrigin::Root.into(),
228			members,
229			None,
230			T::MaxMembers::get(),
231		)?;
232
233		let threshold = m;
234		// Add previous proposals.
235		for i in 0..p - 1 {
236			T::Consideration::ensure_successful(&caller, i);
237			// Proposals should be different so that different proposal hashes are generated
238			let proposal: T::Proposal =
239				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
240			Collective::<T, I>::propose(
241				SystemOrigin::Signed(caller.clone()).into(),
242				threshold,
243				Box::new(proposal),
244				bytes_in_storage,
245			)?;
246		}
247
248		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
249
250		T::Consideration::ensure_successful(&caller, p);
251
252		let proposal: T::Proposal =
253			SystemCall::<T>::remark { remark: id_to_remark_data(p, b as usize) }.into();
254		#[extrinsic_call]
255		propose(
256			SystemOrigin::Signed(caller.clone()),
257			threshold,
258			Box::new(proposal.clone()),
259			bytes_in_storage,
260		);
261
262		// New proposal is recorded
263		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
264		let proposal_hash = T::Hashing::hash_of(&proposal);
265		assert_last_event::<T, I>(
266			Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold }
267				.into(),
268		);
269		Ok(())
270	}
271
272	#[benchmark]
273	// We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
274	fn vote(m: Linear<5, { T::MaxMembers::get() }>) -> Result<(), BenchmarkError> {
275		let p = T::MaxProposals::get();
276		let b = MAX_BYTES;
277		let bytes_in_storage = b + size_of::<u32>() as u32;
278
279		// Construct `members`.
280		let mut members = vec![];
281		let proposer: T::AccountId = account::<T::AccountId>("proposer", 0, SEED);
282		members.push(proposer.clone());
283		for i in 1..m - 1 {
284			let member = account::<T::AccountId>("member", i, SEED);
285			members.push(member);
286		}
287		let voter: T::AccountId = account::<T::AccountId>("voter", 0, SEED);
288		members.push(voter.clone());
289		Collective::<T, I>::set_members(
290			SystemOrigin::Root.into(),
291			members.clone(),
292			None,
293			T::MaxMembers::get(),
294		)?;
295
296		// Threshold is 1 less than the number of members so that one person can vote nay
297		let threshold = m - 1;
298
299		// Add previous proposals
300		let mut last_hash = T::Hash::default();
301		for i in 0..p {
302			T::Consideration::ensure_successful(&proposer, i);
303			// Proposals should be different so that different proposal hashes are generated
304			let proposal: T::Proposal =
305				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
306			Collective::<T, I>::propose(
307				SystemOrigin::Signed(proposer.clone()).into(),
308				threshold,
309				Box::new(proposal.clone()),
310				bytes_in_storage,
311			)?;
312			last_hash = T::Hashing::hash_of(&proposal);
313		}
314
315		let index = p - 1;
316		// Have almost everyone vote aye on last proposal, while keeping it from passing.
317		for j in 0..m - 3 {
318			let voter = &members[j as usize];
319			let approve = true;
320			Collective::<T, I>::vote(
321				SystemOrigin::Signed(voter.clone()).into(),
322				last_hash,
323				index,
324				approve,
325			)?;
326		}
327		// Voter votes aye without resolving the vote.
328		let approve = true;
329		Collective::<T, I>::vote(
330			SystemOrigin::Signed(voter.clone()).into(),
331			last_hash,
332			index,
333			approve,
334		)?;
335
336		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
337
338		// Voter switches vote to nay, but does not kill the vote, just updates + inserts
339		let approve = false;
340
341		// Whitelist voter account from further DB operations.
342		let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
343		frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
344
345		#[extrinsic_call]
346		_(SystemOrigin::Signed(voter), last_hash, index, approve);
347
348		// All proposals exist and the last proposal has just been updated.
349		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
350		let voting = Voting::<T, I>::get(&last_hash).ok_or("Proposal Missing")?;
351		assert_eq!(voting.ayes.len(), (m - 3) as usize);
352		assert_eq!(voting.nays.len(), 1);
353		Ok(())
354	}
355
356	// We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
357	#[benchmark]
358	fn close_early_disapproved(
359		m: Linear<4, { T::MaxMembers::get() }>,
360		p: Linear<1, { T::MaxProposals::get() }>,
361	) -> Result<(), BenchmarkError> {
362		let bytes = 100;
363		let bytes_in_storage = bytes + size_of::<u32>() as u32;
364
365		// Construct `members`.
366		let mut members = vec![];
367		let proposer = account::<T::AccountId>("proposer", 0, SEED);
368		members.push(proposer.clone());
369		for i in 1..m - 1 {
370			let member = account::<T::AccountId>("member", i, SEED);
371			members.push(member);
372		}
373		let voter = account::<T::AccountId>("voter", 0, SEED);
374		members.push(voter.clone());
375		Collective::<T, I>::set_members(
376			SystemOrigin::Root.into(),
377			members.clone(),
378			None,
379			T::MaxMembers::get(),
380		)?;
381
382		// Threshold is total members so that one nay will disapprove the vote
383		let threshold = m;
384
385		// Add previous proposals
386		let mut last_hash = T::Hash::default();
387		for i in 0..p {
388			T::Consideration::ensure_successful(&proposer, i);
389			// Proposals should be different so that different proposal hashes are generated
390			let proposal: T::Proposal =
391				SystemCall::<T>::remark { remark: id_to_remark_data(i, bytes as usize) }.into();
392			Collective::<T, I>::propose(
393				SystemOrigin::Signed(proposer.clone()).into(),
394				threshold,
395				Box::new(proposal.clone()),
396				bytes_in_storage,
397			)?;
398			last_hash = T::Hashing::hash_of(&proposal);
399		}
400
401		let index = p - 1;
402		// Have most everyone vote aye on last proposal, while keeping it from passing.
403		for j in 0..m - 2 {
404			let voter = &members[j as usize];
405			let approve = true;
406			Collective::<T, I>::vote(
407				SystemOrigin::Signed(voter.clone()).into(),
408				last_hash,
409				index,
410				approve,
411			)?;
412		}
413		// Voter votes aye without resolving the vote.
414		let approve = true;
415		Collective::<T, I>::vote(
416			SystemOrigin::Signed(voter.clone()).into(),
417			last_hash,
418			index,
419			approve,
420		)?;
421
422		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
423
424		// Voter switches vote to nay, which kills the vote
425		let approve = false;
426		Collective::<T, I>::vote(
427			SystemOrigin::Signed(voter.clone()).into(),
428			last_hash,
429			index,
430			approve,
431		)?;
432
433		// Whitelist voter account from further DB operations.
434		let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
435		frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
436
437		#[extrinsic_call]
438		close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage);
439
440		// The last proposal is removed.
441		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
442		assert_last_event::<T, I>(Event::Disapproved { proposal_hash: last_hash }.into());
443		Ok(())
444	}
445
446	// m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
447	#[benchmark]
448	fn close_early_approved(
449		b: Linear<2, MAX_BYTES>,
450		m: Linear<4, { T::MaxMembers::get() }>,
451		p: Linear<1, { T::MaxProposals::get() }>,
452	) -> Result<(), BenchmarkError> {
453		let bytes_in_storage = b + size_of::<u32>() as u32;
454
455		// Construct `members`.
456		let mut members = vec![];
457		for i in 0..m - 1 {
458			let member = account::<T::AccountId>("member", i, SEED);
459			members.push(member);
460		}
461		let caller: T::AccountId = whitelisted_caller();
462		members.push(caller.clone());
463		Collective::<T, I>::set_members(
464			SystemOrigin::Root.into(),
465			members.clone(),
466			None,
467			T::MaxMembers::get(),
468		)?;
469
470		// Threshold is 2 so any two ayes will approve the vote
471		let threshold = 2;
472
473		// Add previous proposals
474		let mut last_hash = T::Hash::default();
475		for i in 0..p {
476			T::Consideration::ensure_successful(&caller, i);
477			// Proposals should be different so that different proposal hashes are generated
478			let proposal: T::Proposal =
479				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
480			Collective::<T, I>::propose(
481				SystemOrigin::Signed(caller.clone()).into(),
482				threshold,
483				Box::new(proposal.clone()),
484				bytes_in_storage,
485			)?;
486			last_hash = T::Hashing::hash_of(&proposal);
487		}
488
489		// Caller switches vote to nay on their own proposal, allowing them to be the deciding
490		// approval vote
491		Collective::<T, I>::vote(
492			SystemOrigin::Signed(caller.clone()).into(),
493			last_hash,
494			p - 1,
495			false,
496		)?;
497
498		// Have almost everyone vote nay on last proposal, while keeping it from failing.
499		for j in 2..m - 1 {
500			let voter = &members[j as usize];
501			let approve = false;
502			Collective::<T, I>::vote(
503				SystemOrigin::Signed(voter.clone()).into(),
504				last_hash,
505				p - 1,
506				approve,
507			)?;
508		}
509
510		// Member zero is the first aye
511		Collective::<T, I>::vote(
512			SystemOrigin::Signed(members[0].clone()).into(),
513			last_hash,
514			p - 1,
515			true,
516		)?;
517
518		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
519
520		// Caller switches vote to aye, which passes the vote
521		let index = p - 1;
522		let approve = true;
523		Collective::<T, I>::vote(
524			SystemOrigin::Signed(caller.clone()).into(),
525			last_hash,
526			index,
527			approve,
528		)?;
529
530		#[extrinsic_call]
531		close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage);
532
533		// The last proposal is removed.
534		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
535		assert_last_event::<T, I>(
536			Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(),
537		);
538		Ok(())
539	}
540
541	// m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
542	#[benchmark]
543	fn close_disapproved(
544		m: Linear<4, { T::MaxMembers::get() }>,
545		p: Linear<1, { T::MaxProposals::get() }>,
546	) -> Result<(), BenchmarkError> {
547		let bytes = 100;
548		let bytes_in_storage = bytes + size_of::<u32>() as u32;
549
550		// Construct `members`.
551		let mut members = vec![];
552		for i in 0..m - 1 {
553			let member = account::<T::AccountId>("member", i, SEED);
554			members.push(member);
555		}
556		let caller: T::AccountId = whitelisted_caller();
557		members.push(caller.clone());
558		Collective::<T, I>::set_members(
559			SystemOrigin::Root.into(),
560			members.clone(),
561			Some(caller.clone()),
562			T::MaxMembers::get(),
563		)?;
564
565		// Threshold is one less than total members so that two nays will disapprove the vote
566		let threshold = m - 1;
567
568		// Add proposals
569		let mut last_hash = T::Hash::default();
570		for i in 0..p {
571			T::Consideration::ensure_successful(&caller, i);
572			// Proposals should be different so that different proposal hashes are generated
573			let proposal: T::Proposal =
574				SystemCall::<T>::remark { remark: id_to_remark_data(i, bytes as usize) }.into();
575			Collective::<T, I>::propose(
576				SystemOrigin::Signed(caller.clone()).into(),
577				threshold,
578				Box::new(proposal.clone()),
579				bytes_in_storage,
580			)?;
581			last_hash = T::Hashing::hash_of(&proposal);
582		}
583
584		let index = p - 1;
585		// Have almost everyone vote aye on last proposal, while keeping it from passing.
586		// A few abstainers will be the nay votes needed to fail the vote.
587		let mut yes_votes: MemberCount = 0;
588		for j in 2..m - 1 {
589			let voter = &members[j as usize];
590			let approve = true;
591			yes_votes += 1;
592			// vote aye till a prime nay vote keeps the proposal disapproved.
593			if <<T as Config<I>>::DefaultVote as DefaultVote>::default_vote(
594				Some(false),
595				yes_votes,
596				0,
597				m,
598			) {
599				break;
600			}
601			Collective::<T, I>::vote(
602				SystemOrigin::Signed(voter.clone()).into(),
603				last_hash,
604				index,
605				approve,
606			)?;
607		}
608
609		// caller is prime, prime votes nay
610		Collective::<T, I>::vote(
611			SystemOrigin::Signed(caller.clone()).into(),
612			last_hash,
613			index,
614			false,
615		)?;
616
617		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
618		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
619
620		// Prime nay will close it as disapproved
621		#[extrinsic_call]
622		close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage);
623
624		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
625		assert_last_event::<T, I>(Event::Disapproved { proposal_hash: last_hash }.into());
626		Ok(())
627	}
628
629	// m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
630	#[benchmark]
631	fn close_approved(
632		b: Linear<2, MAX_BYTES>,
633		m: Linear<4, { T::MaxMembers::get() }>,
634		p: Linear<1, { T::MaxProposals::get() }>,
635	) -> Result<(), BenchmarkError> {
636		let bytes_in_storage = b + size_of::<u32>() as u32;
637
638		// Construct `members`.
639		let mut members = vec![];
640		for i in 0..m - 1 {
641			let member = account::<T::AccountId>("member", i, SEED);
642			members.push(member);
643		}
644		let caller: T::AccountId = whitelisted_caller();
645		members.push(caller.clone());
646		Collective::<T, I>::set_members(
647			SystemOrigin::Root.into(),
648			members.clone(),
649			Some(caller.clone()),
650			T::MaxMembers::get(),
651		)?;
652
653		// Threshold is two, so any two ayes will pass the vote
654		let threshold = 2;
655
656		// Add proposals
657		let mut last_hash = T::Hash::default();
658		for i in 0..p {
659			T::Consideration::ensure_successful(&caller, i);
660			// Proposals should be different so that different proposal hashes are generated
661			let proposal: T::Proposal =
662				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
663			Collective::<T, I>::propose(
664				SystemOrigin::Signed(caller.clone()).into(),
665				threshold,
666				Box::new(proposal.clone()),
667				bytes_in_storage,
668			)?;
669			last_hash = T::Hashing::hash_of(&proposal);
670		}
671
672		// The prime member votes aye, so abstentions default to aye.
673		Collective::<T, _>::vote(
674			SystemOrigin::Signed(caller.clone()).into(),
675			last_hash,
676			p - 1,
677			true, // Vote aye.
678		)?;
679
680		// Have almost everyone vote nay on last proposal, while keeping it from failing.
681		// A few abstainers will be the aye votes needed to pass the vote.
682		for j in 2..m - 1 {
683			let voter = &members[j as usize];
684			let approve = false;
685			Collective::<T, I>::vote(
686				SystemOrigin::Signed(voter.clone()).into(),
687				last_hash,
688				p - 1,
689				approve,
690			)?;
691		}
692
693		// caller is prime, prime already votes aye by creating the proposal
694		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
695		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
696
697		// Prime aye will close it as approved
698		#[extrinsic_call]
699		close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage);
700
701		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
702		assert_last_event::<T, I>(
703			Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(),
704		);
705		Ok(())
706	}
707
708	#[benchmark]
709	fn disapprove_proposal(p: Linear<1, { T::MaxProposals::get() }>) -> Result<(), BenchmarkError> {
710		let m = 3;
711		let b = MAX_BYTES;
712		let bytes_in_storage = b + size_of::<u32>() as u32;
713
714		// Construct `members`.
715		let mut members = vec![];
716		for i in 0..m - 1 {
717			let member = account::<T::AccountId>("member", i, SEED);
718			members.push(member);
719		}
720		let caller = account::<T::AccountId>("caller", 0, SEED);
721		members.push(caller.clone());
722		Collective::<T, I>::set_members(
723			SystemOrigin::Root.into(),
724			members.clone(),
725			Some(caller.clone()),
726			T::MaxMembers::get(),
727		)?;
728
729		// Threshold is one less than total members so that two nays will disapprove the vote
730		let threshold = m - 1;
731
732		// Add proposals
733		let mut last_hash = T::Hash::default();
734		for i in 0..p {
735			T::Consideration::ensure_successful(&caller, i);
736			// Proposals should be different so that different proposal hashes are generated
737			let proposal: T::Proposal =
738				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
739			Collective::<T, I>::propose(
740				SystemOrigin::Signed(caller.clone()).into(),
741				threshold,
742				Box::new(proposal.clone()),
743				bytes_in_storage,
744			)?;
745			last_hash = T::Hashing::hash_of(&proposal);
746		}
747
748		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
749		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
750
751		let origin =
752			T::DisapproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
753
754		#[extrinsic_call]
755		_(origin as <T as frame_system::Config>::RuntimeOrigin, last_hash);
756
757		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
758		assert_last_event::<T, I>(Event::Disapproved { proposal_hash: last_hash }.into());
759		Ok(())
760	}
761
762	// d: `0` - if deposit is not present and `1` otherwise.
763	#[benchmark]
764	fn kill(
765		d: Linear<0, 1>,
766		p: Linear<1, { T::MaxProposals::get() }>,
767	) -> Result<(), BenchmarkError> {
768		let m = 3;
769		let b = MAX_BYTES;
770		let bytes_in_storage = b + size_of::<u32>() as u32;
771
772		// Construct `members`.
773		let mut members = vec![];
774		for i in 0..m - 1 {
775			let member = account::<T::AccountId>("member", i, SEED);
776			members.push(member);
777		}
778		let caller = account::<T::AccountId>("caller", 0, SEED);
779		members.push(caller.clone());
780		Collective::<T, I>::set_members(
781			SystemOrigin::Root.into(),
782			members.clone(),
783			Some(caller.clone()),
784			T::MaxMembers::get(),
785		)?;
786
787		// Threshold is one less than total members so that two nays will disapprove the vote
788		let threshold = m - 1;
789
790		// Add proposals
791		let mut last_hash = T::Hash::default();
792		for i in 0..p {
793			T::Consideration::ensure_successful(&caller, i);
794
795			// Proposals should be different so that different proposal hashes are generated
796			let proposal: T::Proposal =
797				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
798			Collective::<T, I>::propose(
799				SystemOrigin::Signed(caller.clone()).into(),
800				threshold,
801				Box::new(proposal.clone()),
802				bytes_in_storage,
803			)?;
804			last_hash = T::Hashing::hash_of(&proposal);
805		}
806
807		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
808		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
809
810		if d == 0 {
811			CostOf::<T, I>::remove(last_hash);
812		}
813		let cost_present = CostOf::<T, I>::get(last_hash).is_some();
814
815		let origin =
816			T::KillOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
817
818		#[extrinsic_call]
819		_(origin as <T as frame_system::Config>::RuntimeOrigin, last_hash);
820
821		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
822		assert_last_event::<T, I>(Event::Killed { proposal_hash: last_hash }.into());
823		if cost_present {
824			assert_has_event::<T, I>(
825				Event::ProposalCostBurned { proposal_hash: last_hash, who: caller }.into(),
826			);
827		}
828		Ok(())
829	}
830
831	#[benchmark]
832	fn release_proposal_cost() -> Result<(), BenchmarkError> {
833		let m = 3;
834		let p = T::MaxProposals::get();
835		let b = MAX_BYTES;
836		let bytes_in_storage = b + size_of::<u32>() as u32;
837
838		// Construct `members`.
839		let mut members = vec![];
840		for i in 0..m - 1 {
841			let member = account::<T::AccountId>("member", i, SEED);
842			members.push(member);
843		}
844		let caller = account::<T::AccountId>("caller", 0, SEED);
845		members.push(caller.clone());
846		Collective::<T, I>::set_members(
847			SystemOrigin::Root.into(),
848			members.clone(),
849			Some(caller.clone()),
850			T::MaxMembers::get(),
851		)?;
852
853		// Add proposals
854		let threshold = 2;
855		let mut last_hash = T::Hash::default();
856		for i in 0..p {
857			T::Consideration::ensure_successful(&caller, i);
858
859			// Proposals should be different so that different proposal hashes are generated
860			let proposal: T::Proposal =
861				SystemCall::<T>::remark { remark: id_to_remark_data(i, b as usize) }.into();
862			Collective::<T, I>::propose(
863				SystemOrigin::Signed(caller.clone()).into(),
864				threshold,
865				Box::new(proposal.clone()),
866				bytes_in_storage,
867			)?;
868			last_hash = T::Hashing::hash_of(&proposal);
869		}
870
871		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
872		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
873
874		assert_eq!(Proposals::<T, I>::get().len(), p as usize);
875		let _ = Collective::<T, I>::remove_proposal(last_hash);
876		assert_eq!(Proposals::<T, I>::get().len(), (p - 1) as usize);
877
878		let cost_present = CostOf::<T, I>::get(last_hash).is_some();
879
880		#[extrinsic_call]
881		_(SystemOrigin::Signed(caller.clone()), last_hash);
882
883		assert_eq!(CostOf::<T, I>::get(last_hash), None);
884		if cost_present {
885			assert_last_event::<T, I>(
886				Event::ProposalCostReleased { proposal_hash: last_hash, who: caller }.into(),
887			);
888		}
889		Ok(())
890	}
891
892	impl_benchmark_test_suite!(
893		Collective,
894		crate::tests::ExtBuilder::default().build(),
895		crate::tests::Test
896	);
897}