astar_collator/
command.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19//! Astar collator CLI handlers.
20use 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
175/// Parse command line arguments into service configuration.
176pub 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                        &para_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}