referrerpolicy=no-referrer-when-downgrade

pallet_alliance/
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//! Alliance pallet benchmarking.
19
20#![cfg(feature = "runtime-benchmarks")]
21
22use core::{cmp, mem::size_of};
23use sp_runtime::traits::{Bounded, Hash, StaticLookup};
24
25use frame_benchmarking::{account, v2::*, BenchmarkError};
26use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable};
27use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin as SystemOrigin};
28
29use super::{Call as AllianceCall, Pallet as Alliance, *};
30
31const SEED: u32 = 0;
32
33const MAX_BYTES: u32 = 1_024;
34
35fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
36	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
37}
38
39fn cid(input: impl AsRef<[u8]>) -> Cid {
40	let result = sp_crypto_hashing::sha2_256(input.as_ref());
41	Cid::new_v0(result)
42}
43
44fn rule(input: impl AsRef<[u8]>) -> Cid {
45	cid(input)
46}
47
48fn announcement(input: impl AsRef<[u8]>) -> Cid {
49	cid(input)
50}
51
52fn funded_account<T: Config<I>, I: 'static>(name: &'static str, index: u32) -> T::AccountId {
53	let account: T::AccountId = account(name, index, SEED);
54	T::Currency::make_free_balance_be(&account, BalanceOf::<T, I>::max_value() / 100u8.into());
55	account
56}
57
58fn fellow<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
59	funded_account::<T, I>("fellow", index)
60}
61
62fn ally<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
63	funded_account::<T, I>("ally", index)
64}
65
66fn outsider<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
67	funded_account::<T, I>("outsider", index)
68}
69
70fn generate_unscrupulous_account<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
71	funded_account::<T, I>("unscrupulous", index)
72}
73
74fn set_members<T: Config<I>, I: 'static>() {
75	let fellows: BoundedVec<_, T::MaxMembersCount> =
76		BoundedVec::try_from(vec![fellow::<T, I>(1), fellow::<T, I>(2)]).unwrap();
77	fellows.iter().for_each(|who| {
78		T::Currency::reserve(&who, T::AllyDeposit::get()).unwrap();
79		<DepositOf<T, I>>::insert(&who, T::AllyDeposit::get());
80	});
81	Members::<T, I>::insert(MemberRole::Fellow, fellows.clone());
82
83	let allies: BoundedVec<_, T::MaxMembersCount> =
84		BoundedVec::try_from(vec![ally::<T, I>(1)]).unwrap();
85	allies.iter().for_each(|who| {
86		T::Currency::reserve(&who, T::AllyDeposit::get()).unwrap();
87		<DepositOf<T, I>>::insert(&who, T::AllyDeposit::get());
88	});
89	Members::<T, I>::insert(MemberRole::Ally, allies);
90
91	T::InitializeMembers::initialize_members(&[fellows.as_slice()].concat());
92}
93
94#[instance_benchmarks]
95mod benchmarks {
96	use super::*;
97
98	// This tests when proposal is created and queued as "proposed"
99	#[benchmark]
100	fn propose_proposed(
101		b: Linear<1, MAX_BYTES>,
102		m: Linear<2, { T::MaxFellows::get() }>,
103		p: Linear<1, { T::MaxProposals::get() }>,
104	) -> Result<(), BenchmarkError> {
105		let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
106
107		// Construct `members`.
108		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
109		let proposer = fellows[0].clone();
110
111		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
112
113		let threshold = m;
114		// Add previous proposals.
115		for i in 0..p - 1 {
116			// Proposals should be different so that different proposal hashes are generated
117			let proposal: T::Proposal =
118				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; b as usize]) }.into();
119			Alliance::<T, I>::propose(
120				SystemOrigin::Signed(proposer.clone()).into(),
121				threshold,
122				Box::new(proposal),
123				bytes_in_storage,
124			)?;
125		}
126
127		let proposal: T::Proposal =
128			AllianceCall::<T, I>::set_rule { rule: rule(vec![p as u8; b as usize]) }.into();
129
130		#[extrinsic_call]
131		propose(
132			SystemOrigin::Signed(proposer.clone()),
133			threshold,
134			Box::new(proposal.clone()),
135			bytes_in_storage,
136		);
137
138		let proposal_hash = T::Hashing::hash_of(&proposal);
139		assert_eq!(T::ProposalProvider::proposal_of(proposal_hash), Some(proposal));
140		Ok(())
141	}
142
143	#[benchmark]
144	fn vote(m: Linear<5, { T::MaxFellows::get() }>) -> Result<(), BenchmarkError> {
145		let p = T::MaxProposals::get();
146		let b = MAX_BYTES;
147		let _bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
148
149		// Construct `members`.
150		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
151		let proposer = fellows[0].clone();
152
153		let members = fellows.clone();
154
155		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
156
157		// Threshold is 1 less than the number of members so that one person can vote nay
158		let threshold = m - 1;
159
160		// Add previous proposals
161		let mut last_hash = T::Hash::default();
162		for i in 0..p {
163			// Proposals should be different so that different proposal hashes are generated
164			let proposal: T::Proposal =
165				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; b as usize]) }.into();
166			Alliance::<T, I>::propose(
167				SystemOrigin::Signed(proposer.clone()).into(),
168				threshold,
169				Box::new(proposal.clone()),
170				b,
171			)?;
172			last_hash = T::Hashing::hash_of(&proposal);
173		}
174
175		let index = p - 1;
176		// Have almost everyone vote aye on last proposal, while keeping it from passing.
177		for j in 0..m - 3 {
178			let voter = &members[j as usize];
179			Alliance::<T, I>::vote(
180				SystemOrigin::Signed(voter.clone()).into(),
181				last_hash,
182				index,
183				true,
184			)?;
185		}
186
187		let voter = members[m as usize - 3].clone();
188		// Voter votes aye without resolving the vote.
189		Alliance::<T, I>::vote(SystemOrigin::Signed(voter.clone()).into(), last_hash, index, true)?;
190
191		// Voter switches vote to nay, but does not kill the vote, just updates + inserts
192		let approve = false;
193
194		// Whitelist voter account from further DB operations.
195		let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
196		frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
197
198		#[extrinsic_call]
199		_(SystemOrigin::Signed(voter), last_hash, index, approve);
200
201		//nothing to verify
202		Ok(())
203	}
204
205	#[benchmark]
206	fn close_early_disapproved(
207		m: Linear<4, { T::MaxFellows::get() }>,
208		p: Linear<1, { T::MaxProposals::get() }>,
209	) -> Result<(), BenchmarkError> {
210		let bytes = 100;
211		let bytes_in_storage = bytes + size_of::<Cid>() as u32 + 32;
212
213		// Construct `members`.
214		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
215
216		let members = fellows.clone();
217
218		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
219
220		let proposer = members[0].clone();
221		let voter = members[1].clone();
222
223		// Threshold is total members so that one nay will disapprove the vote
224		let threshold = m;
225
226		// Add previous proposals
227		let mut last_hash = T::Hash::default();
228		for i in 0..p {
229			// Proposals should be different so that different proposal hashes are generated
230			let proposal: T::Proposal =
231				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; bytes as usize]) }.into();
232			Alliance::<T, I>::propose(
233				SystemOrigin::Signed(proposer.clone()).into(),
234				threshold,
235				Box::new(proposal.clone()),
236				bytes_in_storage,
237			)?;
238			last_hash = T::Hashing::hash_of(&proposal);
239			assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
240		}
241
242		let index = p - 1;
243		// Have most everyone vote aye on last proposal, while keeping it from passing.
244		for j in 2..m - 1 {
245			let voter = &members[j as usize];
246			Alliance::<T, I>::vote(
247				SystemOrigin::Signed(voter.clone()).into(),
248				last_hash,
249				index,
250				true,
251			)?;
252		}
253
254		// Voter votes aye without resolving the vote.
255		Alliance::<T, I>::vote(SystemOrigin::Signed(voter.clone()).into(), last_hash, index, true)?;
256
257		// Voter switches vote to nay, which kills the vote
258		Alliance::<T, I>::vote(
259			SystemOrigin::Signed(voter.clone()).into(),
260			last_hash,
261			index,
262			false,
263		)?;
264
265		// Whitelist voter account from further DB operations.
266		let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
267		frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
268
269		#[extrinsic_call]
270		close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage);
271
272		assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
273		Ok(())
274	}
275
276	// We choose 4 as a minimum for param m, so we always trigger a vote in the voting loop (`for j
277	// in ...`)
278	#[benchmark]
279	fn close_early_approved(
280		b: Linear<1, MAX_BYTES>,
281		m: Linear<4, { T::MaxFellows::get() }>,
282		p: Linear<1, { T::MaxProposals::get() }>,
283	) -> Result<(), BenchmarkError> {
284		let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
285
286		// Construct `members`.
287		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
288
289		let members = fellows.clone();
290
291		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
292
293		let proposer = members[0].clone();
294		// Threshold is 2 so any two ayes will approve the vote
295		let threshold = 2;
296
297		// Add previous proposals
298		let mut last_hash = T::Hash::default();
299		for i in 0..p {
300			// Proposals should be different so that different proposal hashes are generated
301			let proposal: T::Proposal =
302				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; b as usize]) }.into();
303			Alliance::<T, I>::propose(
304				SystemOrigin::Signed(proposer.clone()).into(),
305				threshold,
306				Box::new(proposal.clone()),
307				bytes_in_storage,
308			)?;
309			last_hash = T::Hashing::hash_of(&proposal);
310			assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
311		}
312
313		let index = p - 1;
314		// Caller switches vote to nay on their own proposal, allowing them to be the deciding
315		// approval vote
316		Alliance::<T, I>::vote(
317			SystemOrigin::Signed(proposer.clone()).into(),
318			last_hash,
319			index,
320			false,
321		)?;
322
323		// Have almost everyone vote nay on last proposal, while keeping it from failing.
324		for j in 2..m - 1 {
325			let voter = &members[j as usize];
326			Alliance::<T, I>::vote(
327				SystemOrigin::Signed(voter.clone()).into(),
328				last_hash,
329				index,
330				false,
331			)?;
332		}
333
334		// Member zero is the first aye
335		Alliance::<T, I>::vote(
336			SystemOrigin::Signed(members[0].clone()).into(),
337			last_hash,
338			index,
339			true,
340		)?;
341
342		let voter = members[1].clone();
343		// Caller switches vote to aye, which passes the vote
344		Alliance::<T, I>::vote(SystemOrigin::Signed(voter.clone()).into(), last_hash, index, true)?;
345
346		#[extrinsic_call]
347		close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage);
348
349		assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
350		Ok(())
351	}
352
353	#[benchmark]
354	fn close_disapproved(
355		m: Linear<2, { T::MaxFellows::get() }>,
356		p: Linear<1, { T::MaxProposals::get() }>,
357	) -> Result<(), BenchmarkError> {
358		let bytes = 100;
359		let bytes_in_storage = bytes + size_of::<Cid>() as u32 + 32;
360
361		// Construct `members`.
362		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
363
364		let members = fellows.clone();
365
366		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
367
368		let proposer = members[0].clone();
369		let voter = members[1].clone();
370
371		// Threshold is one less than total members so that two nays will disapprove the vote
372		let threshold = m - 1;
373
374		// Add proposals
375		let mut last_hash = T::Hash::default();
376		for i in 0..p {
377			// Proposals should be different so that different proposal hashes are generated
378			let proposal: T::Proposal =
379				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; bytes as usize]) }.into();
380			Alliance::<T, I>::propose(
381				SystemOrigin::Signed(proposer.clone()).into(),
382				threshold,
383				Box::new(proposal.clone()),
384				bytes_in_storage,
385			)?;
386			last_hash = T::Hashing::hash_of(&proposal);
387			assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
388		}
389
390		let index = p - 1;
391		// Have almost everyone vote aye on last proposal, while keeping it from passing.
392		// A few abstainers will be the nay votes needed to fail the vote.
393		for j in 2..m - 1 {
394			let voter = &members[j as usize];
395			Alliance::<T, I>::vote(
396				SystemOrigin::Signed(voter.clone()).into(),
397				last_hash,
398				index,
399				true,
400			)?;
401		}
402
403		Alliance::<T, I>::vote(
404			SystemOrigin::Signed(voter.clone()).into(),
405			last_hash,
406			index,
407			false,
408		)?;
409
410		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
411
412		#[extrinsic_call]
413		close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage);
414
415		// The last proposal is removed.
416		assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
417		Ok(())
418	}
419
420	// We choose 5 fellows as a minimum so we always trigger a vote in the voting loop (`for j in
421	// ...`)
422	#[benchmark]
423	fn close_approved(
424		b: Linear<1, MAX_BYTES>,
425		m: Linear<5, { T::MaxFellows::get() }>,
426		p: Linear<1, { T::MaxProposals::get() }>,
427	) -> Result<(), BenchmarkError> {
428		let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
429
430		// Construct `members`.
431		let fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
432
433		let members = fellows.clone();
434
435		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows, vec![])?;
436
437		let proposer = members[0].clone();
438		// Threshold is two, so any two ayes will pass the vote
439		let threshold = 2;
440
441		// Add proposals
442		let mut last_hash = T::Hash::default();
443		for i in 0..p {
444			// Proposals should be different so that different proposal hashes are generated
445			let proposal: T::Proposal =
446				AllianceCall::<T, I>::set_rule { rule: rule(vec![i as u8; b as usize]) }.into();
447			Alliance::<T, I>::propose(
448				SystemOrigin::Signed(proposer.clone()).into(),
449				threshold,
450				Box::new(proposal.clone()),
451				bytes_in_storage,
452			)?;
453			last_hash = T::Hashing::hash_of(&proposal);
454			assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
455		}
456
457		// The prime member votes aye, so abstentions default to aye.
458		Alliance::<T, I>::vote(
459			SystemOrigin::Signed(proposer.clone()).into(),
460			last_hash,
461			p - 1,
462			true, // Vote aye.
463		)?;
464
465		let index = p - 1;
466		// Have almost everyone vote nay on last proposal, while keeping it from failing.
467		// A few abstainers will be the aye votes needed to pass the vote.
468		for j in 2..m - 1 {
469			let voter = &members[j as usize];
470			Alliance::<T, I>::vote(
471				SystemOrigin::Signed(voter.clone()).into(),
472				last_hash,
473				index,
474				false,
475			)?;
476		}
477
478		// caller is prime, prime already votes aye by creating the proposal
479		System::<T>::set_block_number(BlockNumberFor::<T>::max_value());
480
481		#[extrinsic_call]
482		close(SystemOrigin::Signed(proposer), last_hash, index, Weight::MAX, bytes_in_storage);
483
484		assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
485		Ok(())
486	}
487
488	#[benchmark]
489	fn init_members(
490		m: Linear<1, { T::MaxFellows::get() }>,
491		z: Linear<0, { T::MaxAllies::get() }>,
492	) -> Result<(), BenchmarkError> {
493		let mut fellows = (0..m).map(fellow::<T, I>).collect::<Vec<_>>();
494		let mut allies = (0..z).map(ally::<T, I>).collect::<Vec<_>>();
495
496		#[extrinsic_call]
497		_(SystemOrigin::Root, fellows.clone(), allies.clone());
498
499		fellows.sort();
500		allies.sort();
501		assert_last_event::<T, I>(
502			Event::MembersInitialized { fellows: fellows.clone(), allies: allies.clone() }.into(),
503		);
504		assert_eq!(Members::<T, I>::get(MemberRole::Fellow), fellows);
505		assert_eq!(Members::<T, I>::get(MemberRole::Ally), allies);
506		Ok(())
507	}
508
509	#[benchmark]
510	fn disband(
511		x: Linear<1, { T::MaxFellows::get() }>,
512		y: Linear<0, { T::MaxAllies::get() }>,
513		z: Linear<0, { T::MaxMembersCount::get() / 2 }>,
514	) -> Result<(), BenchmarkError> {
515		let fellows = (0..x).map(fellow::<T, I>).collect::<Vec<_>>();
516		let allies = (0..y).map(ally::<T, I>).collect::<Vec<_>>();
517		let witness = DisbandWitness { fellow_members: x, ally_members: y };
518
519		// setting the Alliance to disband on the benchmark call
520		Alliance::<T, I>::init_members(SystemOrigin::Root.into(), fellows.clone(), allies.clone())?;
521
522		// reserve deposits
523		let deposit = T::AllyDeposit::get();
524		for member in fellows.iter().chain(allies.iter()).take(z as usize) {
525			T::Currency::reserve(&member, deposit)?;
526			<DepositOf<T, I>>::insert(&member, deposit);
527		}
528
529		assert_eq!(Alliance::<T, I>::voting_members_count(), x);
530		assert_eq!(Alliance::<T, I>::ally_members_count(), y);
531
532		#[extrinsic_call]
533		_(SystemOrigin::Root, witness);
534
535		assert_last_event::<T, I>(
536			Event::AllianceDisbanded {
537				fellow_members: x,
538				ally_members: y,
539				unreserved: cmp::min(z, x + y),
540			}
541			.into(),
542		);
543
544		assert!(!Alliance::<T, I>::is_initialized());
545		Ok(())
546	}
547
548	#[benchmark]
549	fn set_rule() -> Result<(), BenchmarkError> {
550		set_members::<T, I>();
551
552		let rule = rule(b"hello world");
553
554		let call = Call::<T, I>::set_rule { rule: rule.clone() };
555		let origin =
556			T::AdminOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
557
558		#[block]
559		{
560			call.dispatch_bypass_filter(origin)?;
561		}
562		assert_eq!(Rule::<T, I>::get(), Some(rule.clone()));
563		assert_last_event::<T, I>(Event::NewRuleSet { rule }.into());
564		Ok(())
565	}
566
567	#[benchmark]
568	fn announce() -> Result<(), BenchmarkError> {
569		set_members::<T, I>();
570
571		let announcement = announcement(b"hello world");
572
573		let call = Call::<T, I>::announce { announcement: announcement.clone() };
574		let origin = T::AnnouncementOrigin::try_successful_origin()
575			.map_err(|_| BenchmarkError::Weightless)?;
576
577		#[block]
578		{
579			call.dispatch_bypass_filter(origin)?;
580		}
581
582		assert!(Announcements::<T, I>::get().contains(&announcement));
583		assert_last_event::<T, I>(Event::Announced { announcement }.into());
584		Ok(())
585	}
586
587	#[benchmark]
588	fn remove_announcement() -> Result<(), BenchmarkError> {
589		set_members::<T, I>();
590
591		let announcement = announcement(b"hello world");
592		let announcements: BoundedVec<_, T::MaxAnnouncementsCount> =
593			BoundedVec::try_from(vec![announcement.clone()]).unwrap();
594		Announcements::<T, I>::put(announcements);
595
596		let call = Call::<T, I>::remove_announcement { announcement: announcement.clone() };
597		let origin = T::AnnouncementOrigin::try_successful_origin()
598			.map_err(|_| BenchmarkError::Weightless)?;
599
600		#[block]
601		{
602			call.dispatch_bypass_filter(origin)?;
603		}
604
605		assert!(!Announcements::<T, I>::get().contains(&announcement));
606		assert_last_event::<T, I>(Event::AnnouncementRemoved { announcement }.into());
607		Ok(())
608	}
609
610	#[benchmark]
611	fn join_alliance() -> Result<(), BenchmarkError> {
612		set_members::<T, I>();
613
614		let outsider = outsider::<T, I>(1);
615		assert!(!Alliance::<T, I>::is_member(&outsider));
616		assert_eq!(DepositOf::<T, I>::get(&outsider), None);
617
618		#[extrinsic_call]
619		_(SystemOrigin::Signed(outsider.clone()));
620
621		assert!(Alliance::<T, I>::is_member_of(&outsider, MemberRole::Ally)); // outsider is now an ally
622		assert_eq!(DepositOf::<T, I>::get(&outsider), Some(T::AllyDeposit::get())); // with a deposit
623		assert!(!Alliance::<T, I>::has_voting_rights(&outsider)); // allies don't have voting rights
624		assert_last_event::<T, I>(
625			Event::NewAllyJoined {
626				ally: outsider,
627				nominator: None,
628				reserved: Some(T::AllyDeposit::get()),
629			}
630			.into(),
631		);
632		Ok(())
633	}
634
635	#[benchmark]
636	fn nominate_ally() -> Result<(), BenchmarkError> {
637		set_members::<T, I>();
638
639		let fellow1 = fellow::<T, I>(1);
640		assert!(Alliance::<T, I>::is_member_of(&fellow1, MemberRole::Fellow));
641
642		let outsider = outsider::<T, I>(1);
643		assert!(!Alliance::<T, I>::is_member(&outsider));
644		assert_eq!(DepositOf::<T, I>::get(&outsider), None);
645
646		let outsider_lookup = T::Lookup::unlookup(outsider.clone());
647
648		#[extrinsic_call]
649		_(SystemOrigin::Signed(fellow1.clone()), outsider_lookup);
650
651		assert!(Alliance::<T, I>::is_member_of(&outsider, MemberRole::Ally)); // outsider is now an ally
652		assert_eq!(DepositOf::<T, I>::get(&outsider), None); // without a deposit
653		assert!(!Alliance::<T, I>::has_voting_rights(&outsider)); // allies don't have voting rights
654		assert_last_event::<T, I>(
655			Event::NewAllyJoined { ally: outsider, nominator: Some(fellow1), reserved: None }
656				.into(),
657		);
658
659		Ok(())
660	}
661
662	#[benchmark]
663	fn elevate_ally() -> Result<(), BenchmarkError> {
664		set_members::<T, I>();
665
666		let ally1 = ally::<T, I>(1);
667		assert!(Alliance::<T, I>::is_ally(&ally1));
668
669		let ally1_lookup = T::Lookup::unlookup(ally1.clone());
670		let call = Call::<T, I>::elevate_ally { ally: ally1_lookup };
671		let origin = T::MembershipManager::try_successful_origin()
672			.map_err(|_| BenchmarkError::Weightless)?;
673
674		#[block]
675		{
676			call.dispatch_bypass_filter(origin)?;
677		}
678
679		assert!(!Alliance::<T, I>::is_ally(&ally1));
680		assert!(Alliance::<T, I>::has_voting_rights(&ally1));
681		assert_last_event::<T, I>(Event::AllyElevated { ally: ally1 }.into());
682		Ok(())
683	}
684
685	#[benchmark]
686	fn give_retirement_notice() -> Result<(), BenchmarkError> {
687		set_members::<T, I>();
688		let fellow2 = fellow::<T, I>(2);
689
690		assert!(Alliance::<T, I>::has_voting_rights(&fellow2));
691
692		#[extrinsic_call]
693		_(SystemOrigin::Signed(fellow2.clone()));
694
695		assert!(Alliance::<T, I>::is_member_of(&fellow2, MemberRole::Retiring));
696
697		assert_eq!(
698			RetiringMembers::<T, I>::get(&fellow2),
699			Some(System::<T>::block_number() + T::RetirementPeriod::get())
700		);
701		assert_last_event::<T, I>(Event::MemberRetirementPeriodStarted { member: fellow2 }.into());
702		Ok(())
703	}
704
705	#[benchmark]
706	fn retire() -> Result<(), BenchmarkError> {
707		set_members::<T, I>();
708
709		let fellow2 = fellow::<T, I>(2);
710		assert!(Alliance::<T, I>::has_voting_rights(&fellow2));
711
712		assert_eq!(
713			Alliance::<T, I>::give_retirement_notice(SystemOrigin::Signed(fellow2.clone()).into()),
714			Ok(())
715		);
716		System::<T>::set_block_number(System::<T>::block_number() + T::RetirementPeriod::get());
717
718		assert_eq!(DepositOf::<T, I>::get(&fellow2), Some(T::AllyDeposit::get()));
719
720		#[extrinsic_call]
721		_(SystemOrigin::Signed(fellow2.clone()));
722
723		assert!(!Alliance::<T, I>::is_member(&fellow2));
724		assert_eq!(DepositOf::<T, I>::get(&fellow2), None);
725		assert_last_event::<T, I>(
726			Event::MemberRetired { member: fellow2, unreserved: Some(T::AllyDeposit::get()) }
727				.into(),
728		);
729		Ok(())
730	}
731
732	#[benchmark]
733	fn kick_member() -> Result<(), BenchmarkError> {
734		set_members::<T, I>();
735
736		let fellow2 = fellow::<T, I>(2);
737		assert!(Alliance::<T, I>::is_member_of(&fellow2, MemberRole::Fellow));
738		assert_eq!(DepositOf::<T, I>::get(&fellow2), Some(T::AllyDeposit::get()));
739
740		let fellow2_lookup = T::Lookup::unlookup(fellow2.clone());
741		let call = Call::<T, I>::kick_member { who: fellow2_lookup };
742		let origin = T::MembershipManager::try_successful_origin()
743			.map_err(|_| BenchmarkError::Weightless)?;
744
745		#[block]
746		{
747			call.dispatch_bypass_filter(origin)?;
748		}
749
750		assert!(!Alliance::<T, I>::is_member(&fellow2));
751		assert_eq!(DepositOf::<T, I>::get(&fellow2), None);
752		assert_last_event::<T, I>(
753			Event::MemberKicked { member: fellow2, slashed: Some(T::AllyDeposit::get()) }.into(),
754		);
755		Ok(())
756	}
757
758	#[benchmark]
759	fn add_unscrupulous_items(
760		n: Linear<0, { T::MaxUnscrupulousItems::get() }>,
761		l: Linear<0, { T::MaxWebsiteUrlLength::get() }>,
762	) -> Result<(), BenchmarkError> {
763		set_members::<T, I>();
764
765		let accounts = (0..n).map(|i| generate_unscrupulous_account::<T, I>(i)).collect::<Vec<_>>();
766		let websites = (0..n)
767			.map(|i| -> BoundedVec<u8, T::MaxWebsiteUrlLength> {
768				BoundedVec::try_from(vec![i as u8; l as usize]).unwrap()
769			})
770			.collect::<Vec<_>>();
771
772		let mut unscrupulous_list = Vec::with_capacity(accounts.len() + websites.len());
773		unscrupulous_list.extend(accounts.into_iter().map(UnscrupulousItem::AccountId));
774		unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website));
775
776		let call = Call::<T, I>::add_unscrupulous_items { items: unscrupulous_list.clone() };
777		let origin = T::AnnouncementOrigin::try_successful_origin()
778			.map_err(|_| BenchmarkError::Weightless)?;
779
780		#[block]
781		{
782			call.dispatch_bypass_filter(origin)?;
783		}
784
785		assert_last_event::<T, I>(Event::UnscrupulousItemAdded { items: unscrupulous_list }.into());
786		Ok(())
787	}
788
789	#[benchmark]
790	fn remove_unscrupulous_items(
791		n: Linear<0, { T::MaxUnscrupulousItems::get() }>,
792		l: Linear<0, { T::MaxWebsiteUrlLength::get() }>,
793	) -> Result<(), BenchmarkError> {
794		set_members::<T, I>();
795
796		let mut accounts =
797			(0..n).map(|i| generate_unscrupulous_account::<T, I>(i)).collect::<Vec<_>>();
798		accounts.sort();
799		let accounts: BoundedVec<_, T::MaxUnscrupulousItems> = accounts.try_into().unwrap();
800		UnscrupulousAccounts::<T, I>::put(accounts.clone());
801
802		let mut websites = (0..n)
803			.map(|i| -> BoundedVec<_, T::MaxWebsiteUrlLength> {
804				BoundedVec::try_from(vec![i as u8; l as usize]).unwrap()
805			})
806			.collect::<Vec<_>>();
807		websites.sort();
808		let websites: BoundedVec<_, T::MaxUnscrupulousItems> = websites.try_into().unwrap();
809		UnscrupulousWebsites::<T, I>::put(websites.clone());
810
811		let mut unscrupulous_list = Vec::with_capacity(accounts.len() + websites.len());
812		unscrupulous_list.extend(accounts.into_iter().map(UnscrupulousItem::AccountId));
813		unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website));
814
815		let call = Call::<T, I>::remove_unscrupulous_items { items: unscrupulous_list.clone() };
816		let origin = T::AnnouncementOrigin::try_successful_origin()
817			.map_err(|_| BenchmarkError::Weightless)?;
818
819		#[block]
820		{
821			call.dispatch_bypass_filter(origin)?;
822		}
823
824		assert_last_event::<T, I>(
825			Event::UnscrupulousItemRemoved { items: unscrupulous_list }.into(),
826		);
827		Ok(())
828	}
829
830	#[benchmark]
831	fn abdicate_fellow_status() -> Result<(), BenchmarkError> {
832		set_members::<T, I>();
833		let fellow2 = fellow::<T, I>(2);
834		assert!(Alliance::<T, I>::has_voting_rights(&fellow2));
835
836		#[extrinsic_call]
837		_(SystemOrigin::Signed(fellow2.clone()));
838
839		assert_last_event::<T, I>(Event::FellowAbdicated { fellow: fellow2 }.into());
840		Ok(())
841	}
842
843	impl_benchmark_test_suite!(Alliance, crate::mock::new_bench_ext(), crate::mock::Test);
844}