referrerpolicy=no-referrer-when-downgrade

cumulus_test_service/
bench_utils.rs

1// This file is part of Cumulus.
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
18use codec::Encode;
19use sc_block_builder::BlockBuilderBuilder;
20
21use crate::{construct_extrinsic, Client as TestClient};
22use cumulus_pallet_parachain_system::parachain_inherent::{
23	BasicParachainInherentData, InboundMessagesData,
24};
25use cumulus_primitives_core::{relay_chain::AccountId, PersistedValidationData};
26use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
27use cumulus_test_runtime::{
28	BalancesCall, GluttonCall, NodeBlock, SudoCall, UncheckedExtrinsic, WASM_BINARY,
29};
30use frame_system_rpc_runtime_api::AccountNonceApi;
31use polkadot_primitives::HeadData;
32use sc_client_api::UsageProvider;
33use sc_consensus::{
34	block_import::{BlockImportParams, ForkChoiceStrategy},
35	BlockImport, ImportResult, StateAction,
36};
37use sc_executor::DEFAULT_HEAP_ALLOC_STRATEGY;
38use sc_executor_common::runtime_blob::RuntimeBlob;
39use sp_api::ProvideRuntimeApi;
40use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed};
41use sp_consensus::BlockOrigin;
42use sp_core::{sr25519, Pair};
43use sp_keyring::Sr25519Keyring::Alice;
44use sp_runtime::{
45	transaction_validity::{InvalidTransaction, TransactionValidityError},
46	AccountId32, FixedU64, MultiAddress, OpaqueExtrinsic,
47};
48
49/// Accounts to use for transfer transactions. Enough for 5000 transactions.
50const NUM_ACCOUNTS: usize = 10000;
51
52/// Create accounts by deriving from Alice
53pub fn create_benchmark_accounts() -> (Vec<sr25519::Pair>, Vec<sr25519::Pair>, Vec<AccountId32>) {
54	let accounts: Vec<sr25519::Pair> = (0..NUM_ACCOUNTS)
55		.map(|idx| {
56			Pair::from_string(&format!("{}/{}", Alice.to_seed(), idx), None)
57				.expect("Creates account pair")
58		})
59		.collect();
60	let account_ids = accounts
61		.iter()
62		.map(|account| AccountId::from(account.public()))
63		.collect::<Vec<AccountId>>();
64	let (src_accounts, dst_accounts) = accounts.split_at(NUM_ACCOUNTS / 2);
65	(src_accounts.to_vec(), dst_accounts.to_vec(), account_ids)
66}
67
68/// Create a timestamp extrinsic ahead by `MinimumPeriod` of the last known timestamp
69pub fn extrinsic_set_time(client: &TestClient) -> OpaqueExtrinsic {
70	let best_number = client.usage_info().chain.best_number;
71
72	let timestamp = best_number as u64 * cumulus_test_runtime::MinimumPeriod::get();
73	cumulus_test_runtime::UncheckedExtrinsic::new_bare(
74		cumulus_test_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set {
75			now: timestamp,
76		}),
77	)
78	.into()
79}
80
81/// Create a set validation data extrinsic
82pub fn extrinsic_set_validation_data(
83	parent_header: cumulus_test_runtime::Header,
84) -> OpaqueExtrinsic {
85	let parent_head = HeadData(parent_header.encode());
86	let sproof_builder = RelayStateSproofBuilder {
87		para_id: cumulus_test_runtime::PARACHAIN_ID.into(),
88		included_para_head: parent_head.clone().into(),
89		..Default::default()
90	};
91
92	let (relay_parent_storage_root, relay_chain_state, relay_parent_descendants) =
93		sproof_builder.into_state_root_proof_and_descendants(1);
94	let data = BasicParachainInherentData {
95		validation_data: PersistedValidationData {
96			parent_head,
97			relay_parent_number: 10,
98			relay_parent_storage_root,
99			max_pov_size: 10000,
100		},
101		relay_chain_state,
102		relay_parent_descendants,
103		collator_peer_id: None,
104	};
105
106	let inbound_messages_data = InboundMessagesData {
107		downward_messages: Default::default(),
108		horizontal_messages: Default::default(),
109	};
110
111	cumulus_test_runtime::UncheckedExtrinsic::new_bare(
112		cumulus_test_runtime::RuntimeCall::ParachainSystem(
113			cumulus_pallet_parachain_system::Call::set_validation_data {
114				data,
115				inbound_messages_data,
116			},
117		),
118	)
119	.into()
120}
121
122/// Import block into the given client and make sure the import was successful
123pub async fn import_block(client: &TestClient, block: &NodeBlock, import_existing: bool) {
124	let mut params = BlockImportParams::new(BlockOrigin::File, block.header.clone());
125	params.body = Some(block.extrinsics.clone());
126	params.state_action = StateAction::Execute;
127	params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
128	params.import_existing = import_existing;
129	let import_result = client.import_block(params).await;
130	assert!(
131		matches!(import_result, Ok(ImportResult::Imported(_))),
132		"Unexpected block import result: {:?}!",
133		import_result
134	);
135}
136
137/// Creates transfer extrinsics pair-wise from elements of `src_accounts` to `dst_accounts`.
138pub fn create_benchmarking_transfer_extrinsics(
139	client: &TestClient,
140	src_accounts: &[sr25519::Pair],
141	dst_accounts: &[sr25519::Pair],
142) -> (usize, Vec<OpaqueExtrinsic>) {
143	let chain = client.usage_info().chain;
144	// Add as many transfer extrinsics as possible into a single block.
145	let mut block_builder = BlockBuilderBuilder::new(client)
146		.on_parent_block(chain.best_hash)
147		.with_parent_block_number(chain.best_number)
148		.build()
149		.expect("Creates block builder");
150	let mut max_transfer_count = 0;
151	let mut extrinsics = Vec::new();
152	// Every block needs one timestamp extrinsic.
153	let time_ext = extrinsic_set_time(client);
154	extrinsics.push(time_ext);
155
156	// Every block needs tone set_validation_data extrinsic.
157	let parent_hash = client.usage_info().chain.best_hash;
158	let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap();
159	let set_validation_data_extrinsic = extrinsic_set_validation_data(parent_header);
160	extrinsics.push(set_validation_data_extrinsic);
161
162	for (src, dst) in src_accounts.iter().zip(dst_accounts.iter()) {
163		let extrinsic: UncheckedExtrinsic = construct_extrinsic(
164			client,
165			BalancesCall::transfer_keep_alive {
166				dest: MultiAddress::Id(AccountId::from(dst.public())),
167				value: 10000,
168			},
169			src.clone(),
170			Some(0),
171		);
172
173		match block_builder.push(extrinsic.clone().into()) {
174			Ok(_) => {},
175			Err(ApplyExtrinsicFailed(Validity(TransactionValidityError::Invalid(
176				InvalidTransaction::ExhaustsResources,
177			)))) => break,
178			Err(error) => panic!("{}", error),
179		}
180
181		extrinsics.push(extrinsic.into());
182		max_transfer_count += 1;
183	}
184
185	if max_transfer_count >= src_accounts.len() {
186		panic!("Block could fit more transfers, increase NUM_ACCOUNTS to generate more accounts.");
187	}
188
189	(max_transfer_count, extrinsics)
190}
191
192/// Prepare cumulus test runtime for execution
193pub fn get_wasm_module() -> Box<dyn sc_executor_common::wasm_runtime::WasmModule> {
194	let blob = RuntimeBlob::uncompress_if_needed(
195		WASM_BINARY.expect("You need to build the WASM binaries to run the benchmark!"),
196	)
197	.unwrap();
198
199	let config = sc_executor_wasmtime::Config {
200		allow_missing_func_imports: true,
201		cache_path: None,
202		semantics: sc_executor_wasmtime::Semantics {
203			heap_alloc_strategy: DEFAULT_HEAP_ALLOC_STRATEGY,
204			instantiation_strategy: sc_executor::WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
205			deterministic_stack_limit: None,
206			canonicalize_nans: false,
207			parallel_compilation: true,
208			wasm_multi_value: false,
209			wasm_bulk_memory: false,
210			wasm_reference_types: false,
211			wasm_simd: false,
212		},
213	};
214	Box::new(
215		sc_executor_wasmtime::create_runtime::<sp_io::SubstrateHostFunctions>(blob, config)
216			.expect("Unable to create wasm module."),
217	)
218}
219
220/// Create a block containing setup extrinsics for the glutton pallet.
221pub fn set_glutton_parameters(
222	client: &TestClient,
223	initialize: bool,
224	compute_ratio: &FixedU64,
225	storage_ratio: &FixedU64,
226) -> NodeBlock {
227	let parent_hash = client.usage_info().chain.best_hash;
228	let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap();
229
230	let mut last_nonce = client
231		.runtime_api()
232		.account_nonce(parent_hash, Alice.into())
233		.expect("Fetching account nonce works; qed");
234
235	let mut extrinsics = vec![];
236	if initialize {
237		// Initialize the pallet
238		extrinsics.push(construct_extrinsic(
239			client,
240			SudoCall::sudo {
241				call: Box::new(
242					GluttonCall::initialize_pallet { new_count: 5000, witness_count: None }.into(),
243				),
244			},
245			Alice.into(),
246			Some(last_nonce),
247		));
248		last_nonce += 1;
249	}
250
251	// Set compute weight that should be consumed per block
252	let set_compute = construct_extrinsic(
253		client,
254		SudoCall::sudo {
255			call: Box::new(GluttonCall::set_compute { compute: *compute_ratio }.into()),
256		},
257		Alice.into(),
258		Some(last_nonce),
259	);
260	last_nonce += 1;
261	extrinsics.push(set_compute);
262
263	// Set storage weight that should be consumed per block
264	let set_storage = construct_extrinsic(
265		client,
266		SudoCall::sudo {
267			call: Box::new(GluttonCall::set_storage { storage: *storage_ratio }.into()),
268		},
269		Alice.into(),
270		Some(last_nonce),
271	);
272	extrinsics.push(set_storage);
273	let chain = client.usage_info().chain;
274
275	let mut block_builder = BlockBuilderBuilder::new(client)
276		.on_parent_block(chain.best_hash)
277		.with_parent_block_number(chain.best_number)
278		.build()
279		.unwrap();
280	block_builder.push(extrinsic_set_time(client)).unwrap();
281	block_builder.push(extrinsic_set_validation_data(parent_header)).unwrap();
282	for extrinsic in extrinsics {
283		block_builder.push(extrinsic.into()).unwrap();
284	}
285
286	let built_block = block_builder.build().unwrap();
287	built_block.block
288}