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					ReferendumInfo::Ongoing(_) => (),
557				}
558			}
559		}
560		Ok(())
561	}
562
563	#[benchmark]
564	fn on_initialize_base_with_launch_period(
565		r: Linear<0, { T::MaxVotes::get() - 1 }>,
566	) -> Result<(), BenchmarkError> {
567		for i in 0..r {
568			add_referendum::<T>(i);
569		}
570
571		for (key, mut info) in ReferendumInfoOf::<T>::iter() {
572			if let ReferendumInfo::Ongoing(ref mut status) = info {
573				status.end += 100u32.into();
574			}
575			ReferendumInfoOf::<T>::insert(key, info);
576		}
577
578		assert_eq!(ReferendumCount::<T>::get(), r, "referenda not created");
579		assert_eq!(LowestUnbaked::<T>::get(), 0, "invalid referenda init");
580
581		let block_number = T::LaunchPeriod::get();
582
583		#[block]
584		{
585			Democracy::<T>::on_initialize(block_number);
586		}
587
588		// All should be on going
589		for i in 0..r {
590			if let Some(value) = ReferendumInfoOf::<T>::get(i) {
591				match value {
592					ReferendumInfo::Finished { .. } =>
593						return Err("Referendum has been finished".into()),
594					ReferendumInfo::Ongoing(_) => (),
595				}
596			}
597		}
598		Ok(())
599	}
600
601	#[benchmark]
602	fn delegate(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
603		let initial_balance: BalanceOf<T> = 100u32.into();
604		let delegated_balance: BalanceOf<T> = 1000u32.into();
605
606		let caller = funded_account::<T>("caller", 0);
607		// Caller will initially delegate to `old_delegate`
608		let old_delegate: T::AccountId = funded_account::<T>("old_delegate", r);
609		let old_delegate_lookup = T::Lookup::unlookup(old_delegate.clone());
610		Democracy::<T>::delegate(
611			RawOrigin::Signed(caller.clone()).into(),
612			old_delegate_lookup,
613			Conviction::Locked1x,
614			delegated_balance,
615		)?;
616		let (target, balance) = match VotingOf::<T>::get(&caller) {
617			Voting::Delegating { target, balance, .. } => (target, balance),
618			_ => return Err("Votes are not direct".into()),
619		};
620		assert_eq!(target, old_delegate, "delegation target didn't work");
621		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
622		// Caller will now switch to `new_delegate`
623		let new_delegate: T::AccountId = funded_account::<T>("new_delegate", r);
624		let new_delegate_lookup = T::Lookup::unlookup(new_delegate.clone());
625		let account_vote = account_vote::<T>(initial_balance);
626		// We need to create existing direct votes for the `new_delegate`
627		for i in 0..r {
628			let ref_index = add_referendum::<T>(i).0;
629			Democracy::<T>::vote(
630				RawOrigin::Signed(new_delegate.clone()).into(),
631				ref_index,
632				account_vote,
633			)?;
634		}
635		let votes = match VotingOf::<T>::get(&new_delegate) {
636			Voting::Direct { votes, .. } => votes,
637			_ => return Err("Votes are not direct".into()),
638		};
639		assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
640		whitelist_account!(caller);
641
642		#[extrinsic_call]
643		_(
644			RawOrigin::Signed(caller.clone()),
645			new_delegate_lookup,
646			Conviction::Locked1x,
647			delegated_balance,
648		);
649
650		let (target, balance) = match VotingOf::<T>::get(&caller) {
651			Voting::Delegating { target, balance, .. } => (target, balance),
652			_ => return Err("Votes are not direct".into()),
653		};
654		assert_eq!(target, new_delegate, "delegation target didn't work");
655		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
656		let delegations = match VotingOf::<T>::get(&new_delegate) {
657			Voting::Direct { delegations, .. } => delegations,
658			_ => return Err("Votes are not direct".into()),
659		};
660		assert_eq!(delegations.capital, delegated_balance, "delegation was not recorded.");
661		Ok(())
662	}
663
664	#[benchmark]
665	fn undelegate(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
666		let initial_balance: BalanceOf<T> = 100u32.into();
667		let delegated_balance: BalanceOf<T> = 1000u32.into();
668
669		let caller = funded_account::<T>("caller", 0);
670		// Caller will delegate
671		let the_delegate: T::AccountId = funded_account::<T>("delegate", r);
672		let the_delegate_lookup = T::Lookup::unlookup(the_delegate.clone());
673		Democracy::<T>::delegate(
674			RawOrigin::Signed(caller.clone()).into(),
675			the_delegate_lookup,
676			Conviction::Locked1x,
677			delegated_balance,
678		)?;
679		let (target, balance) = match VotingOf::<T>::get(&caller) {
680			Voting::Delegating { target, balance, .. } => (target, balance),
681			_ => return Err("Votes are not direct".into()),
682		};
683		assert_eq!(target, the_delegate, "delegation target didn't work");
684		assert_eq!(balance, delegated_balance, "delegation balance didn't work");
685		// We need to create votes direct votes for the `delegate`
686		let account_vote = account_vote::<T>(initial_balance);
687		for i in 0..r {
688			let ref_index = add_referendum::<T>(i).0;
689			Democracy::<T>::vote(
690				RawOrigin::Signed(the_delegate.clone()).into(),
691				ref_index,
692				account_vote,
693			)?;
694		}
695		let votes = match VotingOf::<T>::get(&the_delegate) {
696			Voting::Direct { votes, .. } => votes,
697			_ => return Err("Votes are not direct".into()),
698		};
699		assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
700		whitelist_account!(caller);
701
702		#[extrinsic_call]
703		_(RawOrigin::Signed(caller.clone()));
704
705		// Voting should now be direct
706		match VotingOf::<T>::get(&caller) {
707			Voting::Direct { .. } => (),
708			_ => return Err("undelegation failed".into()),
709		}
710		Ok(())
711	}
712
713	#[benchmark]
714	fn clear_public_proposals() -> Result<(), BenchmarkError> {
715		add_proposal::<T>(0)?;
716
717		#[extrinsic_call]
718		_(RawOrigin::Root);
719
720		Ok(())
721	}
722
723	// Test when unlock will remove locks
724	#[benchmark]
725	fn unlock_remove(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
726		let locker = funded_account::<T>("locker", 0);
727		let locker_lookup = T::Lookup::unlookup(locker.clone());
728		// Populate votes so things are locked
729		let base_balance: BalanceOf<T> = 100u32.into();
730		let small_vote = account_vote::<T>(base_balance);
731		// Vote and immediately unvote
732		for i in 0..r {
733			let ref_index = add_referendum::<T>(i).0;
734			Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?;
735			Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?;
736		}
737
738		let caller = funded_account::<T>("caller", 0);
739		whitelist_account!(caller);
740
741		#[extrinsic_call]
742		unlock(RawOrigin::Signed(caller), locker_lookup);
743
744		// Note that we may want to add a `get_lock` api to actually verify
745		let voting = VotingOf::<T>::get(&locker);
746		assert_eq!(voting.locked_balance(), BalanceOf::<T>::zero());
747		Ok(())
748	}
749
750	// Test when unlock will set a new value
751	#[benchmark]
752	fn unlock_set(r: Linear<0, { T::MaxVotes::get() - 1 }>) -> Result<(), BenchmarkError> {
753		let locker = funded_account::<T>("locker", 0);
754		let locker_lookup = T::Lookup::unlookup(locker.clone());
755		// Populate votes so things are locked
756		let base_balance: BalanceOf<T> = 100u32.into();
757		let small_vote = account_vote::<T>(base_balance);
758		for i in 0..r {
759			let ref_index = add_referendum::<T>(i).0;
760			Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?;
761		}
762
763		// Create a big vote so lock increases
764		let big_vote = account_vote::<T>(base_balance * 10u32.into());
765		let ref_index = add_referendum::<T>(r).0;
766		Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, big_vote)?;
767
768		let votes = match VotingOf::<T>::get(&locker) {
769			Voting::Direct { votes, .. } => votes,
770			_ => return Err("Votes are not direct".into()),
771		};
772		assert_eq!(votes.len(), (r + 1) as usize, "Votes were not recorded.");
773
774		let voting = VotingOf::<T>::get(&locker);
775		assert_eq!(voting.locked_balance(), base_balance * 10u32.into());
776
777		Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?;
778
779		let caller = funded_account::<T>("caller", 0);
780		whitelist_account!(caller);
781
782		#[extrinsic_call]
783		unlock(RawOrigin::Signed(caller), locker_lookup);
784
785		let votes = match VotingOf::<T>::get(&locker) {
786			Voting::Direct { votes, .. } => votes,
787			_ => return Err("Votes are not direct".into()),
788		};
789		assert_eq!(votes.len(), r as usize, "Vote was not removed");
790
791		let voting = VotingOf::<T>::get(&locker);
792		// Note that we may want to add a `get_lock` api to actually verify
793		assert_eq!(voting.locked_balance(), if r > 0 { base_balance } else { 0u32.into() });
794		Ok(())
795	}
796
797	#[benchmark]
798	fn remove_vote(r: Linear<1, { T::MaxVotes::get() }>) -> Result<(), BenchmarkError> {
799		let caller = funded_account::<T>("caller", 0);
800		let account_vote = account_vote::<T>(100u32.into());
801
802		for i in 0..r {
803			let ref_index = add_referendum::<T>(i).0;
804			Democracy::<T>::vote(
805				RawOrigin::Signed(caller.clone()).into(),
806				ref_index,
807				account_vote,
808			)?;
809		}
810
811		let votes = match VotingOf::<T>::get(&caller) {
812			Voting::Direct { votes, .. } => votes,
813			_ => return Err("Votes are not direct".into()),
814		};
815		assert_eq!(votes.len(), r as usize, "Votes not created");
816
817		let ref_index = r - 1;
818		whitelist_account!(caller);
819
820		#[extrinsic_call]
821		_(RawOrigin::Signed(caller.clone()), ref_index);
822
823		let votes = match VotingOf::<T>::get(&caller) {
824			Voting::Direct { votes, .. } => votes,
825			_ => return Err("Votes are not direct".into()),
826		};
827		assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
828		Ok(())
829	}
830
831	// Worst case is when target == caller and referendum is ongoing
832	#[benchmark]
833	fn remove_other_vote(r: Linear<1, { T::MaxVotes::get() }>) -> Result<(), BenchmarkError> {
834		let caller = funded_account::<T>("caller", r);
835		let caller_lookup = T::Lookup::unlookup(caller.clone());
836		let account_vote = account_vote::<T>(100u32.into());
837
838		for i in 0..r {
839			let ref_index = add_referendum::<T>(i).0;
840			Democracy::<T>::vote(
841				RawOrigin::Signed(caller.clone()).into(),
842				ref_index,
843				account_vote,
844			)?;
845		}
846
847		let votes = match VotingOf::<T>::get(&caller) {
848			Voting::Direct { votes, .. } => votes,
849			_ => return Err("Votes are not direct".into()),
850		};
851		assert_eq!(votes.len(), r as usize, "Votes not created");
852
853		let ref_index = r - 1;
854		whitelist_account!(caller);
855
856		#[extrinsic_call]
857		_(RawOrigin::Signed(caller.clone()), caller_lookup, ref_index);
858
859		let votes = match VotingOf::<T>::get(&caller) {
860			Voting::Direct { votes, .. } => votes,
861			_ => return Err("Votes are not direct".into()),
862		};
863		assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
864		Ok(())
865	}
866
867	#[benchmark]
868	fn set_external_metadata() -> Result<(), BenchmarkError> {
869		let origin = T::ExternalOrigin::try_successful_origin()
870			.expect("ExternalOrigin has no successful origin required for the benchmark");
871		assert_ok!(Democracy::<T>::external_propose(origin.clone(), make_proposal::<T>(0)));
872		let owner = MetadataOwner::External;
873		let hash = note_preimage::<T>();
874
875		#[extrinsic_call]
876		set_metadata(origin as T::RuntimeOrigin, owner.clone(), Some(hash));
877
878		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
879		Ok(())
880	}
881
882	#[benchmark]
883	fn clear_external_metadata() -> Result<(), BenchmarkError> {
884		let origin = T::ExternalOrigin::try_successful_origin()
885			.expect("ExternalOrigin has no successful origin required for the benchmark");
886		assert_ok!(Democracy::<T>::external_propose(origin.clone(), make_proposal::<T>(0)));
887		let owner = MetadataOwner::External;
888		let _proposer = funded_account::<T>("proposer", 0);
889		let hash = note_preimage::<T>();
890		assert_ok!(Democracy::<T>::set_metadata(origin.clone(), owner.clone(), Some(hash)));
891
892		#[extrinsic_call]
893		set_metadata(origin as T::RuntimeOrigin, owner.clone(), None);
894
895		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
896		Ok(())
897	}
898
899	#[benchmark]
900	fn set_proposal_metadata() -> Result<(), BenchmarkError> {
901		// Place our proposal at the end to make sure it's worst case.
902		for i in 0..T::MaxProposals::get() {
903			add_proposal::<T>(i)?;
904		}
905		let owner = MetadataOwner::Proposal(0);
906		let proposer = funded_account::<T>("proposer", 0);
907		let hash = note_preimage::<T>();
908
909		#[extrinsic_call]
910		set_metadata(RawOrigin::Signed(proposer), owner.clone(), Some(hash));
911
912		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
913		Ok(())
914	}
915
916	#[benchmark]
917	fn clear_proposal_metadata() -> Result<(), BenchmarkError> {
918		// Place our proposal at the end to make sure it's worst case.
919		for i in 0..T::MaxProposals::get() {
920			add_proposal::<T>(i)?;
921		}
922		let proposer = funded_account::<T>("proposer", 0);
923		let owner = MetadataOwner::Proposal(0);
924		let hash = note_preimage::<T>();
925		assert_ok!(Democracy::<T>::set_metadata(
926			RawOrigin::Signed(proposer.clone()).into(),
927			owner.clone(),
928			Some(hash)
929		));
930
931		#[extrinsic_call]
932		set_metadata::<T::RuntimeOrigin>(RawOrigin::Signed(proposer), owner.clone(), None);
933
934		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
935		Ok(())
936	}
937
938	#[benchmark]
939	fn set_referendum_metadata() -> Result<(), BenchmarkError> {
940		// create not ongoing referendum.
941		ReferendumInfoOf::<T>::insert(
942			0,
943			ReferendumInfo::Finished { end: BlockNumberFor::<T>::zero(), approved: true },
944		);
945		let owner = MetadataOwner::Referendum(0);
946		let _caller = funded_account::<T>("caller", 0);
947		let hash = note_preimage::<T>();
948
949		#[extrinsic_call]
950		set_metadata::<T::RuntimeOrigin>(RawOrigin::Root, owner.clone(), Some(hash));
951
952		assert_last_event::<T>(crate::Event::MetadataSet { owner, hash }.into());
953		Ok(())
954	}
955
956	#[benchmark]
957	fn clear_referendum_metadata() -> Result<(), BenchmarkError> {
958		// create not ongoing referendum.
959		ReferendumInfoOf::<T>::insert(
960			0,
961			ReferendumInfo::Finished { end: BlockNumberFor::<T>::zero(), approved: true },
962		);
963		let owner = MetadataOwner::Referendum(0);
964		let hash = note_preimage::<T>();
965		MetadataOf::<T>::insert(owner.clone(), hash);
966		let caller = funded_account::<T>("caller", 0);
967
968		#[extrinsic_call]
969		set_metadata::<T::RuntimeOrigin>(RawOrigin::Signed(caller), owner.clone(), None);
970
971		assert_last_event::<T>(crate::Event::MetadataCleared { owner, hash }.into());
972		Ok(())
973	}
974
975	impl_benchmark_test_suite!(Democracy, crate::tests::new_test_ext(), crate::tests::Test);
976}