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::{traits::DispatchTransaction, DispatchResult};
30
31const SEED: u32 = 0;
32
33const MAX_CLAIMS: u32 = 10_000;
34const VALUE: u32 = 1_000_000;
35
36fn create_claim<T: Config>(input: u32) -> DispatchResult {
37	let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&input.encode())).unwrap();
38	let eth_address = eth(&secret_key);
39	let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
40	super::Pallet::<T>::mint_claim(
41		RawOrigin::Root.into(),
42		eth_address,
43		VALUE.into(),
44		vesting,
45		None,
46	)?;
47	Ok(())
48}
49
50fn create_claim_attest<T: Config>(input: u32) -> DispatchResult {
51	let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&input.encode())).unwrap();
52	let eth_address = eth(&secret_key);
53	let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
54	super::Pallet::<T>::mint_claim(
55		RawOrigin::Root.into(),
56		eth_address,
57		VALUE.into(),
58		vesting,
59		Some(Default::default()),
60	)?;
61	Ok(())
62}
63
64#[benchmarks(
65		where
66			<T as frame_system::Config>::RuntimeCall: IsSubType<Call<T>> + From<Call<T>>,
67			<T as frame_system::Config>::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
68			<<T as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + AsTransactionAuthorizedOrigin + Clone,
69			<<T as frame_system::Config>::RuntimeCall as Dispatchable>::PostInfo: Default,
70	)]
71mod benchmarks {
72	use super::*;
73
74	#[allow(deprecated)]
75	use sp_runtime::traits::ValidateUnsigned;
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			#[allow(deprecated)]
108			super::Pallet::<T>::validate_unsigned(source, &call)
109				.map_err(|e| -> &'static str { e.into() })?;
110			call.dispatch_bypass_filter(RawOrigin::None.into())?;
111		}
112
113		assert_eq!(Claims::<T>::get(eth_address), None);
114		Ok(())
115	}
116
117	// Benchmark `mint_claim` when there already exists `c` claims in storage.
118	#[benchmark]
119	fn mint_claim() -> Result<(), BenchmarkError> {
120		let c = MAX_CLAIMS;
121		for _ in 0..c / 2 {
122			create_claim::<T>(c)?;
123			create_claim_attest::<T>(u32::MAX - c)?;
124		}
125		let eth_address = account("eth_address", 0, SEED);
126		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
127		let statement = StatementKind::Regular;
128
129		#[extrinsic_call]
130		_(RawOrigin::Root, eth_address, VALUE.into(), vesting, Some(statement));
131
132		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
133		Ok(())
134	}
135
136	// Benchmark `claim_attest` including `validate_unsigned` logic.
137	#[benchmark]
138	fn claim_attest() -> Result<(), BenchmarkError> {
139		let c = MAX_CLAIMS;
140		for _ in 0..c / 2 {
141			create_claim::<T>(c)?;
142			create_claim_attest::<T>(u32::MAX - c)?;
143		}
144		// Crate signature
145		let attest_c = u32::MAX - c;
146		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
147		let eth_address = eth(&secret_key);
148		let account: T::AccountId = account("user", c, SEED);
149		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
150		let statement = StatementKind::Regular;
151		let signature = sig::<T>(&secret_key, &account.encode(), statement.to_text());
152		super::Pallet::<T>::mint_claim(
153			RawOrigin::Root.into(),
154			eth_address,
155			VALUE.into(),
156			vesting,
157			Some(statement),
158		)?;
159		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
160		let call_enc = Call::<T>::claim_attest {
161			dest: account.clone(),
162			ethereum_signature: signature.clone(),
163			statement: StatementKind::Regular.to_text().to_vec(),
164		}
165		.encode();
166		let source = sp_runtime::transaction_validity::TransactionSource::External;
167
168		#[block]
169		{
170			let call = <Call<T> as Decode>::decode(&mut &*call_enc)
171				.expect("call is encoded above, encoding must be correct");
172			#[allow(deprecated)]
173			super::Pallet::<T>::validate_unsigned(source, &call)
174				.map_err(|e| -> &'static str { e.into() })?;
175			call.dispatch_bypass_filter(RawOrigin::None.into())?;
176		}
177
178		assert_eq!(Claims::<T>::get(eth_address), None);
179		Ok(())
180	}
181
182	// Benchmark `attest` including prevalidate logic.
183	#[benchmark]
184	fn attest() -> Result<(), BenchmarkError> {
185		let c = MAX_CLAIMS;
186		for _ in 0..c / 2 {
187			create_claim::<T>(c)?;
188			create_claim_attest::<T>(u32::MAX - c)?;
189		}
190		let attest_c = u32::MAX - c;
191		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
192		let eth_address = eth(&secret_key);
193		let account: T::AccountId = account("user", c, SEED);
194		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
195		let statement = StatementKind::Regular;
196		super::Pallet::<T>::mint_claim(
197			RawOrigin::Root.into(),
198			eth_address,
199			VALUE.into(),
200			vesting,
201			Some(statement),
202		)?;
203		Preclaims::<T>::insert(&account, eth_address);
204		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
205
206		let stmt = StatementKind::Regular.to_text().to_vec();
207
208		#[extrinsic_call]
209		_(RawOrigin::Signed(account), stmt);
210
211		assert_eq!(Claims::<T>::get(eth_address), None);
212		Ok(())
213	}
214
215	#[benchmark]
216	fn move_claim() -> Result<(), BenchmarkError> {
217		let c = MAX_CLAIMS;
218		for _ in 0..c / 2 {
219			create_claim::<T>(c)?;
220			create_claim_attest::<T>(u32::MAX - c)?;
221		}
222		let attest_c = u32::MAX - c;
223		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
224		let eth_address = eth(&secret_key);
225
226		let new_secret_key =
227			libsecp256k1::SecretKey::parse(&keccak_256(&(u32::MAX / 2).encode())).unwrap();
228		let new_eth_address = eth(&new_secret_key);
229
230		let account: T::AccountId = account("user", c, SEED);
231		Preclaims::<T>::insert(&account, eth_address);
232
233		assert!(Claims::<T>::contains_key(eth_address));
234		assert!(!Claims::<T>::contains_key(new_eth_address));
235
236		#[extrinsic_call]
237		_(RawOrigin::Root, eth_address, new_eth_address, Some(account));
238
239		assert!(!Claims::<T>::contains_key(eth_address));
240		assert!(Claims::<T>::contains_key(new_eth_address));
241		Ok(())
242	}
243
244	// Benchmark the time it takes to do `repeat` number of keccak256 hashes
245	#[benchmark(extra)]
246	fn keccak256(i: Linear<0, 10_000>) {
247		let bytes = (i).encode();
248
249		#[block]
250		{
251			for _ in 0..i {
252				let _hash = keccak_256(&bytes);
253			}
254		}
255	}
256
257	// Benchmark the time it takes to do `repeat` number of `eth_recover`
258	#[benchmark(extra)]
259	fn eth_recover(i: Linear<0, 1_000>) {
260		// Crate signature
261		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&i.encode())).unwrap();
262		let account: T::AccountId = account("user", i, SEED);
263		let signature = sig::<T>(&secret_key, &account.encode(), &[][..]);
264		let data = account.using_encoded(to_ascii_hex);
265		let extra = StatementKind::default().to_text();
266
267		#[block]
268		{
269			for _ in 0..i {
270				assert!(super::Pallet::<T>::eth_recover(&signature, &data, extra).is_some());
271			}
272		}
273	}
274
275	#[benchmark]
276	fn prevalidate_attests() -> Result<(), BenchmarkError> {
277		let c = MAX_CLAIMS;
278		for _ in 0..c / 2 {
279			create_claim::<T>(c)?;
280			create_claim_attest::<T>(u32::MAX - c)?;
281		}
282		let ext = PrevalidateAttests::<T>::new();
283		let call = super::Call::attest { statement: StatementKind::Regular.to_text().to_vec() };
284		let call: <T as frame_system::Config>::RuntimeCall = call.into();
285		let info = call.get_dispatch_info();
286		let attest_c = u32::MAX - c;
287		let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap();
288		let eth_address = eth(&secret_key);
289		let account: T::AccountId = account("user", c, SEED);
290		let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
291		let statement = StatementKind::Regular;
292		super::Pallet::<T>::mint_claim(
293			RawOrigin::Root.into(),
294			eth_address,
295			VALUE.into(),
296			vesting,
297			Some(statement),
298		)?;
299		Preclaims::<T>::insert(&account, eth_address);
300		assert_eq!(Claims::<T>::get(eth_address), Some(VALUE.into()));
301
302		#[block]
303		{
304			assert!(ext
305				.test_run(RawOrigin::Signed(account).into(), &call, &info, 0, 0, |_| {
306					Ok(Default::default())
307				})
308				.unwrap()
309				.is_ok());
310		}
311
312		Ok(())
313	}
314
315	impl_benchmark_test_suite!(
316		Pallet,
317		crate::claims::mock::new_test_ext(),
318		crate::claims::mock::Test,
319	);
320}