referrerpolicy=no-referrer-when-downgrade

pallet_democracy/
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//! Democracy pallet benchmarking.
19
20#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23
24use frame_benchmarking::v2::*;
25use frame_support::{
26	assert_noop, assert_ok,
27	traits::{Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable},
28};
29use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
30use sp_runtime::{traits::Bounded, BoundedVec};
31
32use crate::Pallet as Democracy;
33
34const REFERENDUM_COUNT_HINT: u32 = 10;
35const SEED: u32 = 0;
36
37fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId {
38	let caller: T::AccountId = account(name, index, SEED);
39	// Give the account half of the maximum value of the `Balance` type.
40	// Otherwise some transfers will fail with an overflow error.
41	T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
42	caller
43}
44
45fn make_proposal<T: Config>(n: u32) -> BoundedCallOf<T> {
46	let call: CallOf<T> = frame_system::Call::remark { remark: n.encode() }.into();
47	<T as Config>::Preimages::bound(call).unwrap()
48}
49
50fn add_proposal<T: Config>(n: u32) -> Result<T::Hash, &'static str> {
51	let other = funded_account::<T>("proposer", n);
52	let value = T::MinimumDeposit::get();
53	let proposal = make_proposal::<T>(n);
54	Democracy::<T>::propose(RawOrigin::Signed(other).into(), proposal.clone(), value)?;
55	Ok(proposal.hash())
56}
57
58// add a referendum with a metadata.
59fn add_referendum<T: Config>(n: u32) -> (ReferendumIndex, T::Hash, T::Hash) {
60	let vote_threshold = VoteThreshold::SimpleMajority;
61	let proposal = make_proposal::<T>(n);
62	let hash = proposal.hash();
63	let index = Democracy::<T>::inject_referendum(
64		T::LaunchPeriod::get(),
65		proposal,
66		vote_threshold,
67		0u32.into(),
68	);
69	let preimage_hash = note_preimage::<T>();
70	MetadataOf::<T>::insert(crate::MetadataOwner::Referendum(index), preimage_hash);
71	(index, hash, preimage_hash)
72}
73
74fn account_vote<T: Config>(b: BalanceOf<T>) -> AccountVote<BalanceOf<T>> {
75	let v = Vote { aye: true, conviction: Conviction::Locked1x };
76
77	AccountVote::Standard { vote: v, balance: b }
78}
79
80fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
81	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
82}
83
84fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
85	frame_system::Pallet::<T>::assert_has_event(generic_event.into());
86}
87
88// note a new preimage.
89fn note_preimage<T: Config>() -> T::Hash {
90	use alloc::borrow::Cow;
91	use core::sync::atomic::{AtomicU8, Ordering};
92	// note a new preimage on every function invoke.
93	static COUNTER: AtomicU8 = AtomicU8::new(0);
94	let data = Cow::from(vec![COUNTER.fetch_add(1, Ordering::Relaxed)]);
95	let hash = <T as Config>::Preimages::note(data).unwrap();
96	hash
97}
98
99#[benchmarks]
100mod benchmarks {
101	use super::*;
102
103	#[benchmark]
104	fn propose() -> Result<(), BenchmarkError> {
105		let p = T::MaxProposals::get();
106
107		for i in 0..(p - 1) {
108			add_proposal::<T>(i)?;
109		}
110
111		let caller = funded_account::<T>("caller", 0);
112		let proposal = make_proposal::<T>(0);
113		let value = T::MinimumDeposit::get();
114		whitelist_account!(caller);
115
116		#[extrinsic_call]
117		_(RawOrigin::Signed(caller), proposal, value);
118
119		assert_eq!(PublicProps::<T>::get().len(), p as usize, "Proposals not created.");
120		Ok(())
121	}
122
123	#[benchmark]
124	fn second() -> Result<(), BenchmarkError> {
125		let caller = funded_account::<T>("caller", 0);
126		add_proposal::<T>(0)?;
127
128		// Create s existing "seconds"
129		// we must reserve one deposit for the `proposal` and one for our benchmarked `second` call.
130		for i in 0..T::MaxDeposits::get() - 2 {
131			let seconder = funded_account::<T>("seconder", i);
132			Democracy::<T>::second(RawOrigin::Signed(seconder).into(), 0)?;
133		}
134
135		let deposits = DepositOf::<T>::get(0).ok_or("Proposal not created")?;
136		assert_eq!(deposits.0.len(), (T::MaxDeposits::get() - 1) as usize, "Seconds not recorded");
137		whitelist_account!(caller);
138
139		#[extrinsic_call]
140		_(RawOrigin::Signed(caller), 0);
141
142		let deposits = DepositOf::<T>::get(0).ok_or("Proposal not created")?;
143		assert_eq!(
144			deposits.0.len(),
145			(T::MaxDeposits::get()) as usize,
146			"`second` benchmark did not work"
147		);
148		Ok(())
149	}
150
151	#[benchmark]
152	fn vote_new() -> Result<(), BenchmarkError> {
153		let caller = funded_account::<T>("caller", 0);
154		let account_vote = account_vote::<T>(100u32.into());
155
156		// We need to create existing direct votes
157		for i in 0..T::MaxVotes::get() - 1 {
158			let ref_index = add_referendum::<T>(i).0;
159			Democracy::<T>::vote(
160				RawOrigin::Signed(caller.clone()).into(),
161				ref_index,
162				account_vote,
163			)?;
164		}
165		let votes = match VotingOf::<T>::get(&caller) {
166			Voting::Direct { votes, .. } => votes,
167			_ => return Err("Votes are not direct".into()),
168		};
169		assert_eq!(votes.len(), (T::MaxVotes::get() - 1) as usize, "Votes were not recorded.");
170
171		let ref_index = add_referendum::<T>(T::MaxVotes::get() - 1).0;
172		whitelist_account!(caller);
173
174		#[extrinsic_call]
175		vote(RawOrigin::Signed(caller.clone()), ref_index, account_vote);
176
177		let votes = match VotingOf::<T>::get(&caller) {
178			Voting::Direct { votes, .. } => votes,
179			_ => return Err("Votes are not direct".into()),
180		};
181
182		assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Vote was not recorded.");
183		Ok(())
184	}
185
186	#[benchmark]
187	fn vote_existing() -> Result<(), BenchmarkError> {
188		let caller = funded_account::<T>("caller", 0);
189		let account_vote = account_vote::<T>(100u32.into());
190
191		// We need to create existing direct votes
192		for i in 0..T::MaxVotes::get() {
193			let ref_index = add_referendum::<T>(i).0;
194			Democracy::<T>::vote(
195				RawOrigin::Signed(caller.clone()).into(),
196				ref_index,
197				account_vote,
198			)?;
199		}
200		let votes = match VotingOf::<T>::get(&caller) {
201			Voting::Direct { votes, .. } => votes,
202			_ => return Err("Votes are not direct".into()),
203		};
204		assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Votes were not recorded.");
205
206		// Change vote from aye to nay
207		let nay = Vote { aye: false, conviction: Conviction::Locked1x };
208		let new_vote = AccountVote::Standard { vote: nay, balance: 1000u32.into() };
209		let ref_index = ReferendumCount::<T>::get() - 1;
210
211		// This tests when a user changes a vote
212		whitelist_account!(caller);
213
214		#[extrinsic_call]
215		vote(RawOrigin::Signed(caller.clone()), ref_index, new_vote);
216
217		let votes = match VotingOf::<T>::get(&caller) {
218			Voting::Direct { votes, .. } => votes,
219			_ => return Err("Votes are not direct".into()),
220		};
221		assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Vote was incorrectly added");
222		let referendum_info =
223			ReferendumInfoOf::<T>::get(ref_index).ok_or("referendum doesn't exist")?;
224		let tally = match referendum_info {
225			ReferendumInfo::Ongoing(r) => r.tally,
226			_ => return Err("referendum not ongoing".into()),
227		};
228		assert_eq!(tally.nays, 1000u32.into(), "changed vote was not recorded");
229		Ok(())
230	}
231
232	#[benchmark]
233	fn emergency_cancel() -> Result<(), BenchmarkError> {
234		let origin = T::CancellationOrigin::try_successful_origin()
235			.map_err(|_| BenchmarkError::Weightless)?;
236		let (ref_index, _, preimage_hash) = add_referendum::<T>(0);
237		assert_ok!(Democracy::<T>::referendum_status(ref_index));
238
239		#[extrinsic_call]
240		_(origin as T::RuntimeOrigin, ref_index);
241		// Referendum has been canceled
242		assert_noop!(Democracy::<T>::referendum_status(ref_index), Error::<T>::ReferendumInvalid,);
243		assert_last_event::<T>(
244			crate::Event::MetadataCleared {
245				owner: MetadataOwner::Referendum(ref_index),
246				hash: preimage_hash,
247			}
248			.into(),
249		);
250		Ok(())
251	}
252
253	#[benchmark]
254	fn blacklist() -> Result<(), BenchmarkError> {
255		// Place our proposal at the end to make sure it's worst case.
256		for i in 0..T::MaxProposals::get() - 1 {
257			add_proposal::<T>(i)?;
258		}
259		// We should really add a lot of seconds here, but we're not doing it elsewhere.
260
261		// Add a referendum of our proposal.
262		let (ref_index, hash, preimage_hash) = add_referendum::<T>(0);
263		assert_ok!(Democracy::<T>::referendum_status(ref_index));
264		// Place our proposal in the external queue, too.
265		assert_ok!(Democracy::<T>::external_propose(
266			T::ExternalOrigin::try_successful_origin()
267				.expect("ExternalOrigin has no successful origin required for the benchmark"),
268			make_proposal::<T>(0)
269		));
270		let origin =
271			T::BlacklistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
272		#[extrinsic_call]
273		_(origin as T::RuntimeOrigin, hash, Some(ref_index));
274
275		// Referendum has been canceled
276		assert_noop!(Democracy::<T>::referendum_status(ref_index), Error::<T>::ReferendumInvalid);
277		assert_has_event::<T>(
278			crate::Event::MetadataCleared {
279				owner: MetadataOwner::Referendum(ref_index),
280				hash: preimage_hash,
281			}
282			.into(),
283		);
284		Ok(())
285	}
286
287	// Worst case scenario, we external propose a previously blacklisted proposal
288	#[benchmark]
289	fn external_propose() -> Result<(), BenchmarkError> {
290		let origin =
291			T::ExternalOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
292		let proposal = make_proposal::<T>(0);
293		// Add proposal to blacklist with block number 0
294
295		let addresses: BoundedVec<_, _> = (0..(T::MaxBlacklisted::get() - 1))
296			.into_iter()
297			.map(|i| account::<T::AccountId>("blacklist", i, SEED))
298			.collect::<Vec<_>>()
299			.try_into()
300			.unwrap();
301		Blacklist::<T>::insert(proposal.hash(), (BlockNumberFor::<T>::zero(), addresses));
302		#[extrinsic_call]
303		_(origin as T::RuntimeOrigin, proposal);
304
305		// External proposal created
306		ensure!(NextExternal::<T>::exists(), "External proposal didn't work");
307		Ok(())
308	}
309
310	#[benchmark]
311	fn external_propose_majority() -> Result<(), BenchmarkError> {
312		let origin = T::ExternalMajorityOrigin::try_successful_origin()
313			.map_err(|_| BenchmarkError::Weightless)?;
314		let proposal = make_proposal::<T>(0);
315		#[extrinsic_call]
316		_(origin as T::RuntimeOrigin, proposal);
317
318		// External proposal created
319		ensure!(NextExternal::<T>::exists(), "External proposal didn't work");
320		Ok(())
321	}
322
323	#[benchmark]
324	fn external_propose_default() -> Result<(), BenchmarkError> {
325		let origin = T::ExternalDefaultOrigin::try_successful_origin()
326			.map_err(|_| BenchmarkError::Weightless)?;
327		let proposal = make_proposal::<T>(0);
328		#[extrinsic_call]
329		_(origin as T::RuntimeOrigin, proposal);
330
331		// External proposal created
332		ensure!(NextExternal::<T>::exists(), "External proposal didn't work");
333		Ok(())
334	}
335
336	#[benchmark]
337	fn fast_track() -> Result<(), BenchmarkError> {
338		let origin_propose = T::ExternalDefaultOrigin::try_successful_origin()
339			.expect("ExternalDefaultOrigin has no successful origin required for the benchmark");
340		let proposal = make_proposal::<T>(0);
341		let proposal_hash = proposal.hash();
342		Democracy::<T>::external_propose_default(origin_propose.clone(), proposal)?;
343		// Set metadata to the external proposal.
344		let preimage_hash = note_preimage::<T>();
345		assert_ok!(Democracy::<T>::set_metadata(
346			origin_propose,
347			MetadataOwner::External,
348			Some(preimage_hash)
349		));
350		// NOTE: Instant origin may invoke a little bit more logic, but may not always succeed.
351		let origin_fast_track =
352			T::FastTrackOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
353		let voting_period = T::FastTrackVotingPeriod::get();
354		let delay = 0u32;
355		#[extrinsic_call]
356		_(origin_fast_track as T::RuntimeOrigin, proposal_hash, voting_period, delay.into());
357
358		assert_eq!(ReferendumCount::<T>::get(), 1, "referendum not created");
359		assert_last_event::<T>(
360			crate::Event::MetadataTransferred {
361				prev_owner: MetadataOwner::External,
362				owner: MetadataOwner::Referendum(0),
363				hash: preimage_hash,
364			}
365			.into(),
366		);
367		Ok(())
368	}
369
370	#[benchmark]
371	fn veto_external() -> Result<(), BenchmarkError> {
372		let proposal = make_proposal::<T>(0);
373		let proposal_hash = proposal.hash();
374
375		let origin_propose = T::ExternalDefaultOrigin::try_successful_origin()
376			.expect("ExternalDefaultOrigin has no successful origin required for the benchmark");
377		Democracy::<T>::external_propose_default(origin_propose.clone(), proposal)?;
378
379		let preimage_hash = note_preimage::<T>();
380		assert_ok!(Democracy::<T>::set_metadata(
381			origin_propose,
382			MetadataOwner::External,
383			Some(preimage_hash)
384		));
385
386		let mut vetoers: BoundedVec<T::AccountId, _> = Default::default();
387		for i in 0..(T::MaxBlacklisted::get() - 1) {
388			vetoers.try_push(account::<T::AccountId>("vetoer", i, SEED)).unwrap();
389		}
390		vetoers.sort();
391		Blacklist::<T>::insert(proposal_hash, (BlockNumberFor::<T>::zero(), vetoers));
392
393		let origin =
394			T::VetoOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
395		ensure!(NextExternal::<T>::get().is_some(), "no external proposal");
396		#[extrinsic_call]
397		_(origin as T::RuntimeOrigin, proposal_hash);
398
399		assert!(NextExternal::<T>::get().is_none());
400		let (_, new_vetoers) = Blacklist::<T>::get(&proposal_hash).ok_or("no blacklist")?;
401		assert_eq!(new_vetoers.len(), T::MaxBlacklisted::get() as usize, "vetoers not added");
402		Ok(())
403	}
404
405	#[benchmark]
406	fn cancel_proposal() -> Result<(), BenchmarkError> {
407		// Place our proposal at the end to make sure it's worst case.
408		for i in 0..T::MaxProposals::get() {
409			add_proposal::<T>(i)?;
410		}
411		// Add metadata to the first proposal.
412		let proposer = funded_account::<T>("proposer", 0);
413		let preimage_hash = note_preimage::<T>();
414		assert_ok!(Democracy::<T>::set_metadata(
415			RawOrigin::Signed(proposer).into(),
416			MetadataOwner::Proposal(0),
417			Some(preimage_hash)
418		));
419		let cancel_origin = T::CancelProposalOrigin::try_successful_origin()
420			.map_err(|_| BenchmarkError::Weightless)?;
421		#[extrinsic_call]
422		_(cancel_origin as T::RuntimeOrigin, 0);
423
424		assert_last_event::<T>(
425			crate::Event::MetadataCleared {
426				owner: MetadataOwner::Proposal(0),
427				hash: preimage_hash,
428			}
429			.into(),
430		);
431		Ok(())
432	}
433
434	#[benchmark]
435	fn cancel_referendum() -> Result<(), BenchmarkError> {
436		let (ref_index, _, preimage_hash) = add_referendum::<T>(0);
437		#[extrinsic_call]
438		_(RawOrigin::Root, ref_index);
439
440		assert_last_event::<T>(
441			crate::Event::MetadataCleared {
442				owner: MetadataOwner::Referendum(0),
443				hash: preimage_hash,
444			}
445			.into(),
446		);
447		Ok(())
448	}
449
450	#[benchmark(extra)]
451	fn on_initialize_external(r: Linear<0, REFERENDUM_COUNT_HINT>) -> Result<(), BenchmarkError> {
452		for i in 0..r {
453			add_referendum::<T>(i);
454		}
455
456		assert_eq!(ReferendumCount::<T>::get(), r, "referenda not created");
457
458		// Launch external
459		LastTabledWasExternal::<T>::put(false);
460
461		let origin = T::ExternalMajorityOrigin::try_successful_origin()
462			.map_err(|_| BenchmarkError::Weightless)?;
463		let proposal = make_proposal::<T>(r);
464		let call = Call::<T>::external_propose_majority { proposal };
465		call.dispatch_bypass_filter(origin)?;
466		// External proposal created
467		ensure!(NextExternal::<T>::exists(), "External proposal didn't work");
468
469		let block_number = T::LaunchPeriod::get();
470
471		#[block]
472		{
473			Democracy::<T>::on_initialize(block_number);
474		}
475
476		// One extra because of next external
477		assert_eq!(ReferendumCount::<T>::get(), r + 1, "referenda not created");
478		ensure!(!NextExternal::<T>::exists(), "External wasn't taken");
479
480		// All but the new next external should be finished
481		for i in 0..r {
482			if let Some(value) = ReferendumInfoOf::<T>::get(i) {
483				match value {
484					ReferendumInfo::Finished { .. } => (),
485					ReferendumInfo::Ongoing(_) => return Err("Referendum was not finished".into()),
486				}
487			}
488		}
489		Ok(())
490	}
491
492	#[benchmark(extra)]
493	fn on_initialize_public(
494		r: Linear<0, { T::MaxVotes::get() - 1 }>,
495	) -> Result<(), BenchmarkError> {
496		for i in 0..r {
497			add_referendum::<T>(i);
498		}
499
500		assert_eq!(ReferendumCount::<T>::get(), r, "referenda not created");
501
502		// Launch public
503		assert!(add_proposal::<T>(r).is_ok(), "proposal not created");
504		LastTabledWasExternal::<T>::put(true);
505
506		let block_number = T::LaunchPeriod::get();
507
508		#[block]
509		{
510			Democracy::<T>::on_initialize(block_number);
511		}
512
513		// One extra because of next public
514		assert_eq!(ReferendumCount::<T>::get(), r + 1, "proposal not accepted");
515
516		// All should be finished
517		for i in 0..r {
518			if let Some(value) = ReferendumInfoOf::<T>::get(i) {
519				match value {
520					ReferendumInfo::Finished { .. } => (),
521					ReferendumInfo::Ongoing(_) => return Err("Referendum was not finished".into()),
522				}
523			}
524		}
525		Ok(())
526	}
527
528	// No launch no maturing referenda.
529	#[benchmark]
530	fn on_initialize_base(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
531		for i in 0..r {
532			add_referendum::<T>(i);
533		}
534
535		for (key, mut info) in ReferendumInfoOf::<T>::iter() {
536			if let ReferendumInfo::Ongoing(ref mut status) = info {
537				status.end += 100u32.into();
538			}
539			ReferendumInfoOf::<T>::insert(key, info);
540		}
541
542		assert_eq!(ReferendumCount::<T>::get(), r, "referenda not created");
543		assert_eq!(LowestUnbaked::<T>::get(), 0, "invalid referenda init");
544
545		#[block]
546		{
547			Democracy::<T>::on_initialize(1u32.into());
548		}
549
550		// All should be on going
551		for i in 0..r {
552			if let Some(value) = ReferendumInfoOf::<T>::get(i) {
553				match value {
554					ReferendumInfo::Finished { .. } => {
555						return Err("Referendum has been finished".into())
556					},
557					ReferendumInfo::Ongoing(_) => (),
558				}
559			}
560		}
561		Ok(())
562	}
563
564	#[benchmark]
565	fn on_initialize_base_with_launch_period(
566		r: Linear<0, { T::MaxVotes::get() - 1 }>,
567	) -> Result<(), BenchmarkError> {
568		for i in 0..r {
569			add_referendum::<T>(i);
570		}
571
572		for (key, mut info) in ReferendumInfoOf::<T>::iter() {
573			if let ReferendumInfo::Ongoing(ref mut status) = info {
574				status.end += 100u32.into();
575			}
576			ReferendumInfoOf::<T>::insert(key, info);
577		}
578
579		assert_eq!(ReferendumCount::<T>::get(), r, "referenda not created");
580		assert_eq!(LowestUnbaked::<T>::get(), 0, "invalid referenda init");
581
582		let block_number = T::LaunchPeriod::get();
583
584		#[block]
585		{
586			Democracy::<T>::on_initialize(block_number);
587		}
588
589		// All should be on going
590		for i in 0..r {
591			if let Some(value) = ReferendumInfoOf::<T>::get(i) {
592				match value {
593					ReferendumInfo::Finished { .. } => {
594						return Err("Referendum has been finished".into())
595					},
596					ReferendumInfo::Ongoing(_) => (),
597				}
598			}
599		}
600		Ok(())
601	}
602
603	#[benchmark]
604	fn delegate(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
605		let initial_balance: BalanceOf<T> = 100u32.into();
606		let delegated_balance: BalanceOf<T> = 1000u32.into();
607
608		let caller = funded_account::<T>("caller", 0);
609		// Caller will initially delegate to `old_delegate`
610		let old_delegate: T::AccountId = funded_account::<T>("old_delegate", r);
611		let old_delegate_lookup = T::Lookup::unlookup(old_delegate.clone());
612		Democracy::<T>::delegate(
613			RawOrigin::Signed(caller.clone()).into(),
614			old_delegate_lookup,
615			Conviction::Locked1x,
616			delegated_balance,
617		)?;
618		let (target, balance) = match VotingOf::<T>::get(&caller) {
619			Voting::Delegating { target, balance, .. } => (target, balance),
620			_ => return Err("Votes are not direct".into()),
621		};
622		assert_eq!(target, old_delegate, "delegation target didn't work");
623		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
624		// Caller will now switch to `new_delegate`
625		let new_delegate: T::AccountId = funded_account::<T>("new_delegate", r);
626		let new_delegate_lookup = T::Lookup::unlookup(new_delegate.clone());
627		let account_vote = account_vote::<T>(initial_balance);
628		// We need to create existing direct votes for the `new_delegate`
629		for i in 0..r {
630			let ref_index = add_referendum::<T>(i).0;
631			Democracy::<T>::vote(
632				RawOrigin::Signed(new_delegate.clone()).into(),
633				ref_index,
634				account_vote,
635			)?;
636		}
637		let votes = match VotingOf::<T>::get(&new_delegate) {
638			Voting::Direct { votes, .. } => votes,
639			_ => return Err("Votes are not direct".into()),
640		};
641		assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
642		whitelist_account!(caller);
643
644		#[extrinsic_call]
645		_(
646			RawOrigin::Signed(caller.clone()),
647			new_delegate_lookup,
648			Conviction::Locked1x,
649			delegated_balance,
650		);
651
652		let (target, balance) = match VotingOf::<T>::get(&caller) {
653			Voting::Delegating { target, balance, .. } => (target, balance),
654			_ => return Err("Votes are not direct".into()),
655		};
656		assert_eq!(target, new_delegate, "delegation target didn't work");
657		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
658		let delegations = match VotingOf::<T>::get(&new_delegate) {
659			Voting::Direct { delegations, .. } => delegations,
660			_ => return Err("Votes are not direct".into()),
661		};
662		assert_eq!(delegations.capital, delegated_balance, "delegation was not recorded.");
663		Ok(())
664	}
665
666	#[benchmark]
667	fn undelegate(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
668		let initial_balance: BalanceOf<T> = 100u32.into();
669		let delegated_balance: BalanceOf<T> = 1000u32.into();
670
671		let caller = funded_account::<T>("caller", 0);
672		// Caller will delegate
673		let the_delegate: T::AccountId = funded_account::<T>("delegate", r);
674		let the_delegate_lookup = T::Lookup::unlookup(the_delegate.clone());
675		Democracy::<T>::delegate(
676			RawOrigin::Signed(caller.clone()).into(),
677			the_delegate_lookup,
678			Conviction::Locked1x,
679			delegated_balance,
680		)?;
681		let (target, balance) = match VotingOf::<T>::get(&caller) {
682			Voting::Delegating { target, balance, .. } => (target, balance),
683			_ => return Err("Votes are not direct".into()),
684		};
685		assert_eq!(target, the_delegate, "delegation target didn't work");
686		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
687		// We need to create votes direct votes for the `delegate`
688		let account_vote = account_vote::<T>(initial_balance);
689		for i in 0..r {
690			let ref_index = add_referendum::<T>(i).0;
691			Democracy::<T>::vote(
692				RawOrigin::Signed(the_delegate.clone()).into(),
693				ref_index,
694				account_vote,
695			)?;
696		}
697		let votes = match VotingOf::<T>::get(&the_delegate) {
698			Voting::Direct { votes, .. } => votes,
699			_ => return Err("Votes are not direct".into()),
700		};
701		assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
702		whitelist_account!(caller);
703
704		#[extrinsic_call]
705		_(RawOrigin::Signed(caller.clone()));
706
707		// Voting should now be direct
708		match VotingOf::<T>::get(&caller) {
709			Voting::Direct { .. } => (),
710			_ => return Err("undelegation failed".into()),
711		}
712		Ok(())
713	}
714
715	#[benchmark]
716	fn clear_public_proposals() -> Result<(), BenchmarkError> {
717		add_proposal::<T>(0)?;
718
719		#[extrinsic_call]
720		_(RawOrigin::Root);
721
722		Ok(())
723	}
724
725	// Test when unlock will remove locks
726	#[benchmark]
727	fn unlock_remove(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
728		let locker = funded_account::<T>("locker", 0);
729		let locker_lookup = T::Lookup::unlookup(locker.clone());
730		// Populate votes so things are locked
731		let base_balance: BalanceOf<T> = 100u32.into();
732		let small_vote = account_vote::<T>(base_balance);
733		// Vote and immediately unvote
734		for i in 0..r {
735			let ref_index = add_referendum::<T>(i).0;
736			Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?;
737			Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?;
738		}
739
740		let caller = funded_account::<T>("caller", 0);
741		whitelist_account!(caller);
742
743		#[extrinsic_call]
744		unlock(RawOrigin::Signed(caller), locker_lookup);
745
746		// Note that we may want to add a `get_lock` api to actually verify
747		let voting = VotingOf::<T>::get(&locker);
748		assert_eq!(voting.locked_balance(), BalanceOf::<T>::zero());
749		Ok(())
750	}
751
752	// Test when unlock will set a new value
753	#[benchmark]
754	fn unlock_set(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
755		let locker = funded_account::<T>("locker", 0);
756		let locker_lookup = T::Lookup::unlookup(locker.clone());
757		// Populate votes so things are locked
758		let base_balance: BalanceOf<T> = 100u32.into();
759		let small_vote = account_vote::<T>(base_balance);
760		for i in 0..r {
761			let ref_index = add_referendum::<T>(i).0;
762			Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?;
763		}
764
765		// Create a big vote so lock increases
766		let big_vote = account_vote::<T>(base_balance * 10u32.into());
767		let ref_index = add_referendum::<T>(r).0;
768		Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, big_vote)?;
769
770		let votes = match VotingOf::<T>::get(&locker) {
771			Voting::Direct { votes, .. } => votes,
772			_ => return Err("Votes are not direct".into()),
773		};
774		assert_eq!(votes.len(), (r + 1) as usize, "Votes were not recorded.");
775
776		let voting = VotingOf::<T>::get(&locker);
777		assert_eq!(voting.locked_balance(), base_balance * 10u32.into());
778
779		Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?;
780
781		let caller = funded_account::<T>("caller", 0);
782		whitelist_account!(caller);
783
784		#[extrinsic_call]
785		unlock(RawOrigin::Signed(caller), locker_lookup);
786
787		let votes = match VotingOf::<T>::get(&locker) {
788			Voting::Direct { votes, .. } => votes,
789			_ => return Err("Votes are not direct".into()),
790		};
791		assert_eq!(votes.len(), r as usize, "Vote was not removed");
792
793		let voting = VotingOf::<T>::get(&locker);
794		// Note that we may want to add a `get_lock` api to actually verify
795		assert_eq!(voting.locked_balance(), if r > 0 { base_balance } else { 0u32.into() });
796		Ok(())
797	}
798
799	#[benchmark]
800	fn remove_vote(r: Linear<1, { T::MaxVotes::get() }>) -> Result<(), BenchmarkError> {
801		let caller = funded_account::<T>("caller", 0);
802		let account_vote = account_vote::<T>(100u32.into());
803
804		for i in 0..r {
805			let ref_index = add_referendum::<T>(i).0;
806			Democracy::<T>::vote(
807				RawOrigin::Signed(caller.clone()).into(),
808				ref_index,
809				account_vote,
810			)?;
811		}
812
813		let votes = match VotingOf::<T>::get(&caller) {
814			Voting::Direct { votes, .. } => votes,
815			_ => return Err("Votes are not direct".into()),
816		};
817		assert_eq!(votes.len(), r as usize, "Votes not created");
818
819		let ref_index = r - 1;
820		whitelist_account!(caller);
821
822		#[extrinsic_call]
823		_(RawOrigin::Signed(caller.clone()), ref_index);
824
825		let votes = match VotingOf::<T>::get(&caller) {
826			Voting::Direct { votes, .. } => votes,
827			_ => return Err("Votes are not direct".into()),
828		};
829		assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
830		Ok(())
831	}
832
833	// Worst case is when target == caller and referendum is ongoing
834	#[benchmark]
835	fn remove_other_vote(r: Linear<1, { T::MaxVotes::get() }>) -> Result<(), BenchmarkError> {
836		let caller = funded_account::<T>("caller", r);
837		let caller_lookup = T::Lookup::unlookup(caller.clone());
838		let account_vote = account_vote::<T>(100u32.into());
839
840		for i in 0..r {
841			let ref_index = add_referendum::<T>(i).0;
842			Democracy::<T>::vote(
843				RawOrigin::Signed(caller.clone()).into(),
844				ref_index,
845				account_vote,
846			)?;
847		}
848
849		let votes = match VotingOf::<T>::get(&caller) {
850			Voting::Direct { votes, .. } => votes,
851			_ => return Err("Votes are not direct".into()),
852		};
853		assert_eq!(votes.len(), r as usize, "Votes not created");
854
855		let ref_index = r - 1;
856		whitelist_account!(caller);
857
858		#[extrinsic_call]
859		_(RawOrigin::Signed(caller.clone()), caller_lookup, ref_index);
860
861		let votes = match VotingOf::<T>::get(&caller) {
862			Voting::Direct { votes, .. } => votes,
863			_ => return Err("Votes are not direct".into()),
864		};
865		assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
866		Ok(())
867	}
868
869	#[benchmark]
870	fn set_external_metadata() -> Result<(), BenchmarkError> {
871		let origin = T::ExternalOrigin::try_successful_origin()
872			.expect("ExternalOrigin has no successful origin required for the benchmark");
873		assert_ok!(Democracy::<T>::external_propose(origin.clone(), make_proposal::<T>(0)));
874		let owner = MetadataOwner::External;
875		let hash = note_preimage::<T>();
876
877		#[extrinsic_call]
878		set_metadata(origin as T::RuntimeOrigin, owner.clone(), Some(hash));
879
880		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
881		Ok(())
882	}
883
884	#[benchmark]
885	fn clear_external_metadata() -> Result<(), BenchmarkError> {
886		let origin = T::ExternalOrigin::try_successful_origin()
887			.expect("ExternalOrigin has no successful origin required for the benchmark");
888		assert_ok!(Democracy::<T>::external_propose(origin.clone(), make_proposal::<T>(0)));
889		let owner = MetadataOwner::External;
890		let _proposer = funded_account::<T>("proposer", 0);
891		let hash = note_preimage::<T>();
892		assert_ok!(Democracy::<T>::set_metadata(origin.clone(), owner.clone(), Some(hash)));
893
894		#[extrinsic_call]
895		set_metadata(origin as T::RuntimeOrigin, owner.clone(), None);
896
897		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
898		Ok(())
899	}
900
901	#[benchmark]
902	fn set_proposal_metadata() -> Result<(), BenchmarkError> {
903		// Place our proposal at the end to make sure it's worst case.
904		for i in 0..T::MaxProposals::get() {
905			add_proposal::<T>(i)?;
906		}
907		let owner = MetadataOwner::Proposal(0);
908		let proposer = funded_account::<T>("proposer", 0);
909		let hash = note_preimage::<T>();
910
911		#[extrinsic_call]
912		set_metadata(RawOrigin::Signed(proposer), owner.clone(), Some(hash));
913
914		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
915		Ok(())
916	}
917
918	#[benchmark]
919	fn clear_proposal_metadata() -> Result<(), BenchmarkError> {
920		// Place our proposal at the end to make sure it's worst case.
921		for i in 0..T::MaxProposals::get() {
922			add_proposal::<T>(i)?;
923		}
924		let proposer = funded_account::<T>("proposer", 0);
925		let owner = MetadataOwner::Proposal(0);
926		let hash = note_preimage::<T>();
927		assert_ok!(Democracy::<T>::set_metadata(
928			RawOrigin::Signed(proposer.clone()).into(),
929			owner.clone(),
930			Some(hash)
931		));
932
933		#[extrinsic_call]
934		set_metadata::<T::RuntimeOrigin>(RawOrigin::Signed(proposer), owner.clone(), None);
935
936		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
937		Ok(())
938	}
939
940	#[benchmark]
941	fn set_referendum_metadata() -> Result<(), BenchmarkError> {
942		// create not ongoing referendum.
943		ReferendumInfoOf::<T>::insert(
944			0,
945			ReferendumInfo::Finished { end: BlockNumberFor::<T>::zero(), approved: true },
946		);
947		let owner = MetadataOwner::Referendum(0);
948		let _caller = funded_account::<T>("caller", 0);
949		let hash = note_preimage::<T>();
950
951		#[extrinsic_call]
952		set_metadata::<T::RuntimeOrigin>(RawOrigin::Root, owner.clone(), Some(hash));
953
954		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
955		Ok(())
956	}
957
958	#[benchmark]
959	fn clear_referendum_metadata() -> Result<(), BenchmarkError> {
960		// create not ongoing referendum.
961		ReferendumInfoOf::<T>::insert(
962			0,
963			ReferendumInfo::Finished { end: BlockNumberFor::<T>::zero(), approved: true },
964		);
965		let owner = MetadataOwner::Referendum(0);
966		let hash = note_preimage::<T>();
967		MetadataOf::<T>::insert(owner.clone(), hash);
968		let caller = funded_account::<T>("caller", 0);
969
970		#[extrinsic_call]
971		set_metadata::<T::RuntimeOrigin>(RawOrigin::Signed(caller), owner.clone(), None);
972
973		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
974		Ok(())
975	}
976
977	impl_benchmark_test_suite!(Democracy, crate::tests::new_test_ext(), crate::tests::Test);
978}