1use crate::{
21 cli::{Cli, RelayChainCli, Subcommand},
22 local::{self, development_config},
23 parachain::{self, chain_spec, service::AdditionalConfig},
24};
25use cumulus_primitives_core::ParaId;
26use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE;
27use log::info;
28use sc_cli::{
29 ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
30 NetworkParams, Result, RpcEndpoint, SharedParams, SubstrateCli,
31};
32use sc_service::{
33 config::{BasePath, PrometheusConfig},
34 PartialComponents,
35};
36use sp_runtime::traits::AccountIdConversion;
37
38trait IdentifyChain {
39 fn is_astar(&self) -> bool;
40 fn is_dev(&self) -> bool;
41 fn is_shiden(&self) -> bool;
42 fn is_shibuya(&self) -> bool;
43}
44
45impl IdentifyChain for dyn sc_service::ChainSpec {
46 fn is_astar(&self) -> bool {
47 self.id().starts_with("astar")
48 }
49 fn is_dev(&self) -> bool {
50 self.id().starts_with("dev")
51 }
52 fn is_shiden(&self) -> bool {
53 self.id().starts_with("shiden")
54 }
55 fn is_shibuya(&self) -> bool {
56 self.id().starts_with("shibuya")
57 }
58}
59
60impl<T: sc_service::ChainSpec + 'static> IdentifyChain for T {
61 fn is_astar(&self) -> bool {
62 <dyn sc_service::ChainSpec>::is_astar(self)
63 }
64 fn is_dev(&self) -> bool {
65 <dyn sc_service::ChainSpec>::is_dev(self)
66 }
67 fn is_shiden(&self) -> bool {
68 <dyn sc_service::ChainSpec>::is_shiden(self)
69 }
70 fn is_shibuya(&self) -> bool {
71 <dyn sc_service::ChainSpec>::is_shibuya(self)
72 }
73}
74
75fn load_spec(id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
76 Ok(match id {
77 "dev" => Box::new(development_config()),
78 "astar-dev" => Box::new(chain_spec::astar::get_chain_spec()),
79 "shibuya-dev" => Box::new(chain_spec::shibuya::get_chain_spec()),
80 "shiden-dev" => Box::new(chain_spec::shiden::get_chain_spec()),
81 "astar" => Box::new(chain_spec::AstarChainSpec::from_json_bytes(
82 &include_bytes!("../res/astar.raw.json")[..],
83 )?),
84 "shiden" => Box::new(chain_spec::ShidenChainSpec::from_json_bytes(
85 &include_bytes!("../res/shiden.raw.json")[..],
86 )?),
87 "shibuya" => Box::new(chain_spec::ShibuyaChainSpec::from_json_bytes(
88 &include_bytes!("../res/shibuya.raw.json")[..],
89 )?),
90 path => {
91 let chain_spec = chain_spec::ShibuyaChainSpec::from_json_file(path.into())?;
92 if chain_spec.is_astar() {
93 Box::new(chain_spec::AstarChainSpec::from_json_file(path.into())?)
94 } else if chain_spec.is_shiden() {
95 Box::new(chain_spec::ShidenChainSpec::from_json_file(path.into())?)
96 } else if chain_spec.is_shibuya() {
97 Box::new(chain_spec)
98 } else {
99 Err("Unclear which chain spec to base this chain on. Name should start with astar, shiden or shibuya if custom name is used")?
100 }
101 }
102 })
103}
104
105impl SubstrateCli for Cli {
106 fn impl_name() -> String {
107 "Astar Collator".into()
108 }
109
110 fn impl_version() -> String {
111 env!("SUBSTRATE_CLI_IMPL_VERSION").into()
112 }
113
114 fn description() -> String {
115 format!(
116 "Astar Collator\n\nThe command-line arguments provided first will be \
117 passed to the parachain node, while the arguments provided after -- will be passed \
118 to the relaychain node.\n\n\
119 {} [parachain-args] -- [relaychain-args]",
120 Self::executable_name()
121 )
122 }
123
124 fn author() -> String {
125 env!("CARGO_PKG_AUTHORS").into()
126 }
127
128 fn support_url() -> String {
129 "https://github.com/AstarNetwork/Astar/issues/new".into()
130 }
131
132 fn copyright_start_year() -> i32 {
133 2019
134 }
135
136 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
137 load_spec(id)
138 }
139}
140
141impl SubstrateCli for RelayChainCli {
142 fn impl_name() -> String {
143 "Astar Collator".into()
144 }
145
146 fn impl_version() -> String {
147 env!("SUBSTRATE_CLI_IMPL_VERSION").into()
148 }
149
150 fn description() -> String {
151 "Astar Collator\n\nThe command-line arguments provided first will be \
152 passed to the parachain node, while the arguments provided after -- will be passed \
153 to the relaychain node.\n\n\
154 astar-collator [parachain-args] -- [relaychain-args]"
155 .into()
156 }
157
158 fn author() -> String {
159 env!("CARGO_PKG_AUTHORS").into()
160 }
161
162 fn support_url() -> String {
163 "https://github.com/AstarNetwork/Astar/issues/new".into()
164 }
165
166 fn copyright_start_year() -> i32 {
167 2019
168 }
169
170 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
171 polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)
172 }
173}
174
175pub fn run() -> Result<()> {
177 let cli = Cli::from_args();
178
179 match &cli.subcommand {
180 Some(Subcommand::BuildSpec(cmd)) => {
181 let runner = cli.create_runner(cmd)?;
182 runner.sync_run(|config| cmd.run(config.chain_spec, config.network))
183 }
184 Some(Subcommand::CheckBlock(cmd)) => {
185 let runner = cli.create_runner(cmd)?;
186 let rpc_config = cli.eth_api_options.new_rpc_config();
187 runner.async_run(|config| {
188 let PartialComponents {
189 client,
190 task_manager,
191 import_queue,
192 ..
193 } = parachain::new_partial(&config, &rpc_config)?;
194 Ok((cmd.run(client, import_queue), task_manager))
195 })
196 }
197 Some(Subcommand::ExportBlocks(cmd)) => {
198 let runner = cli.create_runner(cmd)?;
199 let rpc_config = cli.eth_api_options.new_rpc_config();
200 runner.async_run(|config| {
201 let PartialComponents {
202 client,
203 task_manager,
204 ..
205 } = parachain::new_partial(&config, &rpc_config)?;
206 Ok((cmd.run(client, config.database), task_manager))
207 })
208 }
209 Some(Subcommand::ExportState(cmd)) => {
210 let runner = cli.create_runner(cmd)?;
211 let rpc_config = cli.eth_api_options.new_rpc_config();
212 runner.async_run(|config| {
213 let PartialComponents {
214 client,
215 task_manager,
216 ..
217 } = parachain::new_partial(&config, &rpc_config)?;
218 Ok((cmd.run(client, config.chain_spec), task_manager))
219 })
220 }
221 Some(Subcommand::ImportBlocks(cmd)) => {
222 let runner = cli.create_runner(cmd)?;
223 let rpc_config = cli.eth_api_options.new_rpc_config();
224 runner.async_run(|config| {
225 let PartialComponents {
226 client,
227 task_manager,
228 import_queue,
229 ..
230 } = parachain::new_partial(&config, &rpc_config)?;
231 Ok((cmd.run(client, import_queue), task_manager))
232 })
233 }
234 Some(Subcommand::PurgeChain(cmd)) => {
235 let runner = cli.create_runner(cmd)?;
236 runner.sync_run(|config| {
237 let polkadot_cli = RelayChainCli::new(
238 &config,
239 [RelayChainCli::executable_name()]
240 .iter()
241 .chain(cli.relaychain_args.iter()),
242 );
243 let polkadot_config = SubstrateCli::create_configuration(
244 &polkadot_cli,
245 &polkadot_cli,
246 config.tokio_handle.clone(),
247 )
248 .map_err(|err| format!("Relay chain argument error: {}", err))?;
249
250 cmd.run(config, polkadot_config)
251 })
252 }
253 Some(Subcommand::Revert(cmd)) => {
254 let runner = cli.create_runner(cmd)?;
255 let chain_spec = &runner.config().chain_spec;
256 let rpc_config = cli.eth_api_options.new_rpc_config();
257 if chain_spec.is_dev() {
258 runner.async_run(|config| {
259 let PartialComponents {
260 client,
261 task_manager,
262 backend,
263 ..
264 } = local::new_partial(&config, &rpc_config)?;
265 let aux_revert = Box::new(|client, _, blocks| {
266 sc_consensus_grandpa::revert(client, blocks)?;
267 Ok(())
268 });
269 Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
270 })
271 } else {
272 runner.async_run(|config| {
273 let PartialComponents {
274 client,
275 task_manager,
276 backend,
277 ..
278 } = parachain::new_partial(&config, &rpc_config)?;
279 Ok((cmd.run(client, backend, None), task_manager))
280 })
281 }
282 }
283 Some(Subcommand::ExportGenesisState(cmd)) => {
284 let runner = cli.create_runner(cmd)?;
285 let rpc_config = cli.eth_api_options.new_rpc_config();
286 runner.sync_run(|config| {
287 let PartialComponents { client, .. } =
288 parachain::new_partial(&config, &rpc_config)?;
289 cmd.run(client)
290 })
291 }
292 Some(Subcommand::ExportGenesisWasm(cmd)) => {
293 let runner = cli.create_runner(cmd)?;
294
295 runner.sync_run(|_config| {
296 let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
297 cmd.run(&*spec)
298 })
299 }
300 Some(Subcommand::Key(cmd)) => cmd.run(&cli),
301 Some(Subcommand::Sign(cmd)) => cmd.run(),
302 Some(Subcommand::Verify(cmd)) => cmd.run(),
303 Some(Subcommand::Vanity(cmd)) => cmd.run(),
304 #[cfg(feature = "runtime-benchmarks")]
305 Some(Subcommand::Benchmark(cmd)) => {
306 use frame_benchmarking_cli::BenchmarkCmd;
307 use sp_runtime::traits::HashingFor;
308
309 let runner = cli.create_runner(cmd)?;
310 let rpc_config = cli.eth_api_options.new_rpc_config();
311 let chain_spec = &runner.config().chain_spec;
312
313 match cmd {
314 BenchmarkCmd::Pallet(cmd) => {
315 if chain_spec.is_astar() {
316 runner.sync_run(|config| {
317 cmd.run_with_spec::<HashingFor<astar_runtime::Block>, parachain::HostFunctions>(
318 Some(config.chain_spec),
319 )
320 })
321 } else if chain_spec.is_shiden() {
322 runner.sync_run(|config| {
323 cmd.run_with_spec::<HashingFor<shiden_runtime::Block>, parachain::HostFunctions>(
324 Some(config.chain_spec),
325 )
326 })
327 } else if chain_spec.is_shibuya() {
328 runner.sync_run(|config| {
329 cmd.run_with_spec::<HashingFor<shibuya_runtime::Block>, parachain::HostFunctions>(
330 Some(config.chain_spec),
331 )
332 })
333 } else {
334 runner.sync_run(|config| {
335 cmd.run_with_spec::<HashingFor<local_runtime::Block>, local::HostFunctions>(
336 Some(config.chain_spec),
337 )
338 })
339 }
340 }
341 BenchmarkCmd::Block(cmd) => {
342 if chain_spec.is_dev() {
343 runner.sync_run(|config| {
344 let params = local::new_partial(&config, &rpc_config)?;
345 cmd.run(params.client)
346 })
347 } else {
348 runner.sync_run(|config| {
349 let params = parachain::new_partial(&config, &rpc_config)?;
350 cmd.run(params.client)
351 })
352 }
353 }
354 BenchmarkCmd::Storage(cmd) => {
355 if chain_spec.is_dev() {
356 runner.sync_run(|config| {
357 let params = local::new_partial(&config, &rpc_config)?;
358 let db = params.backend.expose_db();
359 let storage = params.backend.expose_storage();
360
361 cmd.run(config, params.client, db, storage, None)
362 })
363 } else {
364 runner.sync_run(|config| {
365 let params = parachain::new_partial(&config, &rpc_config)?;
366 let db = params.backend.expose_db();
367 let storage = params.backend.expose_storage();
368
369 cmd.run(config, params.client, db, storage, None)
370 })
371 }
372 }
373 BenchmarkCmd::Overhead(_) => {
374 todo!("Overhead benchmarking is not supported. Use 'frame-omni-bencher' tool instead.");
375 }
376 BenchmarkCmd::Extrinsic(_) => {
377 todo!("Extrinsic benchmarking is not supported.");
378 }
379 BenchmarkCmd::Machine(cmd) => {
380 runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))
381 }
382 }
383 }
384 None => {
385 let runner = cli.create_runner(&cli.run.normalize())?;
386 let collator_options = cli.run.collator_options();
387
388 let evm_tracing_config = cli.eth_api_options.new_rpc_config();
389
390 runner.run_node_until_exit(|config| async move {
391 if config.chain_spec.is_dev() {
392 return local::start_node::<sc_network::NetworkWorker<_, _>>(
393 config,
394 evm_tracing_config,
395 )
396 .map_err(Into::into);
397 }
398
399 let polkadot_cli = RelayChainCli::new(
400 &config,
401 [RelayChainCli::executable_name()]
402 .iter()
403 .chain(cli.relaychain_args.iter()),
404 );
405
406 let para_id = ParaId::from(
407 chain_spec::Extensions::try_get(&*config.chain_spec)
408 .map(|e| e.para_id)
409 .ok_or("ParaId not found in chain spec extension")?,
410 );
411
412 let parachain_account =
413 AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(
414 ¶_id,
415 );
416
417 let polkadot_config = SubstrateCli::create_configuration(
418 &polkadot_cli,
419 &polkadot_cli,
420 config.tokio_handle.clone(),
421 )
422 .map_err(|err| format!("Relay chain argument error: {}", err))?;
423
424 info!("Parachain id: {:?}", para_id);
425 info!("Parachain Account: {}", parachain_account);
426 info!(
427 "Is collating: {}",
428 if config.role.is_authority() {
429 "yes"
430 } else {
431 "no"
432 }
433 );
434
435 let hwbench = (!cli.no_hardware_benchmarks)
436 .then_some(config.database.path().map(|database_path| {
437 let _ = std::fs::create_dir_all(database_path);
438 sc_sysinfo::gather_hwbench(
439 Some(database_path),
440 &SUBSTRATE_REFERENCE_HARDWARE,
441 )
442 }))
443 .flatten();
444
445 let additional_config = AdditionalConfig {
446 evm_tracing_config,
447 enable_evm_rpc: cli.enable_evm_rpc,
448 proposer_block_size_limit: cli.proposer_block_size_limit,
449 proposer_soft_deadline_percent: cli.proposer_soft_deadline_percent,
450 hwbench,
451 };
452
453 parachain::start_node(
454 config,
455 polkadot_config,
456 collator_options,
457 para_id,
458 additional_config,
459 )
460 .await
461 .map(|r| r.0)
462 .map_err(Into::into)
463 })
464 }
465 }
466}
467
468impl DefaultConfigurationValues for RelayChainCli {
469 fn p2p_listen_port() -> u16 {
470 30334
471 }
472
473 fn rpc_listen_port() -> u16 {
474 9945
475 }
476
477 fn prometheus_listen_port() -> u16 {
478 9616
479 }
480}
481
482impl CliConfiguration<Self> for RelayChainCli {
483 fn shared_params(&self) -> &SharedParams {
484 self.base.base.shared_params()
485 }
486
487 fn import_params(&self) -> Option<&ImportParams> {
488 self.base.base.import_params()
489 }
490
491 fn network_params(&self) -> Option<&NetworkParams> {
492 self.base.base.network_params()
493 }
494
495 fn keystore_params(&self) -> Option<&KeystoreParams> {
496 self.base.base.keystore_params()
497 }
498
499 fn base_path(&self) -> Result<Option<BasePath>> {
500 Ok(self
501 .shared_params()
502 .base_path()?
503 .or_else(|| self.base_path.clone().map(Into::into)))
504 }
505
506 fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
507 self.base.base.rpc_addr(default_listen_port)
508 }
509
510 fn prometheus_config(
511 &self,
512 default_listen_port: u16,
513 chain_spec: &Box<dyn ChainSpec>,
514 ) -> Result<Option<PrometheusConfig>> {
515 self.base
516 .base
517 .prometheus_config(default_listen_port, chain_spec)
518 }
519
520 fn init<F>(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()>
521 where
522 F: FnOnce(&mut sc_cli::LoggerBuilder),
523 {
524 unreachable!("PolkadotCli is never initialized; qed");
525 }
526
527 fn chain_id(&self, is_dev: bool) -> Result<String> {
528 let chain_id = self.base.base.chain_id(is_dev)?;
529
530 Ok(if chain_id.is_empty() {
531 self.chain_id.clone().unwrap_or_default()
532 } else {
533 chain_id
534 })
535 }
536
537 fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
538 self.base.base.role(is_dev)
539 }
540
541 fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
542 self.base.base.transaction_pool(is_dev)
543 }
544
545 fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
546 self.base.base.trie_cache_maximum_size()
547 }
548
549 fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
550 self.base.base.rpc_methods()
551 }
552
553 fn rpc_max_connections(&self) -> Result<u32> {
554 self.base.base.rpc_max_connections()
555 }
556
557 fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
558 self.base.base.rpc_cors(is_dev)
559 }
560
561 fn default_heap_pages(&self) -> Result<Option<u64>> {
562 self.base.base.default_heap_pages()
563 }
564
565 fn force_authoring(&self) -> Result<bool> {
566 self.base.base.force_authoring()
567 }
568
569 fn disable_grandpa(&self) -> Result<bool> {
570 self.base.base.disable_grandpa()
571 }
572
573 fn max_runtime_instances(&self) -> Result<Option<usize>> {
574 self.base.base.max_runtime_instances()
575 }
576
577 fn announce_block(&self) -> Result<bool> {
578 self.base.base.announce_block()
579 }
580
581 fn telemetry_endpoints(
582 &self,
583 chain_spec: &Box<dyn ChainSpec>,
584 ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
585 self.base.base.telemetry_endpoints(chain_spec)
586 }
587}