referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_common/claims/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Benchmarking for claims pallet
18
19#[cfg(feature = "runtime-benchmarks")]
20use super::*;
21use crate::claims::Call;
22use frame_benchmarking::v2::*;
23use frame_support::{
24	dispatch::{DispatchInfo, GetDispatchInfo},
25	traits::UnfilteredDispatchable,
26};
27use frame_system::RawOrigin;
28use secp_utils::*;
29use sp_runtime::{
30	traits::{DispatchTransaction, ValidateUnsigned},
31	DispatchResult,
32};
33
34const SEED: u32 = 0;
35
36const MAX_CLAIMS: u32 = 10_000;
37const VALUE: u32 = 1_000_000;
38
39fn create_claim<T: Config>(input: u32) -> DispatchResult {
40	let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&input.encode())).unwrap();
41	let eth_address = eth(&secret_key);
42	let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
43	super::Pallet::<T>::mint_claim(
44		RawOrigin::Root.into(),
45		eth_address,
46		VALUE.into(),
47		vesting,
48		None,
49	)?;
50	Ok(())
51}
52
53fn create_claim_attest<T: Config>(input: u32) -> DispatchResult {
54	let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&input.encode())).unwrap();
55	let eth_address = eth(&secret_key);
56	let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
57	super::Pallet::<T>::mint_claim(
58		RawOrigin::Root.into(),
59		eth_address,
60		VALUE.into(),
61		vesting,
62		Some(Default::default()),
63	)?;
64	Ok(())
65}
66
67#[benchmarks(
68		where
69			<T as frame_system::Config>::RuntimeCall: IsSubType<Call<T>> + From<Call<T>>,
70			<T as frame_system::Config>::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
71			<<T as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + AsTransactionAuthorizedOrigin + Clone,
72			<<T as frame_system::Config>::RuntimeCall as Dispatchable>::PostInfo: Default,
73	)]
74mod benchmarks {
75	use super::*;
76
77	// Benchmark `claim` including `validate_unsigned` logic.
78	#[benchmark]
79	fn claim() -> Result<(), BenchmarkError> {
80		let c = MAX_CLAIMS;
81		for _ in 0..c / 2 {
82			create_claim::<T>(c)?;
83			create_claim_attest::<T>(u32::MAX - c)?;
84		}
85		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&c.encode())).unwrap();
86		let eth_address = eth(&secret_key);
87		let account: T::AccountId = account("user", c, SEED);
88		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
89		let signature = sig::<T>(&secret_key, &account.encode(), &[][..]);
90		super::Pallet::<T>::mint_claim(
91			RawOrigin::Root.into(),
92			eth_address,
93			VALUE.into(),
94			vesting,
95			None,
96		)?;
97		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
98		let source = sp_runtime::transaction_validity::TransactionSource::External;
99		let call_enc =
100			Call::<T>::claim { dest: account.clone(), ethereum_signature: signature.clone() }
101				.encode();
102
103		#[block]
104		{
105			let call = <Call<T> as Decode>::decode(&mut &*call_enc)
106				.expect("call is encoded above, encoding must be correct");
107			super::Pallet::<T>::validate_unsigned(source, &call)
108				.map_err(|e| -> &'static str { e.into() })?;
109			call.dispatch_bypass_filter(RawOrigin::None.into())?;
110		}
111
112		assert_eq!(Claims::<T>::get(eth_address), None);
113		Ok(())
114	}
115
116	// Benchmark `mint_claim` when there already exists `c` claims in storage.
117	#[benchmark]
118	fn mint_claim() -> Result<(), BenchmarkError> {
119		let c = MAX_CLAIMS;
120		for _ in 0..c / 2 {
121			create_claim::<T>(c)?;
122			create_claim_attest::<T>(u32::MAX - c)?;
123		}
124		let eth_address = account("eth_address", 0, SEED);
125		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
126		let statement = StatementKind::Regular;
127
128		#[extrinsic_call]
129		_(RawOrigin::Root, eth_address, VALUE.into(), vesting, Some(statement));
130
131		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
132		Ok(())
133	}
134
135	// Benchmark `claim_attest` including `validate_unsigned` logic.
136	#[benchmark]
137	fn claim_attest() -> Result<(), BenchmarkError> {
138		let c = MAX_CLAIMS;
139		for _ in 0..c / 2 {
140			create_claim::<T>(c)?;
141			create_claim_attest::<T>(u32::MAX - c)?;
142		}
143		// Crate signature
144		let attest_c = u32::MAX - c;
145		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
146		let eth_address = eth(&secret_key);
147		let account: T::AccountId = account("user", c, SEED);
148		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
149		let statement = StatementKind::Regular;
150		let signature = sig::<T>(&secret_key, &account.encode(), statement.to_text());
151		super::Pallet::<T>::mint_claim(
152			RawOrigin::Root.into(),
153			eth_address,
154			VALUE.into(),
155			vesting,
156			Some(statement),
157		)?;
158		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
159		let call_enc = Call::<T>::claim_attest {
160			dest: account.clone(),
161			ethereum_signature: signature.clone(),
162			statement: StatementKind::Regular.to_text().to_vec(),
163		}
164		.encode();
165		let source = sp_runtime::transaction_validity::TransactionSource::External;
166
167		#[block]
168		{
169			let call = <Call<T> as Decode>::decode(&mut &*call_enc)
170				.expect("call is encoded above, encoding must be correct");
171			super::Pallet::<T>::validate_unsigned(source, &call)
172				.map_err(|e| -> &'static str { e.into() })?;
173			call.dispatch_bypass_filter(RawOrigin::None.into())?;
174		}
175
176		assert_eq!(Claims::<T>::get(eth_address), None);
177		Ok(())
178	}
179
180	// Benchmark `attest` including prevalidate logic.
181	#[benchmark]
182	fn attest() -> Result<(), BenchmarkError> {
183		let c = MAX_CLAIMS;
184		for _ in 0..c / 2 {
185			create_claim::<T>(c)?;
186			create_claim_attest::<T>(u32::MAX - c)?;
187		}
188		let attest_c = u32::MAX - c;
189		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
190		let eth_address = eth(&secret_key);
191		let account: T::AccountId = account("user", c, SEED);
192		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
193		let statement = StatementKind::Regular;
194		super::Pallet::<T>::mint_claim(
195			RawOrigin::Root.into(),
196			eth_address,
197			VALUE.into(),
198			vesting,
199			Some(statement),
200		)?;
201		Preclaims::<T>::insert(&account, eth_address);
202		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
203
204		let stmt = StatementKind::Regular.to_text().to_vec();
205
206		#[extrinsic_call]
207		_(RawOrigin::Signed(account), stmt);
208
209		assert_eq!(Claims::<T>::get(eth_address), None);
210		Ok(())
211	}
212
213	#[benchmark]
214	fn move_claim() -> Result<(), BenchmarkError> {
215		let c = MAX_CLAIMS;
216		for _ in 0..c / 2 {
217			create_claim::<T>(c)?;
218			create_claim_attest::<T>(u32::MAX - c)?;
219		}
220		let attest_c = u32::MAX - c;
221		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
222		let eth_address = eth(&secret_key);
223
224		let new_secret_key =
225			libsecp256k1::SecretKey::parse(&keccak_256(&(u32::MAX / 2).encode())).unwrap();
226		let new_eth_address = eth(&new_secret_key);
227
228		let account: T::AccountId = account("user", c, SEED);
229		Preclaims::<T>::insert(&account, eth_address);
230
231		assert!(Claims::<T>::contains_key(eth_address));
232		assert!(!Claims::<T>::contains_key(new_eth_address));
233
234		#[extrinsic_call]
235		_(RawOrigin::Root, eth_address, new_eth_address, Some(account));
236
237		assert!(!Claims::<T>::contains_key(eth_address));
238		assert!(Claims::<T>::contains_key(new_eth_address));
239		Ok(())
240	}
241
242	// Benchmark the time it takes to do `repeat` number of keccak256 hashes
243	#[benchmark(extra)]
244	fn keccak256(i: Linear<0, 10_000>) {
245		let bytes = (i).encode();
246
247		#[block]
248		{
249			for _ in 0..i {
250				let _hash = keccak_256(&bytes);
251			}
252		}
253	}
254
255	// Benchmark the time it takes to do `repeat` number of `eth_recover`
256	#[benchmark(extra)]
257	fn eth_recover(i: Linear<0, 1_000>) {
258		// Crate signature
259		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&i.encode())).unwrap();
260		let account: T::AccountId = account("user", i, SEED);
261		let signature = sig::<T>(&secret_key, &account.encode(), &[][..]);
262		let data = account.using_encoded(to_ascii_hex);
263		let extra = StatementKind::default().to_text();
264
265		#[block]
266		{
267			for _ in 0..i {
268				assert!(super::Pallet::<T>::eth_recover(&signature, &data, extra).is_some());
269			}
270		}
271	}
272
273	#[benchmark]
274	fn prevalidate_attests() -> Result<(), BenchmarkError> {
275		let c = MAX_CLAIMS;
276		for _ in 0..c / 2 {
277			create_claim::<T>(c)?;
278			create_claim_attest::<T>(u32::MAX - c)?;
279		}
280		let ext = PrevalidateAttests::<T>::new();
281		let call = super::Call::attest { statement: StatementKind::Regular.to_text().to_vec() };
282		let call: <T as frame_system::Config>::RuntimeCall = call.into();
283		let info = call.get_dispatch_info();
284		let attest_c = u32::MAX - c;
285		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
286		let eth_address = eth(&secret_key);
287		let account: T::AccountId = account("user", c, SEED);
288		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
289		let statement = StatementKind::Regular;
290		super::Pallet::<T>::mint_claim(
291			RawOrigin::Root.into(),
292			eth_address,
293			VALUE.into(),
294			vesting,
295			Some(statement),
296		)?;
297		Preclaims::<T>::insert(&account, eth_address);
298		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
299
300		#[block]
301		{
302			assert!(ext
303				.test_run(RawOrigin::Signed(account).into(), &call, &info, 0, 0, |_| {
304					Ok(Default::default())
305				})
306				.unwrap()
307				.is_ok());
308		}
309
310		Ok(())
311	}
312
313	impl_benchmark_test_suite!(
314		Pallet,
315		crate::claims::mock::new_test_ext(),
316		crate::claims::mock::Test,
317	);
318}