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                    Ok((cmd.run(client, backend, None), task_manager))
266                })
267            } else {
268                runner.async_run(|config| {
269                    let PartialComponents {
270                        client,
271                        task_manager,
272                        backend,
273                        ..
274                    } = parachain::new_partial(&config, &rpc_config)?;
275                    Ok((cmd.run(client, backend, None), task_manager))
276                })
277            }
278        }
279        Some(Subcommand::ExportGenesisState(cmd)) => {
280            let runner = cli.create_runner(cmd)?;
281            let rpc_config = cli.eth_api_options.new_rpc_config();
282            runner.sync_run(|config| {
283                let PartialComponents { client, .. } =
284                    parachain::new_partial(&config, &rpc_config)?;
285                cmd.run(client)
286            })
287        }
288        Some(Subcommand::ExportGenesisWasm(cmd)) => {
289            let runner = cli.create_runner(cmd)?;
290
291            runner.sync_run(|_config| {
292                let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
293                cmd.run(&*spec)
294            })
295        }
296        Some(Subcommand::Key(cmd)) => cmd.run(&cli),
297        Some(Subcommand::Sign(cmd)) => cmd.run(),
298        Some(Subcommand::Verify(cmd)) => cmd.run(),
299        Some(Subcommand::Vanity(cmd)) => cmd.run(),
300        #[cfg(feature = "runtime-benchmarks")]
301        Some(Subcommand::Benchmark(cmd)) => {
302            use frame_benchmarking_cli::BenchmarkCmd;
303            use sp_runtime::traits::HashingFor;
304
305            let runner = cli.create_runner(cmd)?;
306            let rpc_config = cli.eth_api_options.new_rpc_config();
307            let chain_spec = &runner.config().chain_spec;
308
309            match cmd {
310                BenchmarkCmd::Pallet(cmd) => {
311                    if chain_spec.is_astar() {
312                        runner.sync_run(|config| {
313                            cmd.run_with_spec::<HashingFor<astar_runtime::Block>, parachain::HostFunctions>(
314                                Some(config.chain_spec),
315                            )
316                        })
317                    } else if chain_spec.is_shiden() {
318                        runner.sync_run(|config| {
319                            cmd.run_with_spec::<HashingFor<shiden_runtime::Block>, parachain::HostFunctions>(
320                                Some(config.chain_spec),
321                            )
322                        })
323                    } else if chain_spec.is_shibuya() {
324                        runner.sync_run(|config| {
325                            cmd.run_with_spec::<HashingFor<shibuya_runtime::Block>, parachain::HostFunctions>(
326                                Some(config.chain_spec),
327                            )
328                        })
329                    } else {
330                        runner.sync_run(|config| {
331                            cmd.run_with_spec::<HashingFor<shibuya_runtime::Block>, local::HostFunctions>(
332                                Some(config.chain_spec),
333                            )
334                        })
335                    }
336                }
337                BenchmarkCmd::Block(cmd) => {
338                    if chain_spec.is_dev() {
339                        runner.sync_run(|config| {
340                            let params = local::new_partial(&config, &rpc_config)?;
341                            cmd.run(params.client)
342                        })
343                    } else {
344                        runner.sync_run(|config| {
345                            let params = parachain::new_partial(&config, &rpc_config)?;
346                            cmd.run(params.client)
347                        })
348                    }
349                }
350                BenchmarkCmd::Storage(cmd) => {
351                    if chain_spec.is_dev() {
352                        runner.sync_run(|config| {
353                            let params = local::new_partial(&config, &rpc_config)?;
354                            let db = params.backend.expose_db();
355                            let storage = params.backend.expose_storage();
356
357                            cmd.run(config, params.client, db, storage, None)
358                        })
359                    } else {
360                        runner.sync_run(|config| {
361                            let params = parachain::new_partial(&config, &rpc_config)?;
362                            let db = params.backend.expose_db();
363                            let storage = params.backend.expose_storage();
364
365                            cmd.run(config, params.client, db, storage, None)
366                        })
367                    }
368                }
369                BenchmarkCmd::Overhead(_) => {
370                    todo!("Overhead benchmarking is not supported. Use 'frame-omni-bencher' tool instead.");
371                }
372                BenchmarkCmd::Extrinsic(_) => {
373                    todo!("Extrinsic benchmarking is not supported.");
374                }
375                BenchmarkCmd::Machine(cmd) => {
376                    runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))
377                }
378            }
379        }
380        None => {
381            let runner = cli.create_runner(&cli.run.normalize())?;
382            let collator_options = cli.run.collator_options();
383
384            let evm_tracing_config = cli.eth_api_options.new_rpc_config();
385
386            runner.run_node_until_exit(|config| async move {
387                if config.chain_spec.is_dev() {
388                    return local::start_node::<sc_network::NetworkWorker<_, _>>(
389                        config,
390                        evm_tracing_config,
391                    )
392                    .map_err(Into::into);
393                }
394
395                let polkadot_cli = RelayChainCli::new(
396                    &config,
397                    [RelayChainCli::executable_name()]
398                        .iter()
399                        .chain(cli.relaychain_args.iter()),
400                );
401
402                let para_id = ParaId::from(
403                    chain_spec::Extensions::try_get(&*config.chain_spec)
404                        .map(|e| e.para_id)
405                        .ok_or("ParaId not found in chain spec extension")?,
406                );
407
408                let parachain_account =
409                    AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(
410                        &para_id,
411                    );
412
413                let polkadot_config = SubstrateCli::create_configuration(
414                    &polkadot_cli,
415                    &polkadot_cli,
416                    config.tokio_handle.clone(),
417                )
418                .map_err(|err| format!("Relay chain argument error: {}", err))?;
419
420                info!("Parachain id: {:?}", para_id);
421                info!("Parachain Account: {}", parachain_account);
422                info!(
423                    "Is collating: {}",
424                    if config.role.is_authority() {
425                        "yes"
426                    } else {
427                        "no"
428                    }
429                );
430
431                let hwbench = (!cli.no_hardware_benchmarks)
432                    .then_some(config.database.path().map(|database_path| {
433                        let _ = std::fs::create_dir_all(database_path);
434                        sc_sysinfo::gather_hwbench(
435                            Some(database_path),
436                            &SUBSTRATE_REFERENCE_HARDWARE,
437                        )
438                    }))
439                    .flatten();
440
441                let additional_config = AdditionalConfig {
442                    evm_tracing_config,
443                    enable_evm_rpc: cli.enable_evm_rpc,
444                    proposer_block_size_limit: cli.proposer_block_size_limit,
445                    proposer_soft_deadline_percent: cli.proposer_soft_deadline_percent,
446                    hwbench,
447                };
448
449                parachain::start_node(
450                    config,
451                    polkadot_config,
452                    collator_options,
453                    para_id,
454                    additional_config,
455                )
456                .await
457                .map(|r| r.0)
458                .map_err(Into::into)
459            })
460        }
461    }
462}
463
464impl DefaultConfigurationValues for RelayChainCli {
465    fn p2p_listen_port() -> u16 {
466        30334
467    }
468
469    fn rpc_listen_port() -> u16 {
470        9945
471    }
472
473    fn prometheus_listen_port() -> u16 {
474        9616
475    }
476}
477
478impl CliConfiguration<Self> for RelayChainCli {
479    fn shared_params(&self) -> &SharedParams {
480        self.base.base.shared_params()
481    }
482
483    fn import_params(&self) -> Option<&ImportParams> {
484        self.base.base.import_params()
485    }
486
487    fn network_params(&self) -> Option<&NetworkParams> {
488        self.base.base.network_params()
489    }
490
491    fn keystore_params(&self) -> Option<&KeystoreParams> {
492        self.base.base.keystore_params()
493    }
494
495    fn base_path(&self) -> Result<Option<BasePath>> {
496        Ok(self
497            .shared_params()
498            .base_path()?
499            .or_else(|| self.base_path.clone().map(Into::into)))
500    }
501
502    fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
503        self.base.base.rpc_addr(default_listen_port)
504    }
505
506    fn prometheus_config(
507        &self,
508        default_listen_port: u16,
509        chain_spec: &Box<dyn ChainSpec>,
510    ) -> Result<Option<PrometheusConfig>> {
511        self.base
512            .base
513            .prometheus_config(default_listen_port, chain_spec)
514    }
515
516    fn init<F>(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()>
517    where
518        F: FnOnce(&mut sc_cli::LoggerBuilder),
519    {
520        unreachable!("PolkadotCli is never initialized; qed");
521    }
522
523    fn chain_id(&self, is_dev: bool) -> Result<String> {
524        let chain_id = self.base.base.chain_id(is_dev)?;
525
526        Ok(if chain_id.is_empty() {
527            self.chain_id.clone().unwrap_or_default()
528        } else {
529            chain_id
530        })
531    }
532
533    fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
534        self.base.base.role(is_dev)
535    }
536
537    fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
538        self.base.base.transaction_pool(is_dev)
539    }
540
541    fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
542        self.base.base.trie_cache_maximum_size()
543    }
544
545    fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
546        self.base.base.rpc_methods()
547    }
548
549    fn rpc_max_connections(&self) -> Result<u32> {
550        self.base.base.rpc_max_connections()
551    }
552
553    fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
554        self.base.base.rpc_cors(is_dev)
555    }
556
557    fn default_heap_pages(&self) -> Result<Option<u64>> {
558        self.base.base.default_heap_pages()
559    }
560
561    fn force_authoring(&self) -> Result<bool> {
562        self.base.base.force_authoring()
563    }
564
565    fn disable_grandpa(&self) -> Result<bool> {
566        self.base.base.disable_grandpa()
567    }
568
569    fn max_runtime_instances(&self) -> Result<Option<usize>> {
570        self.base.base.max_runtime_instances()
571    }
572
573    fn announce_block(&self) -> Result<bool> {
574        self.base.base.announce_block()
575    }
576
577    fn telemetry_endpoints(
578        &self,
579        chain_spec: &Box<dyn ChainSpec>,
580    ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
581        self.base.base.telemetry_endpoints(chain_spec)
582    }
583}