,
+ pub pool: Arc,
+ pub deny_unsafe: DenyUnsafe,
+}
+
+pub fn create_full(deps: FullDeps) -> Result>
+where
+ C: ProvideRuntimeApi
+ + HeaderBackend
+ + AuxStore
+ + HeaderMetadata
+ + Send
+ + Sync
+ + 'static,
+ C::Api: TransactionPaymentRuntimeApi,
+ C::Api: AccountNonceApi,
+ C::Api: BlockBuilder,
+ P: TransactionPool + Sync + Send + 'static,
+{
+ use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
+ use substrate_frame_rpc_system::{System, SystemApiServer};
+
+ let mut module = RpcExtension::new(());
+ let FullDeps {
+ client,
+ pool,
+ deny_unsafe,
+ } = deps;
+
+ module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?;
+ module.merge(TransactionPayment::new(client).into_rpc())?;
+ Ok(module)
+}
diff --git a/dip-template/nodes/dip-receiver/src/service.rs b/dip-template/nodes/dip-receiver/src/service.rs
new file mode 100644
index 0000000000..20a0b57027
--- /dev/null
+++ b/dip-template/nodes/dip-receiver/src/service.rs
@@ -0,0 +1,409 @@
+// KILT Blockchain – https://botlabs.org
+// Copyright (C) 2019-2023 BOTLabs GmbH
+
+// The KILT Blockchain is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The KILT Blockchain is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// If you feel like getting in touch with us, you can do so at info@botlabs.org
+
+use std::{error::Error, sync::Arc, time::Duration};
+
+use cumulus_client_cli::CollatorOptions;
+use cumulus_client_consensus_aura::{
+ import_queue, slot_duration, AuraConsensus, BuildAuraConsensusParams, ImportQueueParams, SlotProportion,
+};
+use cumulus_client_consensus_common::{ParachainBlockImport as TParachainBlockImport, ParachainConsensus};
+use cumulus_client_network::BlockAnnounceValidator;
+use cumulus_client_service::{
+ build_relay_chain_interface, prepare_node_config, start_collator, start_full_node, StartCollatorParams,
+ StartFullNodeParams,
+};
+use cumulus_primitives_core::ParaId;
+use cumulus_primitives_parachain_inherent::ParachainInherentData;
+use cumulus_relay_chain_interface::RelayChainInterface;
+use dip_receiver_runtime_template::{api, native_version, Hash, NodeBlock as Block, RuntimeApi};
+use frame_benchmarking::benchmarking::HostFunctions;
+use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE;
+use sc_basic_authorship::ProposerFactory;
+use sc_consensus::{DefaultImportQueue, ImportQueue};
+use sc_executor::NativeElseWasmExecutor;
+use sc_network::{NetworkBlock, NetworkService};
+use sc_service::{
+ build_network, build_offchain_workers, new_full_parts, spawn_tasks, BuildNetworkParams, Configuration,
+ PartialComponents, SpawnTasksParams, TFullBackend, TFullClient, TaskManager,
+};
+use sc_sysinfo::{initialize_hwbench_telemetry, print_hwbench, HwBench};
+use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle};
+use sc_transaction_pool::{BasicPool, FullPool};
+use sp_keystore::SyncCryptoStorePtr;
+use substrate_prometheus_endpoint::Registry;
+
+use crate::rpc::{create_full, FullDeps};
+
+pub struct ParachainNativeExecutor;
+
+impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor {
+ type ExtendHostFunctions = HostFunctions;
+
+ fn dispatch(method: &str, data: &[u8]) -> Option> {
+ api::dispatch(method, data)
+ }
+
+ fn native_version() -> sc_executor::NativeVersion {
+ native_version()
+ }
+}
+
+type ParachainExecutor = NativeElseWasmExecutor;
+type ParachainClient = TFullClient;
+type ParachainBackend = TFullBackend;
+type ParachainBlockImport = TParachainBlockImport, ParachainBackend>;
+
+#[allow(clippy::type_complexity)]
+pub fn new_partial(
+ config: &Configuration,
+) -> Result<
+ PartialComponents<
+ ParachainClient,
+ ParachainBackend,
+ (),
+ DefaultImportQueue,
+ FullPool,
+ (ParachainBlockImport, Option, Option),
+ >,
+ sc_service::Error,
+> {
+ let telemetry = config
+ .telemetry_endpoints
+ .clone()
+ .filter(|x| !x.is_empty())
+ .map(|endpoints| -> Result<_, sc_telemetry::Error> {
+ let worker = TelemetryWorker::new(16)?;
+ let telemetry = worker.handle().new_telemetry(endpoints);
+ Ok((worker, telemetry))
+ })
+ .transpose()?;
+
+ let executor = ParachainExecutor::new(
+ config.wasm_method,
+ config.default_heap_pages,
+ config.max_runtime_instances,
+ config.runtime_cache_size,
+ );
+
+ let (client, backend, keystore_container, task_manager) = new_full_parts::(
+ config,
+ telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
+ executor,
+ )?;
+ let client = Arc::new(client);
+
+ let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
+
+ let telemetry = telemetry.map(|(worker, telemetry)| {
+ task_manager.spawn_handle().spawn("telemetry", None, worker.run());
+ telemetry
+ });
+
+ let transaction_pool = BasicPool::new_full(
+ config.transaction_pool.clone(),
+ config.role.is_authority().into(),
+ config.prometheus_registry(),
+ task_manager.spawn_essential_handle(),
+ client.clone(),
+ );
+
+ let block_import = ParachainBlockImport::new(client.clone(), backend.clone());
+
+ let import_queue = build_import_queue(
+ client.clone(),
+ block_import.clone(),
+ config,
+ telemetry.as_ref().map(|telemetry| telemetry.handle()),
+ &task_manager,
+ )?;
+
+ Ok(PartialComponents {
+ backend,
+ client,
+ import_queue,
+ keystore_container,
+ task_manager,
+ transaction_pool,
+ select_chain: (),
+ other: (block_import, telemetry, telemetry_worker_handle),
+ })
+}
+
+#[sc_tracing::logging::prefix_logs_with("Parachain")]
+async fn start_node_impl(
+ parachain_config: Configuration,
+ polkadot_config: Configuration,
+ collator_options: CollatorOptions,
+ para_id: ParaId,
+ hwbench: Option,
+) -> sc_service::error::Result<(TaskManager, Arc)> {
+ let parachain_config = prepare_node_config(parachain_config);
+
+ let params = new_partial(¶chain_config)?;
+ let (block_import, mut telemetry, telemetry_worker_handle) = params.other;
+
+ let client = params.client.clone();
+ let backend = params.backend.clone();
+ let mut task_manager = params.task_manager;
+
+ let (relay_chain_interface, collator_key) = build_relay_chain_interface(
+ polkadot_config,
+ ¶chain_config,
+ telemetry_worker_handle,
+ &mut task_manager,
+ collator_options.clone(),
+ hwbench.clone(),
+ )
+ .await
+ .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?;
+
+ let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), para_id);
+
+ let force_authoring = parachain_config.force_authoring;
+ let validator = parachain_config.role.is_authority();
+ let prometheus_registry = parachain_config.prometheus_registry().cloned();
+ let transaction_pool = params.transaction_pool.clone();
+ let import_queue_service = params.import_queue.service();
+
+ let (network, system_rpc_tx, tx_handler_controller, start_network) = build_network(BuildNetworkParams {
+ config: ¶chain_config,
+ client: client.clone(),
+ transaction_pool: transaction_pool.clone(),
+ spawn_handle: task_manager.spawn_handle(),
+ import_queue: params.import_queue,
+ block_announce_validator_builder: Some(Box::new(|_| Box::new(block_announce_validator))),
+ warp_sync: None,
+ })?;
+
+ if parachain_config.offchain_worker.enabled {
+ build_offchain_workers(
+ ¶chain_config,
+ task_manager.spawn_handle(),
+ client.clone(),
+ network.clone(),
+ );
+ }
+
+ let rpc_builder = {
+ let client = client.clone();
+ let transaction_pool = transaction_pool.clone();
+
+ Box::new(move |deny_unsafe, _| {
+ let deps = FullDeps {
+ client: client.clone(),
+ pool: transaction_pool.clone(),
+ deny_unsafe,
+ };
+
+ create_full(deps).map_err(Into::into)
+ })
+ };
+
+ spawn_tasks(SpawnTasksParams {
+ rpc_builder,
+ client: client.clone(),
+ transaction_pool: transaction_pool.clone(),
+ task_manager: &mut task_manager,
+ config: parachain_config,
+ keystore: params.keystore_container.sync_keystore(),
+ backend,
+ network: network.clone(),
+ system_rpc_tx,
+ tx_handler_controller,
+ telemetry: telemetry.as_mut(),
+ })?;
+
+ if let Some(hwbench) = hwbench {
+ print_hwbench(&hwbench);
+ if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && validator {
+ log::warn!("⚠️ The hardware does not meet the minimal requirements for role 'Authority'.");
+ }
+
+ if let Some(ref mut telemetry) = telemetry {
+ let telemetry_handle = telemetry.handle();
+ task_manager.spawn_handle().spawn(
+ "telemetry_hwbench",
+ None,
+ initialize_hwbench_telemetry(telemetry_handle, hwbench),
+ );
+ }
+ }
+
+ let announce_block = {
+ let network = network.clone();
+ Arc::new(move |hash, data| network.announce_block(hash, data))
+ };
+
+ let relay_chain_slot_duration = Duration::from_secs(6);
+
+ if validator {
+ let parachain_consensus = build_consensus(
+ client.clone(),
+ block_import,
+ prometheus_registry.as_ref(),
+ telemetry.as_ref().map(|t| t.handle()),
+ &task_manager,
+ relay_chain_interface.clone(),
+ transaction_pool,
+ network,
+ params.keystore_container.sync_keystore(),
+ force_authoring,
+ para_id,
+ )?;
+
+ let spawner = task_manager.spawn_handle();
+ let params = StartCollatorParams {
+ para_id,
+ block_status: client.clone(),
+ announce_block,
+ client: client.clone(),
+ task_manager: &mut task_manager,
+ relay_chain_interface,
+ spawner,
+ parachain_consensus,
+ import_queue: import_queue_service,
+ collator_key: collator_key.expect("Command line arguments do not allow this. qed"),
+ relay_chain_slot_duration,
+ };
+
+ start_collator(params).await?;
+ } else {
+ let params = StartFullNodeParams {
+ client: client.clone(),
+ announce_block,
+ task_manager: &mut task_manager,
+ para_id,
+ relay_chain_interface,
+ relay_chain_slot_duration,
+ import_queue: import_queue_service,
+ };
+
+ start_full_node(params)?;
+ }
+
+ start_network.start_network();
+
+ Ok((task_manager, client))
+}
+
+fn build_import_queue(
+ client: Arc,
+ block_import: ParachainBlockImport,
+ config: &Configuration,
+ telemetry: Option,
+ task_manager: &TaskManager,
+) -> Result, sc_service::Error> {
+ let slot_duration = slot_duration(&*client)?;
+
+ import_queue::(ImportQueueParams {
+ block_import,
+ client,
+ create_inherent_data_providers: move |_, _| async move {
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+
+ let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration(
+ *timestamp,
+ slot_duration,
+ );
+
+ Ok((slot, timestamp))
+ },
+ registry: config.prometheus_registry(),
+ spawner: &task_manager.spawn_essential_handle(),
+ telemetry,
+ })
+ .map_err(Into::into)
+}
+
+#[allow(clippy::too_many_arguments)]
+fn build_consensus(
+ client: Arc,
+ block_import: ParachainBlockImport,
+ prometheus_registry: Option<&Registry>,
+ telemetry: Option,
+ task_manager: &TaskManager,
+ relay_chain_interface: Arc,
+ transaction_pool: Arc>,
+ sync_oracle: Arc>,
+ keystore: SyncCryptoStorePtr,
+ force_authoring: bool,
+ para_id: ParaId,
+) -> Result>, sc_service::Error> {
+ let slot_duration = slot_duration(&*client)?;
+
+ let proposer_factory = ProposerFactory::with_proof_recording(
+ task_manager.spawn_handle(),
+ client.clone(),
+ transaction_pool,
+ prometheus_registry,
+ telemetry.clone(),
+ );
+
+ let params = BuildAuraConsensusParams {
+ proposer_factory,
+ create_inherent_data_providers: move |_, (relay_parent, validation_data)| {
+ let relay_chain_interface = relay_chain_interface.clone();
+ async move {
+ let parachain_inherent =
+ ParachainInherentData::create_at(relay_parent, &relay_chain_interface, &validation_data, para_id)
+ .await;
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+
+ let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration(
+ *timestamp,
+ slot_duration,
+ );
+
+ let parachain_inherent = parachain_inherent
+ .ok_or_else(|| Box::::from("Failed to create parachain inherent"))?;
+ Ok((slot, timestamp, parachain_inherent))
+ }
+ },
+ block_import,
+ para_client: client,
+ backoff_authoring_blocks: Option::<()>::None,
+ sync_oracle,
+ keystore,
+ force_authoring,
+ slot_duration,
+ block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32),
+ max_block_proposal_slot_portion: Some(SlotProportion::new(1f32 / 16f32)),
+ telemetry,
+ };
+
+ Ok(AuraConsensus::build::<
+ sp_consensus_aura::sr25519::AuthorityPair,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ >(params))
+}
+
+pub async fn start_parachain_node(
+ parachain_config: Configuration,
+ polkadot_config: Configuration,
+ collator_options: CollatorOptions,
+ para_id: ParaId,
+ hwbench: Option,
+) -> sc_service::error::Result<(TaskManager, Arc)> {
+ start_node_impl(parachain_config, polkadot_config, collator_options, para_id, hwbench).await
+}
diff --git a/dip-template/nodes/dip-sender/Cargo.toml b/dip-template/nodes/dip-sender/Cargo.toml
new file mode 100644
index 0000000000..142abbd1d7
--- /dev/null
+++ b/dip-template/nodes/dip-sender/Cargo.toml
@@ -0,0 +1,68 @@
+[package]
+authors.workspace = true
+documentation.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license-file.workspace = true
+readme.workspace = true
+repository.workspace = true
+version.workspace = true
+name = "dip-sender-node-template"
+description = "Node template for the KILT Decentralized Identity Provider (DIP) sender."
+build = "build.rs"
+
+[dependencies]
+clap = {workspace = true, features = ["std", "derive"]}
+log = { workspace = true, features = ["std"] }
+codec = { workspace = true, features = ["std"] }
+serde = {workspace = true, features = ["std", "derive"]}
+jsonrpsee = {workspace = true, features = ["server"]}
+
+# Internal dependencies
+dip-sender-runtime-template = { workspace = true, features = ["std"] }
+
+# Substrate
+frame-benchmarking.workspace = true
+frame-benchmarking-cli.workspace = true
+pallet-transaction-payment-rpc.workspace = true
+sc-basic-authorship.workspace = true
+sc-chain-spec.workspace = true
+sc-cli.workspace = true
+sc-client-api.workspace = true
+sc-consensus.workspace = true
+sc-executor.workspace = true
+sc-network.workspace = true
+sc-rpc-api.workspace = true
+sc-service.workspace = true
+sc-sysinfo.workspace = true
+sc-telemetry.workspace = true
+sc-tracing.workspace = true
+sc-transaction-pool.workspace = true
+sc-transaction-pool-api.workspace = true
+sp-api = { workspace = true, features = ["std"] }
+sp-block-builder = { workspace = true, features = ["std"] }
+sp-blockchain.workspace = true
+sp-consensus-aura = { workspace = true, features = ["std"] }
+sp-core = { workspace = true, features = ["std"] }
+sp-keystore = { workspace = true, features = ["std"] }
+sp-runtime = { workspace = true, features = ["std"] }
+sp-timestamp = { workspace = true, features = ["std"] }
+substrate-frame-rpc-system.workspace = true
+substrate-prometheus-endpoint.workspace = true
+
+# Polkadot
+polkadot-cli.workspace = true
+polkadot-primitives = { workspace = true, features = ["std"] }
+
+# Cumulus
+cumulus-client-cli.workspace = true
+cumulus-client-consensus-aura.workspace = true
+cumulus-client-consensus-common.workspace = true
+cumulus-client-network.workspace = true
+cumulus-client-service.workspace = true
+cumulus-primitives-core = { workspace = true, features = ["std"] }
+cumulus-primitives-parachain-inherent = { workspace = true, features = ["std"] }
+cumulus-relay-chain-interface.workspace = true
+
+[build-dependencies]
+substrate-build-script-utils.workspace = true
diff --git a/dip-template/nodes/dip-sender/build.rs b/dip-template/nodes/dip-sender/build.rs
new file mode 100644
index 0000000000..961231c478
--- /dev/null
+++ b/dip-template/nodes/dip-sender/build.rs
@@ -0,0 +1,25 @@
+// KILT Blockchain – https://botlabs.org
+// Copyright (C) 2019-2023 BOTLabs GmbH
+
+// The KILT Blockchain is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The KILT Blockchain is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// If you feel like getting in touch with us, you can do so at info@botlabs.org
+
+use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
+
+fn main() {
+ generate_cargo_keys();
+
+ rerun_if_git_head_changed();
+}
diff --git a/dip-template/nodes/dip-sender/src/chain_spec.rs b/dip-template/nodes/dip-sender/src/chain_spec.rs
new file mode 100644
index 0000000000..8fe5269ada
--- /dev/null
+++ b/dip-template/nodes/dip-sender/src/chain_spec.rs
@@ -0,0 +1,143 @@
+// KILT Blockchain – https://botlabs.org
+// Copyright (C) 2019-2023 BOTLabs GmbH
+
+// The KILT Blockchain is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The KILT Blockchain is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// If you feel like getting in touch with us, you can do so at info@botlabs.org
+
+use cumulus_primitives_core::ParaId;
+use dip_sender_runtime_template::{
+ AccountId, AuraId, BalancesConfig, CollatorSelectionConfig, GenesisConfig, ParachainInfoConfig, SessionConfig,
+ SessionKeys, Signature, SudoConfig, SystemConfig, EXISTENTIAL_DEPOSIT, SS58_PREFIX, WASM_BINARY,
+};
+use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, Properties};
+use sc_service::{ChainType, GenericChainSpec};
+use serde::{Deserialize, Serialize};
+use sp_core::{sr25519, Pair, Public};
+use sp_runtime::traits::{IdentifyAccount, Verify};
+
+const PARA_ID: u32 = 2_000;
+
+pub type ChainSpec = GenericChainSpec;
+type AccountPublic = ::Signer;
+
+pub(crate) fn get_from_seed(seed: &str) -> ::Public {
+ TPublic::Pair::from_string(&format!("//{}", seed), None)
+ .expect("static values are valid; qed")
+ .public()
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
+#[serde(deny_unknown_fields)]
+pub struct Extensions {
+ pub relay_chain: String,
+ pub para_id: u32,
+}
+
+impl Extensions {
+ pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
+ sc_chain_spec::get_extension(chain_spec.extensions())
+ }
+}
+
+pub fn get_collator_keys_from_seed(seed: &str) -> AuraId {
+ get_from_seed::(seed)
+}
+
+pub fn get_account_id_from_seed(seed: &str) -> AccountId
+where
+ AccountPublic: From<::Public>,
+{
+ AccountPublic::from(get_from_seed::(seed)).into_account()
+}
+
+pub fn template_session_keys(keys: AuraId) -> SessionKeys {
+ SessionKeys { aura: keys }
+}
+
+fn testnet_genesis(
+ invulnerables: Vec<(AccountId, AuraId)>,
+ endowed_accounts: Vec,
+ id: ParaId,
+) -> GenesisConfig {
+ GenesisConfig {
+ system: SystemConfig {
+ code: WASM_BINARY
+ .expect("WASM binary was not build, please build it!")
+ .to_vec(),
+ },
+ parachain_system: Default::default(),
+ parachain_info: ParachainInfoConfig { parachain_id: id },
+ sudo: SudoConfig {
+ key: Some(endowed_accounts.first().unwrap().clone()),
+ },
+ balances: BalancesConfig {
+ balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
+ },
+ transaction_payment: Default::default(),
+ collator_selection: CollatorSelectionConfig {
+ invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(),
+ candidacy_bond: EXISTENTIAL_DEPOSIT * 16,
+ ..Default::default()
+ },
+ session: SessionConfig {
+ keys: invulnerables
+ .into_iter()
+ .map(|(acc, aura)| (acc.clone(), acc, template_session_keys(aura)))
+ .collect(),
+ },
+ aura: Default::default(),
+ aura_ext: Default::default(),
+ polkadot_xcm: Default::default(),
+ }
+}
+
+pub fn development_config() -> ChainSpec {
+ let mut properties = Properties::new();
+ properties.insert("tokenSymbol".into(), "SEILT".into());
+ properties.insert("tokenDecimals".into(), 12.into());
+ properties.insert("ss58Format".into(), SS58_PREFIX.into());
+
+ ChainSpec::from_genesis(
+ "DIP sender dev",
+ "dip-sender-dev",
+ ChainType::Development,
+ move || {
+ testnet_genesis(
+ vec![(
+ get_account_id_from_seed::("Alice"),
+ get_collator_keys_from_seed("Alice"),
+ )],
+ vec![
+ get_account_id_from_seed::("Alice"),
+ get_account_id_from_seed::("Alice//stash"),
+ get_account_id_from_seed::("Bob"),
+ get_account_id_from_seed::("Bob//stash"),
+ get_account_id_from_seed::("Charlie"),
+ get_account_id_from_seed::("Charlie//stash"),
+ ],
+ PARA_ID.into(),
+ )
+ },
+ Vec::new(),
+ None,
+ "dip-sender-dev".into(),
+ None,
+ None,
+ Extensions {
+ relay_chain: "rococo-local".into(),
+ para_id: PARA_ID,
+ },
+ )
+}
diff --git a/dip-template/nodes/dip-sender/src/cli.rs b/dip-template/nodes/dip-sender/src/cli.rs
new file mode 100644
index 0000000000..3cec4a2550
--- /dev/null
+++ b/dip-template/nodes/dip-sender/src/cli.rs
@@ -0,0 +1,89 @@
+// KILT Blockchain – https://botlabs.org
+// Copyright (C) 2019-2023 BOTLabs GmbH
+
+// The KILT Blockchain is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The KILT Blockchain is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// If you feel like getting in touch with us, you can do so at info@botlabs.org
+
+use std::path::PathBuf;
+
+use cumulus_client_cli::{ExportGenesisStateCommand, ExportGenesisWasmCommand, PurgeChainCmd};
+use polkadot_cli::RunCmd;
+use sc_cli::{BuildSpecCmd, CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd};
+use sc_service::Configuration;
+
+use crate::chain_spec::Extensions;
+
+#[derive(Debug, clap::Subcommand)]
+pub enum Subcommand {
+ BuildSpec(BuildSpecCmd),
+
+ CheckBlock(CheckBlockCmd),
+
+ ExportBlocks(ExportBlocksCmd),
+
+ ExportState(ExportStateCmd),
+
+ ImportBlocks(ImportBlocksCmd),
+
+ Revert(RevertCmd),
+
+ PurgeChain(PurgeChainCmd),
+
+ ExportGenesisState(ExportGenesisStateCommand),
+
+ ExportGenesisWasm(ExportGenesisWasmCommand),
+}
+
+#[derive(Debug, clap::Parser)]
+#[command(
+ propagate_version = true,
+ args_conflicts_with_subcommands = true,
+ subcommand_negates_reqs = true
+)]
+pub struct Cli {
+ #[command(subcommand)]
+ pub subcommand: Option,
+
+ #[command(flatten)]
+ pub run: cumulus_client_cli::RunCmd,
+
+ #[arg(long)]
+ pub no_hardware_benchmarks: bool,
+
+ #[arg(raw = true)]
+ pub relay_chain_args: Vec,
+}
+
+#[derive(Debug)]
+pub struct RelayChainCli {
+ pub base: RunCmd,
+
+ pub chain_id: Option,
+
+ pub base_path: Option,
+}
+
+impl RelayChainCli {
+ pub fn new<'a>(para_config: &Configuration, relay_chain_args: impl Iterator- ) -> Self {
+ let extension = Extensions::try_get(&*para_config.chain_spec);
+ let chain_id = extension.map(|e| e.relay_chain.clone());
+ let base_path = para_config.base_path.as_ref().map(|x| x.path().join("polkadot"));
+ Self {
+ base_path,
+ chain_id,
+ base: clap::Parser::parse_from(relay_chain_args),
+ }
+ }
+}
diff --git a/dip-template/nodes/dip-sender/src/command.rs b/dip-template/nodes/dip-sender/src/command.rs
new file mode 100644
index 0000000000..9105231d16
--- /dev/null
+++ b/dip-template/nodes/dip-sender/src/command.rs
@@ -0,0 +1,400 @@
+// KILT Blockchain – https://botlabs.org
+// Copyright (C) 2019-2023 BOTLabs GmbH
+
+// The KILT Blockchain is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The KILT Blockchain is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// If you feel like getting in touch with us, you can do so at info@botlabs.org
+
+use std::{fs::create_dir_all, net::SocketAddr};
+
+use codec::Encode;
+use cumulus_client_cli::generate_genesis_block;
+use cumulus_primitives_core::ParaId;
+use dip_sender_runtime_template::Block;
+use log::{info, warn};
+use sc_cli::{
+ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, LoggerBuilder,
+ NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,
+};
+use sc_service::{
+ config::{BasePath, PrometheusConfig},
+ Configuration, Role, RpcMethods, TransactionPoolOptions,
+};
+use sc_sysinfo::gather_hwbench;
+use sc_telemetry::TelemetryEndpoints;
+use sp_core::hexdisplay::HexDisplay;
+use sp_runtime::traits::{AccountIdConversion, Block as BlockT};
+
+use crate::{
+ chain_spec::{development_config, Extensions},
+ cli::{Cli, RelayChainCli, Subcommand},
+ service::{new_partial, start_parachain_node},
+};
+
+fn load_spec(id: &str) -> std::result::Result, String> {
+ match id {
+ "dev" => Ok(Box::new(development_config())),
+ _ => Err("Unrecognized spec ID.".into()),
+ }
+}
+
+impl SubstrateCli for Cli {
+ fn impl_name() -> String {
+ "DIP Sender Node Template".into()
+ }
+
+ fn impl_version() -> String {
+ env!("SUBSTRATE_CLI_IMPL_VERSION").into()
+ }
+
+ fn description() -> String {
+ format!(
+ "DIP Sender Node Template\n\nThe command-line arguments provided first will be \
+ passed to the parachain node, while the arguments provided after -- will be passed \
+ to the relay chain node.\n\n\
+ {} -- ",
+ Self::executable_name()
+ )
+ }
+
+ fn author() -> String {
+ env!("CARGO_PKG_AUTHORS").into()
+ }
+
+ fn support_url() -> String {
+ "https://github.com/kiltprotocol/kilt-node/issues/new".into()
+ }
+
+ fn copyright_start_year() -> i32 {
+ 2023
+ }
+
+ fn load_spec(&self, id: &str) -> std::result::Result, String> {
+ load_spec(id)
+ }
+
+ fn native_runtime_version(_spec: &Box) -> &'static RuntimeVersion {
+ &dip_sender_runtime_template::VERSION
+ }
+}
+
+impl SubstrateCli for RelayChainCli {
+ fn impl_name() -> String {
+ "DIP Sender Node Template".into()
+ }
+
+ fn impl_version() -> String {
+ env!("SUBSTRATE_CLI_IMPL_VERSION").into()
+ }
+
+ fn description() -> String {
+ format!(
+ "DIP Sender Node Template\n\nThe command-line arguments provided first will be \
+ passed to the parachain node, while the arguments provided after -- will be passed \
+ to the relay chain node.\n\n\
+ {} -- ",
+ Self::executable_name()
+ )
+ }
+
+ fn author() -> String {
+ env!("CARGO_PKG_AUTHORS").into()
+ }
+
+ fn support_url() -> String {
+ "https://github.com/kiltprotocol/kilt-node/issues/new".into()
+ }
+
+ fn copyright_start_year() -> i32 {
+ 2023
+ }
+
+ fn load_spec(&self, id: &str) -> std::result::Result, String> {
+ polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)
+ }
+
+ fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion {
+ polkadot_cli::Cli::native_runtime_version(chain_spec)
+ }
+}
+
+macro_rules! construct_async_run {
+ (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{
+ let runner = $cli.create_runner($cmd)?;
+ runner.async_run(|$config| {
+ let $components = new_partial(&$config)?;
+ let task_manager = $components.task_manager;
+ { $( $code )* }.map(|v| (v, task_manager))
+ })
+ }}
+}
+
+pub fn run() -> Result<()> {
+ let cli = Cli::from_args();
+
+ match &cli.subcommand {
+ Some(Subcommand::BuildSpec(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|config| cmd.run(config.chain_spec, config.network))
+ }
+ Some(Subcommand::CheckBlock(cmd)) => {
+ construct_async_run!(|components, cli, cmd, config| {
+ Ok(cmd.run(components.client, components.import_queue))
+ })
+ }
+ Some(Subcommand::ExportBlocks(cmd)) => {
+ construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.database)))
+ }
+ Some(Subcommand::ExportState(cmd)) => {
+ construct_async_run!(|components, cli, cmd, config| Ok(cmd.run(components.client, config.chain_spec)))
+ }
+ Some(Subcommand::ImportBlocks(cmd)) => {
+ construct_async_run!(|components, cli, cmd, config| {
+ Ok(cmd.run(components.client, components.import_queue))
+ })
+ }
+ Some(Subcommand::Revert(cmd)) => {
+ construct_async_run!(|components, cli, cmd, config| {
+ Ok(cmd.run(components.client, components.backend, None))
+ })
+ }
+ Some(Subcommand::PurgeChain(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+
+ runner.sync_run(|config| {
+ let polkadot_cli = RelayChainCli::new(
+ &config,
+ [RelayChainCli::executable_name()]
+ .iter()
+ .chain(cli.relay_chain_args.iter()),
+ );
+
+ let polkadot_config =
+ SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, config.tokio_handle.clone())
+ .map_err(|err| format!("Relay chain argument error: {}", err))?;
+
+ cmd.run(config, polkadot_config)
+ })
+ }
+ Some(Subcommand::ExportGenesisState(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|_config| {
+ let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
+ let state_version = Cli::native_runtime_version(&spec).state_version();
+ cmd.run::(&*spec, state_version)
+ })
+ }
+ Some(Subcommand::ExportGenesisWasm(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|_config| {
+ let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
+ cmd.run(&*spec)
+ })
+ }
+ None => {
+ let runner = cli.create_runner(&cli.run.normalize())?;
+ let collator_options = cli.run.collator_options();
+
+ runner.run_node_until_exit(|config| async move {
+ let hwbench = (!cli.no_hardware_benchmarks).then_some(
+ config.database.path().map(|database_path| {
+ let _ = create_dir_all(database_path);
+ gather_hwbench(Some(database_path))
+ })).flatten();
+
+ let para_id = Extensions::try_get(&*config.chain_spec)
+ .map(|e| e.para_id)
+ .ok_or("Could not find parachain ID in chain-spec.")?;
+
+ let polkadot_cli = RelayChainCli::new(
+ &config,
+ [RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()),
+ );
+
+ let id = ParaId::from(para_id);
+
+ let parachain_account =
+ AccountIdConversion::::into_account_truncating(&id);
+
+ let state_version = Cli::native_runtime_version(&config.chain_spec).state_version();
+ let block: Block = generate_genesis_block(&*config.chain_spec, state_version)
+ .map_err(|e| format!("{:?}", e))?;
+ let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
+
+ let tokio_handle = config.tokio_handle.clone();
+ let polkadot_config =
+ SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
+ .map_err(|err| format!("Relay chain argument error: {}", err))?;
+
+ info!("Parachain id: {:?}", id);
+ info!("Parachain Account: {}", parachain_account);
+ info!("Parachain genesis state: {}", genesis_state);
+ info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
+
+ if !collator_options.relay_chain_rpc_urls.is_empty() && cli.relay_chain_args.is_empty() {
+ warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options.");
+ }
+
+ start_parachain_node(
+ config,
+ polkadot_config,
+ collator_options,
+ id,
+ hwbench,
+ )
+ .await
+ .map(|r| r.0)
+ .map_err(Into::into)
+ })
+ }
+ }
+}
+
+impl DefaultConfigurationValues for RelayChainCli {
+ fn p2p_listen_port() -> u16 {
+ 30334
+ }
+
+ fn rpc_ws_listen_port() -> u16 {
+ 9945
+ }
+
+ fn rpc_http_listen_port() -> u16 {
+ 9934
+ }
+
+ fn prometheus_listen_port() -> u16 {
+ 9616
+ }
+}
+
+impl CliConfiguration for RelayChainCli {
+ fn shared_params(&self) -> &SharedParams {
+ self.base.base.shared_params()
+ }
+
+ fn import_params(&self) -> Option<&ImportParams> {
+ self.base.base.import_params()
+ }
+
+ fn network_params(&self) -> Option<&NetworkParams> {
+ self.base.base.network_params()
+ }
+
+ fn keystore_params(&self) -> Option<&KeystoreParams> {
+ self.base.base.keystore_params()
+ }
+
+ fn base_path(&self) -> Result