referrerpolicy=no-referrer-when-downgrade

pallet_multisig/
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// Benchmarks for Multisig Pallet
19
20#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use frame::benchmarking::prelude::*;
24
25use crate::Pallet as Multisig;
26
27const SEED: u32 = 0;
28
29fn setup_multi<T: Config>(
30	s: u32,
31	z: u32,
32) -> Result<(Vec<T::AccountId>, Box<<T as Config>::RuntimeCall>), &'static str> {
33	let mut signatories: Vec<T::AccountId> = Vec::new();
34	for i in 0..s {
35		let signatory = account("signatory", i, SEED);
36		// Give them some balance for a possible deposit
37		let balance = BalanceOf::<T>::max_value();
38		T::Currency::make_free_balance_be(&signatory, balance);
39		signatories.push(signatory);
40	}
41	signatories.sort();
42	// Must first convert to runtime call type.
43	let call: <T as Config>::RuntimeCall =
44		frame_system::Call::<T>::remark { remark: vec![0; z as usize] }.into();
45	Ok((signatories, Box::new(call)))
46}
47
48#[benchmarks]
49mod benchmarks {
50	use super::*;
51
52	/// `z`: Transaction Length
53	#[benchmark]
54	fn as_multi_threshold_1(z: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
55		let max_signatories = T::MaxSignatories::get().into();
56		let (mut signatories, _) = setup_multi::<T>(max_signatories, z)?;
57		let call: <T as Config>::RuntimeCall =
58			frame_system::Call::<T>::remark { remark: vec![0; z as usize] }.into();
59		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
60		// Whitelist caller account from further DB operations.
61		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
62		add_to_whitelist(caller_key.into());
63
64		#[extrinsic_call]
65		_(RawOrigin::Signed(caller.clone()), signatories, Box::new(call));
66
67		// If the benchmark resolves, then the call was dispatched successfully.
68		Ok(())
69	}
70
71	/// `z`: Transaction Length
72	/// `s`: Signatories, need at least 2 people
73	#[benchmark]
74	fn as_multi_create(
75		s: Linear<2, { T::MaxSignatories::get() }>,
76		z: Linear<0, 10_000>,
77	) -> Result<(), BenchmarkError> {
78		let (mut signatories, call) = setup_multi::<T>(s, z)?;
79		let call_hash = call.using_encoded(blake2_256);
80		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
81		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
82		// Whitelist caller account from further DB operations.
83		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
84		add_to_whitelist(caller_key.into());
85
86		#[extrinsic_call]
87		as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, Weight::zero());
88
89		assert!(Multisigs::<T>::contains_key(multi_account_id, call_hash));
90
91		Ok(())
92	}
93
94	/// `z`: Transaction Length
95	/// `s`: Signatories, need at least 3 people (so we don't complete the multisig)
96	#[benchmark]
97	fn as_multi_approve(
98		s: Linear<3, { T::MaxSignatories::get() }>,
99		z: Linear<0, 10_000>,
100	) -> Result<(), BenchmarkError> {
101		let (mut signatories, call) = setup_multi::<T>(s, z)?;
102		let call_hash = call.using_encoded(blake2_256);
103		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
104		let mut signatories2 = signatories.clone();
105		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
106		// before the call, get the timepoint
107		let timepoint = Multisig::<T>::timepoint();
108		// Create the multi
109		Multisig::<T>::as_multi(
110			RawOrigin::Signed(caller).into(),
111			s as u16,
112			signatories,
113			None,
114			call.clone(),
115			Weight::zero(),
116		)?;
117		let caller2 = signatories2.remove(0);
118		// Whitelist caller account from further DB operations.
119		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2);
120		add_to_whitelist(caller_key.into());
121
122		#[extrinsic_call]
123		as_multi(
124			RawOrigin::Signed(caller2),
125			s as u16,
126			signatories2,
127			Some(timepoint),
128			call,
129			Weight::zero(),
130		);
131
132		let multisig =
133			Multisigs::<T>::get(multi_account_id, call_hash).ok_or("multisig not created")?;
134		assert_eq!(multisig.approvals.len(), 2);
135
136		Ok(())
137	}
138
139	/// `z`: Transaction Length
140	/// `s`: Signatories, need at least 2 people
141	#[benchmark]
142	fn as_multi_complete(
143		s: Linear<2, { T::MaxSignatories::get() }>,
144		z: Linear<0, 10_000>,
145	) -> Result<(), BenchmarkError> {
146		let (mut signatories, call) = setup_multi::<T>(s, z)?;
147		let call_hash = call.using_encoded(blake2_256);
148		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
149		let mut signatories2 = signatories.clone();
150		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
151		// before the call, get the timepoint
152		let timepoint = Multisig::<T>::timepoint();
153		// Create the multi
154		Multisig::<T>::as_multi(
155			RawOrigin::Signed(caller).into(),
156			s as u16,
157			signatories,
158			None,
159			call.clone(),
160			Weight::zero(),
161		)?;
162		// Everyone except the first person approves
163		for i in 1..s - 1 {
164			let mut signatories_loop = signatories2.clone();
165			let caller_loop = signatories_loop.remove(i as usize);
166			let o = RawOrigin::Signed(caller_loop).into();
167			Multisig::<T>::as_multi(
168				o,
169				s as u16,
170				signatories_loop,
171				Some(timepoint),
172				call.clone(),
173				Weight::zero(),
174			)?;
175		}
176		let caller2 = signatories2.remove(0);
177		assert!(Multisigs::<T>::contains_key(&multi_account_id, call_hash));
178		// Whitelist caller account from further DB operations.
179		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2);
180		add_to_whitelist(caller_key.into());
181
182		#[extrinsic_call]
183		as_multi(
184			RawOrigin::Signed(caller2),
185			s as u16,
186			signatories2,
187			Some(timepoint),
188			call,
189			Weight::MAX,
190		);
191
192		assert!(!Multisigs::<T>::contains_key(&multi_account_id, call_hash));
193
194		Ok(())
195	}
196
197	/// `s`: Signatories, need at least 2 people
198	#[benchmark]
199	fn approve_as_multi_create(
200		s: Linear<2, { T::MaxSignatories::get() }>,
201	) -> Result<(), BenchmarkError> {
202		// The call is neither in storage or an argument, so just use any:
203		let call_len = 10_000;
204		let (mut signatories, call) = setup_multi::<T>(s, call_len)?;
205		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
206		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
207		let call_hash = call.using_encoded(blake2_256);
208		// Whitelist caller account from further DB operations.
209		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
210		add_to_whitelist(caller_key.into());
211
212		// Create the multi
213		#[extrinsic_call]
214		approve_as_multi(
215			RawOrigin::Signed(caller),
216			s as u16,
217			signatories,
218			None,
219			call_hash,
220			Weight::zero(),
221		);
222
223		assert!(Multisigs::<T>::contains_key(multi_account_id, call_hash));
224
225		Ok(())
226	}
227
228	/// `s`: Signatories, need at least 2 people
229	#[benchmark]
230	fn approve_as_multi_approve(
231		s: Linear<2, { T::MaxSignatories::get() }>,
232	) -> Result<(), BenchmarkError> {
233		// The call is neither in storage or an argument, so just use any:
234		let call_len = 10_000;
235		let (mut signatories, call) = setup_multi::<T>(s, call_len)?;
236		let mut signatories2 = signatories.clone();
237		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
238		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
239		let call_hash = call.using_encoded(blake2_256);
240		// before the call, get the timepoint
241		let timepoint = Multisig::<T>::timepoint();
242		// Create the multi
243		Multisig::<T>::as_multi(
244			RawOrigin::Signed(caller).into(),
245			s as u16,
246			signatories,
247			None,
248			call,
249			Weight::zero(),
250		)?;
251		let caller2 = signatories2.remove(0);
252		// Whitelist caller account from further DB operations.
253		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2);
254		add_to_whitelist(caller_key.into());
255
256		#[extrinsic_call]
257		approve_as_multi(
258			RawOrigin::Signed(caller2),
259			s as u16,
260			signatories2,
261			Some(timepoint),
262			call_hash,
263			Weight::zero(),
264		);
265
266		let multisig =
267			Multisigs::<T>::get(multi_account_id, call_hash).ok_or("multisig not created")?;
268		assert_eq!(multisig.approvals.len(), 2);
269
270		Ok(())
271	}
272
273	/// `s`: Signatories, need at least 2 people
274	#[benchmark]
275	fn cancel_as_multi(s: Linear<2, { T::MaxSignatories::get() }>) -> Result<(), BenchmarkError> {
276		// The call is neither in storage or an argument, so just use any:
277		let call_len = 10_000;
278		let (mut signatories, call) = setup_multi::<T>(s, call_len)?;
279		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
280		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
281		let call_hash = call.using_encoded(blake2_256);
282		let timepoint = Multisig::<T>::timepoint();
283		// Create the multi
284		let o = RawOrigin::Signed(caller.clone()).into();
285		Multisig::<T>::as_multi(o, s as u16, signatories.clone(), None, call, Weight::zero())?;
286		assert!(Multisigs::<T>::contains_key(&multi_account_id, call_hash));
287		// Whitelist caller account from further DB operations.
288		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
289		add_to_whitelist(caller_key.into());
290
291		#[extrinsic_call]
292		_(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash);
293
294		assert!(!Multisigs::<T>::contains_key(multi_account_id, call_hash));
295
296		Ok(())
297	}
298
299	/// `s`: Signatories, need at least 2 people
300	#[benchmark]
301	fn poke_deposit(s: Linear<2, { T::MaxSignatories::get() }>) -> Result<(), BenchmarkError> {
302		// The call is neither in storage or an argument, so just use any:
303		let call_len = 10_000;
304		let (mut signatories, call) = setup_multi::<T>(s, call_len)?;
305		let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap());
306		let caller = signatories.pop().ok_or("signatories should have len 2 or more")?;
307		let call_hash = call.using_encoded(blake2_256);
308		// Create the multi
309		Multisig::<T>::as_multi(
310			RawOrigin::Signed(caller.clone()).into(),
311			s as u16,
312			signatories.clone(),
313			None,
314			call,
315			Weight::zero(),
316		)?;
317
318		// Get the current multisig data
319		let multisig = Multisigs::<T>::get(multi_account_id.clone(), call_hash)
320			.ok_or("multisig not created")?;
321		// The original deposit
322		let old_deposit = multisig.deposit;
323		assert_eq!(T::Currency::reserved_balance(&caller), old_deposit);
324
325		let additional_amount = 2u32.into();
326		let new_deposit = old_deposit.saturating_add(additional_amount);
327
328		// Reserve the additional amount from the caller's balance
329		T::Currency::reserve(&caller, additional_amount)?;
330		assert_eq!(T::Currency::reserved_balance(&caller), new_deposit);
331		// Update the storage with the new deposit
332		Multisigs::<T>::try_mutate(
333			&multi_account_id,
334			call_hash,
335			|maybe_multisig| -> DispatchResult {
336				let mut multisig = maybe_multisig.take().ok_or(Error::<T>::NotFound)?;
337				multisig.deposit = new_deposit;
338				*maybe_multisig = Some(multisig);
339				Ok(())
340			},
341		)
342		.map_err(|_| BenchmarkError::Stop("Mutating storage to change deposits failed"))?;
343		// Check that the deposit was updated in storage
344		let multisig = Multisigs::<T>::get(multi_account_id.clone(), call_hash)
345			.ok_or("Multisig not created")?;
346		assert_eq!(multisig.deposit, new_deposit);
347
348		// Whitelist caller account
349		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
350		add_to_whitelist(caller_key.into());
351
352		#[extrinsic_call]
353		_(RawOrigin::Signed(caller.clone()), s as u16, signatories, call_hash);
354
355		let multisig = Multisigs::<T>::get(multi_account_id.clone(), call_hash)
356			.ok_or("Multisig not created")?;
357		assert_eq!(multisig.deposit, old_deposit);
358		assert_eq!(T::Currency::reserved_balance(&caller), old_deposit);
359		Ok(())
360	}
361
362	impl_benchmark_test_suite!(Multisig, crate::tests::new_test_ext(), crate::tests::Test);
363}