1use 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
49const NUM_ACCOUNTS: usize = 10000;
51
52pub 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
68pub 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
81pub 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
122pub 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
137pub 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 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 let time_ext = extrinsic_set_time(client);
154 extrinsics.push(time_ext);
155
156 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
192pub 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 epoch_interruption: false,
213 },
214 };
215 Box::new(
216 sc_executor_wasmtime::create_runtime::<sp_io::SubstrateHostFunctions>(blob, config)
217 .expect("Unable to create wasm module."),
218 )
219}
220
221pub fn set_glutton_parameters(
223 client: &TestClient,
224 initialize: bool,
225 compute_ratio: &FixedU64,
226 storage_ratio: &FixedU64,
227) -> NodeBlock {
228 let parent_hash = client.usage_info().chain.best_hash;
229 let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap();
230
231 let mut last_nonce = client
232 .runtime_api()
233 .account_nonce(parent_hash, Alice.into())
234 .expect("Fetching account nonce works; qed");
235
236 let mut extrinsics = vec![];
237 if initialize {
238 extrinsics.push(construct_extrinsic(
240 client,
241 SudoCall::sudo {
242 call: Box::new(
243 GluttonCall::initialize_pallet { new_count: 5000, witness_count: None }.into(),
244 ),
245 },
246 Alice.into(),
247 Some(last_nonce),
248 ));
249 last_nonce += 1;
250 }
251
252 let set_compute = construct_extrinsic(
254 client,
255 SudoCall::sudo {
256 call: Box::new(GluttonCall::set_compute { compute: *compute_ratio }.into()),
257 },
258 Alice.into(),
259 Some(last_nonce),
260 );
261 last_nonce += 1;
262 extrinsics.push(set_compute);
263
264 let set_storage = construct_extrinsic(
266 client,
267 SudoCall::sudo {
268 call: Box::new(GluttonCall::set_storage { storage: *storage_ratio }.into()),
269 },
270 Alice.into(),
271 Some(last_nonce),
272 );
273 extrinsics.push(set_storage);
274 let chain = client.usage_info().chain;
275
276 let mut block_builder = BlockBuilderBuilder::new(client)
277 .on_parent_block(chain.best_hash)
278 .with_parent_block_number(chain.best_number)
279 .build()
280 .unwrap();
281 block_builder.push(extrinsic_set_time(client)).unwrap();
282 block_builder.push(extrinsic_set_validation_data(parent_header)).unwrap();
283 for extrinsic in extrinsics {
284 block_builder.push(extrinsic.into()).unwrap();
285 }
286
287 let built_block = block_builder.build().unwrap();
288 built_block.block
289}