1#![warn(missing_docs)]
20
21pub mod chain_spec;
22
23pub use chain_spec::*;
24use futures::{future::Future, stream::StreamExt};
25use polkadot_node_primitives::{CollationGenerationConfig, CollatorFn};
26use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProtocolMessage};
27use polkadot_overseer::Handle;
28use polkadot_primitives::{Balance, CollatorPair, HeadData, Id as ParaId, ValidationCode};
29use polkadot_runtime_common::BlockHashCount;
30use polkadot_runtime_parachains::paras::{ParaGenesisArgs, ParaKind};
31use polkadot_service::{
32 Error, FullClient, IsParachainNode, NewFull, OverseerGen, PrometheusConfig,
33};
34use polkadot_test_runtime::{
35 ParasCall, ParasSudoWrapperCall, Runtime, SignedPayload, SudoCall, TxExtension,
36 UncheckedExtrinsic, VERSION,
37};
38
39use sc_chain_spec::ChainSpec;
40use sc_client_api::BlockchainEvents;
41use sc_network::{
42 config::{NetworkConfiguration, TransportConfig},
43 multiaddr,
44 service::traits::NetworkService,
45 NetworkStateInfo,
46};
47use sc_service::{
48 config::{
49 DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, RpcBatchRequestConfig,
50 WasmExecutionMethod, WasmtimeInstantiationStrategy,
51 },
52 BasePath, BlocksPruning, Configuration, Role, RpcHandlers, TaskManager,
53};
54use sp_arithmetic::traits::SaturatedConversion;
55use sp_blockchain::HeaderBackend;
56use sp_keyring::Sr25519Keyring;
57use sp_runtime::{codec::Encode, generic, traits::IdentifyAccount, MultiSigner};
58use sp_state_machine::BasicExternalities;
59use std::{
60 collections::HashSet,
61 net::{Ipv4Addr, SocketAddr},
62 path::PathBuf,
63 sync::Arc,
64};
65use substrate_test_client::{
66 BlockchainEventsExt, RpcHandlersExt, RpcTransactionError, RpcTransactionOutput,
67};
68
69pub type Client = FullClient;
71
72pub use polkadot_service::{FullBackend, GetLastTimestamp};
73use sc_service::config::{ExecutorConfiguration, RpcConfiguration};
74
75#[sc_tracing::logging::prefix_logs_with(custom_log_prefix.unwrap_or(config.network.node_name.as_str()))]
77pub fn new_full<OverseerGenerator: OverseerGen>(
78 config: Configuration,
79 is_parachain_node: IsParachainNode,
80 workers_path: Option<PathBuf>,
81 overseer_gen: OverseerGenerator,
82 custom_log_prefix: Option<&'static str>,
83) -> Result<NewFull, Error> {
84 let workers_path = Some(workers_path.unwrap_or_else(get_relative_workers_path_for_test));
85
86 let params = polkadot_service::NewFullParams {
87 is_parachain_node,
88 enable_beefy: true,
89 force_authoring_backoff: false,
90 telemetry_worker_handle: None,
91 node_version: None,
92 secure_validator_mode: false,
93 workers_path,
94 workers_names: None,
95 overseer_gen,
96 overseer_message_channel_capacity_override: None,
97 malus_finality_delay: None,
98 hwbench: None,
99 execute_workers_max_num: None,
100 prepare_workers_hard_max_num: None,
101 prepare_workers_soft_max_num: None,
102 keep_finalized_for: None,
103 };
104
105 match config.network.network_backend {
106 sc_network::config::NetworkBackendType::Libp2p =>
107 polkadot_service::new_full::<_, sc_network::NetworkWorker<_, _>>(config, params),
108 sc_network::config::NetworkBackendType::Litep2p =>
109 polkadot_service::new_full::<_, sc_network::Litep2pNetworkBackend>(config, params),
110 }
111}
112
113fn get_relative_workers_path_for_test() -> PathBuf {
114 let mut exe_path = std::env::current_exe()
117 .expect("for test purposes it's reasonable to expect that this will not fail");
118 let _ = exe_path.pop();
119 let _ = exe_path.pop();
120 exe_path
121}
122
123pub fn test_prometheus_config(port: u16) -> PrometheusConfig {
125 PrometheusConfig::new_with_default_registry(
126 SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port),
127 "test-chain".to_string(),
128 )
129}
130
131pub fn node_config(
139 storage_update_func: impl Fn(),
140 tokio_handle: tokio::runtime::Handle,
141 key: Sr25519Keyring,
142 boot_nodes: Vec<MultiaddrWithPeerId>,
143 is_validator: bool,
144) -> Configuration {
145 let base_path = BasePath::new_temp_dir().expect("could not create temporary directory");
146 let root = base_path.path().join(key.to_string());
147 let role = if is_validator { Role::Authority } else { Role::Full };
148 let key_seed = key.to_seed();
149 let mut spec = polkadot_local_testnet_config();
150 let mut storage = spec.as_storage_builder().build_storage().expect("could not build storage");
151
152 BasicExternalities::execute_with_storage(&mut storage, storage_update_func);
153 spec.set_storage(storage);
154
155 let mut network_config = NetworkConfiguration::new(
156 key_seed.to_string(),
157 "network/test/0.1",
158 Default::default(),
159 None,
160 );
161
162 network_config.boot_nodes = boot_nodes;
163
164 network_config.allow_non_globals_in_dht = true;
165
166 let addr: multiaddr::Multiaddr = "/ip4/127.0.0.1/tcp/0".parse().expect("valid address; qed");
169 network_config.listen_addresses.push(addr.clone());
170 network_config.public_addresses.push(addr);
171 network_config.transport =
172 TransportConfig::Normal { enable_mdns: false, allow_private_ip: true };
173
174 Configuration {
175 impl_name: "polkadot-test-node".to_string(),
176 impl_version: "0.1".to_string(),
177 role,
178 tokio_handle,
179 transaction_pool: Default::default(),
180 network: network_config,
181 keystore: KeystoreConfig::InMemory,
182 database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 },
183 trie_cache_maximum_size: Some(64 * 1024 * 1024),
184 warm_up_trie_cache: None,
185 state_pruning: Default::default(),
186 blocks_pruning: BlocksPruning::KeepFinalized,
187 chain_spec: Box::new(spec),
188 executor: ExecutorConfiguration {
189 wasm_method: WasmExecutionMethod::Compiled {
190 instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
191 },
192 ..ExecutorConfiguration::default()
193 },
194 wasm_runtime_overrides: Default::default(),
195 rpc: RpcConfiguration {
196 addr: Default::default(),
197 max_request_size: Default::default(),
198 max_response_size: Default::default(),
199 max_connections: Default::default(),
200 cors: None,
201 methods: Default::default(),
202 id_provider: None,
203 max_subs_per_conn: Default::default(),
204 port: 9944,
205 message_buffer_capacity: Default::default(),
206 batch_config: RpcBatchRequestConfig::Unlimited,
207 rate_limit: None,
208 rate_limit_whitelisted_ips: Default::default(),
209 rate_limit_trust_proxy_headers: Default::default(),
210 },
211 prometheus_config: None,
212 telemetry_endpoints: None,
213 offchain_worker: Default::default(),
214 force_authoring: false,
215 disable_grandpa: false,
216 dev_key_seed: Some(key_seed),
217 tracing_targets: None,
218 tracing_receiver: Default::default(),
219 announce_block: true,
220 data_path: root,
221 base_path,
222 }
223}
224
225pub async fn get_listen_address(network: Arc<dyn NetworkService>) -> sc_network::Multiaddr {
229 loop {
230 let listen_addresses = network.listen_addresses();
237
238 match listen_addresses.into_iter().find(|addr| {
240 addr.iter().any(|protocol| match protocol {
241 multiaddr::Protocol::Tcp(port) => port > 0,
242 _ => false,
243 })
244 }) {
245 Some(multiaddr) => return multiaddr,
246 None => {
247 tokio::time::sleep(std::time::Duration::from_millis(500)).await;
248 continue;
249 },
250 }
251 }
252}
253
254pub async fn run_validator_node(
256 config: Configuration,
257 worker_program_path: Option<PathBuf>,
258) -> PolkadotTestNode {
259 let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = new_full(
260 config,
261 IsParachainNode::No,
262 worker_program_path,
263 polkadot_service::ValidatorOverseerGen,
264 None,
265 )
266 .expect("could not create Polkadot test service");
267
268 let overseer_handle = overseer_handle.expect("test node must have an overseer handle");
269 let peer_id = network.local_peer_id();
270 let multiaddr = get_listen_address(network).await;
271
272 let addr = MultiaddrWithPeerId { multiaddr, peer_id };
273
274 PolkadotTestNode { task_manager, client, overseer_handle, addr, rpc_handlers }
275}
276
277pub async fn run_collator_node(
290 tokio_handle: tokio::runtime::Handle,
291 key: Sr25519Keyring,
292 storage_update_func: impl Fn(),
293 boot_nodes: Vec<MultiaddrWithPeerId>,
294 collator_pair: CollatorPair,
295) -> PolkadotTestNode {
296 let config = node_config(storage_update_func, tokio_handle, key, boot_nodes, false);
297 let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = new_full(
298 config,
299 IsParachainNode::Collator(collator_pair),
300 None,
301 polkadot_service::CollatorOverseerGen,
302 None,
303 )
304 .expect("could not create Polkadot test service");
305
306 let overseer_handle = overseer_handle.expect("test node must have an overseer handle");
307 let peer_id = network.local_peer_id();
308
309 let multiaddr = get_listen_address(network).await;
310 let addr = MultiaddrWithPeerId { multiaddr, peer_id };
311
312 PolkadotTestNode { task_manager, client, overseer_handle, addr, rpc_handlers }
313}
314
315pub struct PolkadotTestNode {
317 pub task_manager: TaskManager,
319 pub client: Arc<Client>,
321 pub overseer_handle: Handle,
323 pub addr: MultiaddrWithPeerId,
326 pub rpc_handlers: RpcHandlers,
328}
329
330impl PolkadotTestNode {
331 async fn send_sudo(
333 &self,
334 call: impl Into<polkadot_test_runtime::RuntimeCall>,
335 caller: Sr25519Keyring,
336 nonce: u32,
337 ) -> Result<(), RpcTransactionError> {
338 let sudo = SudoCall::sudo { call: Box::new(call.into()) };
339
340 let extrinsic = construct_extrinsic(&self.client, sudo, caller, nonce);
341 self.rpc_handlers.send_transaction(extrinsic.into()).await.map(drop)
342 }
343
344 pub async fn send_extrinsic(
346 &self,
347 function: impl Into<polkadot_test_runtime::RuntimeCall>,
348 caller: Sr25519Keyring,
349 ) -> Result<RpcTransactionOutput, RpcTransactionError> {
350 let extrinsic = construct_extrinsic(&self.client, function, caller, 0);
351
352 self.rpc_handlers.send_transaction(extrinsic.into()).await
353 }
354
355 pub async fn register_parachain(
357 &self,
358 id: ParaId,
359 validation_code: impl Into<ValidationCode>,
360 genesis_head: impl Into<HeadData>,
361 ) -> Result<(), RpcTransactionError> {
362 let validation_code: ValidationCode = validation_code.into();
363 let call = ParasSudoWrapperCall::sudo_schedule_para_initialize {
364 id,
365 genesis: ParaGenesisArgs {
366 genesis_head: genesis_head.into(),
367 validation_code: validation_code.clone(),
368 para_kind: ParaKind::Parachain,
369 },
370 };
371
372 self.send_sudo(call, Sr25519Keyring::Alice, 0).await?;
373
374 let call = ParasCall::add_trusted_validation_code { validation_code };
376 self.send_sudo(call, Sr25519Keyring::Alice, 1).await
377 }
378
379 pub fn wait_for_blocks(&self, count: usize) -> impl Future<Output = ()> {
383 self.client.wait_for_blocks(count)
384 }
385
386 pub async fn wait_for_finalized_blocks(&self, count: usize) {
389 let mut import_notification_stream = self.client.finality_notification_stream();
390 let mut blocks = HashSet::new();
391
392 while let Some(notification) = import_notification_stream.next().await {
393 blocks.insert(notification.hash);
394 if blocks.len() == count {
395 break
396 }
397 }
398 }
399
400 pub async fn register_collator(
402 &mut self,
403 collator_key: CollatorPair,
404 para_id: ParaId,
405 collator: CollatorFn,
406 ) {
407 let config =
408 CollationGenerationConfig { key: collator_key, collator: Some(collator), para_id };
409
410 self.overseer_handle
411 .send_msg(CollationGenerationMessage::Initialize(config), "Collator")
412 .await;
413
414 self.overseer_handle
415 .send_msg(CollatorProtocolMessage::CollateOn(para_id), "Collator")
416 .await;
417 }
418}
419
420pub fn construct_extrinsic(
422 client: &Client,
423 function: impl Into<polkadot_test_runtime::RuntimeCall>,
424 caller: Sr25519Keyring,
425 nonce: u32,
426) -> UncheckedExtrinsic {
427 let function = function.into();
428 let current_block_hash = client.info().best_hash;
429 let current_block = client.info().best_number.saturated_into();
430 let genesis_block = client.hash(0).unwrap().unwrap();
431 let period =
432 BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;
433 let tip = 0;
434 let tx_ext: TxExtension = (
435 frame_system::AuthorizeCall::<Runtime>::new(),
436 frame_system::CheckNonZeroSender::<Runtime>::new(),
437 frame_system::CheckSpecVersion::<Runtime>::new(),
438 frame_system::CheckTxVersion::<Runtime>::new(),
439 frame_system::CheckGenesis::<Runtime>::new(),
440 frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
441 frame_system::CheckNonce::<Runtime>::from(nonce),
442 frame_system::CheckWeight::<Runtime>::new(),
443 pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
444 frame_system::WeightReclaim::<Runtime>::new(),
445 )
446 .into();
447 let raw_payload = SignedPayload::from_raw(
448 function.clone(),
449 tx_ext.clone(),
450 (
451 (),
452 (),
453 VERSION.spec_version,
454 VERSION.transaction_version,
455 genesis_block,
456 current_block_hash,
457 (),
458 (),
459 (),
460 (),
461 ),
462 );
463 let signature = raw_payload.using_encoded(|e| caller.sign(e));
464 UncheckedExtrinsic::new_signed(
465 function.clone(),
466 polkadot_test_runtime::Address::Id(caller.public().into()),
467 polkadot_primitives::Signature::Sr25519(signature),
468 tx_ext.clone(),
469 )
470}
471
472pub fn construct_transfer_extrinsic(
474 client: &Client,
475 origin: sp_keyring::Sr25519Keyring,
476 dest: sp_keyring::Sr25519Keyring,
477 value: Balance,
478) -> UncheckedExtrinsic {
479 let function =
480 polkadot_test_runtime::RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death {
481 dest: MultiSigner::from(dest.public()).into_account().into(),
482 value,
483 });
484
485 construct_extrinsic(client, function, origin, 0)
486}