diff --git a/.gitignore b/.gitignore index 058330f49e..6892a1980e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,4 @@ # ignore vscode settings .vscode -# Custom Polkadot-js apps types -pallets/did/src/custom_types.json - **/__pycache__ diff --git a/Cargo.lock b/Cargo.lock index bb757cfbda..651a4b19de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2187,6 +2187,238 @@ dependencies = [ "subtle", ] +[[package]] +name = "dip-receiver-node-template" +version = "1.11.0-dev" +dependencies = [ + "clap", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dip-receiver-runtime-template", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-rpc-api", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "dip-receiver-runtime-template" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-did-lookup", + "pallet-dip-receiver", + "pallet-session", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "dip-sender-node-template" +version = "1.11.0-dev" +dependencies = [ + "clap", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "dip-sender-runtime-template", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-rpc-api", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "dip-sender-runtime-template" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "did", + "dip-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-dip-sender", + "pallet-session", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "dip-support" +version = "1.11.0-dev" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "dip-templates-xmc-tests" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-xcmp-queue", + "dip-receiver-runtime-template", + "dip-sender-runtime-template", + "frame-support", + "frame-system", + "pallet-balances", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rococo-runtime", + "scale-info", + "sp-io", + "sp-runtime", + "xcm", + "xcm-emulator", + "xcm-executor", +] + [[package]] name = "directories" version = "4.0.1" @@ -5804,6 +6036,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-collator-selection" +version = "3.0.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.38#3275e271e8937c06dca44331e98f31ef020a71e6" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", +] + [[package]] name = "pallet-collective" version = "4.0.0-dev" @@ -5883,6 +6134,33 @@ dependencies = [ "test-log", ] +[[package]] +name = "pallet-dip-receiver" +version = "1.11.0-dev" +dependencies = [ + "cumulus-pallet-xcm", + "dip-support", + "frame-support", + "frame-system", + "kilt-support", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "pallet-dip-sender" +version = "1.11.0-dev" +dependencies = [ + "dip-support", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-std", + "xcm", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" @@ -13630,6 +13908,32 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "xcm-emulator" +version = "0.1.0" +source = "git+https://github.com/shaunxw/xcm-simulator?branch=master#92d371839f6d5c52dd35b7e3d61cbdefc792cc42" +dependencies = [ + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "parachain-info", + "parity-scale-codec", + "paste", + "polkadot-primitives", + "polkadot-runtime-parachains", + "quote", + "sp-arithmetic", + "sp-io", + "sp-std", + "xcm", + "xcm-executor", +] + [[package]] name = "xcm-executor" version = "0.9.38" diff --git a/Cargo.toml b/Cargo.toml index d6efef793b..25cd99b971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,14 @@ version = "1.11.0-dev" [workspace] members = [ + "crates/*", + "dip-template/nodes/*", + "dip-template/runtimes/*", "nodes/*", "pallets/*", "runtime-api/*", "runtimes/*", "support", - "crates/*", ] [profile.release] @@ -49,6 +51,8 @@ attestation = {path = "pallets/attestation", default-features = false} ctype = {path = "pallets/ctype", default-features = false} delegation = {path = "pallets/delegation", default-features = false} did = {path = "pallets/did", default-features = false} +pallet-dip-receiver = {path = "pallets/pallet-dip-receiver", default-features = false} +pallet-dip-sender = {path = "pallets/pallet-dip-sender", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "pallets/pallet-inflation", default-features = false} pallet-web3-names = {path = "pallets/pallet-web3-names", default-features = false} @@ -56,10 +60,15 @@ parachain-staking = {path = "pallets/parachain-staking", default-features = fals public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) +dip-support = {path = "crates/dip", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} +# Templates +dip-receiver-runtime-template = {path = "dip-template/runtimes/dip-receiver", default-features = false} +dip-sender-runtime-template = {path = "dip-template/runtimes/dip-sender", default-features = false} + # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} kilt-runtime-api-public-credentials = {path = "runtime-api/public-credentials", default-features = false} @@ -133,6 +142,8 @@ try-runtime-cli = {git = "https://github.com/paritytech/substrate", default-feat # Polkadot (with default disabled) pallet-xcm = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} polkadot-parachain = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} +rococo-runtime = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} +polkadot-runtime-parachains = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} xcm = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} xcm-builder = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} xcm-executor = {git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38"} diff --git a/crates/dip/Cargo.toml b/crates/dip/Cargo.toml new file mode 100644 index 0000000000..8d5c6aab7b --- /dev/null +++ b/crates/dip/Cargo.toml @@ -0,0 +1,29 @@ +[package] +authors.workspace = true +description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-support" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +# Parity dependencies +codec = {package = "parity-scale-codec", workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true + +# Substrate dependencies +frame-support.workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "sp-std/std", + "frame-support/std" +] diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs new file mode 100644 index 0000000000..e71b1697d5 --- /dev/null +++ b/crates/dip/src/lib.rs @@ -0,0 +1,57 @@ +// 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 + +// TODO: Crate documentation + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; + +// Export v1 behind a namespace and also as the latest +pub mod v1; +pub mod latest { + pub use crate::v1::*; +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum VersionedIdentityProofAction { + #[codec(index = 1)] + V1(v1::IdentityProofAction), +} + +impl From> + for VersionedIdentityProofAction +{ + fn from(value: v1::IdentityProofAction) -> Self { + Self::V1(value) + } +} + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] +pub enum VersionedIdentityProof { + #[codec(index = 1)] + V1(v1::Proof), +} + +impl From> for VersionedIdentityProof { + fn from(value: v1::Proof) -> Self { + Self::V1(value) + } +} diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs new file mode 100644 index 0000000000..c57ebec201 --- /dev/null +++ b/crates/dip/src/v1.rs @@ -0,0 +1,31 @@ +// 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 codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +pub enum IdentityProofAction { + Updated(Identifier, Proof, Details), + Deleted(Identifier), +} + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] +pub struct Proof(Vec<(LeafKey, LeafValue)>); diff --git a/dip-template/nodes/dip-receiver/Cargo.toml b/dip-template/nodes/dip-receiver/Cargo.toml new file mode 100644 index 0000000000..a7f87191d4 --- /dev/null +++ b/dip-template/nodes/dip-receiver/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-receiver-node-template" +description = "Node template for the KILT Decentralized Identity Provider (DIP) receiver." +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-receiver-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-receiver/build.rs b/dip-template/nodes/dip-receiver/build.rs new file mode 100644 index 0000000000..961231c478 --- /dev/null +++ b/dip-template/nodes/dip-receiver/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-receiver/src/chain_spec.rs b/dip-template/nodes/dip-receiver/src/chain_spec.rs new file mode 100644 index 0000000000..50daa7a567 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/chain_spec.rs @@ -0,0 +1,144 @@ +// 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_receiver_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_001; + +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(), + did_lookup: Default::default(), + } +} + +pub fn development_config() -> ChainSpec { + let mut properties = Properties::new(); + properties.insert("tokenSymbol".into(), "REILT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + properties.insert("ss58Format".into(), SS58_PREFIX.into()); + + ChainSpec::from_genesis( + "DIP receiver dev", + "dip-receiver-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-receiver-dev".into(), + None, + None, + Extensions { + relay_chain: "rococo-local".into(), + para_id: PARA_ID, + }, + ) +} diff --git a/dip-template/nodes/dip-receiver/src/cli.rs b/dip-template/nodes/dip-receiver/src/cli.rs new file mode 100644 index 0000000000..3cec4a2550 --- /dev/null +++ b/dip-template/nodes/dip-receiver/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-receiver/src/command.rs b/dip-template/nodes/dip-receiver/src/command.rs new file mode 100644 index 0000000000..b94ef67aa1 --- /dev/null +++ b/dip-template/nodes/dip-receiver/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_receiver_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 Receiver Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Receiver 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_receiver_runtime_template::VERSION + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "DIP Receiver Node Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "DIP Receiver 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> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &Configuration, + ) -> Result<()> + where + F: FnOnce(&mut LoggerBuilder, &Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints(&self, chain_spec: &Box) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/dip-template/nodes/dip-receiver/src/main.rs b/dip-template/nodes/dip-receiver/src/main.rs new file mode 100644 index 0000000000..e0250cebc8 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/main.rs @@ -0,0 +1,32 @@ +// 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 + +//! KILT Decentralized Identity Provider (DIP) receiver CLI. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; +mod rpc; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/dip-template/nodes/dip-receiver/src/rpc.rs b/dip-template/nodes/dip-receiver/src/rpc.rs new file mode 100644 index 0000000000..30a8bd3b09 --- /dev/null +++ b/dip-template/nodes/dip-receiver/src/rpc.rs @@ -0,0 +1,70 @@ +// 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 + +#![warn(missing_docs)] + +pub use sc_rpc_api::DenyUnsafe; +use substrate_frame_rpc_system::AccountNonceApi; + +use std::{error::Error, sync::Arc}; + +use dip_receiver_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; +use sc_client_api::AuxStore; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; + +pub type RpcExtension = RpcModule<()>; + +pub struct FullDeps { + pub client: Arc, + 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> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &Configuration, + ) -> Result<()> + where + F: FnOnce(&mut LoggerBuilder, &Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { + self.chain_id.clone().unwrap_or_default() + } else { + chain_id + }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints(&self, chain_spec: &Box) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> Result { + self.base.base.node_name() + } +} diff --git a/dip-template/nodes/dip-sender/src/main.rs b/dip-template/nodes/dip-sender/src/main.rs new file mode 100644 index 0000000000..a3e57e48c6 --- /dev/null +++ b/dip-template/nodes/dip-sender/src/main.rs @@ -0,0 +1,32 @@ +// 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 + +//! KILT Decentralized Identity Provider (DIP) sender CLI. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; +mod rpc; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/dip-template/nodes/dip-sender/src/rpc.rs b/dip-template/nodes/dip-sender/src/rpc.rs new file mode 100644 index 0000000000..79365ebb0e --- /dev/null +++ b/dip-template/nodes/dip-sender/src/rpc.rs @@ -0,0 +1,70 @@ +// 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 + +#![warn(missing_docs)] + +pub use sc_rpc_api::DenyUnsafe; +use substrate_frame_rpc_system::AccountNonceApi; + +use std::{error::Error, sync::Arc}; + +use dip_sender_runtime_template::{AccountId, Balance, Index as Nonce, NodeBlock as Block}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; +use sc_client_api::AuxStore; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; + +pub type RpcExtension = RpcModule<()>; + +pub struct FullDeps { + pub client: Arc, + 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-sender/src/service.rs b/dip-template/nodes/dip-sender/src/service.rs new file mode 100644 index 0000000000..1dee07bcfd --- /dev/null +++ b/dip-template/nodes/dip-sender/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_sender_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/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml new file mode 100644 index 0000000000..3b559bb0ca --- /dev/null +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -0,0 +1,123 @@ +[package] +authors.workspace = true +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) receiver." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-receiver-runtime-template" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[build-dependencies] +substrate-wasm-builder.workspace = true + +[dependencies] +codec = {package = "parity-scale-codec", workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# DIP +pallet-did-lookup.workspace = true +pallet-dip-receiver.workspace = true + +# Substrate +frame-executive.workspace = true +frame-support.workspace = true +frame-system.workspace = true +frame-system-rpc-runtime-api.workspace = true +pallet-aura.workspace = true +pallet-authorship.workspace = true +pallet-balances.workspace = true +pallet-session.workspace = true +pallet-sudo.workspace = true +pallet-timestamp.workspace = true +pallet-transaction-payment.workspace = true +pallet-transaction-payment-rpc-runtime-api.workspace = true +polkadot-parachain.workspace = true +sp-api.workspace = true +sp-block-builder.workspace = true +sp-consensus-aura.workspace = true +sp-core.workspace = true +sp-inherents.workspace = true +sp-offchain.workspace = true +sp-runtime.workspace = true +sp-session.workspace = true +sp-std.workspace = true +sp-transaction-pool.workspace = true +sp-version.workspace = true + +# Polkadot +pallet-xcm.workspace = true +xcm.workspace = true +xcm-builder.workspace = true +xcm-executor.workspace = true + +# Cumulus +cumulus-pallet-aura-ext.workspace = true +cumulus-pallet-dmp-queue.workspace = true +cumulus-pallet-parachain-system.workspace = true +cumulus-pallet-xcm.workspace = true +cumulus-pallet-xcmp-queue.workspace = true +cumulus-primitives-core.workspace = true +cumulus-primitives-timestamp.workspace = true +cumulus-primitives-utility.workspace = true +pallet-collator-selection.workspace = true +parachain-info.workspace = true + +[features] +default = [ + "std", +] +std = [ + "codec/std", + "scale-info/std", + "pallet-did-lookup/std", + "pallet-dip-receiver/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-system-rpc-runtime-api/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "polkadot-parachain/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "pallet-xcm/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "pallet-collator-selection/std", + "parachain-info/std" +] + +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-dip-receiver/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", +] diff --git a/dip-template/runtimes/dip-receiver/build.rs b/dip-template/runtimes/dip-receiver/build.rs new file mode 100644 index 0000000000..a394382833 --- /dev/null +++ b/dip-template/runtimes/dip-receiver/build.rs @@ -0,0 +1,27 @@ +// 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_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs new file mode 100644 index 0000000000..b36d425abd --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/dip.rs @@ -0,0 +1,36 @@ +// 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 pallet_dip_receiver::traits::SuccessfulProofVerifier; + +use crate::{DidIdentifier, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; + +impl pallet_dip_receiver::Config for Runtime { + type Identifier = DidIdentifier; + // TODO: Change with right one + type ProofDigest = [u8; 32]; + // TODO: Change with right one + type ProofLeafKey = [u8; 4]; + // TODO: Change with right one + type ProofLeafValue = [u8; 4]; + // TODO: Change with right one + type ProofVerifier = SuccessfulProofVerifier; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; +} diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs new file mode 100644 index 0000000000..6dd9a52193 --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -0,0 +1,501 @@ +// 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 + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +use cumulus_pallet_parachain_system::{ + register_validate_block, ParachainSetCode, RelayChainStateProof, RelayNumberStrictlyIncreases, +}; +use cumulus_primitives_core::CollationInfo; +use cumulus_primitives_timestamp::InherentDataProvider; +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8, Everything}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + IdentityFee, Weight, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + ChainContext, EnsureRoot, +}; +use pallet_balances::AccountData; +use pallet_collator_selection::IdentityCollator; +use pallet_dip_receiver::{DipOrigin, EnsureDipOrigin}; +use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; +use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::SlotDuration; +use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; +use sp_inherents::{CheckInherentsResult, InherentData}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::{prelude::*, time::Duration}; +use sp_version::RuntimeVersion; + +mod dip; +mod xcm_config; +pub use crate::{dip::*, xcm_config::*}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type Address = MultiAddress; +pub type Balance = u128; +pub type Block = generic::Block; +pub type BlockNumber = u32; +pub type DidIdentifier = AccountId; +pub type Hash = sp_core::H256; +pub type Header = generic::Header; +pub type Index = u32; +pub type Signature = MultiSignature; + +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type Executive = frame_executive::Executive, Runtime, AllPalletsWithSystem>; +pub type NodeBlock = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; + +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = UNIT / 1_000; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = NodeBlock, + UncheckedExtrinsic = UncheckedExtrinsic, + { + // System + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + + // Money + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Collators + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + DmpQueue: cumulus_pallet_dmp_queue = 31, + PolkadotXcm: pallet_xcm = 32, + CumulusXcm: cumulus_pallet_xcm = 33, + + // DID lookup + DidLookup: pallet_did_lookup = 40, + + // DIP + DipReceiver: pallet_dip_receiver = 50, + } +); + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("dip-receiver-runtime-template"), + impl_name: create_runtime_str!("dip-receiver-runtime-template"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents(block: &Block, relay_state_proof: &RelayChainStateProof) -> CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + InherentDataProvider::from_relay_chain_slot_and_duration(relay_chain_slot, Duration::from_secs(6)) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} + +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +pub const SS58_PREFIX: u16 = 101; + +impl frame_system::Config for Runtime { + type AccountData = AccountData; + type AccountId = AccountId; + type BaseCallFilter = Everything; + type BlockHashCount = ConstU32<256>; + type BlockLength = RuntimeBlockLength; + type BlockNumber = BlockNumber; + type BlockWeights = RuntimeBlockWeights; + type DbWeight = RocksDbWeight; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Header = generic::Header; + type Index = Index; + type Lookup = AccountIdLookup; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = ParachainSetCode; + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16; + type SystemWeightInfo = (); + type Version = Version; +} + +parameter_types! { + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; + type DmpMessageHandler = DmpQueue; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = XcmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type ReservedXcmpWeight = ReservedXcmpWeight; + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainInfo; + type XcmpMessageHandler = XcmpQueue; +} + +impl pallet_timestamp::Config for Runtime { + type MinimumPeriod = ConstU64<{ MILLISECS_PER_BLOCK / 2 }>; + type Moment = u64; + type OnTimestampSet = Aura; + type WeightInfo = (); +} + +impl parachain_info::Config for Runtime {} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ConstU128; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = CurrencyAdapter; + type FeeMultiplierUpdate = (); + type LengthToFee = IdentityFee; + type OperationalFeeMultiplier = ConstU8<1>; + type RuntimeEvent = RuntimeEvent; + type WeightToFee = IdentityFee; +} + +impl pallet_authorship::Config for Runtime { + type EventHandler = (CollatorSelection,); + type FindAuthor = FindAccountFromAuthorIndex; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); +} + +impl pallet_collator_selection::Config for Runtime { + type Currency = Balances; + type PotId = PotId; + type KickThreshold = ConstU32<{ 6 * HOURS }>; + type MaxCandidates = ConstU32<1_000>; + type MaxInvulnerables = ConstU32<100>; + type MinCandidates = ConstU32<5>; + type RuntimeEvent = RuntimeEvent; + type UpdateOrigin = EnsureRoot; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +impl pallet_session::Config for Runtime { + type Keys = SessionKeys; + type NextSessionRotation = PeriodicSessions, ConstU32<0>>; + type RuntimeEvent = RuntimeEvent; + type SessionHandler = ::KeyTypeIdProviders; + type SessionManager = CollatorSelection; + type ShouldEndSession = PeriodicSessions, ConstU32<0>>; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +parameter_types! { + pub const LinkDeposit: Balance = UNIT; +} + +impl pallet_did_lookup::Config for Runtime { + type Currency = Balances; + type Deposit = ConstU128; + type DidIdentifier = DidIdentifier; + type EnsureOrigin = EnsureDipOrigin; + type OriginSuccess = DipOrigin; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> SlotDuration { + SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } +} diff --git a/dip-template/runtimes/dip-receiver/src/xcm_config.rs b/dip-template/runtimes/dip-receiver/src/xcm_config.rs new file mode 100644 index 0000000000..cc0ee09fcc --- /dev/null +++ b/dip-template/runtimes/dip-receiver/src/xcm_config.rs @@ -0,0 +1,149 @@ +// 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_utility::ParentAsUmp; +use frame_support::{ + parameter_types, + traits::{ConstU32, Contains, Everything, Nothing}, + weights::{IdentityFee, Weight}, +}; +use frame_system::EnsureRoot; +use pallet_xcm::TestWeightInfo; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedToAccountId32, UsingComponents, +}; +use xcm_executor::XcmExecutor; + +use crate::{ + AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, XcmpQueue, +}; + +parameter_types! { + pub HereLocation: MultiLocation = MultiLocation::here(); + pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +pub type Barrier = AllowTopLevelPaidExecutionFrom; +pub type AssetTransactorLocationConverter = SiblingParachainConvertsVia; +pub type LocalAssetTransactor = + CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; +pub type XcmRouter = (ParentAsUmp, XcmpQueue); + +pub struct DipTransactSafeCalls; + +impl Contains for DipTransactSafeCalls { + fn contains(t: &RuntimeCall) -> bool { + matches!( + t, + RuntimeCall::DipReceiver(pallet_dip_receiver::Call::process_identity_action { .. }) + ) + } +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = (); + type AssetTransactor = LocalAssetTransactor; + type AssetTrap = (); + type Barrier = Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = ConstU32<64>; + type MessageExporter = (); + type OriginConverter = SiblingParachainAsNative; + type PalletInstancesInfo = AllPalletsWithSystem; + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = DipTransactSafeCalls; + type SubscriptionService = (); + type UniversalAliases = Nothing; + type UniversalLocation = UniversalLocation; + type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; + type Weigher = FixedWeightBounds>; + type XcmSender = XcmRouter; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type ChannelInfo = ParachainSystem; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = (); + type ExecuteOverweightOrigin = EnsureRoot; + type PriceForSiblingDelivery = (); + type RuntimeEvent = RuntimeEvent; + type VersionWrapper = (); + type WeightInfo = (); + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type ExecuteOverweightOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +const MAX_INSTRUCTIONS: u32 = 100; + +parameter_types! { + pub RelayNetwork: Option = None; +} +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = (); + type TrustedLockers = (); + type UniversalLocation = UniversalLocation; + type Weigher = FixedWeightBounds>; + type WeightInfo = TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Nothing; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml new file mode 100644 index 0000000000..307bb37ff3 --- /dev/null +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -0,0 +1,123 @@ +[package] +authors.workspace = true +description = "Parachain runtime template for the KILT Decentralized Identity Provider (DIP) sender." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "dip-sender-runtime-template" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[build-dependencies] +substrate-wasm-builder.workspace = true + +[dependencies] +codec = {package = "parity-scale-codec", workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# DIP +did.workspace = true +dip-support.workspace = true +pallet-dip-sender.workspace = true + +# Substrate +frame-executive.workspace = true +frame-support.workspace = true +frame-system.workspace = true +frame-system-rpc-runtime-api.workspace = true +pallet-aura.workspace = true +pallet-authorship.workspace = true +pallet-balances.workspace = true +pallet-session.workspace = true +pallet-sudo.workspace = true +pallet-timestamp.workspace = true +pallet-transaction-payment.workspace = true +pallet-transaction-payment-rpc-runtime-api.workspace = true +sp-api.workspace = true +sp-block-builder.workspace = true +sp-consensus-aura.workspace = true +sp-core.workspace = true +sp-inherents.workspace = true +sp-offchain.workspace = true +sp-runtime.workspace = true +sp-session.workspace = true +sp-std.workspace = true +sp-transaction-pool.workspace = true +sp-version.workspace = true + +# Polkadot +pallet-xcm.workspace = true +xcm.workspace = true +xcm-builder.workspace = true +xcm-executor.workspace = true + +# Cumulus +cumulus-pallet-aura-ext.workspace = true +cumulus-pallet-dmp-queue.workspace = true +cumulus-pallet-parachain-system.workspace = true +cumulus-pallet-xcm.workspace = true +cumulus-pallet-xcmp-queue.workspace = true +cumulus-primitives-core.workspace = true +cumulus-primitives-timestamp.workspace = true +cumulus-primitives-utility.workspace = true +pallet-collator-selection.workspace = true +parachain-info.workspace = true + +[features] +default = [ + "std", +] +std = [ + "codec/std", + "scale-info/std", + "did/std", + "dip-support/std", + "pallet-dip-sender/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-system-rpc-runtime-api/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "pallet-xcm/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "pallet-collator-selection/std", + "parachain-info/std" +] +runtime-benchmarks = [ + "did/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-dip-sender/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks" +] diff --git a/dip-template/runtimes/dip-sender/build.rs b/dip-template/runtimes/dip-sender/build.rs new file mode 100644 index 0000000000..a394382833 --- /dev/null +++ b/dip-template/runtimes/dip-sender/build.rs @@ -0,0 +1,27 @@ +// 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_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/dip-template/runtimes/dip-sender/src/dip.rs b/dip-template/runtimes/dip-sender/src/dip.rs new file mode 100644 index 0000000000..2d43c62db4 --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/dip.rs @@ -0,0 +1,70 @@ +// 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 codec::{Decode, Encode}; +use dip_support::VersionedIdentityProofAction; +use pallet_dip_sender::traits::{ + DefaultIdentityProofGenerator, DefaultIdentityProvider, TxBuilder, XcmRouterDispatcher, +}; +use xcm::{latest::MultiLocation, DoubleEncoded}; + +use crate::{DidIdentifier, Runtime, RuntimeEvent, XcmRouter}; + +#[derive(Encode, Decode)] +enum ReceiverParachainCalls { + #[codec(index = 50)] + DipReceiver(ReceiverParachainDipReceiverCalls), +} + +#[derive(Encode, Decode)] +enum ReceiverParachainDipReceiverCalls { + #[codec(index = 0)] + ProcessIdentityAction(VersionedIdentityProofAction), +} + +pub struct ReceiverParachainTxBuilder; +impl TxBuilder for ReceiverParachainTxBuilder { + type Error = (); + + fn build( + _dest: MultiLocation, + action: VersionedIdentityProofAction, + ) -> Result, Self::Error> { + let double_encoded: DoubleEncoded<()> = + ReceiverParachainCalls::DipReceiver(ReceiverParachainDipReceiverCalls::ProcessIdentityAction(action)) + .encode() + .into(); + Ok(double_encoded) + } +} + +impl pallet_dip_sender::Config for Runtime { + type Identifier = DidIdentifier; + // TODO: Change with right one + type Identity = u32; + // TODO: Change with right one + type IdentityProofDispatcher = XcmRouterDispatcher; + // TODO: Change with right one + type IdentityProofGenerator = DefaultIdentityProofGenerator; + // TODO: Change with right one + type IdentityProvider = DefaultIdentityProvider; + // TODO: Change with right one + type ProofOutput = [u8; 32]; + type RuntimeEvent = RuntimeEvent; + type TxBuilder = ReceiverParachainTxBuilder; +} diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs new file mode 100644 index 0000000000..d81684c1e7 --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -0,0 +1,528 @@ +// 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 + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +use cumulus_pallet_parachain_system::{ + register_validate_block, ParachainSetCode, RelayChainStateProof, RelayNumberStrictlyIncreases, +}; +use cumulus_primitives_core::CollationInfo; +use cumulus_primitives_timestamp::InherentDataProvider; +use did::{DidRawOrigin, EnsureDidOrigin}; +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8, Everything}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + IdentityFee, Weight, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + ChainContext, EnsureRoot, +}; +use pallet_balances::AccountData; +use pallet_collator_selection::IdentityCollator; +use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; +use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::SlotDuration; +use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; +use sp_inherents::{CheckInherentsResult, InherentData}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::{prelude::*, time::Duration}; +use sp_version::RuntimeVersion; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; + +mod dip; +mod xcm_config; +pub use crate::{dip::*, xcm_config::*}; + +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type Address = MultiAddress; +pub type Balance = u128; +pub type Block = generic::Block; +pub type BlockNumber = u32; +pub type DidIdentifier = AccountId; +pub type Hash = sp_core::H256; +pub type Header = generic::Header; +pub type Index = u32; +pub type Signature = MultiSignature; + +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type Executive = frame_executive::Executive, Runtime, AllPalletsWithSystem>; +pub type NodeBlock = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; + +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = UNIT / 1_000; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = NodeBlock, + UncheckedExtrinsic = UncheckedExtrinsic, + { + // System + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + Timestamp: pallet_timestamp = 2, + ParachainInfo: parachain_info = 3, + Sudo: pallet_sudo = 4, + + // Money + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Collators + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + DmpQueue: cumulus_pallet_dmp_queue = 31, + PolkadotXcm: pallet_xcm = 32, + CumulusXcm: cumulus_pallet_xcm = 33, + + // DID + Did: did = 40, + + // DIP + DipSender: pallet_dip_sender = 50, + } +); + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("dip-sender-runtime-template"), + impl_name: create_runtime_str!("dip-sender-runtime-template"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { + runtime_version: VERSION, + can_author_with: Default::default(), + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents(block: &Block, relay_state_proof: &RelayChainStateProof) -> CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + InherentDataProvider::from_relay_chain_slot_and_duration(relay_chain_slot, Duration::from_secs(6)) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} + +pub const SS58_PREFIX: u16 = 100; +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +impl frame_system::Config for Runtime { + type AccountData = AccountData; + type AccountId = AccountId; + type BaseCallFilter = Everything; + type BlockHashCount = ConstU32<256>; + type BlockLength = RuntimeBlockLength; + type BlockNumber = BlockNumber; + type BlockWeights = RuntimeBlockWeights; + type DbWeight = RocksDbWeight; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Header = generic::Header; + type Index = Index; + type Lookup = AccountIdLookup; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = ParachainSetCode; + type PalletInfo = PalletInfo; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SS58Prefix = ConstU16; + type SystemWeightInfo = (); + type Version = Version; +} + +parameter_types! { + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; + type DmpMessageHandler = DmpQueue; + type OnSystemEvent = (); + type OutboundXcmpMessageSource = XcmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type ReservedXcmpWeight = ReservedXcmpWeight; + type RuntimeEvent = RuntimeEvent; + type SelfParaId = ParachainInfo; + type XcmpMessageHandler = XcmpQueue; +} + +impl pallet_timestamp::Config for Runtime { + type MinimumPeriod = ConstU64<{ MILLISECS_PER_BLOCK / 2 }>; + type Moment = u64; + type OnTimestampSet = Aura; + type WeightInfo = (); +} + +impl parachain_info::Config for Runtime {} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ConstU128; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = CurrencyAdapter; + type FeeMultiplierUpdate = (); + type LengthToFee = IdentityFee; + type OperationalFeeMultiplier = ConstU8<1>; + type RuntimeEvent = RuntimeEvent; + type WeightToFee = IdentityFee; +} + +impl pallet_authorship::Config for Runtime { + type EventHandler = (CollatorSelection,); + type FindAuthor = FindAccountFromAuthorIndex; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); +} + +impl pallet_collator_selection::Config for Runtime { + type Currency = Balances; + type PotId = PotId; + type KickThreshold = ConstU32<{ 6 * HOURS }>; + type MaxCandidates = ConstU32<1_000>; + type MaxInvulnerables = ConstU32<100>; + type MinCandidates = ConstU32<5>; + type RuntimeEvent = RuntimeEvent; + type UpdateOrigin = EnsureRoot; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +impl pallet_session::Config for Runtime { + type Keys = SessionKeys; + type NextSessionRotation = PeriodicSessions, ConstU32<0>>; + type RuntimeEvent = RuntimeEvent; + type SessionHandler = ::KeyTypeIdProviders; + type SessionManager = CollatorSelection; + type ShouldEndSession = PeriodicSessions, ConstU32<0>>; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityCollator; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { + fn derive_verification_key_relationship(&self) -> did::DeriveDidCallKeyRelationshipResult { + Ok(did::DidVerificationKeyRelationship::Authentication) + } + + #[cfg(feature = "runtime-benchmarks")] + fn get_call_for_did_call_benchmark() -> Self { + RuntimeCall::System(frame_system::Call::remark { + remark: b"test-remark".to_vec(), + }) + } +} + +parameter_types! { + #[derive(Debug, Clone, Eq, PartialEq)] + pub const MaxTotalKeyAgreementKeys: u32 = 1; +} + +impl did::Config for Runtime { + type Currency = Balances; + type Deposit = ConstU128; + type DidIdentifier = DidIdentifier; + type EnsureOrigin = EnsureDidOrigin; + type Fee = ConstU128; + type FeeCollector = (); + type MaxBlocksTxValidity = ConstU32; + type MaxNewKeyAgreementKeys = ConstU32<1>; + type MaxNumberOfServicesPerDid = ConstU32<1>; + type MaxNumberOfTypesPerService = ConstU32<1>; + type MaxNumberOfUrlsPerService = ConstU32<1>; + type MaxPublicKeysPerDid = ConstU32<4>; + type MaxServiceIdLength = ConstU32<100>; + type MaxServiceTypeLength = ConstU32<100>; + type MaxServiceUrlLength = ConstU32<100>; + type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; + type OriginSuccess = DidRawOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type WeightInfo = (); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> SlotDuration { + SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } +} diff --git a/dip-template/runtimes/dip-sender/src/xcm_config.rs b/dip-template/runtimes/dip-sender/src/xcm_config.rs new file mode 100644 index 0000000000..d73c126189 --- /dev/null +++ b/dip-template/runtimes/dip-sender/src/xcm_config.rs @@ -0,0 +1,130 @@ +// 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_utility::ParentAsUmp; +use frame_support::{ + parameter_types, + traits::{ConstU32, Nothing}, + weights::{IdentityFee, Weight}, +}; +use frame_system::EnsureRoot; +use pallet_xcm::TestWeightInfo; +use xcm::latest::prelude::*; +use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; +use xcm_executor::XcmExecutor; + +use crate::{ + AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, XcmpQueue, +}; + +parameter_types! { + pub HereLocation: MultiLocation = Junctions::Here.into(); + pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +pub type XcmRouter = (ParentAsUmp, XcmpQueue); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = (); + type AssetTransactor = (); + type AssetTrap = (); + type Barrier = (); + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = ConstU32<64>; + type MessageExporter = (); + type OriginConverter = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Nothing; + type SubscriptionService = (); + type UniversalAliases = Nothing; + type UniversalLocation = UniversalLocation; + type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; + type Weigher = FixedWeightBounds>; + type XcmSender = XcmRouter; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type ChannelInfo = ParachainSystem; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = (); + type ExecuteOverweightOrigin = EnsureRoot; + type PriceForSiblingDelivery = (); + type RuntimeEvent = RuntimeEvent; + type VersionWrapper = (); + type WeightInfo = (); + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type ExecuteOverweightOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +const MAX_INSTRUCTIONS: u32 = 100; + +parameter_types! { + pub RelayNetwork: Option = None; +} +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = (); + type TrustedLockers = (); + type UniversalLocation = UniversalLocation; + type Weigher = FixedWeightBounds>; + type WeightInfo = TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Nothing; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml new file mode 100644 index 0000000000..37619c2724 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -0,0 +1,38 @@ +[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-templates-xmc-tests" +description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) sender and receiver templates." + +[dev-dependencies] +cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } + +[dependencies] +codec = {package = "parity-scale-codec", workspace = true, features = ["std", "derive"]} +dip-receiver-runtime-template = { workspace = true, features = ["std"] } +dip-sender-runtime-template = { workspace = true, features = ["std"] } +frame-support = { workspace = true, features = ["std"] } +frame-system = { workspace = true, features = ["std"] } +pallet-balances = { workspace = true, features = ["std"] } +parachain-info = { workspace = true, features = ["std"] } +polkadot-parachain = { workspace = true, features = ["std"] } +polkadot-primitives = { workspace = true, features = ["std"] } +polkadot-runtime-parachains = { workspace = true, features = ["std"] } +rococo-runtime = { workspace = true, features = ["std"] } +scale-info = { workspace = true, features = ["std"] } +sp-io = { workspace = true, features = ["std"] } +sp-runtime = { workspace = true, features = ["std"] } +xcm = { workspace = true, features = ["std"] } +xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } +xcm-executor = { workspace = true, features = ["std"] } + +[features] +runtime-benchmarks = [ + "rococo-runtime/runtime-benchmarks" +] diff --git a/dip-template/runtimes/xcm-tests/src/lib.rs b/dip-template/runtimes/xcm-tests/src/lib.rs new file mode 100644 index 0000000000..9eb8ed5de7 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/lib.rs @@ -0,0 +1,59 @@ +// 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 xcm_emulator::{decl_test_network, decl_test_parachain}; + +use crate::relay::RococoChain; + +mod para; +mod relay; + +#[cfg(test)] +mod tests; + +decl_test_parachain! { + pub struct SenderParachain { + Runtime = para::sender::Runtime, + RuntimeOrigin = para::sender::RuntimeOrigin, + XcmpMessageHandler = para::sender::XcmpQueue, + DmpMessageHandler = para::sender::DmpQueue, + new_ext = para::sender::para_ext(), + } +} + +decl_test_parachain! { + pub struct ReceiverParachain { + Runtime = para::receiver::Runtime, + RuntimeOrigin = para::receiver::RuntimeOrigin, + XcmpMessageHandler = para::receiver::XcmpQueue, + DmpMessageHandler = para::receiver::DmpQueue, + new_ext = para::receiver::para_ext(), + } +} + +decl_test_network! { + pub struct Network { + relay_chain = RococoChain, + parachains = vec![ + // TODO: Change when and if the macro will allow arbitrary expressions. + // Until then, these have to match the PARA_ID consts in the para submodules. + (2_000, SenderParachain), + (2_001, ReceiverParachain), + ], + } +} diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs new file mode 100644 index 0000000000..ca30e62ba9 --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -0,0 +1,107 @@ +// 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 crate::{_Messenger, _hrmp_channel_parachain_inherent_data, _process_messages}; +use frame_support::traits::GenesisBuild; +use sp_io::TestExternalities; +use xcm_emulator::decl_test_parachain; + +pub(super) mod sender { + pub(crate) use dip_sender_runtime_template::{DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + + use super::*; + + pub const PARA_ID: u32 = 2_000; + + pub(crate) fn para_ext() -> TestExternalities { + use dip_sender_runtime_template::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_info_config = parachain_info::GenesisConfig { + parachain_id: PARA_ID.into(), + }; + + >::assimilate_storage(¶chain_info_config, &mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } + + decl_test_parachain! { + pub struct SenderParachain { + Runtime = Runtime, + RuntimeOrigin = RuntimeOrigin, + XcmpMessageHandler = XcmpQueue, + DmpMessageHandler = DmpQueue, + new_ext = para_ext(), + } + } +} + +pub(super) mod receiver { + pub(crate) use dip_receiver_runtime_template::{ + AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, + }; + + use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; + use xcm_executor::traits::Convert; + + use super::*; + + pub const PARA_ID: u32 = 2_001; + const INITIAL_BALANCE: Balance = 1_000_000_000; + + pub(crate) fn sender_parachain_account() -> AccountId { + AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(sender::PARA_ID))).into()) + .expect("Conversion of account from sender parachain to receiver parachain should not fail.") + } + + pub(crate) fn para_ext() -> TestExternalities { + use dip_receiver_runtime_template::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_info_config = parachain_info::GenesisConfig { + parachain_id: PARA_ID.into(), + }; + + >::assimilate_storage(¶chain_info_config, &mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(sender_parachain_account(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } +} diff --git a/dip-template/runtimes/xcm-tests/src/relay.rs b/dip-template/runtimes/xcm-tests/src/relay.rs new file mode 100644 index 0000000000..7eb45f064f --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/relay.rs @@ -0,0 +1,91 @@ +// 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 crate::{_Messenger, _para_ids, _process_messages}; +use frame_support::{traits::GenesisBuild, weights::Weight}; +use polkadot_primitives::{runtime_api::runtime_decl_for_ParachainHost::ParachainHostV3, BlockNumber}; +use polkadot_runtime_parachains::configuration::HostConfiguration; +use rococo_runtime::{xcm_config::XcmConfig, Runtime}; +use sp_io::TestExternalities; +use xcm_emulator::decl_test_relay_chain; + +fn default_parachains_host_configuration() -> HostConfiguration { + use polkadot_primitives::v2::{MAX_CODE_SIZE, MAX_POV_SIZE}; + + HostConfiguration { + minimum_validation_upgrade_delay: 5, + validation_upgrade_cooldown: 10u32, + validation_upgrade_delay: 10, + code_retention_period: 1200, + max_code_size: MAX_CODE_SIZE, + max_pov_size: MAX_POV_SIZE, + max_head_data_size: 32 * 1024, + group_rotation_frequency: 20, + chain_availability_period: 4, + thread_availability_period: 4, + max_upward_queue_count: 8, + max_upward_queue_size: 1024 * 1024, + max_downward_message_size: 1024, + ump_service_total_weight: Weight::from_ref_time(4 * 1_000_000_000), + max_upward_message_size: 50 * 1024, + max_upward_message_num_per_candidate: 5, + hrmp_sender_deposit: 0, + hrmp_recipient_deposit: 0, + hrmp_channel_max_capacity: 8, + hrmp_channel_max_total_size: 8 * 1024, + hrmp_max_parachain_inbound_channels: 4, + hrmp_max_parathread_inbound_channels: 4, + hrmp_channel_max_message_size: 1024 * 1024, + hrmp_max_parachain_outbound_channels: 4, + hrmp_max_parathread_outbound_channels: 4, + hrmp_max_message_num_per_candidate: 5, + dispute_period: 6, + no_show_slots: 2, + n_delay_tranches: 25, + needed_approvals: 2, + relay_vrf_modulo_samples: 2, + zeroth_delay_tranche_width: 0, + ..Default::default() + } +} + +fn relay_ext() -> TestExternalities { + use rococo_runtime::System; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + polkadot_runtime_parachains::configuration::GenesisConfig:: { + config: default_parachains_host_configuration(), + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +decl_test_relay_chain! { + pub struct RococoChain { + Runtime = Runtime, + XcmConfig = XcmConfig, + new_ext = relay_ext(), + } +} diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs new file mode 100644 index 0000000000..037561708c --- /dev/null +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -0,0 +1,75 @@ +// 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 super::*; + +use dip_receiver_runtime_template::{ + AccountId as ReceiverAccountId, DidIdentifier as ReceiverDidIdentifier, DipReceiver, +}; +use dip_sender_runtime_template::DipSender; + +use frame_support::{assert_ok, weights::Weight}; +use frame_system::RawOrigin; +use xcm::latest::{ + Junction::Parachain, + Junctions::{Here, X1}, + ParentThen, +}; +use xcm_emulator::TestExt; + +#[test] +fn commit_identity() { + Network::reset(); + + ReceiverParachain::execute_with(|| { + use dip_receiver_runtime_template::Balances; + use para::receiver::sender_parachain_account; + + let sender_balance = Balances::free_balance(sender_parachain_account()); + println!("Sender balance: {:?}", sender_balance); + }); + + // 1. Send identity proof from DIP sender to DIP receiver. + SenderParachain::execute_with(|| { + assert_ok!(DipSender::commit_identity( + RawOrigin::Signed(ReceiverAccountId::from([0u8; 32])).into(), + ReceiverDidIdentifier::from([0u8; 32]), + Box::new(ParentThen(X1(Parachain(para::receiver::PARA_ID))).into()), + Box::new((Here, 1_000_000_000).into()), + Weight::from_ref_time(4_000), + )); + }); + // 2. Verify that the proof has made it to the DIP receiver. + ReceiverParachain::execute_with(|| { + use cumulus_pallet_xcmp_queue::Event as XcmpEvent; + use dip_receiver_runtime_template::{RuntimeEvent, System}; + + // 2.1 Verify that there was no XCM error. + assert!(!System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XcmpQueue(XcmpEvent::Fail { + error: _, + message_hash: _, + weight: _ + }) + ))); + // 2.2 Verify the proof digest is the same that was sent. + let details = DipReceiver::identity_proofs(dip_sender_runtime_template::AccountId::from([0u8; 32])); + assert_eq!(details, Some([0u8; 32])); + }); +} diff --git a/pallets/pallet-dip-receiver/Cargo.toml b/pallets/pallet-dip-receiver/Cargo.toml new file mode 100644 index 0000000000..a6357271b0 --- /dev/null +++ b/pallets/pallet-dip-receiver/Cargo.toml @@ -0,0 +1,41 @@ +[package] +authors.workspace = true +description = "Pallet enabling receiving identity information from providers via the pallet-dip-provider pallet." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-dip-receiver" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = {package = "parity-scale-codec", workspace = true, features = ["derive"]} +cumulus-pallet-xcm.workspace = true +dip-support.workspace = true +frame-support.workspace = true +frame-system.workspace = true +kilt-support.workspace = true +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "cumulus-pallet-xcm/std", + "dip-support/std", + "frame-support/std", + "frame-system/std", + "kilt-support/std", + "scale-info/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs new file mode 100644 index 0000000000..551b6bc80a --- /dev/null +++ b/pallets/pallet-dip-receiver/src/lib.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 + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +mod origin; +pub mod traits; + +pub use crate::{origin::*, pallet::*}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use cumulus_pallet_xcm::ensure_sibling_para; + use frame_support::{dispatch::Dispatchable, pallet_prelude::*, Twox64Concat}; + use frame_system::pallet_prelude::*; + use sp_std::boxed::Box; + + use dip_support::{latest::IdentityProofAction, VersionedIdentityProof, VersionedIdentityProofAction}; + + use crate::traits::IdentityProofVerifier; + + pub type VersionedIdentityProofOf = + VersionedIdentityProof<::ProofLeafKey, ::ProofLeafValue>; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + // TODO: Store also additional details received by the provider. + #[pallet::storage] + #[pallet::getter(fn identity_proofs)] + pub(crate) type IdentityProofs = + StorageMap<_, Twox64Concat, ::Identifier, ::ProofDigest>; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Identifier: Parameter + MaxEncodedLen; + type ProofLeafKey: Parameter; + type ProofLeafValue: Parameter; + type ProofDigest: Parameter + MaxEncodedLen; + type ProofVerifier: IdentityProofVerifier< + ProofDigest = Self::ProofDigest, + LeafKey = Self::ProofLeafKey, + LeafValue = Self::ProofLeafValue, + >; + type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeOrigin: From> + + From<::RuntimeOrigin> + + Into::RuntimeOrigin>>; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + IdentityInfoDeleted(T::Identifier), + IdentityInfoUpdated(T::Identifier, T::ProofDigest), + } + + #[pallet::error] + pub enum Error { + Dispatch, + IdentityNotFound, + InvalidProof, + } + + // The new origin other pallets can use. + #[pallet::origin] + pub type Origin = DipOrigin<::Identifier, ::AccountId>; + + // TODO: Benchmarking + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn process_identity_action( + origin: OriginFor, + action: VersionedIdentityProofAction, + ) -> DispatchResult { + ensure_sibling_para(::RuntimeOrigin::from(origin))?; + + let event = match action { + VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { + IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); + Event::::IdentityInfoUpdated(identifier, proof) + } + VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { + IdentityProofs::::remove(&identifier); + Event::::IdentityInfoDeleted(identifier) + } + }; + + Self::deposit_event(event); + + Ok(()) + } + + // TODO: Replace with a SignedExtra. + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn dispatch_as( + origin: OriginFor, + identifier: T::Identifier, + proof: VersionedIdentityProofOf, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let submitter = ensure_signed(origin)?; + let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let _ = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) + .map_err(|_| Error::::InvalidProof)?; + // TODO: Proper DID signature verification (and cross-chain replay protection) + let did_origin = DipOrigin { + identifier, + account_address: submitter, + }; + // TODO: Use dispatch info for weight calculation + let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; + Ok(()) + } + } +} diff --git a/pallets/pallet-dip-receiver/src/origin.rs b/pallets/pallet-dip-receiver/src/origin.rs new file mode 100644 index 0000000000..84b73cceae --- /dev/null +++ b/pallets/pallet-dip-receiver/src/origin.rs @@ -0,0 +1,79 @@ +// 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 codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::EnsureOrigin, RuntimeDebug}; +use scale_info::TypeInfo; +use sp_std::marker::PhantomData; + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct DipOrigin { + pub identifier: Identifier, + pub account_address: AccountId, +} + +pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId)>); + +#[cfg(not(feature = "runtime-benchmarks"))] +impl EnsureOrigin for EnsureDipOrigin +where + OuterOrigin: From> + Into, OuterOrigin>>, +{ + type Success = DipOrigin; + + fn try_origin(o: OuterOrigin) -> Result { + o.into() + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl EnsureOrigin for EnsureDipOrigin +where + OuterOrigin: From> + Into, OuterOrigin>>, + // Additional trait bounds only valid when benchmarking + Identifier: From<[u8; 32]>, + AccountId: From<[u8; 32]>, +{ + type Success = DipOrigin; + + fn try_origin(o: OuterOrigin) -> Result { + o.into() + } + + fn try_successful_origin() -> Result { + Ok(OuterOrigin::from(DipOrigin { + identifier: Identifier::from([0u8; 32]), + account_address: AccountId::from([0u8; 32]), + })) + } +} + +impl kilt_support::traits::CallSources + for DipOrigin +where + Identifier: Clone, + AccountId: Clone, +{ + fn sender(&self) -> AccountId { + self.account_address.clone() + } + + fn subject(&self) -> Identifier { + self.identifier.clone() + } +} diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs new file mode 100644 index 0000000000..460dc48b0b --- /dev/null +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -0,0 +1,52 @@ +// 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 dip_support::VersionedIdentityProof; +use sp_std::marker::PhantomData; + +pub trait IdentityProofVerifier { + type ProofDigest; + type LeafKey; + type LeafValue; + type VerificationResult; + type Error; + + fn verify_proof_against_digest( + proof: VersionedIdentityProof, + digest: Self::ProofDigest, + ) -> Result; +} + +// Always returns success. +pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, LeafKey, LeafValue)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier +{ + type ProofDigest = ProofDigest; + type Error = (); + type LeafKey = LeafKey; + type LeafValue = LeafValue; + type VerificationResult = (); + + fn verify_proof_against_digest( + _proof: VersionedIdentityProof, + _digest: Self::ProofDigest, + ) -> Result { + Ok(()) + } +} diff --git a/pallets/pallet-dip-sender/Cargo.toml b/pallets/pallet-dip-sender/Cargo.toml new file mode 100644 index 0000000000..c5c1b90149 --- /dev/null +++ b/pallets/pallet-dip-sender/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors.workspace = true +description = "Pallet enabling to send some form of identity information to a specified destination." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-dip-sender" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = {package = "parity-scale-codec", workspace = true, features = ["derive"]} +dip-support.workspace = true +frame-support.workspace = true +frame-system.workspace = true +scale-info = {workspace = true, features = ["derive"]} +sp-std.workspace = true +xcm.workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "dip-support/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] diff --git a/pallets/pallet-dip-sender/src/lib.rs b/pallets/pallet-dip-sender/src/lib.rs new file mode 100644 index 0000000000..57d3c38fa0 --- /dev/null +++ b/pallets/pallet-dip-sender/src/lib.rs @@ -0,0 +1,129 @@ +// 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 + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod traits; + +pub use crate::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use frame_support::{pallet_prelude::*, weights::Weight}; + use frame_system::pallet_prelude::*; + use sp_std::{boxed::Box, fmt::Debug}; + use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; + + use dip_support::{v1::IdentityProofAction, VersionedIdentityProofAction}; + + use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; + + pub type IdentityProofActionOf = IdentityProofAction<::Identifier, ::ProofOutput>; + pub type VersionedIdentityProofActionOf = + VersionedIdentityProofAction<::Identifier, ::ProofOutput>; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::config] + pub trait Config: frame_system::Config { + type Identifier: Parameter; + type Identity; + type ProofOutput: Clone + Eq + Debug; + type IdentityProofGenerator: IdentityProofGenerator; + type IdentityProofDispatcher: IdentityProofDispatcher; + type IdentityProvider: IdentityProvider; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type TxBuilder: TxBuilder; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + // #[pallet::storage] + // #[pallet::getter(fn destination_info)] + // pub type DestinationInfos = StorageMap<_, Blake2_128Concat, NetworkId, + // ()>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + IdentityInfoDispatched(VersionedIdentityProofActionOf, Box), + } + + #[pallet::error] + pub enum Error { + BadVersion, + Dispatch, + IdentityNotFound, + IdentityProofGeneration, + Predispatch, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + // TODO: Update weight + #[pallet::weight(0)] + pub fn commit_identity( + origin: OriginFor, + identifier: T::Identifier, + destination: Box, + asset: Box, + weight: Weight, + ) -> DispatchResult { + // TODO: Charge the dispatcher based on the destination weight configuration + ensure_signed(origin)?; + + let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; + let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { + Ok(Some((identity, _))) => { + let identity_proof = T::IdentityProofGenerator::generate_proof(&identifier, &identity) + .map_err(|_| Error::::IdentityProofGeneration)?; + Ok(IdentityProofAction::Updated(identifier, identity_proof, ())) + } + Ok(None) => Ok(IdentityProofAction::Deleted(identifier)), + Err(_) => Err(Error::::IdentityNotFound), + }?; + // TODO: Add correct version creation based on lookup (?) + let versioned_action = VersionedIdentityProofAction::V1(action); + + let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; + + let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( + versioned_action.clone(), + asset, + weight, + destination, + ) + .map_err(|_| Error::::Predispatch)?; + + // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the + // fee, in addition to the cost on the target chain. + T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; + + Self::deposit_event(Event::IdentityInfoDispatched(versioned_action, Box::new(destination))); + Ok(()) + } + } +} diff --git a/pallets/pallet-dip-sender/src/traits.rs b/pallets/pallet-dip-sender/src/traits.rs new file mode 100644 index 0000000000..63209fd8d9 --- /dev/null +++ b/pallets/pallet-dip-sender/src/traits.rs @@ -0,0 +1,192 @@ +// 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 dip_support::VersionedIdentityProofAction; +use xcm::{latest::prelude::*, DoubleEncoded}; + +pub use identity_generation::*; +pub mod identity_generation { + + pub trait IdentityProofGenerator { + type Error; + + fn generate_proof(identifier: &Identifier, identity: &Identity) -> Result; + } + + // Implement the `IdentityProofGenerator` by returning the `Default` value for + // the `Output` type. + pub struct DefaultIdentityProofGenerator; + + impl IdentityProofGenerator + for DefaultIdentityProofGenerator + where + Output: Default, + { + type Error = (); + + fn generate_proof(_identifier: &Identifier, _identity: &Identity) -> Result { + Ok(Output::default()) + } + } +} + +pub use identity_dispatch::*; +pub mod identity_dispatch { + use super::*; + + use codec::Encode; + use frame_support::weights::Weight; + use sp_std::{marker::PhantomData, vec}; + + pub trait IdentityProofDispatcher { + type PreDispatchOutput; + type Error; + + fn pre_dispatch>( + action: VersionedIdentityProofAction, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error>; + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error>; + } + + // Returns `Ok` without doing anything. + pub struct NullIdentityProofDispatcher; + + impl IdentityProofDispatcher + for NullIdentityProofDispatcher + { + type PreDispatchOutput = (); + type Error = (); + + fn pre_dispatch<_B>( + _action: VersionedIdentityProofAction, + _asset: MultiAsset, + _weight: Weight, + _destination: MultiLocation, + ) -> Result<((), MultiAssets), Self::Error> { + Ok(((), MultiAssets::default())) + } + + fn dispatch(_pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Ok(()) + } + } + + // Dispatcher using a type implementing the `SendXcm` trait. + // It properly encodes the `Transact` operation, then delegates everything else + // to the sender, similarly to what the XCM pallet's `send` extrinsic does. + pub struct XcmRouterDispatcher( + PhantomData<(Router, Identifier, ProofOutput, Details)>, + ); + + impl IdentityProofDispatcher + for XcmRouterDispatcher + where + Router: SendXcm, + Identifier: Encode, + ProofOutput: Encode, + { + type PreDispatchOutput = Router::Ticket; + type Error = SendError; + + fn pre_dispatch>( + action: VersionedIdentityProofAction, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { + // TODO: Replace with proper error handling + let dest_tx = Builder::build(destination, action) + .map_err(|_| ()) + .expect("Failed to build call"); + + // TODO: Set an error handler and an appendix to refund any leftover funds to + // the sender parachain sovereign account. + let operation = [vec![ + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + // TODO: Configurable weight limit? + weight_limit: Unlimited, + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + ]] + .concat(); + let op = Xcm(operation); + Router::validate(&mut Some(destination), &mut Some(op)) + } + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Router::deliver(pre_output).map(|_| ()) + } + } +} + +pub use identity_provision::*; +pub mod identity_provision { + + pub trait IdentityProvider { + type Error; + + fn retrieve(identifier: &Identifier) -> Result, Self::Error>; + } + + // Return the `Default` value if `Identity` adn `Details` both implement it. + pub struct DefaultIdentityProvider; + + impl IdentityProvider for DefaultIdentityProvider + where + Identity: Default, + Details: Default, + { + type Error = (); + + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + Ok(Some((Identity::default(), Details::default()))) + } + } + + // Always return `None`. Might be useful for tests. + pub struct NoneIdentityProvider; + + impl IdentityProvider for NoneIdentityProvider { + type Error = (); + + fn retrieve(_identifier: &Identifier) -> Result, Self::Error> { + Ok(None) + } + } +} + +// Given a destination and an identity action, creates and encodes the proper +// `Transact` call. +pub trait TxBuilder { + type Error; + + fn build( + dest: MultiLocation, + action: VersionedIdentityProofAction, + ) -> Result, Self::Error>; +}