feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM docker.io/paritytech/ci-unified:latest as builder
|
||||
|
||||
WORKDIR /polkadot
|
||||
COPY . /polkadot
|
||||
|
||||
RUN cargo fetch
|
||||
RUN cargo build --workspace --locked --release
|
||||
|
||||
FROM docker.io/parity/base-bin:latest
|
||||
|
||||
COPY --from=builder /polkadot/target/release/minimal-template-node /usr/local/bin
|
||||
|
||||
USER root
|
||||
RUN useradd -m -u 1001 -U -s /bin/sh -d /polkadot polkadot && \
|
||||
mkdir -p /data /polkadot/.local/share && \
|
||||
chown -R polkadot:polkadot /data && \
|
||||
ln -s /data /polkadot/.local/share/polkadot && \
|
||||
# unclutter and minimize the attack surface
|
||||
rm -rf /usr/bin /usr/sbin && \
|
||||
# check if executable works in this container
|
||||
/usr/local/bin/minimal-template-node --version
|
||||
|
||||
USER polkadot
|
||||
|
||||
EXPOSE 30333 9933 9944 9615
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/minimal-template-node"]
|
||||
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
@@ -0,0 +1,149 @@
|
||||
<div align="center">
|
||||
|
||||
# Pezkuwi SDK's Minimal Template
|
||||
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/PezkuwiChain_Logo_Horizontal_Pink_White.png#gh-dark-mode-only"/>
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/PezkuwiChain_Logo_Horizontal_Pink_Black.png#gh-light-mode-only"/>
|
||||
|
||||
> This is a minimal template for creating a blockchain based on Pezkuwi SDK.
|
||||
>
|
||||
> This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Intro](#intro)
|
||||
|
||||
- [Template Structure](#template-structure)
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
|
||||
- [Starting a Minimal Template Chain](#starting-a-minimal-template-chain)
|
||||
|
||||
- [Minimal Template Node](#minimal-template-node)
|
||||
- [Zombienet with Minimal Template Node](#zombienet-with-minimal-template-node)
|
||||
- [Connect with the PezkuwiChain-JS Apps Front-End](#connect-with-the-pezkuwi-js-apps-front-end)
|
||||
- [Takeaways](#takeaways)
|
||||
|
||||
- [Contributing](#contributing)
|
||||
|
||||
- [Getting Help](#getting-help)
|
||||
|
||||
## Intro
|
||||
|
||||
- 🤏 This template is a minimal (in terms of complexity and the number of components)
|
||||
template for building a blockchain node.
|
||||
|
||||
- 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets
|
||||
such as a [Balances pallet](https://docs.pezkuwichain.io/sdk/master/pallet_balances/index.html).
|
||||
|
||||
- 👤 The template has no consensus configured - it is best for experimenting with a single node network.
|
||||
|
||||
|
||||
## Template Structure
|
||||
|
||||
A Pezkuwi SDK based project such as this one consists of:
|
||||
|
||||
- 🧮 the [Runtime](./runtime/README.md) - the core logic of the blockchain.
|
||||
- 🎨 the [Pallets](./pallets/README.md) - from which the runtime is constructed.
|
||||
- 💿 a [Node](./node/README.md) - the binary application (which is not part of the cargo default-members list and is not
|
||||
compiled unless building the entire workspace).
|
||||
|
||||
## Getting Started
|
||||
|
||||
- 🦀 The template is using the Rust language.
|
||||
|
||||
- 👉 Check the
|
||||
[Rust installation instructions](https://www.rust-lang.org/tools/install) for your system.
|
||||
|
||||
- 🛠️ Depending on your operating system and Rust version, there might be additional
|
||||
packages required to compile this template - please take note of the Rust compiler output.
|
||||
|
||||
Fetch minimal template code.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/pezkuwichain/pezkuwi-sdk/issues/25.git minimal-template
|
||||
|
||||
cd minimal-template
|
||||
```
|
||||
|
||||
## Starting a Minimal Template Chain
|
||||
|
||||
### Minimal Template Node
|
||||
|
||||
#### Build both node & runtime
|
||||
|
||||
```sh
|
||||
cargo build --workspace --release
|
||||
```
|
||||
|
||||
🐳 Alternatively, build the docker image which builds all the workspace members,
|
||||
and has as entry point the node binary:
|
||||
|
||||
```sh
|
||||
docker build . -t pezkuwi-sdk-minimal-template
|
||||
```
|
||||
|
||||
#### Start the `minimal-template-node`
|
||||
|
||||
The `minimal-template-node` has dependency on the `minimal-template-runtime`. It will use
|
||||
the `minimal_template_runtime::WASM_BINARY` constant (which holds the WASM blob as a byte
|
||||
array) for chain spec building, while starting.
|
||||
|
||||
```sh
|
||||
<target/release/path/to/minimal-template-node> --tmp --consensus manual-seal-3000
|
||||
# or via docker
|
||||
docker run --rm pezkuwi-sdk-minimal-template
|
||||
```
|
||||
|
||||
#### Zombienet with `minimal-template-node`
|
||||
|
||||
For this one we just need to have `zombienet` installed and run:
|
||||
|
||||
```sh
|
||||
zombienet --provider native spawn zombienet-multi-node.toml
|
||||
```
|
||||
|
||||
### Connect with the PezkuwiChain-JS Apps Front-End
|
||||
|
||||
- 🌐 You can interact with your local node using the
|
||||
hosted version of the [PezkuwiChain/Substrate
|
||||
Portal](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9944).
|
||||
|
||||
- 🪐 A hosted version is also
|
||||
available on [IPFS](https://dotapps.io/).
|
||||
|
||||
- 🧑🔧 You can also find the source code and instructions for hosting your own instance in the
|
||||
[`pezkuwi-js/apps`](https://github.com/polkadot-js/apps) repository.
|
||||
|
||||
### Takeaways
|
||||
|
||||
Previously minimal template's development chains:
|
||||
|
||||
- ❌ Started in a multi-node setup will produce forks because minimal lacks consensus.
|
||||
- 🧹 Do not persist the state.
|
||||
- 💰 Are pre-configured with a genesis state that includes several pre-funded development accounts.
|
||||
- 🧑⚖️ One development account (`ALICE`) is used as `sudo` accounts.
|
||||
|
||||
## Contributing
|
||||
|
||||
- 🔄 This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
- ➡️ Any pull requests should be directed to this [source](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/minimal).
|
||||
|
||||
- 😇 Please refer to the monorepo's
|
||||
[contribution guidelines](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and
|
||||
[Code of Conduct](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Getting Help
|
||||
|
||||
- 🧑🏫 To learn about PezkuwiChain in general, [docs.PezkuwiChain.com](https://docs.pezkuwichain.app/) website is a good starting point.
|
||||
|
||||
- 🧑🔧 For technical introduction, [here](https://github.com/pezkuwichain/pezkuwi-sdk#-documentation) are
|
||||
the Pezkuwi SDK documentation resources.
|
||||
|
||||
- 👥 Additionally, there are [GitHub issues](https://github.com/pezkuwichain/pezkuwi-sdk/issues) and
|
||||
[Substrate StackExchange](https://pezkuwichain.app/community/).
|
||||
- 👥You can also reach out on the [Official PezkuwiChain discord server](https://polkadot-discord.w3f.tools/)
|
||||
- 🧑Reach out on [Telegram](https://t.me/substratedevs) for more questions and discussions
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,38 @@
|
||||
[package]
|
||||
name = "minimal-template-node"
|
||||
description = "A minimal Substrate-based Substrate node, ready for hacking."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
docify = { workspace = true }
|
||||
futures = { features = ["thread-pool"], workspace = true }
|
||||
futures-timer = { workspace = true }
|
||||
jsonrpsee = { features = ["server"], workspace = true }
|
||||
|
||||
minimal-template-runtime = { workspace = true }
|
||||
pezkuwi-sdk = { workspace = true, features = ["experimental", "node"] }
|
||||
|
||||
[build-dependencies]
|
||||
pezkuwi-sdk = { workspace = true, features = ["substrate-build-script-utils"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["minimal-template-runtime/std", "pezkuwi-sdk/std"]
|
||||
runtime-benchmarks = [
|
||||
"minimal-template-runtime/runtime-benchmarks",
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Node
|
||||
|
||||
ℹ️ A node - in PezkuwiChain - is a binary executable, whose primary purpose is to execute the [runtime](../runtime/README.md).
|
||||
|
||||
🔗 It communicates with other nodes in the network, and aims for
|
||||
[consensus](https://docs.pezkuwichain.app/polkadot-protocol/architecture/polkadot-chain/pos-consensus/) among them.
|
||||
|
||||
⚙️ It acts as a remote procedure call (RPC) server, allowing interaction with the blockchain.
|
||||
|
||||
👉 Learn more about the architecture, and a difference between a node and a runtime
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/wasm_meta_protocol/index.html).
|
||||
|
||||
👇 Here are the most important files in this node template:
|
||||
|
||||
- [`chain_spec.rs`](./src/chain_spec.rs): A chain specification is a source code file that defines the chain's
|
||||
initial (genesis) state.
|
||||
- [`service.rs`](./src/service.rs): This file defines the node implementation.
|
||||
It's a place to configure consensus-related topics. In favor of minimalism, this template has no consensus configured.
|
||||
@@ -0,0 +1,23 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezkuwi_sdk::substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
|
||||
|
||||
fn main() {
|
||||
generate_cargo_keys();
|
||||
rerun_if_git_head_changed();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use minimal_template_runtime::WASM_BINARY;
|
||||
use pezkuwi_sdk::{
|
||||
sc_service::{ChainType, Properties},
|
||||
*,
|
||||
};
|
||||
|
||||
/// This is a specialization of the general Substrate ChainSpec type.
|
||||
pub type ChainSpec = sc_service::GenericChainSpec;
|
||||
|
||||
fn props() -> Properties {
|
||||
let mut properties = Properties::new();
|
||||
properties.insert("tokenDecimals".to_string(), 0.into());
|
||||
properties.insert("tokenSymbol".to_string(), "MINI".into());
|
||||
properties
|
||||
}
|
||||
|
||||
pub fn development_chain_spec() -> Result<ChainSpec, String> {
|
||||
Ok(ChainSpec::builder(WASM_BINARY.expect("Development wasm not available"), Default::default())
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET)
|
||||
.with_properties(props())
|
||||
.build())
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Consensus {
|
||||
ManualSeal(u64),
|
||||
InstantSeal,
|
||||
None,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Consensus {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(if s == "instant-seal" {
|
||||
Consensus::InstantSeal
|
||||
} else if let Some(block_time) = s.strip_prefix("manual-seal-") {
|
||||
Consensus::ManualSeal(block_time.parse().map_err(|_| "invalid block time")?)
|
||||
} else if s.to_lowercase() == "none" {
|
||||
Consensus::None
|
||||
} else {
|
||||
return Err("incorrect consensus identifier".into());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
|
||||
#[clap(long, default_value = "manual-seal-3000")]
|
||||
pub consensus: Consensus,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub run: sc_cli::RunCmd,
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
/// Key management cli utilities
|
||||
#[command(subcommand)]
|
||||
Key(sc_cli::KeySubcommand),
|
||||
|
||||
/// Build a chain specification.
|
||||
/// DEPRECATED: `build-spec` command will be removed after 1/04/2026. Use `export-chain-spec`
|
||||
/// command instead.
|
||||
#[deprecated(
|
||||
note = "build-spec command will be removed after 1/04/2026. Use export-chain-spec command instead"
|
||||
)]
|
||||
BuildSpec(sc_cli::BuildSpecCmd),
|
||||
|
||||
/// Export the chain specification.
|
||||
ExportChainSpec(sc_cli::ExportChainSpecCmd),
|
||||
|
||||
/// Validate blocks.
|
||||
CheckBlock(sc_cli::CheckBlockCmd),
|
||||
|
||||
/// Export blocks.
|
||||
ExportBlocks(sc_cli::ExportBlocksCmd),
|
||||
|
||||
/// Export the state of a given block into a chain spec.
|
||||
ExportState(sc_cli::ExportStateCmd),
|
||||
|
||||
/// Import blocks.
|
||||
ImportBlocks(sc_cli::ImportBlocksCmd),
|
||||
|
||||
/// Remove the whole chain.
|
||||
PurgeChain(sc_cli::PurgeChainCmd),
|
||||
|
||||
/// Revert the chain to a previous state.
|
||||
Revert(sc_cli::RevertCmd),
|
||||
|
||||
/// Db meta columns information.
|
||||
ChainInfo(sc_cli::ChainInfoCmd),
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
chain_spec,
|
||||
cli::{Cli, Subcommand},
|
||||
service,
|
||||
};
|
||||
use pezkuwi_sdk::{sc_cli::SubstrateCli, sc_service::PartialComponents, *};
|
||||
|
||||
impl SubstrateCli for Cli {
|
||||
fn impl_name() -> String {
|
||||
"Substrate Node".into()
|
||||
}
|
||||
|
||||
fn impl_version() -> String {
|
||||
env!("SUBSTRATE_CLI_IMPL_VERSION").into()
|
||||
}
|
||||
|
||||
fn description() -> String {
|
||||
env!("CARGO_PKG_DESCRIPTION").into()
|
||||
}
|
||||
|
||||
fn author() -> String {
|
||||
env!("CARGO_PKG_AUTHORS").into()
|
||||
}
|
||||
|
||||
fn support_url() -> String {
|
||||
"support.anonymous.an".into()
|
||||
}
|
||||
|
||||
fn copyright_start_year() -> i32 {
|
||||
2017
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
|
||||
Ok(match id {
|
||||
"dev" => Box::new(chain_spec::development_chain_spec()?),
|
||||
path =>
|
||||
Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse and run command line arguments
|
||||
pub fn run() -> sc_cli::Result<()> {
|
||||
let cli = Cli::from_args();
|
||||
|
||||
match &cli.subcommand {
|
||||
Some(Subcommand::Key(cmd)) => cmd.run(&cli),
|
||||
#[allow(deprecated)]
|
||||
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)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportChainSpec(cmd)) => {
|
||||
let chain_spec = cli.load_spec(&cmd.chain)?;
|
||||
cmd.run(chain_spec)
|
||||
},
|
||||
Some(Subcommand::ExportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, config.database), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportState(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, config.chain_spec), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ImportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::PurgeChain(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run(config.database))
|
||||
},
|
||||
Some(Subcommand::Revert(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, backend, .. } =
|
||||
service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, backend, None), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ChainInfo(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| {
|
||||
cmd.run::<minimal_template_runtime::interface::OpaqueBlock>(&config)
|
||||
})
|
||||
},
|
||||
None => {
|
||||
let runner = cli.create_runner(&cli.run)?;
|
||||
runner.run_node_until_exit(|config| async move {
|
||||
match config.network.network_backend {
|
||||
sc_network::config::NetworkBackendType::Libp2p =>
|
||||
service::new_full::<sc_network::NetworkWorker<_, _>>(config, cli.consensus)
|
||||
.map_err(sc_cli::Error::Service),
|
||||
sc_network::config::NetworkBackendType::Litep2p => service::new_full::<
|
||||
sc_network::Litep2pNetworkBackend,
|
||||
>(config, cli.consensus)
|
||||
.map_err(sc_cli::Error::Service),
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// This file is part of Pezkuwi Sdk.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod chain_spec;
|
||||
pub(crate) mod cli;
|
||||
pub mod rpc;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,29 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Substrate Node Template CLI library.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod chain_spec;
|
||||
mod cli;
|
||||
mod command;
|
||||
mod rpc;
|
||||
mod service;
|
||||
|
||||
fn main() -> pezkuwi_sdk::sc_cli::Result<()> {
|
||||
command::run()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A collection of node-specific RPC methods.
|
||||
//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
|
||||
//! used by Substrate nodes. This file extends those RPC definitions with
|
||||
//! capabilities that are specific to this project's runtime configuration.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use jsonrpsee::RpcModule;
|
||||
use minimal_template_runtime::interface::{AccountId, Nonce, OpaqueBlock};
|
||||
use pezkuwi_sdk::{
|
||||
sc_transaction_pool_api::TransactionPool,
|
||||
sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata},
|
||||
*,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Full client dependencies.
|
||||
pub struct FullDeps<C, P> {
|
||||
/// The client instance to use.
|
||||
pub client: Arc<C>,
|
||||
/// Transaction pool instance.
|
||||
pub pool: Arc<P>,
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
/// Instantiate all full RPC extensions.
|
||||
pub fn create_full<C, P>(
|
||||
deps: FullDeps<C, P>,
|
||||
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
|
||||
where
|
||||
C: Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ sp_api::ProvideRuntimeApi<OpaqueBlock>
|
||||
+ HeaderBackend<OpaqueBlock>
|
||||
+ HeaderMetadata<OpaqueBlock, Error = BlockChainError>
|
||||
+ 'static,
|
||||
C::Api: sp_block_builder::BlockBuilder<OpaqueBlock>,
|
||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<OpaqueBlock, AccountId, Nonce>,
|
||||
P: TransactionPool + 'static,
|
||||
{
|
||||
use pezkuwi_sdk::substrate_frame_rpc_system::{System, SystemApiServer};
|
||||
let mut module = RpcModule::new(());
|
||||
let FullDeps { client, pool } = deps;
|
||||
|
||||
module.merge(System::new(client.clone(), pool.clone()).into_rpc())?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::cli::Consensus;
|
||||
use futures::FutureExt;
|
||||
use minimal_template_runtime::{interface::OpaqueBlock as Block, RuntimeApi};
|
||||
use pezkuwi_sdk::{
|
||||
sc_client_api::backend::Backend,
|
||||
sc_executor::WasmExecutor,
|
||||
sc_service::{error::Error as ServiceError, Configuration, TaskManager},
|
||||
sc_telemetry::{Telemetry, TelemetryWorker},
|
||||
sc_transaction_pool_api::OffchainTransactionPoolFactory,
|
||||
sp_runtime::traits::Block as BlockT,
|
||||
*,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
type HostFunctions = sp_io::SubstrateHostFunctions;
|
||||
|
||||
#[docify::export]
|
||||
pub(crate) type FullClient =
|
||||
sc_service::TFullClient<Block, RuntimeApi, WasmExecutor<HostFunctions>>;
|
||||
|
||||
type FullBackend = sc_service::TFullBackend<Block>;
|
||||
type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
|
||||
|
||||
/// Assembly of PartialComponents (enough to run chain ops subcommands)
|
||||
pub type Service = sc_service::PartialComponents<
|
||||
FullClient,
|
||||
FullBackend,
|
||||
FullSelectChain,
|
||||
sc_consensus::DefaultImportQueue<Block>,
|
||||
sc_transaction_pool::TransactionPoolHandle<Block, FullClient>,
|
||||
Option<Telemetry>,
|
||||
>;
|
||||
|
||||
pub fn new_partial(config: &Configuration) -> Result<Service, ServiceError> {
|
||||
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 = sc_service::new_wasm_executor(&config.executor);
|
||||
|
||||
let (client, backend, keystore_container, task_manager) =
|
||||
sc_service::new_full_parts::<Block, RuntimeApi, _>(
|
||||
config,
|
||||
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
|
||||
executor,
|
||||
)?;
|
||||
let client = Arc::new(client);
|
||||
|
||||
let telemetry = telemetry.map(|(worker, telemetry)| {
|
||||
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
|
||||
telemetry
|
||||
});
|
||||
|
||||
let select_chain = sc_consensus::LongestChain::new(backend.clone());
|
||||
|
||||
let transaction_pool = Arc::from(
|
||||
sc_transaction_pool::Builder::new(
|
||||
task_manager.spawn_essential_handle(),
|
||||
client.clone(),
|
||||
config.role.is_authority().into(),
|
||||
)
|
||||
.with_options(config.transaction_pool.clone())
|
||||
.with_prometheus(config.prometheus_registry())
|
||||
.build(),
|
||||
);
|
||||
|
||||
let import_queue = sc_consensus_manual_seal::import_queue(
|
||||
Box::new(client.clone()),
|
||||
&task_manager.spawn_essential_handle(),
|
||||
config.prometheus_registry(),
|
||||
);
|
||||
|
||||
Ok(sc_service::PartialComponents {
|
||||
client,
|
||||
backend,
|
||||
task_manager,
|
||||
import_queue,
|
||||
keystore_container,
|
||||
select_chain,
|
||||
transaction_pool,
|
||||
other: (telemetry),
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a new service for a full client.
|
||||
pub fn new_full<Network: sc_network::NetworkBackend<Block, <Block as BlockT>::Hash>>(
|
||||
config: Configuration,
|
||||
consensus: Consensus,
|
||||
) -> Result<TaskManager, ServiceError> {
|
||||
let sc_service::PartialComponents {
|
||||
client,
|
||||
backend,
|
||||
mut task_manager,
|
||||
import_queue,
|
||||
keystore_container,
|
||||
select_chain,
|
||||
transaction_pool,
|
||||
other: mut telemetry,
|
||||
} = new_partial(&config)?;
|
||||
|
||||
let net_config = sc_network::config::FullNetworkConfiguration::<
|
||||
Block,
|
||||
<Block as BlockT>::Hash,
|
||||
Network,
|
||||
>::new(
|
||||
&config.network,
|
||||
config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()),
|
||||
);
|
||||
let metrics = Network::register_notification_metrics(
|
||||
config.prometheus_config.as_ref().map(|cfg| &cfg.registry),
|
||||
);
|
||||
|
||||
let (network, system_rpc_tx, tx_handler_controller, sync_service) =
|
||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||
config: &config,
|
||||
net_config,
|
||||
client: client.clone(),
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
spawn_handle: task_manager.spawn_handle(),
|
||||
import_queue,
|
||||
block_announce_validator_builder: None,
|
||||
warp_sync_config: None,
|
||||
block_relay: None,
|
||||
metrics,
|
||||
})?;
|
||||
|
||||
if config.offchain_worker.enabled {
|
||||
let offchain_workers =
|
||||
sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
|
||||
runtime_api_provider: client.clone(),
|
||||
is_validator: config.role.is_authority(),
|
||||
keystore: Some(keystore_container.keystore()),
|
||||
offchain_db: backend.offchain_storage(),
|
||||
transaction_pool: Some(OffchainTransactionPoolFactory::new(
|
||||
transaction_pool.clone(),
|
||||
)),
|
||||
network_provider: Arc::new(network.clone()),
|
||||
enable_http_requests: true,
|
||||
custom_extensions: |_| vec![],
|
||||
})?;
|
||||
task_manager.spawn_handle().spawn(
|
||||
"offchain-workers-runner",
|
||||
"offchain-worker",
|
||||
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
let rpc_extensions_builder = {
|
||||
let client = client.clone();
|
||||
let pool = transaction_pool.clone();
|
||||
|
||||
Box::new(move |_| {
|
||||
let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() };
|
||||
crate::rpc::create_full(deps).map_err(Into::into)
|
||||
})
|
||||
};
|
||||
|
||||
let prometheus_registry = config.prometheus_registry().cloned();
|
||||
|
||||
let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams {
|
||||
network,
|
||||
client: client.clone(),
|
||||
keystore: keystore_container.keystore(),
|
||||
task_manager: &mut task_manager,
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
rpc_builder: rpc_extensions_builder,
|
||||
backend,
|
||||
system_rpc_tx,
|
||||
tx_handler_controller,
|
||||
sync_service,
|
||||
config,
|
||||
telemetry: telemetry.as_mut(),
|
||||
tracing_execute_block: None,
|
||||
})?;
|
||||
|
||||
let proposer = sc_basic_authorship::ProposerFactory::new(
|
||||
task_manager.spawn_handle(),
|
||||
client.clone(),
|
||||
transaction_pool.clone(),
|
||||
prometheus_registry.as_ref(),
|
||||
telemetry.as_ref().map(|x| x.handle()),
|
||||
);
|
||||
|
||||
match consensus {
|
||||
Consensus::InstantSeal => {
|
||||
let params = sc_consensus_manual_seal::InstantSealParams {
|
||||
block_import: client.clone(),
|
||||
env: proposer,
|
||||
client,
|
||||
pool: transaction_pool,
|
||||
select_chain,
|
||||
consensus_data_provider: None,
|
||||
create_inherent_data_providers: move |_, ()| async move {
|
||||
Ok(sp_timestamp::InherentDataProvider::from_system_time())
|
||||
},
|
||||
};
|
||||
|
||||
let authorship_future = sc_consensus_manual_seal::run_instant_seal(params);
|
||||
|
||||
task_manager.spawn_essential_handle().spawn_blocking(
|
||||
"instant-seal",
|
||||
None,
|
||||
authorship_future,
|
||||
);
|
||||
},
|
||||
Consensus::ManualSeal(block_time) => {
|
||||
let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024);
|
||||
task_manager.spawn_handle().spawn("block_authoring", None, async move {
|
||||
loop {
|
||||
futures_timer::Delay::new(std::time::Duration::from_millis(block_time)).await;
|
||||
sink.try_send(sc_consensus_manual_seal::EngineCommand::SealNewBlock {
|
||||
create_empty: true,
|
||||
finalize: true,
|
||||
parent_hash: None,
|
||||
sender: None,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let params = sc_consensus_manual_seal::ManualSealParams {
|
||||
block_import: client.clone(),
|
||||
env: proposer,
|
||||
client,
|
||||
pool: transaction_pool,
|
||||
select_chain,
|
||||
commands_stream: Box::pin(commands_stream),
|
||||
consensus_data_provider: None,
|
||||
create_inherent_data_providers: move |_, ()| async move {
|
||||
Ok(sp_timestamp::InherentDataProvider::from_system_time())
|
||||
},
|
||||
};
|
||||
let authorship_future = sc_consensus_manual_seal::run_manual_seal(params);
|
||||
|
||||
task_manager.spawn_essential_handle().spawn_blocking(
|
||||
"manual-seal",
|
||||
None,
|
||||
authorship_future,
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
Ok(task_manager)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Pallets
|
||||
|
||||
ℹ️ A pallet is a unit of encapsulated logic, with a clearly defined responsibility. A pallet is analogous to a
|
||||
module in the runtime.
|
||||
|
||||
💁 In this template, there is a simple custom pallet based on the FRAME framework.
|
||||
|
||||
👉 Learn more about FRAME
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
||||
|
||||
🧑🏫 Please refer to
|
||||
[this guide](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html)
|
||||
to learn how to write a basic pallet.
|
||||
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "pallet-minimal-template"
|
||||
description = "A minimal pallet built with FRAME, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
pezkuwi-sdk = { workspace = true, default-features = false, features = [
|
||||
"experimental",
|
||||
"runtime",
|
||||
] }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["codec/std", "pezkuwi-sdk/std", "scale-info/std"]
|
||||
runtime-benchmarks = ["pezkuwi-sdk/runtime-benchmarks"]
|
||||
@@ -0,0 +1,26 @@
|
||||
//! A shell pallet built with [`frame`].
|
||||
//!
|
||||
//! To get started with this pallet, try implementing the guide in
|
||||
//! <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html>
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use frame::prelude::*;
|
||||
use pezkuwi_sdk::pezkuwi_sdk_frame as frame;
|
||||
|
||||
// Re-export all pallet parts, this is needed to properly import the pallet into the runtime.
|
||||
pub use pallet::*;
|
||||
|
||||
#[frame::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: pezkuwi_sdk::frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::storage]
|
||||
pub type Value<T> = StorageValue<Value = u32>;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
[package]
|
||||
name = "minimal-template-runtime"
|
||||
description = "A solochain runtime template built with Substrate, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
pezkuwi-sdk = { workspace = true, features = [
|
||||
"pallet-balances",
|
||||
"pallet-sudo",
|
||||
"pallet-timestamp",
|
||||
"pallet-transaction-payment",
|
||||
"pallet-transaction-payment-rpc-runtime-api",
|
||||
"runtime",
|
||||
] }
|
||||
scale-info = { workspace = true }
|
||||
serde_json = { workspace = true, default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
|
||||
# local pallet templates
|
||||
pallet-minimal-template = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
pezkuwi-sdk = { optional = true, workspace = true, features = [
|
||||
"substrate-wasm-builder",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"pallet-minimal-template/std",
|
||||
"pezkuwi-sdk/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"pallet-minimal-template/runtime-benchmarks",
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
# Runtime
|
||||
|
||||
ℹ️ The runtime (in other words, a state transition function), refers to the core logic of the blockchain that is
|
||||
responsible for validating blocks and executing the state changes they define.
|
||||
|
||||
💁 The runtime in this template is constructed using ready-made FRAME pallets that ship with
|
||||
[Pezkuwi SDK](https://github.com/pezkuwichain/pezkuwi-sdk), and a [template for a custom pallet](../pallets/README.md).
|
||||
|
||||
👉 Learn more about FRAME
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
||||
@@ -0,0 +1,23 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
pezkuwi_sdk::substrate_wasm_builder::WasmBuilder::build_using_defaults();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A minimal runtime that includes the template [`pallet`](`pallet_minimal_template`).
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
// Make the WASM binary available.
|
||||
#[cfg(feature = "std")]
|
||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo};
|
||||
use pezkuwi_sdk::{
|
||||
pezkuwi_sdk_frame::{
|
||||
self as frame,
|
||||
deps::sp_genesis_builder,
|
||||
runtime::{apis, prelude::*},
|
||||
},
|
||||
*,
|
||||
};
|
||||
|
||||
/// Provides getters for genesis configuration presets.
|
||||
pub mod genesis_config_presets {
|
||||
use super::*;
|
||||
use crate::{
|
||||
interface::{Balance, MinimumBalance},
|
||||
sp_keyring::Sr25519Keyring,
|
||||
BalancesConfig, RuntimeGenesisConfig, SudoConfig,
|
||||
};
|
||||
|
||||
use alloc::{vec, vec::Vec};
|
||||
use serde_json::Value;
|
||||
|
||||
/// Returns a development genesis config preset.
|
||||
pub fn development_config_genesis() -> Value {
|
||||
let endowment = <MinimumBalance as Get<Balance>>::get().max(1) * 1000;
|
||||
frame_support::build_struct_json_patch!(RuntimeGenesisConfig {
|
||||
balances: BalancesConfig {
|
||||
balances: Sr25519Keyring::iter()
|
||||
.map(|a| (a.to_account_id(), endowment))
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
sudo: SudoConfig { key: Some(Sr25519Keyring::Alice.to_account_id()) },
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the set of the available genesis config presets.
|
||||
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
|
||||
let patch = match id.as_ref() {
|
||||
sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(),
|
||||
_ => return None,
|
||||
};
|
||||
Some(
|
||||
serde_json::to_string(&patch)
|
||||
.expect("serialization to json is expected to work. qed.")
|
||||
.into_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
/// List of supported presets.
|
||||
pub fn preset_names() -> Vec<PresetId> {
|
||||
vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)]
|
||||
}
|
||||
}
|
||||
|
||||
/// The runtime version.
|
||||
#[runtime_version]
|
||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"),
|
||||
authoring_version: 1,
|
||||
spec_version: 0,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
system_version: 1,
|
||||
};
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn native_version() -> NativeVersion {
|
||||
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
|
||||
}
|
||||
|
||||
/// The transaction extensions that are added to the runtime.
|
||||
type TxExtension = (
|
||||
// Authorize calls that validate themselves.
|
||||
frame_system::AuthorizeCall<Runtime>,
|
||||
// Checks that the sender is not the zero address.
|
||||
frame_system::CheckNonZeroSender<Runtime>,
|
||||
// Checks that the runtime version is correct.
|
||||
frame_system::CheckSpecVersion<Runtime>,
|
||||
// Checks that the transaction version is correct.
|
||||
frame_system::CheckTxVersion<Runtime>,
|
||||
// Checks that the genesis hash is correct.
|
||||
frame_system::CheckGenesis<Runtime>,
|
||||
// Checks that the era is valid.
|
||||
frame_system::CheckEra<Runtime>,
|
||||
// Checks that the nonce is valid.
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
// Checks that the weight is valid.
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
// Ensures that the sender has enough funds to pay for the transaction
|
||||
// and deducts the fee from the sender's account.
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
// Reclaim the unused weight from the block using post dispatch information.
|
||||
// It must be last in the pipeline in order to catch the refund in previous transaction
|
||||
// extensions
|
||||
frame_system::WeightReclaim<Runtime>,
|
||||
);
|
||||
|
||||
// Composes the runtime by adding all the used pallets and deriving necessary types.
|
||||
#[frame_construct_runtime]
|
||||
mod runtime {
|
||||
/// The main runtime type.
|
||||
#[runtime::runtime]
|
||||
#[runtime::derive(
|
||||
RuntimeCall,
|
||||
RuntimeEvent,
|
||||
RuntimeError,
|
||||
RuntimeOrigin,
|
||||
RuntimeFreezeReason,
|
||||
RuntimeHoldReason,
|
||||
RuntimeSlashReason,
|
||||
RuntimeLockId,
|
||||
RuntimeTask,
|
||||
RuntimeViewFunction
|
||||
)]
|
||||
pub struct Runtime;
|
||||
|
||||
/// Mandatory system pallet that should always be included in a FRAME runtime.
|
||||
#[runtime::pallet_index(0)]
|
||||
pub type System = frame_system::Pallet<Runtime>;
|
||||
|
||||
/// Provides a way for consensus systems to set and check the onchain time.
|
||||
#[runtime::pallet_index(1)]
|
||||
pub type Timestamp = pallet_timestamp::Pallet<Runtime>;
|
||||
|
||||
/// Provides the ability to keep track of balances.
|
||||
#[runtime::pallet_index(2)]
|
||||
pub type Balances = pallet_balances::Pallet<Runtime>;
|
||||
|
||||
/// Provides a way to execute privileged functions.
|
||||
#[runtime::pallet_index(3)]
|
||||
pub type Sudo = pallet_sudo::Pallet<Runtime>;
|
||||
|
||||
/// Provides the ability to charge for extrinsic execution.
|
||||
#[runtime::pallet_index(4)]
|
||||
pub type TransactionPayment = pallet_transaction_payment::Pallet<Runtime>;
|
||||
|
||||
/// A minimal pallet template.
|
||||
#[runtime::pallet_index(5)]
|
||||
pub type Template = pallet_minimal_template::Pallet<Runtime>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const Version: RuntimeVersion = VERSION;
|
||||
}
|
||||
|
||||
/// Implements the types required for the system pallet.
|
||||
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = Block;
|
||||
type Version = Version;
|
||||
// Use the account data from the balances pallet
|
||||
type AccountData = pallet_balances::AccountData<<Runtime as pallet_balances::Config>::Balance>;
|
||||
}
|
||||
|
||||
// Implements the types required for the balances pallet.
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Runtime {
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
// Implements the types required for the sudo pallet.
|
||||
#[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_sudo::Config for Runtime {}
|
||||
|
||||
// Implements the types required for the sudo pallet.
|
||||
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_timestamp::Config for Runtime {}
|
||||
|
||||
// Implements the types required for the transaction payment pallet.
|
||||
#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter<Balances, ()>;
|
||||
// Setting fee as independent of the weight of the extrinsic for demo purposes
|
||||
type WeightToFee = NoFee<<Self as pallet_balances::Config>::Balance>;
|
||||
// Setting fee as fixed for any length of the call data for demo purposes
|
||||
type LengthToFee = FixedFee<1, <Self as pallet_balances::Config>::Balance>;
|
||||
}
|
||||
|
||||
// Implements the types required for the template pallet.
|
||||
impl pallet_minimal_template::Config for Runtime {}
|
||||
|
||||
type Block = frame::runtime::types_common::BlockOf<Runtime, TxExtension>;
|
||||
type Header = HeaderFor<Runtime>;
|
||||
|
||||
type RuntimeExecutive =
|
||||
Executive<Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllPalletsWithSystem>;
|
||||
|
||||
impl_runtime_apis! {
|
||||
impl apis::Core<Block> for Runtime {
|
||||
fn version() -> RuntimeVersion {
|
||||
VERSION
|
||||
}
|
||||
|
||||
fn execute_block(block: <Block as frame::traits::Block>::LazyBlock) {
|
||||
RuntimeExecutive::execute_block(block)
|
||||
}
|
||||
|
||||
fn initialize_block(header: &Header) -> ExtrinsicInclusionMode {
|
||||
RuntimeExecutive::initialize_block(header)
|
||||
}
|
||||
}
|
||||
impl apis::Metadata<Block> for Runtime {
|
||||
fn metadata() -> OpaqueMetadata {
|
||||
OpaqueMetadata::new(Runtime::metadata().into())
|
||||
}
|
||||
|
||||
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
|
||||
Runtime::metadata_at_version(version)
|
||||
}
|
||||
|
||||
fn metadata_versions() -> Vec<u32> {
|
||||
Runtime::metadata_versions()
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::BlockBuilder<Block> for Runtime {
|
||||
fn apply_extrinsic(extrinsic: ExtrinsicFor<Runtime>) -> ApplyExtrinsicResult {
|
||||
RuntimeExecutive::apply_extrinsic(extrinsic)
|
||||
}
|
||||
|
||||
fn finalize_block() -> HeaderFor<Runtime> {
|
||||
RuntimeExecutive::finalize_block()
|
||||
}
|
||||
|
||||
fn inherent_extrinsics(data: InherentData) -> Vec<ExtrinsicFor<Runtime>> {
|
||||
data.create_extrinsics()
|
||||
}
|
||||
|
||||
fn check_inherents(
|
||||
block: <Block as frame::traits::Block>::LazyBlock,
|
||||
data: InherentData,
|
||||
) -> CheckInherentsResult {
|
||||
data.check_extrinsics(&block)
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(
|
||||
source: TransactionSource,
|
||||
tx: ExtrinsicFor<Runtime>,
|
||||
block_hash: <Runtime as frame_system::Config>::Hash,
|
||||
) -> TransactionValidity {
|
||||
RuntimeExecutive::validate_transaction(source, tx, block_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::OffchainWorkerApi<Block> for Runtime {
|
||||
fn offchain_worker(header: &HeaderFor<Runtime>) {
|
||||
RuntimeExecutive::offchain_worker(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::SessionKeys<Block> for Runtime {
|
||||
fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
_encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, apis::KeyTypeId)>> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::AccountNonceApi<Block, interface::AccountId, interface::Nonce> for Runtime {
|
||||
fn account_nonce(account: interface::AccountId) -> interface::Nonce {
|
||||
System::account_nonce(account)
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<
|
||||
Block,
|
||||
interface::Balance,
|
||||
> for Runtime {
|
||||
fn query_info(uxt: ExtrinsicFor<Runtime>, len: u32) -> RuntimeDispatchInfo<interface::Balance> {
|
||||
TransactionPayment::query_info(uxt, len)
|
||||
}
|
||||
fn query_fee_details(uxt: ExtrinsicFor<Runtime>, len: u32) -> FeeDetails<interface::Balance> {
|
||||
TransactionPayment::query_fee_details(uxt, len)
|
||||
}
|
||||
fn query_weight_to_fee(weight: Weight) -> interface::Balance {
|
||||
TransactionPayment::weight_to_fee(weight)
|
||||
}
|
||||
fn query_length_to_fee(length: u32) -> interface::Balance {
|
||||
TransactionPayment::length_to_fee(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl apis::GenesisBuilder<Block> for Runtime {
|
||||
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
|
||||
build_state::<RuntimeGenesisConfig>(config)
|
||||
}
|
||||
|
||||
fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>> {
|
||||
get_preset::<RuntimeGenesisConfig>(id, self::genesis_config_presets::get_preset)
|
||||
}
|
||||
|
||||
fn preset_names() -> Vec<PresetId> {
|
||||
self::genesis_config_presets::preset_names()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Some re-exports that the node side code needs to know. Some are useful in this context as well.
|
||||
///
|
||||
/// Other types should preferably be private.
|
||||
// TODO: this should be standardized in some way, see:
|
||||
// https://github.com/paritytech/substrate/issues/10579#issuecomment-1600537558
|
||||
pub mod interface {
|
||||
use super::Runtime;
|
||||
use pezkuwi_sdk::{pezkuwi_sdk_frame as frame, *};
|
||||
|
||||
pub type Block = super::Block;
|
||||
pub use frame::runtime::types_common::OpaqueBlock;
|
||||
pub type AccountId = <Runtime as frame_system::Config>::AccountId;
|
||||
pub type Nonce = <Runtime as frame_system::Config>::Nonce;
|
||||
pub type Hash = <Runtime as frame_system::Config>::Hash;
|
||||
pub type Balance = <Runtime as pallet_balances::Config>::Balance;
|
||||
pub type MinimumBalance = <Runtime as pallet_balances::Config>::ExistentialDeposit;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# The setup bellow allows only one node to produce
|
||||
# blocks and the rest will follow.
|
||||
|
||||
[relaychain]
|
||||
chain = "dev"
|
||||
default_command = "minimal-template-node"
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
args = ["--consensus manual-seal-3000"]
|
||||
validator = true
|
||||
ws_port = 9944
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
args = ["--consensus None"]
|
||||
validator = true
|
||||
ws_port = 9955
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "charlie"
|
||||
args = ["--consensus None"]
|
||||
validator = true
|
||||
ws_port = 9966
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "dave"
|
||||
args = ["--consensus None"]
|
||||
validator = true
|
||||
ws_port = 9977
|
||||
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM docker.io/paritytech/ci-unified:latest as builder
|
||||
|
||||
WORKDIR /polkadot
|
||||
COPY . /polkadot
|
||||
|
||||
RUN cargo fetch
|
||||
RUN cargo build --locked --release
|
||||
|
||||
FROM docker.io/parity/base-bin:latest
|
||||
|
||||
COPY --from=builder /polkadot/target/release/solochain-template-node /usr/local/bin
|
||||
|
||||
USER root
|
||||
RUN useradd -m -u 1001 -U -s /bin/sh -d /polkadot polkadot && \
|
||||
mkdir -p /data /polkadot/.local/share && \
|
||||
chown -R polkadot:polkadot /data && \
|
||||
ln -s /data /polkadot/.local/share/polkadot && \
|
||||
# unclutter and minimize the attack surface
|
||||
rm -rf /usr/bin /usr/sbin && \
|
||||
# check if executable works in this container
|
||||
/usr/local/bin/solochain-template-node --version
|
||||
|
||||
USER polkadot
|
||||
|
||||
EXPOSE 30333 9933 9944 9615
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/solochain-template-node"]
|
||||
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
@@ -0,0 +1,232 @@
|
||||
# Substrate Node Template
|
||||
|
||||
A fresh [Substrate](https://substrate.pezkuwichain.app/) node, ready for hacking :rocket:
|
||||
|
||||
A standalone version of this template is available for each release of PezkuwiChain
|
||||
in the [Substrate Developer Hub Teyrchain
|
||||
Template](https://github.com/substrate-developer-hub/substrate-node-template/)
|
||||
repository. The teyrchain template is generated directly at each PezkuwiChain
|
||||
release branch from the [Solochain Template in
|
||||
Substrate](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/solochain)
|
||||
upstream
|
||||
|
||||
It is usually best to use the stand-alone version to start a new project. All
|
||||
bugs, suggestions, and feature requests should be made upstream in the
|
||||
[Substrate](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/substrate)
|
||||
repository.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Depending on your operating system and Rust version, there might be additional
|
||||
packages required to compile this template. Check the
|
||||
[Install](https://docs.pezkuwichain.io/install/) instructions for your platform for
|
||||
the most common dependencies. Alternatively, you can use one of the [alternative
|
||||
installation](#alternatives-installations) options.
|
||||
|
||||
Fetch solochain template code:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/pezkuwichain/pezkuwi-sdk/issues/25.git solochain-template
|
||||
|
||||
cd solochain-template
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
🔨 Use the following command to build the node without launching it:
|
||||
|
||||
```sh
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
### Embedded Docs
|
||||
|
||||
After you build the project, you can use the following command to explore its
|
||||
parameters and subcommands:
|
||||
|
||||
```sh
|
||||
./target/release/solochain-template-node -h
|
||||
```
|
||||
|
||||
You can generate and view the [Rust
|
||||
Docs](https://doc.rust-lang.org/cargo/commands/cargo-doc.html) for this template
|
||||
with this command:
|
||||
|
||||
```sh
|
||||
cargo +nightly doc --open
|
||||
```
|
||||
|
||||
### Single-Node Development Chain
|
||||
|
||||
The following command starts a single-node development chain that doesn't
|
||||
persist state:
|
||||
|
||||
```sh
|
||||
./target/release/solochain-template-node --dev
|
||||
```
|
||||
|
||||
To purge the development chain's state, run the following command:
|
||||
|
||||
```sh
|
||||
./target/release/solochain-template-node purge-chain --dev
|
||||
```
|
||||
|
||||
To start the development chain with detailed logging, run the following command:
|
||||
|
||||
```sh
|
||||
RUST_BACKTRACE=1 ./target/release/solochain-template-node -ldebug --dev
|
||||
```
|
||||
|
||||
Development chains:
|
||||
|
||||
- Maintain state in a `tmp` folder while the node is running.
|
||||
- Use the **Alice** and **Bob** accounts as default validator authorities.
|
||||
- Use the **Alice** account as the default `sudo` account.
|
||||
- Are preconfigured with a genesis state (`/node/src/chain_spec.rs`) that
|
||||
includes several pre-funded development accounts.
|
||||
|
||||
|
||||
To persist chain state between runs, specify a base path by running a command
|
||||
similar to the following:
|
||||
|
||||
```sh
|
||||
// Create a folder to use as the db base path
|
||||
$ mkdir my-chain-state
|
||||
|
||||
// Use of that folder to store the chain state
|
||||
$ ./target/release/solochain-template-node --dev --base-path ./my-chain-state/
|
||||
|
||||
// Check the folder structure created inside the base path after running the chain
|
||||
$ ls ./my-chain-state
|
||||
chains
|
||||
$ ls ./my-chain-state/chains/
|
||||
dev
|
||||
$ ls ./my-chain-state/chains/dev
|
||||
db keystore network
|
||||
```
|
||||
|
||||
### Connect with PezkuwiChain-JS Apps Front-End
|
||||
|
||||
After you start the node template locally, you can interact with it using the
|
||||
hosted version of the [PezkuwiChain/Substrate
|
||||
Portal](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9944)
|
||||
front-end by connecting to the local node endpoint. A hosted version is also
|
||||
available on [IPFS](https://dotapps.io/). You can
|
||||
also find the source code and instructions for hosting your own instance in the
|
||||
[`pezkuwi-js/apps`](https://github.com/polkadot-js/apps) repository.
|
||||
|
||||
### Multi-Node Local Testnet
|
||||
|
||||
If you want to see the multi-node consensus algorithm in action, see [Simulate a
|
||||
network](https://docs.pezkuwichain.io/tutorials/build-a-blockchain/simulate-network/).
|
||||
|
||||
## Template Structure
|
||||
|
||||
A Substrate project such as this consists of a number of components that are
|
||||
spread across a few directories.
|
||||
|
||||
### Node
|
||||
|
||||
A blockchain node is an application that allows users to participate in a
|
||||
blockchain network. Substrate-based blockchain nodes expose a number of
|
||||
capabilities:
|
||||
|
||||
- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking
|
||||
stack to allow the nodes in the network to communicate with one another.
|
||||
- Consensus: Blockchains must have a way to come to
|
||||
[consensus](https://docs.pezkuwichain.io/fundamentals/consensus/) on the state of
|
||||
the network. Substrate makes it possible to supply custom consensus engines
|
||||
and also ships with several consensus mechanisms that have been built on top
|
||||
of [Web3 Foundation
|
||||
research](https://research.web3.foundation/PezkuwiChain/protocols/NPoS).
|
||||
- RPC Server: A remote procedure call (RPC) server is used to interact with
|
||||
Substrate nodes.
|
||||
|
||||
There are several files in the `node` directory. Take special note of the
|
||||
following:
|
||||
|
||||
- [`chain_spec.rs`](./node/src/chain_spec.rs): A [chain
|
||||
specification](https://docs.pezkuwichain.io/build/chain-spec/) is a source code
|
||||
file that defines a Substrate chain's initial (genesis) state. Chain
|
||||
specifications are useful for development and testing, and critical when
|
||||
architecting the launch of a production chain. Take note of the
|
||||
`development_config` and `testnet_genesis` functions. These functions are
|
||||
used to define the genesis state for the local development chain
|
||||
configuration. These functions identify some [well-known
|
||||
accounts](https://docs.pezkuwichain.io/reference/command-line-tools/subkey/) and
|
||||
use them to configure the blockchain's initial state.
|
||||
- [`service.rs`](./node/src/service.rs): This file defines the node
|
||||
implementation. Take note of the libraries that this file imports and the
|
||||
names of the functions it invokes. In particular, there are references to
|
||||
consensus-related topics, such as the [block finalization and
|
||||
forks](https://docs.pezkuwichain.io/fundamentals/consensus/#finalization-and-forks)
|
||||
and other [consensus
|
||||
mechanisms](https://docs.pezkuwichain.io/fundamentals/consensus/#default-consensus-models)
|
||||
such as Aura for block authoring and GRANDPA for finality.
|
||||
|
||||
|
||||
### Runtime
|
||||
|
||||
In Substrate, the terms "runtime" and "state transition function" are analogous.
|
||||
Both terms refer to the core logic of the blockchain that is responsible for
|
||||
validating blocks and executing the state changes they define. The Substrate
|
||||
project in this repository uses
|
||||
[FRAME](https://docs.pezkuwichain.io/learn/runtime-development/#frame) to construct
|
||||
a blockchain runtime. FRAME allows runtime developers to declare domain-specific
|
||||
logic in modules called "pallets". At the heart of FRAME is a helpful [macro
|
||||
language](https://docs.pezkuwichain.io/reference/frame-macros/) that makes it easy
|
||||
to create pallets and flexibly compose them to create blockchains that can
|
||||
address [a variety of needs](https://substrate.pezkuwichain.app/ecosystem/projects/).
|
||||
|
||||
Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this
|
||||
template and note the following:
|
||||
|
||||
- This file configures several pallets to include in the runtime. Each pallet
|
||||
configuration is defined by a code block that begins with `impl
|
||||
$PALLET_NAME::Config for Runtime`.
|
||||
- The pallets are composed into a single runtime by way of the
|
||||
[#[runtime]](https://docs.pezkuwichain.io/sdk/master/frame_support/attr.runtime.html)
|
||||
macro, which is part of the [core FRAME pallet
|
||||
library](https://docs.pezkuwichain.io/reference/frame-pallets/#system-pallets).
|
||||
|
||||
### Pallets
|
||||
|
||||
The runtime in this project is constructed using many FRAME pallets that ship
|
||||
with [the Substrate
|
||||
repository](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/substrate/frame) and a
|
||||
template pallet that is [defined in the
|
||||
`pallets`](./pallets/template/src/lib.rs) directory.
|
||||
|
||||
A FRAME pallet is comprised of a number of blockchain primitives, including:
|
||||
|
||||
- Storage: FRAME defines a rich set of powerful [storage
|
||||
abstractions](https://docs.pezkuwichain.io/build/runtime-storage/) that makes it
|
||||
easy to use Substrate's efficient key-value database to manage the evolving
|
||||
state of a blockchain.
|
||||
- Dispatchables: FRAME pallets define special types of functions that can be
|
||||
invoked (dispatched) from outside of the runtime in order to update its state.
|
||||
- Events: Substrate uses
|
||||
[events](https://docs.pezkuwichain.io/build/events-and-errors/) to notify users
|
||||
of significant state changes.
|
||||
- Errors: When a dispatchable fails, it returns an error.
|
||||
|
||||
Each pallet has its own `Config` trait which serves as a configuration interface
|
||||
to generically define the types and parameters it depends on.
|
||||
|
||||
## Alternatives Installations
|
||||
|
||||
Instead of installing dependencies and building this source directly, consider
|
||||
the following alternatives.
|
||||
|
||||
### Nix
|
||||
|
||||
Install [nix](https://nixos.org/) and
|
||||
[nix-direnv](https://github.com/nix-community/nix-direnv) for a fully
|
||||
plug-and-play experience for setting up the development environment. To get all
|
||||
the correct dependencies, activate direnv `direnv allow`.
|
||||
|
||||
### Docker
|
||||
|
||||
Please follow the [Substrate Docker instructions
|
||||
here](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/substrate/docker/README.md) to
|
||||
build the Docker container with the Substrate Node Template binary.
|
||||
@@ -0,0 +1,218 @@
|
||||
# Installation
|
||||
|
||||
This guide is for reference only, please check the latest information on getting started with Substrate [here](https://docs.pezkuwichain.io/main-docs/install/).
|
||||
|
||||
This page will guide you through the **2 steps** needed to prepare a computer for **Substrate** development. Since
|
||||
Substrate is built with [the Rust programming language](https://www.rust-lang.org/), the first thing you will need to do
|
||||
is prepare the computer for Rust development - these steps will vary based on the computer's operating system. Once Rust
|
||||
is configured, you will use its toolchains to interact with Rust projects; the commands for Rust's toolchains will be
|
||||
the same for all supported, Unix-based operating systems.
|
||||
|
||||
## Build dependencies
|
||||
|
||||
Substrate development is easiest on Unix-based operating systems like macOS or Linux. The examples in the [Substrate
|
||||
Docs](https://github.com/pezkuwichain/docs.pezkuwichain.io) use Unix-style terminals to demonstrate how to interact with Substrate from the command
|
||||
line.
|
||||
|
||||
### Ubuntu/Debian
|
||||
|
||||
Use a terminal shell to execute the following commands:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
# May prompt for location information
|
||||
sudo apt install -y git clang curl libssl-dev llvm libudev-dev
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
Run these commands from a terminal:
|
||||
|
||||
```bash
|
||||
pacman -Syu --needed --noconfirm curl git clang
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
Run these commands from a terminal:
|
||||
|
||||
```bash
|
||||
sudo dnf update
|
||||
sudo dnf install clang curl git openssl-devel
|
||||
```
|
||||
|
||||
### OpenSUSE
|
||||
|
||||
Run these commands from a terminal:
|
||||
|
||||
```bash
|
||||
sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
> **Apple M1 ARM** If you have an Apple M1 ARM system on a chip, make sure that you have Apple Rosetta 2 installed
|
||||
> through `softwareupdate --install-rosetta`. This is only needed to run the `protoc` tool during the build. The build
|
||||
> itself and the target binaries would remain native.
|
||||
|
||||
Open the Terminal application and execute the following commands:
|
||||
|
||||
```bash
|
||||
# Install Homebrew if necessary https://brew.sh/
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
|
||||
|
||||
# Make sure Homebrew is up-to-date, install openssl
|
||||
brew update
|
||||
brew install openssl
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
**_PLEASE NOTE:_** Native Windows development of Substrate is _not_ very well supported! It is _highly_
|
||||
recommended to use [Windows Subsystem Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
|
||||
(WSL) and follow the instructions for [Ubuntu/Debian](#ubuntudebian).
|
||||
Please refer to the separate
|
||||
[guide for native Windows development](https://docs.pezkuwichain.io/main-docs/install/windows/).
|
||||
|
||||
## Rust developer environment
|
||||
|
||||
This guide uses <https://rustup.rs> installer and the `rustup` tool to manage the Rust toolchain. First install and
|
||||
configure `rustup`:
|
||||
|
||||
```bash
|
||||
# Install
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# Configure
|
||||
source ~/.cargo/env
|
||||
```
|
||||
|
||||
Configure the Rust toolchain to default to the latest stable version, add nightly and the nightly wasm target:
|
||||
|
||||
```bash
|
||||
rustup default stable
|
||||
rustup update
|
||||
rustup update nightly
|
||||
rustup target add wasm32-unknown-unknown --toolchain nightly
|
||||
```
|
||||
|
||||
## Test your set-up
|
||||
|
||||
Now the best way to ensure that you have successfully prepared a computer for Substrate development is to follow the
|
||||
steps in [our first Substrate tutorial](https://docs.pezkuwichain.io/tutorials/v3/create-your-first-substrate-chain/).
|
||||
|
||||
## Troubleshooting Substrate builds
|
||||
|
||||
Sometimes you can't get the Substrate node template to compile out of the box. Here are some tips to help you work
|
||||
through that.
|
||||
|
||||
### Rust configuration check
|
||||
|
||||
To see what Rust toolchain you are presently using, run:
|
||||
|
||||
```bash
|
||||
rustup show
|
||||
```
|
||||
|
||||
This will show something like this (Ubuntu example) output:
|
||||
|
||||
```text
|
||||
Default host: x86_64-unknown-linux-gnu
|
||||
rustup home: /home/user/.rustup
|
||||
|
||||
installed toolchains
|
||||
--------------------
|
||||
|
||||
stable-x86_64-unknown-linux-gnu (default)
|
||||
nightly-2020-10-06-x86_64-unknown-linux-gnu
|
||||
nightly-x86_64-unknown-linux-gnu
|
||||
|
||||
installed targets for active toolchain
|
||||
--------------------------------------
|
||||
|
||||
wasm32-unknown-unknown
|
||||
x86_64-unknown-linux-gnu
|
||||
|
||||
active toolchain
|
||||
----------------
|
||||
|
||||
stable-x86_64-unknown-linux-gnu (default)
|
||||
rustc 1.50.0 (cb75ad5db 2021-02-10)
|
||||
```
|
||||
|
||||
As you can see above, the default toolchain is stable, and the `nightly-x86_64-unknown-linux-gnu` toolchain as well as
|
||||
its `wasm32-unknown-unknown` target is installed. You also see that `nightly-2020-10-06-x86_64-unknown-linux-gnu` is
|
||||
installed, but is not used unless explicitly defined as illustrated in the [specify your nightly
|
||||
version](#specifying-nightly-version) section.
|
||||
|
||||
### WebAssembly compilation
|
||||
|
||||
Substrate uses [WebAssembly](https://webassembly.org) (Wasm) to produce portable blockchain runtimes. You will need to
|
||||
configure your Rust compiler to use [`nightly` builds](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) to
|
||||
allow you to compile Substrate runtime code to the Wasm target.
|
||||
|
||||
> There are upstream issues in Rust that need to be resolved before all of Substrate can use the stable Rust toolchain.
|
||||
> [This is our tracking issue](https://github.com/paritytech/substrate/issues/1252) if you're curious as to why and how
|
||||
> this will be resolved.
|
||||
|
||||
#### Latest nightly for Substrate `master`
|
||||
|
||||
Developers who are building Substrate _itself_ should always use the latest bug-free versions of Rust stable and
|
||||
nightly. This is because the Substrate codebase follows the tip of Rust nightly, which means that changes in Substrate
|
||||
often depend on upstream changes in the Rust nightly compiler. To ensure your Rust compiler is always up to date, you
|
||||
should run:
|
||||
|
||||
```bash
|
||||
rustup update
|
||||
rustup update nightly
|
||||
rustup target add wasm32-unknown-unknown --toolchain nightly
|
||||
```
|
||||
|
||||
> NOTE: It may be necessary to occasionally rerun `rustup update` if a change in the upstream Substrate codebase depends
|
||||
> on a new feature of the Rust compiler. When you do this, both your nightly and stable toolchains will be pulled to the
|
||||
> most recent release, and for nightly, it is generally _not_ expected to compile WASM without error (although it very
|
||||
> often does). Be sure to [specify your nightly version](#specifying-nightly-version) if you get WASM build errors from
|
||||
> `rustup` and [downgrade nightly as needed](#downgrading-rust-nightly).
|
||||
|
||||
#### Rust nightly toolchain
|
||||
|
||||
If you want to guarantee that your build works on your computer as you update Rust and other dependencies, you should
|
||||
use a specific Rust nightly version that is known to be compatible with the version of Substrate they are using; this
|
||||
version will vary from project to project and different projects may use different mechanisms to communicate this
|
||||
version to developers. For instance, the Pezkuwi client specifies this information in its [release
|
||||
notes](https://github.com/pezkuwichain/pezkuwi-sdk/releases).
|
||||
|
||||
```bash
|
||||
# Specify the specific nightly toolchain in the date below:
|
||||
rustup install nightly-<yyyy-MM-dd>
|
||||
```
|
||||
|
||||
#### Wasm toolchain
|
||||
|
||||
Now, configure the nightly version to work with the Wasm compilation target:
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-unknown --toolchain nightly-<yyyy-MM-dd>
|
||||
```
|
||||
|
||||
### Specifying nightly version
|
||||
|
||||
Use the `WASM_BUILD_TOOLCHAIN` environment variable to specify the Rust nightly version a Substrate project should use
|
||||
for Wasm compilation:
|
||||
|
||||
```bash
|
||||
WASM_BUILD_TOOLCHAIN=nightly-<yyyy-MM-dd> cargo build --release
|
||||
```
|
||||
|
||||
> Note that this only builds _the runtime_ with the specified nightly. The rest of project will be compiled with **your
|
||||
> default toolchain**, i.e. the latest installed stable toolchain.
|
||||
|
||||
### Downgrading Rust nightly
|
||||
|
||||
If your computer is configured to use the latest Rust nightly and you would like to downgrade to a specific nightly
|
||||
version, follow these steps:
|
||||
|
||||
```bash
|
||||
rustup uninstall nightly
|
||||
rustup install nightly-<yyyy-MM-dd>
|
||||
rustup target add wasm32-unknown-unknown --toolchain nightly-<yyyy-MM-dd>
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
# Env setup
|
||||
|
||||
Special files for setting up an environment to work with the template:
|
||||
|
||||
- `rust-toolchain.toml` when working with `rustup`.
|
||||
- `flake.nix` when working with `nix`.
|
||||
|
||||
These files will be copied by the installer script to the main directory. They are
|
||||
put into this special directory to not interfere with the normal CI.
|
||||
Generated
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1678901627,
|
||||
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1679262748,
|
||||
"narHash": "sha256-DQCrrAFrkxijC6haUzOC5ZoFqpcv/tg2WxnyW3np1Cc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "60c1d71f2ba4c80178ec84523c2ca0801522e0a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
rustup
|
||||
clang
|
||||
protobuf
|
||||
];
|
||||
|
||||
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
components = [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"rust-analyzer",
|
||||
"rust-src",
|
||||
"rust-std",
|
||||
"rustc",
|
||||
"rustc-dev",
|
||||
"rustfmt",
|
||||
]
|
||||
targets = ["wasm32-unknown-unknown"]
|
||||
profile = "minimal"
|
||||
@@ -0,0 +1,111 @@
|
||||
[package]
|
||||
name = "solochain-template-node"
|
||||
description = "A solochain node template built with Substrate, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
build = "build.rs"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
futures = { features = ["thread-pool"], workspace = true }
|
||||
jsonrpsee = { features = ["server"], workspace = true }
|
||||
|
||||
# substrate client
|
||||
sc-basic-authorship = { workspace = true, default-features = true }
|
||||
sc-cli = { workspace = true, default-features = true }
|
||||
sc-client-api = { workspace = true, default-features = true }
|
||||
sc-consensus = { workspace = true, default-features = true }
|
||||
sc-consensus-aura = { workspace = true, default-features = true }
|
||||
sc-consensus-grandpa = { workspace = true, default-features = true }
|
||||
sc-executor = { workspace = true, default-features = true }
|
||||
sc-network = { workspace = true, default-features = true }
|
||||
sc-offchain = { workspace = true, default-features = true }
|
||||
sc-service = { workspace = true, default-features = true }
|
||||
sc-telemetry = { workspace = true, default-features = true }
|
||||
sc-transaction-pool = { workspace = true, default-features = true }
|
||||
sc-transaction-pool-api = { workspace = true, default-features = true }
|
||||
sp-consensus-aura = { workspace = true, default-features = true }
|
||||
sp-core = { workspace = true, default-features = true }
|
||||
sp-genesis-builder = { workspace = true, default-features = true }
|
||||
|
||||
# substrate primitives
|
||||
sp-api = { workspace = true, default-features = true }
|
||||
sp-block-builder = { workspace = true, default-features = true }
|
||||
sp-blockchain = { workspace = true, default-features = true }
|
||||
sp-inherents = { workspace = true, default-features = true }
|
||||
sp-io = { workspace = true, default-features = true }
|
||||
sp-keyring = { workspace = true, default-features = true }
|
||||
sp-runtime = { workspace = true, default-features = true }
|
||||
sp-timestamp = { workspace = true, default-features = true }
|
||||
|
||||
# frame and pallets
|
||||
frame-metadata-hash-extension = { workspace = true, default-features = true }
|
||||
frame-system = { workspace = true, default-features = true }
|
||||
pallet-transaction-payment = { workspace = true, default-features = true }
|
||||
pallet-transaction-payment-rpc = { workspace = true, default-features = true }
|
||||
substrate-frame-rpc-system = { workspace = true, default-features = true }
|
||||
|
||||
# These dependencies are used for runtime benchmarking
|
||||
frame-benchmarking-cli = { workspace = true, default-features = true }
|
||||
|
||||
# Local Dependencies
|
||||
solochain-template-runtime = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
substrate-build-script-utils = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["solochain-template-runtime/std"]
|
||||
# Dependencies that are only required if runtime benchmarking should be build.
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking-cli/runtime-benchmarks",
|
||||
"frame-metadata-hash-extension/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-transaction-payment-rpc/runtime-benchmarks",
|
||||
"pallet-transaction-payment/runtime-benchmarks",
|
||||
"sc-basic-authorship/runtime-benchmarks",
|
||||
"sc-cli/runtime-benchmarks",
|
||||
"sc-client-api/runtime-benchmarks",
|
||||
"sc-consensus-aura/runtime-benchmarks",
|
||||
"sc-consensus-grandpa/runtime-benchmarks",
|
||||
"sc-consensus/runtime-benchmarks",
|
||||
"sc-executor/runtime-benchmarks",
|
||||
"sc-network/runtime-benchmarks",
|
||||
"sc-offchain/runtime-benchmarks",
|
||||
"sc-service/runtime-benchmarks",
|
||||
"sc-transaction-pool-api/runtime-benchmarks",
|
||||
"sc-transaction-pool/runtime-benchmarks",
|
||||
"solochain-template-runtime/runtime-benchmarks",
|
||||
"sp-api/runtime-benchmarks",
|
||||
"sp-block-builder/runtime-benchmarks",
|
||||
"sp-blockchain/runtime-benchmarks",
|
||||
"sp-consensus-aura/runtime-benchmarks",
|
||||
"sp-genesis-builder/runtime-benchmarks",
|
||||
"sp-inherents/runtime-benchmarks",
|
||||
"sp-io/runtime-benchmarks",
|
||||
"sp-keyring/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"sp-timestamp/runtime-benchmarks",
|
||||
"substrate-frame-rpc-system/runtime-benchmarks",
|
||||
]
|
||||
# Enable features that allow the runtime to be tried and debugged. Name might be subject to change
|
||||
# in the near future.
|
||||
try-runtime = [
|
||||
"frame-system/try-runtime",
|
||||
"pallet-transaction-payment/try-runtime",
|
||||
"solochain-template-runtime/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
|
||||
|
||||
fn main() {
|
||||
generate_cargo_keys();
|
||||
|
||||
rerun_if_git_head_changed();
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
//! Setup code for [`super::command`] which would otherwise bloat that module.
|
||||
//!
|
||||
//! Should only be used for benchmarking as it may break in other contexts.
|
||||
|
||||
use crate::service::FullClient;
|
||||
|
||||
use runtime::{AccountId, Balance, BalancesCall, SystemCall};
|
||||
use sc_cli::Result;
|
||||
use sc_client_api::BlockBackend;
|
||||
use solochain_template_runtime as runtime;
|
||||
use sp_core::{Encode, Pair};
|
||||
use sp_inherents::{InherentData, InherentDataProvider};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use sp_runtime::{OpaqueExtrinsic, SaturatedConversion};
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
/// Generates extrinsics for the `benchmark overhead` command.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub struct RemarkBuilder {
|
||||
client: Arc<FullClient>,
|
||||
}
|
||||
|
||||
impl RemarkBuilder {
|
||||
/// Creates a new [`Self`] from the given client.
|
||||
pub fn new(client: Arc<FullClient>) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder {
|
||||
fn pallet(&self) -> &str {
|
||||
"system"
|
||||
}
|
||||
|
||||
fn extrinsic(&self) -> &str {
|
||||
"remark"
|
||||
}
|
||||
|
||||
fn build(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
|
||||
let acc = Sr25519Keyring::Bob.pair();
|
||||
let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic(
|
||||
self.client.as_ref(),
|
||||
acc,
|
||||
SystemCall::remark { remark: vec![] }.into(),
|
||||
nonce,
|
||||
)
|
||||
.into();
|
||||
|
||||
Ok(extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates `Balances::TransferKeepAlive` extrinsics for the benchmarks.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub struct TransferKeepAliveBuilder {
|
||||
client: Arc<FullClient>,
|
||||
dest: AccountId,
|
||||
value: Balance,
|
||||
}
|
||||
|
||||
impl TransferKeepAliveBuilder {
|
||||
/// Creates a new [`Self`] from the given client.
|
||||
pub fn new(client: Arc<FullClient>, dest: AccountId, value: Balance) -> Self {
|
||||
Self { client, dest, value }
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_benchmarking_cli::ExtrinsicBuilder for TransferKeepAliveBuilder {
|
||||
fn pallet(&self) -> &str {
|
||||
"balances"
|
||||
}
|
||||
|
||||
fn extrinsic(&self) -> &str {
|
||||
"transfer_keep_alive"
|
||||
}
|
||||
|
||||
fn build(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
|
||||
let acc = Sr25519Keyring::Bob.pair();
|
||||
let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic(
|
||||
self.client.as_ref(),
|
||||
acc,
|
||||
BalancesCall::transfer_keep_alive { dest: self.dest.clone().into(), value: self.value }
|
||||
.into(),
|
||||
nonce,
|
||||
)
|
||||
.into();
|
||||
|
||||
Ok(extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transaction using the given `call`.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub fn create_benchmark_extrinsic(
|
||||
client: &FullClient,
|
||||
sender: sp_core::sr25519::Pair,
|
||||
call: runtime::RuntimeCall,
|
||||
nonce: u32,
|
||||
) -> runtime::UncheckedExtrinsic {
|
||||
let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed");
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
let best_block = client.chain_info().best_number;
|
||||
|
||||
let period = runtime::configs::BlockHashCount::get()
|
||||
.checked_next_power_of_two()
|
||||
.map(|c| c / 2)
|
||||
.unwrap_or(2) as u64;
|
||||
let tx_ext: runtime::TxExtension = (
|
||||
frame_system::AuthorizeCall::<runtime::Runtime>::new(),
|
||||
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
|
||||
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
|
||||
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
|
||||
frame_system::CheckGenesis::<runtime::Runtime>::new(),
|
||||
frame_system::CheckEra::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
|
||||
period,
|
||||
best_block.saturated_into(),
|
||||
)),
|
||||
frame_system::CheckNonce::<runtime::Runtime>::from(nonce),
|
||||
frame_system::CheckWeight::<runtime::Runtime>::new(),
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
|
||||
frame_metadata_hash_extension::CheckMetadataHash::<runtime::Runtime>::new(false),
|
||||
frame_system::WeightReclaim::<runtime::Runtime>::new(),
|
||||
);
|
||||
|
||||
let raw_payload = runtime::SignedPayload::from_raw(
|
||||
call.clone(),
|
||||
tx_ext.clone(),
|
||||
(
|
||||
(),
|
||||
(),
|
||||
runtime::VERSION.spec_version,
|
||||
runtime::VERSION.transaction_version,
|
||||
genesis_hash,
|
||||
best_hash,
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
None,
|
||||
(),
|
||||
),
|
||||
);
|
||||
let signature = raw_payload.using_encoded(|e| sender.sign(e));
|
||||
|
||||
runtime::UncheckedExtrinsic::new_signed(
|
||||
call,
|
||||
sp_runtime::AccountId32::from(sender.public()).into(),
|
||||
runtime::Signature::Sr25519(signature),
|
||||
tx_ext,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates inherent data for the `benchmark overhead` command.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub fn inherent_benchmark_data() -> Result<InherentData> {
|
||||
let mut inherent_data = InherentData::new();
|
||||
let d = Duration::from_millis(0);
|
||||
let timestamp = sp_timestamp::InherentDataProvider::new(d.into());
|
||||
|
||||
futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data))
|
||||
.map_err(|e| format!("creating inherent data: {e:?}"))?;
|
||||
Ok(inherent_data)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
use sc_service::ChainType;
|
||||
use solochain_template_runtime::WASM_BINARY;
|
||||
|
||||
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
|
||||
pub type ChainSpec = sc_service::GenericChainSpec;
|
||||
|
||||
pub fn development_chain_spec() -> Result<ChainSpec, String> {
|
||||
Ok(ChainSpec::builder(
|
||||
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
|
||||
None,
|
||||
)
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET)
|
||||
.build())
|
||||
}
|
||||
|
||||
pub fn local_chain_spec() -> Result<ChainSpec, String> {
|
||||
Ok(ChainSpec::builder(
|
||||
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
|
||||
None,
|
||||
)
|
||||
.with_name("Local Testnet")
|
||||
.with_id("local_testnet")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
|
||||
.build())
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub run: sc_cli::RunCmd,
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Subcommand {
|
||||
/// Key management cli utilities
|
||||
#[command(subcommand)]
|
||||
Key(sc_cli::KeySubcommand),
|
||||
|
||||
/// Build a chain specification.
|
||||
/// DEPRECATED: `build-spec` command will be removed after 1/04/2026. Use `export-chain-spec`
|
||||
/// command instead.
|
||||
#[deprecated(
|
||||
note = "build-spec command will be removed after 1/04/2026. Use export-chain-spec command instead"
|
||||
)]
|
||||
BuildSpec(sc_cli::BuildSpecCmd),
|
||||
|
||||
/// Export the chain specification.
|
||||
ExportChainSpec(sc_cli::ExportChainSpecCmd),
|
||||
|
||||
/// Validate blocks.
|
||||
CheckBlock(sc_cli::CheckBlockCmd),
|
||||
|
||||
/// Export blocks.
|
||||
ExportBlocks(sc_cli::ExportBlocksCmd),
|
||||
|
||||
/// Export the state of a given block into a chain spec.
|
||||
ExportState(sc_cli::ExportStateCmd),
|
||||
|
||||
/// Import blocks.
|
||||
ImportBlocks(sc_cli::ImportBlocksCmd),
|
||||
|
||||
/// Remove the whole chain.
|
||||
PurgeChain(sc_cli::PurgeChainCmd),
|
||||
|
||||
/// Revert the chain to a previous state.
|
||||
Revert(sc_cli::RevertCmd),
|
||||
|
||||
/// Sub-commands concerned with benchmarking.
|
||||
#[command(subcommand)]
|
||||
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
|
||||
|
||||
/// Db meta columns information.
|
||||
ChainInfo(sc_cli::ChainInfoCmd),
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
use crate::{
|
||||
benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder},
|
||||
chain_spec,
|
||||
cli::{Cli, Subcommand},
|
||||
service,
|
||||
};
|
||||
use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE};
|
||||
use sc_cli::SubstrateCli;
|
||||
use sc_service::PartialComponents;
|
||||
use solochain_template_runtime::{Block, EXISTENTIAL_DEPOSIT};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
impl SubstrateCli for Cli {
|
||||
fn impl_name() -> String {
|
||||
"Substrate Node".into()
|
||||
}
|
||||
|
||||
fn impl_version() -> String {
|
||||
env!("SUBSTRATE_CLI_IMPL_VERSION").into()
|
||||
}
|
||||
|
||||
fn description() -> String {
|
||||
env!("CARGO_PKG_DESCRIPTION").into()
|
||||
}
|
||||
|
||||
fn author() -> String {
|
||||
env!("CARGO_PKG_AUTHORS").into()
|
||||
}
|
||||
|
||||
fn support_url() -> String {
|
||||
"support.anonymous.an".into()
|
||||
}
|
||||
|
||||
fn copyright_start_year() -> i32 {
|
||||
2017
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
|
||||
Ok(match id {
|
||||
"dev" => Box::new(chain_spec::development_chain_spec()?),
|
||||
"" | "local" => Box::new(chain_spec::local_chain_spec()?),
|
||||
path =>
|
||||
Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse and run command line arguments
|
||||
pub fn run() -> sc_cli::Result<()> {
|
||||
let cli = Cli::from_args();
|
||||
|
||||
match &cli.subcommand {
|
||||
Some(Subcommand::Key(cmd)) => cmd.run(&cli),
|
||||
#[allow(deprecated)]
|
||||
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)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportChainSpec(cmd)) => {
|
||||
let chain_spec = cli.load_spec(&cmd.chain)?;
|
||||
cmd.run(chain_spec)
|
||||
},
|
||||
Some(Subcommand::ExportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, config.database), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportState(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, config.chain_spec), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ImportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
service::new_partial(&config)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::PurgeChain(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run(config.database))
|
||||
},
|
||||
Some(Subcommand::Revert(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, backend, .. } =
|
||||
service::new_partial(&config)?;
|
||||
let aux_revert = Box::new(|client, _, blocks| {
|
||||
sc_consensus_grandpa::revert(client, blocks)?;
|
||||
Ok(())
|
||||
});
|
||||
Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::Benchmark(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
|
||||
runner.sync_run(|config| {
|
||||
// This switch needs to be in the client, since the client decides
|
||||
// which sub-commands it wants to support.
|
||||
match cmd {
|
||||
BenchmarkCmd::Pallet(cmd) => {
|
||||
if !cfg!(feature = "runtime-benchmarks") {
|
||||
return Err(
|
||||
"Runtime benchmarking wasn't enabled when building the node. \
|
||||
You can enable it with `--features runtime-benchmarks`."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
cmd.run_with_spec::<sp_runtime::traits::HashingFor<Block>, ()>(Some(
|
||||
config.chain_spec,
|
||||
))
|
||||
},
|
||||
BenchmarkCmd::Block(cmd) => {
|
||||
let PartialComponents { client, .. } = service::new_partial(&config)?;
|
||||
cmd.run(client)
|
||||
},
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
BenchmarkCmd::Storage(_) => Err(
|
||||
"Storage benchmarking can be enabled with `--features runtime-benchmarks`."
|
||||
.into(),
|
||||
),
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
BenchmarkCmd::Storage(cmd) => {
|
||||
let PartialComponents { client, backend, .. } =
|
||||
service::new_partial(&config)?;
|
||||
let db = backend.expose_db();
|
||||
let storage = backend.expose_storage();
|
||||
let shared_cache = backend.expose_shared_trie_cache();
|
||||
|
||||
cmd.run(config, client, db, storage, shared_cache)
|
||||
},
|
||||
BenchmarkCmd::Overhead(cmd) => {
|
||||
let PartialComponents { client, .. } = service::new_partial(&config)?;
|
||||
let ext_builder = RemarkBuilder::new(client.clone());
|
||||
|
||||
cmd.run(
|
||||
config.chain_spec.name().into(),
|
||||
client,
|
||||
inherent_benchmark_data()?,
|
||||
Vec::new(),
|
||||
&ext_builder,
|
||||
false,
|
||||
)
|
||||
},
|
||||
BenchmarkCmd::Extrinsic(cmd) => {
|
||||
let PartialComponents { client, .. } = service::new_partial(&config)?;
|
||||
// Register the *Remark* and *TKA* builders.
|
||||
let ext_factory = ExtrinsicFactory(vec![
|
||||
Box::new(RemarkBuilder::new(client.clone())),
|
||||
Box::new(TransferKeepAliveBuilder::new(
|
||||
client.clone(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
EXISTENTIAL_DEPOSIT,
|
||||
)),
|
||||
]);
|
||||
|
||||
cmd.run(client, inherent_benchmark_data()?, Vec::new(), &ext_factory)
|
||||
},
|
||||
BenchmarkCmd::Machine(cmd) =>
|
||||
cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()),
|
||||
}
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ChainInfo(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run::<Block>(&config))
|
||||
},
|
||||
None => {
|
||||
let runner = cli.create_runner(&cli.run)?;
|
||||
runner.run_node_until_exit(|config| async move {
|
||||
match config.network.network_backend {
|
||||
sc_network::config::NetworkBackendType::Libp2p => service::new_full::<
|
||||
sc_network::NetworkWorker<
|
||||
solochain_template_runtime::opaque::Block,
|
||||
<solochain_template_runtime::opaque::Block as sp_runtime::traits::Block>::Hash,
|
||||
>,
|
||||
>(config)
|
||||
.map_err(sc_cli::Error::Service),
|
||||
sc_network::config::NetworkBackendType::Litep2p =>
|
||||
service::new_full::<sc_network::Litep2pNetworkBackend>(config)
|
||||
.map_err(sc_cli::Error::Service),
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//! Substrate Node Template CLI library.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod benchmarking;
|
||||
mod chain_spec;
|
||||
mod cli;
|
||||
mod command;
|
||||
mod rpc;
|
||||
mod service;
|
||||
|
||||
fn main() -> sc_cli::Result<()> {
|
||||
command::run()
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//! A collection of node-specific RPC methods.
|
||||
//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
|
||||
//! used by Substrate nodes. This file extends those RPC definitions with
|
||||
//! capabilities that are specific to this project's runtime configuration.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use jsonrpsee::RpcModule;
|
||||
use sc_transaction_pool_api::TransactionPool;
|
||||
use solochain_template_runtime::{opaque::Block, AccountId, Balance, Nonce};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_block_builder::BlockBuilder;
|
||||
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
|
||||
|
||||
/// Full client dependencies.
|
||||
pub struct FullDeps<C, P> {
|
||||
/// The client instance to use.
|
||||
pub client: Arc<C>,
|
||||
/// Transaction pool instance.
|
||||
pub pool: Arc<P>,
|
||||
}
|
||||
|
||||
/// Instantiate all full RPC extensions.
|
||||
pub fn create_full<C, P>(
|
||||
deps: FullDeps<C, P>,
|
||||
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
|
||||
where
|
||||
C: ProvideRuntimeApi<Block>,
|
||||
C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
|
||||
C: Send + Sync + 'static,
|
||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
|
||||
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
|
||||
C::Api: BlockBuilder<Block>,
|
||||
P: TransactionPool + 'static,
|
||||
{
|
||||
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
|
||||
use substrate_frame_rpc_system::{System, SystemApiServer};
|
||||
|
||||
let mut module = RpcModule::new(());
|
||||
let FullDeps { client, pool } = deps;
|
||||
|
||||
module.merge(System::new(client.clone(), pool).into_rpc())?;
|
||||
module.merge(TransactionPayment::new(client).into_rpc())?;
|
||||
|
||||
// Extend this RPC with a custom API by using the following syntax.
|
||||
// `YourRpcStruct` should have a reference to a client, which is needed
|
||||
// to call into the runtime.
|
||||
// `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;`
|
||||
|
||||
// You probably want to enable the `rpc v2 chainSpec` API as well
|
||||
//
|
||||
// let chain_name = chain_spec.name().to_string();
|
||||
// let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed");
|
||||
// let properties = chain_spec.properties();
|
||||
// module.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
|
||||
|
||||
use futures::FutureExt;
|
||||
use sc_client_api::{Backend, BlockBackend};
|
||||
use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams};
|
||||
use sc_consensus_grandpa::SharedVoterState;
|
||||
use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncConfig};
|
||||
use sc_telemetry::{Telemetry, TelemetryWorker};
|
||||
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
|
||||
use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block};
|
||||
use sp_consensus_aura::sr25519::AuthorityPair as AuraPair;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
pub(crate) type FullClient = sc_service::TFullClient<
|
||||
Block,
|
||||
RuntimeApi,
|
||||
sc_executor::WasmExecutor<sp_io::SubstrateHostFunctions>,
|
||||
>;
|
||||
type FullBackend = sc_service::TFullBackend<Block>;
|
||||
type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
|
||||
|
||||
/// The minimum period of blocks on which justifications will be
|
||||
/// imported and generated.
|
||||
const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512;
|
||||
|
||||
pub type Service = sc_service::PartialComponents<
|
||||
FullClient,
|
||||
FullBackend,
|
||||
FullSelectChain,
|
||||
sc_consensus::DefaultImportQueue<Block>,
|
||||
sc_transaction_pool::TransactionPoolHandle<Block, FullClient>,
|
||||
(
|
||||
sc_consensus_grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, FullSelectChain>,
|
||||
sc_consensus_grandpa::LinkHalf<Block, FullClient, FullSelectChain>,
|
||||
Option<Telemetry>,
|
||||
),
|
||||
>;
|
||||
|
||||
pub fn new_partial(config: &Configuration) -> Result<Service, ServiceError> {
|
||||
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 = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(&config.executor);
|
||||
let (client, backend, keystore_container, task_manager) =
|
||||
sc_service::new_full_parts::<Block, RuntimeApi, _>(
|
||||
config,
|
||||
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
|
||||
executor,
|
||||
)?;
|
||||
let client = Arc::new(client);
|
||||
|
||||
let telemetry = telemetry.map(|(worker, telemetry)| {
|
||||
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
|
||||
telemetry
|
||||
});
|
||||
|
||||
let select_chain = sc_consensus::LongestChain::new(backend.clone());
|
||||
|
||||
let transaction_pool = Arc::from(
|
||||
sc_transaction_pool::Builder::new(
|
||||
task_manager.spawn_essential_handle(),
|
||||
client.clone(),
|
||||
config.role.is_authority().into(),
|
||||
)
|
||||
.with_options(config.transaction_pool.clone())
|
||||
.with_prometheus(config.prometheus_registry())
|
||||
.build(),
|
||||
);
|
||||
|
||||
let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import(
|
||||
client.clone(),
|
||||
GRANDPA_JUSTIFICATION_PERIOD,
|
||||
&client,
|
||||
select_chain.clone(),
|
||||
telemetry.as_ref().map(|x| x.handle()),
|
||||
)?;
|
||||
|
||||
let cidp_client = client.clone();
|
||||
let import_queue =
|
||||
sc_consensus_aura::import_queue::<AuraPair, _, _, _, _, _>(ImportQueueParams {
|
||||
block_import: grandpa_block_import.clone(),
|
||||
justification_import: Some(Box::new(grandpa_block_import.clone())),
|
||||
client: client.clone(),
|
||||
create_inherent_data_providers: move |parent_hash, _| {
|
||||
let cidp_client = cidp_client.clone();
|
||||
async move {
|
||||
let slot_duration = sc_consensus_aura::standalone::slot_duration_at(
|
||||
&*cidp_client,
|
||||
parent_hash,
|
||||
)?;
|
||||
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))
|
||||
}
|
||||
},
|
||||
spawner: &task_manager.spawn_essential_handle(),
|
||||
registry: config.prometheus_registry(),
|
||||
check_for_equivocation: Default::default(),
|
||||
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
||||
compatibility_mode: Default::default(),
|
||||
})?;
|
||||
|
||||
Ok(sc_service::PartialComponents {
|
||||
client,
|
||||
backend,
|
||||
task_manager,
|
||||
import_queue,
|
||||
keystore_container,
|
||||
select_chain,
|
||||
transaction_pool,
|
||||
other: (grandpa_block_import, grandpa_link, telemetry),
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a new service for a full client.
|
||||
pub fn new_full<
|
||||
N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>,
|
||||
>(
|
||||
config: Configuration,
|
||||
) -> Result<TaskManager, ServiceError> {
|
||||
let sc_service::PartialComponents {
|
||||
client,
|
||||
backend,
|
||||
mut task_manager,
|
||||
import_queue,
|
||||
keystore_container,
|
||||
select_chain,
|
||||
transaction_pool,
|
||||
other: (block_import, grandpa_link, mut telemetry),
|
||||
} = new_partial(&config)?;
|
||||
|
||||
let mut net_config = sc_network::config::FullNetworkConfiguration::<
|
||||
Block,
|
||||
<Block as sp_runtime::traits::Block>::Hash,
|
||||
N,
|
||||
>::new(&config.network, config.prometheus_registry().cloned());
|
||||
let metrics = N::register_notification_metrics(config.prometheus_registry());
|
||||
|
||||
let peer_store_handle = net_config.peer_store_handle();
|
||||
let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name(
|
||||
&client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"),
|
||||
&config.chain_spec,
|
||||
);
|
||||
let (grandpa_protocol_config, grandpa_notification_service) =
|
||||
sc_consensus_grandpa::grandpa_peers_set_config::<_, N>(
|
||||
grandpa_protocol_name.clone(),
|
||||
metrics.clone(),
|
||||
peer_store_handle,
|
||||
);
|
||||
net_config.add_notification_protocol(grandpa_protocol_config);
|
||||
|
||||
let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new(
|
||||
backend.clone(),
|
||||
grandpa_link.shared_authority_set().clone(),
|
||||
Vec::default(),
|
||||
));
|
||||
|
||||
let (network, system_rpc_tx, tx_handler_controller, sync_service) =
|
||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||
config: &config,
|
||||
net_config,
|
||||
client: client.clone(),
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
spawn_handle: task_manager.spawn_handle(),
|
||||
import_queue,
|
||||
block_announce_validator_builder: None,
|
||||
warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)),
|
||||
block_relay: None,
|
||||
metrics,
|
||||
})?;
|
||||
|
||||
if config.offchain_worker.enabled {
|
||||
let offchain_workers =
|
||||
sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
|
||||
runtime_api_provider: client.clone(),
|
||||
is_validator: config.role.is_authority(),
|
||||
keystore: Some(keystore_container.keystore()),
|
||||
offchain_db: backend.offchain_storage(),
|
||||
transaction_pool: Some(OffchainTransactionPoolFactory::new(
|
||||
transaction_pool.clone(),
|
||||
)),
|
||||
network_provider: Arc::new(network.clone()),
|
||||
enable_http_requests: true,
|
||||
custom_extensions: |_| vec![],
|
||||
})?;
|
||||
task_manager.spawn_handle().spawn(
|
||||
"offchain-workers-runner",
|
||||
"offchain-worker",
|
||||
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
let role = config.role;
|
||||
let force_authoring = config.force_authoring;
|
||||
let backoff_authoring_blocks: Option<()> = None;
|
||||
let name = config.network.node_name.clone();
|
||||
let enable_grandpa = !config.disable_grandpa;
|
||||
let prometheus_registry = config.prometheus_registry().cloned();
|
||||
|
||||
let rpc_extensions_builder = {
|
||||
let client = client.clone();
|
||||
let pool = transaction_pool.clone();
|
||||
|
||||
Box::new(move |_| {
|
||||
let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() };
|
||||
crate::rpc::create_full(deps).map_err(Into::into)
|
||||
})
|
||||
};
|
||||
|
||||
let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams {
|
||||
network: Arc::new(network.clone()),
|
||||
client: client.clone(),
|
||||
keystore: keystore_container.keystore(),
|
||||
task_manager: &mut task_manager,
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
rpc_builder: rpc_extensions_builder,
|
||||
backend,
|
||||
system_rpc_tx,
|
||||
tx_handler_controller,
|
||||
sync_service: sync_service.clone(),
|
||||
config,
|
||||
telemetry: telemetry.as_mut(),
|
||||
tracing_execute_block: None,
|
||||
})?;
|
||||
|
||||
if role.is_authority() {
|
||||
let proposer_factory = sc_basic_authorship::ProposerFactory::new(
|
||||
task_manager.spawn_handle(),
|
||||
client.clone(),
|
||||
transaction_pool.clone(),
|
||||
prometheus_registry.as_ref(),
|
||||
telemetry.as_ref().map(|x| x.handle()),
|
||||
);
|
||||
|
||||
let slot_duration = sc_consensus_aura::slot_duration(&*client)?;
|
||||
|
||||
let aura = sc_consensus_aura::start_aura::<AuraPair, _, _, _, _, _, _, _, _, _, _>(
|
||||
StartAuraParams {
|
||||
slot_duration,
|
||||
client,
|
||||
select_chain,
|
||||
block_import,
|
||||
proposer_factory,
|
||||
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))
|
||||
},
|
||||
force_authoring,
|
||||
backoff_authoring_blocks,
|
||||
keystore: keystore_container.keystore(),
|
||||
sync_oracle: sync_service.clone(),
|
||||
justification_sync_link: sync_service.clone(),
|
||||
block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32),
|
||||
max_block_proposal_slot_portion: None,
|
||||
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
||||
compatibility_mode: Default::default(),
|
||||
},
|
||||
)?;
|
||||
|
||||
// the AURA authoring task is considered essential, i.e. if it
|
||||
// fails we take down the service with it.
|
||||
task_manager
|
||||
.spawn_essential_handle()
|
||||
.spawn_blocking("aura", Some("block-authoring"), aura);
|
||||
}
|
||||
|
||||
if enable_grandpa {
|
||||
// if the node isn't actively participating in consensus then it doesn't
|
||||
// need a keystore, regardless of which protocol we use below.
|
||||
let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None };
|
||||
|
||||
let grandpa_config = sc_consensus_grandpa::Config {
|
||||
// FIXME #1578 make this available through chainspec
|
||||
gossip_duration: Duration::from_millis(333),
|
||||
justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD,
|
||||
name: Some(name),
|
||||
observer_enabled: false,
|
||||
keystore,
|
||||
local_role: role,
|
||||
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
||||
protocol_name: grandpa_protocol_name,
|
||||
};
|
||||
|
||||
// start the full GRANDPA voter
|
||||
// NOTE: non-authorities could run the GRANDPA observer protocol, but at
|
||||
// this point the full voter should provide better guarantees of block
|
||||
// and vote data availability than the observer. The observer has not
|
||||
// been tested extensively yet and having most nodes in a network run it
|
||||
// could lead to finality stalls.
|
||||
let grandpa_config = sc_consensus_grandpa::GrandpaParams {
|
||||
config: grandpa_config,
|
||||
link: grandpa_link,
|
||||
network,
|
||||
sync: Arc::new(sync_service),
|
||||
notification_service: grandpa_notification_service,
|
||||
voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(),
|
||||
prometheus_registry,
|
||||
shared_voter_state: SharedVoterState::empty(),
|
||||
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
||||
offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool),
|
||||
};
|
||||
|
||||
// the GRANDPA voter task is considered infallible, i.e.
|
||||
// if it fails we take down the service with it.
|
||||
task_manager.spawn_essential_handle().spawn_blocking(
|
||||
"grandpa-voter",
|
||||
None,
|
||||
sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(task_manager)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
[package]
|
||||
name = "pallet-template"
|
||||
description = "FRAME pallet template for defining custom runtime logic."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
|
||||
# frame deps
|
||||
frame-benchmarking = { optional = true, workspace = true }
|
||||
frame-support = { workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-core = { workspace = true, default-features = true }
|
||||
sp-io = { workspace = true, default-features = true }
|
||||
sp-runtime = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-benchmarking?/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"scale-info/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"sp-io/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
License: MIT-0
|
||||
@@ -0,0 +1,35 @@
|
||||
//! Benchmarking setup for pallet-template
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::Pallet as Template;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn do_something() {
|
||||
let value = 100u32;
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
#[extrinsic_call]
|
||||
do_something(RawOrigin::Signed(caller), value);
|
||||
|
||||
assert_eq!(Something::<T>::get(), Some(value));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn cause_error() {
|
||||
Something::<T>::put(100u32);
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
#[extrinsic_call]
|
||||
cause_error(RawOrigin::Signed(caller));
|
||||
|
||||
assert_eq!(Something::<T>::get(), Some(101u32));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
//! # Template Pallet
|
||||
//!
|
||||
//! A pallet with minimal functionality to help developers understand the essential components of
|
||||
//! writing a FRAME pallet. It is typically used in beginner tutorials or in Substrate template
|
||||
//! nodes as a starting point for creating a new pallet and **not meant to be used in production**.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This template pallet contains basic examples of:
|
||||
//! - declaring a storage item that stores a single `u32` value
|
||||
//! - declaring and using events
|
||||
//! - declaring and using errors
|
||||
//! - a dispatchable function that allows a user to set a new value to storage and emits an event
|
||||
//! upon success
|
||||
//! - another dispatchable function that causes a custom error to be thrown
|
||||
//!
|
||||
//! Each pallet section is annotated with an attribute using the `#[pallet::...]` procedural macro.
|
||||
//! This macro generates the necessary code for a pallet to be aggregated into a FRAME runtime.
|
||||
//!
|
||||
//! Learn more about FRAME macros [here](https://docs.pezkuwichain.io/reference/frame-macros/).
|
||||
//!
|
||||
//! ### Pallet Sections
|
||||
//!
|
||||
//! The pallet sections in this template are:
|
||||
//!
|
||||
//! - A **configuration trait** that defines the types and parameters which the pallet depends on
|
||||
//! (denoted by the `#[pallet::config]` attribute). See: [`Config`].
|
||||
//! - A **means to store pallet-specific data** (denoted by the `#[pallet::storage]` attribute).
|
||||
//! See: [`storage_types`].
|
||||
//! - A **declaration of the events** this pallet emits (denoted by the `#[pallet::event]`
|
||||
//! attribute). See: [`Event`].
|
||||
//! - A **declaration of the errors** that this pallet can throw (denoted by the `#[pallet::error]`
|
||||
//! attribute). See: [`Error`].
|
||||
//! - A **set of dispatchable functions** that define the pallet's functionality (denoted by the
|
||||
//! `#[pallet::call]` attribute). See: [`dispatchables`].
|
||||
//!
|
||||
//! Run `cargo doc --package pallet-template --open` to view this pallet's documentation.
|
||||
|
||||
// We make sure this pallet uses `no_std` for compiling to Wasm.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
// Re-export pallet items so that they can be accessed from the crate namespace.
|
||||
pub use pallet::*;
|
||||
|
||||
// FRAME pallets require their own "mock runtimes" to be able to run unit tests. This module
|
||||
// contains a mock runtime specific for testing this pallet's functionality.
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
// This module contains the unit tests for this pallet.
|
||||
// Learn about pallet unit testing here: https://docs.pezkuwichain.io/test/unit-testing/
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// Every callable function or "dispatchable" a pallet exposes must have weight values that correctly
|
||||
// estimate a dispatchable's execution time. The benchmarking module is used to calculate weights
|
||||
// for each dispatchable and generates this pallet's weight.rs file. Learn more about benchmarking here: https://docs.pezkuwichain.io/test/benchmark/
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
pub mod weights;
|
||||
pub use weights::*;
|
||||
|
||||
// All pallet logic is defined in its own module and must be annotated by the `pallet` attribute.
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
// Import various useful types required by all FRAME pallets.
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
// The `Pallet` struct serves as a placeholder to implement traits, methods and dispatchables
|
||||
// (`Call`s) in this pallet.
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// The pallet's configuration trait.
|
||||
///
|
||||
/// All our types and constants a pallet depends on must be declared here.
|
||||
/// These types are defined generically and made concrete when the pallet is declared in the
|
||||
/// `runtime/src/lib.rs` file of your chain.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching runtime event type.
|
||||
#[allow(deprecated)]
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
/// A type representing the weights required by the dispatchables of this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
/// A storage item for this pallet.
|
||||
///
|
||||
/// In this template, we are declaring a storage item called `Something` that stores a single
|
||||
/// `u32` value. Learn more about runtime storage here: <https://docs.pezkuwichain.io/build/runtime-storage/>
|
||||
#[pallet::storage]
|
||||
pub type Something<T> = StorageValue<_, u32>;
|
||||
|
||||
/// Events that functions in this pallet can emit.
|
||||
///
|
||||
/// Events are a simple means of indicating to the outside world (such as dApps, chain explorers
|
||||
/// or other users) that some notable update in the runtime has occurred. In a FRAME pallet, the
|
||||
/// documentation for each event field and its parameters is added to a node's metadata so it
|
||||
/// can be used by external interfaces or tools.
|
||||
///
|
||||
/// The `generate_deposit` macro generates a function on `Pallet` called `deposit_event` which
|
||||
/// will convert the event type of your pallet into `RuntimeEvent` (declared in the pallet's
|
||||
/// [`Config`] trait) and deposit it using [`frame_system::Pallet::deposit_event`].
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A user has successfully set a new value.
|
||||
SomethingStored {
|
||||
/// The new value set.
|
||||
something: u32,
|
||||
/// The account who set the new value.
|
||||
who: T::AccountId,
|
||||
},
|
||||
}
|
||||
|
||||
/// Errors that can be returned by this pallet.
|
||||
///
|
||||
/// Errors tell users that something went wrong so it's important that their naming is
|
||||
/// informative. Similar to events, error documentation is added to a node's metadata so it's
|
||||
/// equally important that they have helpful documentation associated with them.
|
||||
///
|
||||
/// This type of runtime error can be up to 4 bytes in size should you want to return additional
|
||||
/// information.
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// The value retrieved was `None` as no value was previously set.
|
||||
NoneValue,
|
||||
/// There was an attempt to increment the value in storage over `u32::MAX`.
|
||||
StorageOverflow,
|
||||
}
|
||||
|
||||
/// The pallet's dispatchable functions ([`Call`]s).
|
||||
///
|
||||
/// Dispatchable functions allows users to interact with the pallet and invoke state changes.
|
||||
/// These functions materialize as "extrinsics", which are often compared to transactions.
|
||||
/// They must always return a `DispatchResult` and be annotated with a weight and call index.
|
||||
///
|
||||
/// The [`call_index`] macro is used to explicitly
|
||||
/// define an index for calls in the [`Call`] enum. This is useful for pallets that may
|
||||
/// introduce new dispatchables over time. If the order of a dispatchable changes, its index
|
||||
/// will also change which will break backwards compatibility.
|
||||
///
|
||||
/// The [`weight`] macro is used to assign a weight to each call.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// An example dispatchable that takes a single u32 value as a parameter, writes the value
|
||||
/// to storage and emits an event.
|
||||
///
|
||||
/// It checks that the _origin_ for this call is _Signed_ and returns a dispatch
|
||||
/// error if it isn't. Learn more about origins here: <https://docs.pezkuwichain.io/build/origins/>
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(T::WeightInfo::do_something())]
|
||||
pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult {
|
||||
// Check that the extrinsic was signed and get the signer.
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
// Update storage.
|
||||
Something::<T>::put(something);
|
||||
|
||||
// Emit an event.
|
||||
Self::deposit_event(Event::SomethingStored { something, who });
|
||||
|
||||
// Return a successful `DispatchResult`
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An example dispatchable that may throw a custom error.
|
||||
///
|
||||
/// It checks that the caller is a signed origin and reads the current value from the
|
||||
/// `Something` storage item. If a current value exists, it is incremented by 1 and then
|
||||
/// written back to storage.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// The function will return an error under the following conditions:
|
||||
///
|
||||
/// - If no value has been set ([`Error::NoneValue`])
|
||||
/// - If incrementing the value in storage causes an arithmetic overflow
|
||||
/// ([`Error::StorageOverflow`])
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight(T::WeightInfo::cause_error())]
|
||||
pub fn cause_error(origin: OriginFor<T>) -> DispatchResult {
|
||||
let _who = ensure_signed(origin)?;
|
||||
|
||||
// Read a value from storage.
|
||||
match Something::<T>::get() {
|
||||
// Return an error if the value has not been set.
|
||||
None => Err(Error::<T>::NoneValue.into()),
|
||||
Some(old) => {
|
||||
// Increment the value read from storage. This will cause an error in the event
|
||||
// of overflow.
|
||||
let new = old.checked_add(1).ok_or(Error::<T>::StorageOverflow)?;
|
||||
// Update the value in storage with the incremented result.
|
||||
Something::<T>::put(new);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use crate as pallet_template;
|
||||
use frame_support::derive_impl;
|
||||
use sp_runtime::BuildStorage;
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
#[frame_support::runtime]
|
||||
mod runtime {
|
||||
// The main runtime
|
||||
#[runtime::runtime]
|
||||
// Runtime Types to be generated
|
||||
#[runtime::derive(
|
||||
RuntimeCall,
|
||||
RuntimeEvent,
|
||||
RuntimeError,
|
||||
RuntimeOrigin,
|
||||
RuntimeFreezeReason,
|
||||
RuntimeHoldReason,
|
||||
RuntimeSlashReason,
|
||||
RuntimeLockId,
|
||||
RuntimeTask,
|
||||
RuntimeViewFunction
|
||||
)]
|
||||
pub struct Test;
|
||||
|
||||
#[runtime::pallet_index(0)]
|
||||
pub type System = frame_system::Pallet<Test>;
|
||||
|
||||
#[runtime::pallet_index(1)]
|
||||
pub type Template = pallet_template::Pallet<Test>;
|
||||
}
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
impl pallet_template::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
// Build genesis storage according to the mock runtime.
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
frame_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use crate::{mock::*, Error, Event, Something};
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
|
||||
#[test]
|
||||
fn it_works_for_default_value() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Go past genesis block so events get deposited
|
||||
System::set_block_number(1);
|
||||
// Dispatch a signed extrinsic.
|
||||
assert_ok!(Template::do_something(RuntimeOrigin::signed(1), 42));
|
||||
// Read pallet storage and assert an expected result.
|
||||
assert_eq!(Something::<Test>::get(), Some(42));
|
||||
// Assert that the correct event was deposited
|
||||
System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correct_error_for_none_value() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Ensure the expected error is thrown when no value is present.
|
||||
assert_noop!(Template::cause_error(RuntimeOrigin::signed(1)), Error::<Test>::NoneValue);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
//! Autogenerated weights for pallet_template
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `<UNKNOWN>`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// ../../target/release/node-template
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain
|
||||
// dev
|
||||
// --pallet
|
||||
// pallet_template
|
||||
// --extrinsic
|
||||
// *
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --wasm-execution=compiled
|
||||
// --output
|
||||
// pallets/template/src/weights.rs
|
||||
// --template
|
||||
// ../../.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_template.
|
||||
pub trait WeightInfo {
|
||||
fn do_something() -> Weight;
|
||||
fn cause_error() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_template using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
/// Storage: Template Something (r:0 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn do_something() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 8_000_000 picoseconds.
|
||||
Weight::from_parts(9_000_000, 0)
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: Template Something (r:1 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn cause_error() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `32`
|
||||
// Estimated: `1489`
|
||||
// Minimum execution time: 6_000_000 picoseconds.
|
||||
Weight::from_parts(6_000_000, 1489)
|
||||
.saturating_add(T::DbWeight::get().reads(1_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
/// Storage: Template Something (r:0 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn do_something() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 8_000_000 picoseconds.
|
||||
Weight::from_parts(9_000_000, 0)
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: Template Something (r:1 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn cause_error() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `32`
|
||||
// Estimated: `1489`
|
||||
// Minimum execution time: 6_000_000 picoseconds.
|
||||
Weight::from_parts(6_000_000, 1489)
|
||||
.saturating_add(RocksDbWeight::get().reads(1_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
[package]
|
||||
name = "solochain-template-runtime"
|
||||
description = "A solochain runtime template built with Substrate, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
scale-info = { features = ["derive", "serde"], workspace = true }
|
||||
serde_json = { workspace = true, default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
|
||||
# frame
|
||||
frame-executive = { workspace = true }
|
||||
frame-metadata-hash-extension = { workspace = true }
|
||||
frame-support = { features = ["experimental"], workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
frame-try-runtime = { optional = true, workspace = true }
|
||||
|
||||
# frame pallets
|
||||
pallet-aura = { workspace = true }
|
||||
pallet-balances = { workspace = true }
|
||||
pallet-grandpa = { workspace = true }
|
||||
pallet-sudo = { workspace = true }
|
||||
pallet-timestamp = { workspace = true }
|
||||
pallet-transaction-payment = { workspace = true }
|
||||
|
||||
# primitives
|
||||
sp-api = { workspace = true }
|
||||
sp-block-builder = { workspace = true }
|
||||
sp-consensus-aura = { features = ["serde"], workspace = true }
|
||||
sp-consensus-grandpa = { features = ["serde"], workspace = true }
|
||||
sp-core = { features = ["serde"], workspace = true }
|
||||
sp-genesis-builder = { workspace = true }
|
||||
sp-inherents = { workspace = true }
|
||||
sp-keyring = { workspace = true }
|
||||
sp-offchain = { workspace = true }
|
||||
sp-runtime = { features = ["serde"], workspace = true }
|
||||
sp-session = { workspace = true }
|
||||
sp-storage = { workspace = true }
|
||||
sp-transaction-pool = { workspace = true }
|
||||
sp-version = { features = ["serde"], workspace = true }
|
||||
|
||||
# RPC related
|
||||
frame-system-rpc-runtime-api = { workspace = true }
|
||||
pallet-transaction-payment-rpc-runtime-api = { workspace = true }
|
||||
|
||||
# Used for runtime benchmarking
|
||||
frame-benchmarking = { optional = true, workspace = true }
|
||||
frame-system-benchmarking = { optional = true, workspace = true }
|
||||
|
||||
# The pallet in this template.
|
||||
pallet-template = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-benchmarking?/std",
|
||||
"frame-executive/std",
|
||||
"frame-metadata-hash-extension/std",
|
||||
"frame-support/std",
|
||||
"frame-system-benchmarking?/std",
|
||||
"frame-system-rpc-runtime-api/std",
|
||||
"frame-system/std",
|
||||
"frame-try-runtime?/std",
|
||||
"pallet-aura/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-grandpa/std",
|
||||
"pallet-sudo/std",
|
||||
"pallet-template/std",
|
||||
"pallet-timestamp/std",
|
||||
"pallet-transaction-payment-rpc-runtime-api/std",
|
||||
"pallet-transaction-payment/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
"sp-api/std",
|
||||
"sp-block-builder/std",
|
||||
"sp-consensus-aura/std",
|
||||
"sp-consensus-grandpa/std",
|
||||
"sp-core/std",
|
||||
"sp-genesis-builder/std",
|
||||
"sp-inherents/std",
|
||||
"sp-keyring/std",
|
||||
"sp-offchain/std",
|
||||
"sp-runtime/std",
|
||||
"sp-session/std",
|
||||
"sp-storage/std",
|
||||
"sp-transaction-pool/std",
|
||||
"sp-version/std",
|
||||
"substrate-wasm-builder",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-executive/runtime-benchmarks",
|
||||
"frame-metadata-hash-extension/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system-benchmarking/runtime-benchmarks",
|
||||
"frame-system-rpc-runtime-api/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"frame-try-runtime?/runtime-benchmarks",
|
||||
"pallet-aura/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"pallet-grandpa/runtime-benchmarks",
|
||||
"pallet-sudo/runtime-benchmarks",
|
||||
"pallet-template/runtime-benchmarks",
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
"pallet-transaction-payment-rpc-runtime-api/runtime-benchmarks",
|
||||
"pallet-transaction-payment/runtime-benchmarks",
|
||||
"sp-api/runtime-benchmarks",
|
||||
"sp-block-builder/runtime-benchmarks",
|
||||
"sp-consensus-aura/runtime-benchmarks",
|
||||
"sp-consensus-grandpa/runtime-benchmarks",
|
||||
"sp-genesis-builder/runtime-benchmarks",
|
||||
"sp-inherents/runtime-benchmarks",
|
||||
"sp-keyring/runtime-benchmarks",
|
||||
"sp-offchain/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"sp-session/runtime-benchmarks",
|
||||
"sp-transaction-pool/runtime-benchmarks",
|
||||
"sp-version/runtime-benchmarks",
|
||||
"substrate-wasm-builder?/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-executive/try-runtime",
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"frame-try-runtime/try-runtime",
|
||||
"pallet-aura/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"pallet-grandpa/try-runtime",
|
||||
"pallet-sudo/try-runtime",
|
||||
"pallet-template/try-runtime",
|
||||
"pallet-timestamp/try-runtime",
|
||||
"pallet-transaction-payment/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
|
||||
# Enable the metadata hash generation.
|
||||
#
|
||||
# This is hidden behind a feature because it increases the compile time.
|
||||
# The wasm binary needs to be compiled twice, once to fetch the metadata,
|
||||
# generate the metadata hash and then a second time with the
|
||||
# `RUNTIME_METADATA_HASH` environment variable set for the `CheckMetadataHash`
|
||||
# extension.
|
||||
metadata-hash = ["substrate-wasm-builder/metadata-hash"]
|
||||
|
||||
# A convenience feature for enabling things when doing a build
|
||||
# for an on-chain release.
|
||||
on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"]
|
||||
@@ -0,0 +1,16 @@
|
||||
#[cfg(all(feature = "std", feature = "metadata-hash"))]
|
||||
fn main() {
|
||||
substrate_wasm_builder::WasmBuilder::init_with_defaults()
|
||||
.enable_metadata_hash("UNIT", 12)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(feature = "metadata-hash")))]
|
||||
fn main() {
|
||||
substrate_wasm_builder::WasmBuilder::build_using_defaults();
|
||||
}
|
||||
|
||||
/// The wasm builder is deactivated when compiling
|
||||
/// this crate for wasm to speed up the compilation.
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn main() {}
|
||||
@@ -0,0 +1,304 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
// External crates imports
|
||||
use alloc::vec::Vec;
|
||||
use frame_support::{
|
||||
genesis_builder_helper::{build_state, get_preset},
|
||||
weights::Weight,
|
||||
};
|
||||
use pallet_grandpa::AuthorityId as GrandpaId;
|
||||
use sp_api::impl_runtime_apis;
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, NumberFor},
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult,
|
||||
};
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
// Local module imports
|
||||
use super::{
|
||||
AccountId, Aura, Balance, Block, Executive, Grandpa, InherentDataExt, Nonce, Runtime,
|
||||
RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION,
|
||||
};
|
||||
|
||||
impl_runtime_apis! {
|
||||
impl sp_api::Core<Block> for Runtime {
|
||||
fn version() -> RuntimeVersion {
|
||||
VERSION
|
||||
}
|
||||
|
||||
fn execute_block(block: <Block as BlockT>::LazyBlock) {
|
||||
Executive::execute_block(block);
|
||||
}
|
||||
|
||||
fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
|
||||
Executive::initialize_block(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_api::Metadata<Block> for Runtime {
|
||||
fn metadata() -> OpaqueMetadata {
|
||||
OpaqueMetadata::new(Runtime::metadata().into())
|
||||
}
|
||||
|
||||
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
|
||||
Runtime::metadata_at_version(version)
|
||||
}
|
||||
|
||||
fn metadata_versions() -> Vec<u32> {
|
||||
Runtime::metadata_versions()
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_support::view_functions::runtime_api::RuntimeViewFunction<Block> for Runtime {
|
||||
fn execute_view_function(id: frame_support::view_functions::ViewFunctionId, input: Vec<u8>) -> Result<Vec<u8>, frame_support::view_functions::ViewFunctionDispatchError> {
|
||||
Runtime::execute_view_function(id, input)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_block_builder::BlockBuilder<Block> for Runtime {
|
||||
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
|
||||
Executive::apply_extrinsic(extrinsic)
|
||||
}
|
||||
|
||||
fn finalize_block() -> <Block as BlockT>::Header {
|
||||
Executive::finalize_block()
|
||||
}
|
||||
|
||||
fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
|
||||
data.create_extrinsics()
|
||||
}
|
||||
|
||||
fn check_inherents(
|
||||
block: <Block as BlockT>::LazyBlock,
|
||||
data: sp_inherents::InherentData,
|
||||
) -> sp_inherents::CheckInherentsResult {
|
||||
data.check_extrinsics(&block)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(
|
||||
source: TransactionSource,
|
||||
tx: <Block as BlockT>::Extrinsic,
|
||||
block_hash: <Block as BlockT>::Hash,
|
||||
) -> TransactionValidity {
|
||||
Executive::validate_transaction(source, tx, block_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
|
||||
fn offchain_worker(header: &<Block as BlockT>::Header) {
|
||||
Executive::offchain_worker(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
|
||||
fn slot_duration() -> sp_consensus_aura::SlotDuration {
|
||||
sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())
|
||||
}
|
||||
|
||||
fn authorities() -> Vec<AuraId> {
|
||||
pallet_aura::Authorities::<Runtime>::get().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_session::SessionKeys<Block> for Runtime {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
SessionKeys::generate(seed)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
|
||||
SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_consensus_grandpa::GrandpaApi<Block> for Runtime {
|
||||
fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList {
|
||||
Grandpa::grandpa_authorities()
|
||||
}
|
||||
|
||||
fn current_set_id() -> sp_consensus_grandpa::SetId {
|
||||
Grandpa::current_set_id()
|
||||
}
|
||||
|
||||
fn submit_report_equivocation_unsigned_extrinsic(
|
||||
_equivocation_proof: sp_consensus_grandpa::EquivocationProof<
|
||||
<Block as BlockT>::Hash,
|
||||
NumberFor<Block>,
|
||||
>,
|
||||
_key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof,
|
||||
) -> Option<()> {
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_key_ownership_proof(
|
||||
_set_id: sp_consensus_grandpa::SetId,
|
||||
_authority_id: GrandpaId,
|
||||
) -> Option<sp_consensus_grandpa::OpaqueKeyOwnershipProof> {
|
||||
// NOTE: this is the only implementation possible since we've
|
||||
// defined our key owner proof type as a bottom type (i.e. a type
|
||||
// with no values).
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
|
||||
fn account_nonce(account: AccountId) -> Nonce {
|
||||
System::account_nonce(account)
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {
|
||||
fn query_info(
|
||||
uxt: <Block as BlockT>::Extrinsic,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
|
||||
TransactionPayment::query_info(uxt, len)
|
||||
}
|
||||
fn query_fee_details(
|
||||
uxt: <Block as BlockT>::Extrinsic,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::FeeDetails<Balance> {
|
||||
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<Block, Balance, RuntimeCall>
|
||||
for Runtime
|
||||
{
|
||||
fn query_call_info(
|
||||
call: RuntimeCall,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
|
||||
TransactionPayment::query_call_info(call, len)
|
||||
}
|
||||
fn query_call_fee_details(
|
||||
call: RuntimeCall,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::FeeDetails<Balance> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl frame_benchmarking::Benchmark<Block> for Runtime {
|
||||
fn benchmark_metadata(extra: bool) -> (
|
||||
Vec<frame_benchmarking::BenchmarkList>,
|
||||
Vec<frame_support::traits::StorageInfo>,
|
||||
) {
|
||||
use frame_benchmarking::{baseline, BenchmarkList};
|
||||
use frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench;
|
||||
use baseline::Pallet as BaselineBench;
|
||||
use super::*;
|
||||
|
||||
let mut list = Vec::<BenchmarkList>::new();
|
||||
list_benchmarks!(list, extra);
|
||||
|
||||
let storage_info = AllPalletsWithSystem::storage_info();
|
||||
|
||||
(list, storage_info)
|
||||
}
|
||||
|
||||
#[allow(non_local_definitions)]
|
||||
fn dispatch_benchmark(
|
||||
config: frame_benchmarking::BenchmarkConfig
|
||||
) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, alloc::string::String> {
|
||||
use frame_benchmarking::{baseline, BenchmarkBatch};
|
||||
use sp_storage::TrackedStorageKey;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench;
|
||||
use baseline::Pallet as BaselineBench;
|
||||
use super::*;
|
||||
|
||||
impl frame_system_benchmarking::Config for Runtime {}
|
||||
impl baseline::Config for Runtime {}
|
||||
|
||||
use frame_support::traits::WhitelistedStorageKeys;
|
||||
let whitelist: Vec<TrackedStorageKey> = AllPalletsWithSystem::whitelisted_storage_keys();
|
||||
|
||||
let mut batches = Vec::<BenchmarkBatch>::new();
|
||||
let params = (&config, &whitelist);
|
||||
add_benchmarks!(params, batches);
|
||||
|
||||
Ok(batches)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
impl frame_try_runtime::TryRuntime<Block> for Runtime {
|
||||
fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
|
||||
// NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to
|
||||
// have a backtrace here. If any of the pre/post migration checks fail, we shall stop
|
||||
// right here and right now.
|
||||
let weight = Executive::try_runtime_upgrade(checks).unwrap();
|
||||
(weight, super::configs::RuntimeBlockWeights::get().max_block)
|
||||
}
|
||||
|
||||
fn execute_block(
|
||||
block: <Block as BlockT>::LazyBlock,
|
||||
state_root_check: bool,
|
||||
signature_check: bool,
|
||||
select: frame_try_runtime::TryStateSelect
|
||||
) -> Weight {
|
||||
// NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to
|
||||
// have a backtrace here.
|
||||
Executive::try_execute_block(block, state_root_check, signature_check, select).expect("execute-block failed")
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
|
||||
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
|
||||
build_state::<RuntimeGenesisConfig>(config)
|
||||
}
|
||||
|
||||
fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
|
||||
get_preset::<RuntimeGenesisConfig>(id, crate::genesis_config_presets::get_preset)
|
||||
}
|
||||
|
||||
fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
|
||||
crate::genesis_config_presets::preset_names()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
frame_benchmarking::define_benchmarks!(
|
||||
[frame_benchmarking, BaselineBench::<Runtime>]
|
||||
[frame_system, SystemBench::<Runtime>]
|
||||
[frame_system_extensions, SystemExtensionsBench::<Runtime>]
|
||||
[pallet_balances, Balances]
|
||||
[pallet_timestamp, Timestamp]
|
||||
[pallet_sudo, Sudo]
|
||||
[pallet_template, Template]
|
||||
);
|
||||
@@ -0,0 +1,171 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
// Substrate and Pezkuwi dependencies
|
||||
use frame_support::{
|
||||
derive_impl, parameter_types,
|
||||
traits::{ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, VariantCountOf},
|
||||
weights::{
|
||||
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
|
||||
IdentityFee, Weight,
|
||||
},
|
||||
};
|
||||
use frame_system::limits::{BlockLength, BlockWeights};
|
||||
use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier};
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_runtime::{traits::One, Perbill};
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
// Local module imports
|
||||
use super::{
|
||||
AccountId, Aura, Balance, Balances, Block, BlockNumber, Hash, Nonce, PalletInfo, Runtime,
|
||||
RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
|
||||
System, EXISTENTIAL_DEPOSIT, SLOT_DURATION, VERSION,
|
||||
};
|
||||
|
||||
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: BlockNumber = 2400;
|
||||
pub const Version: RuntimeVersion = VERSION;
|
||||
|
||||
/// We allow for 2 seconds of compute with a 6 second average block time.
|
||||
pub RuntimeBlockWeights: BlockWeights = BlockWeights::with_sensible_defaults(
|
||||
Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
|
||||
NORMAL_DISPATCH_RATIO,
|
||||
);
|
||||
pub RuntimeBlockLength: BlockLength = BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
}
|
||||
|
||||
/// All migrations of the runtime, aside from the ones declared in the pallets.
|
||||
///
|
||||
/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`.
|
||||
#[allow(unused_parens)]
|
||||
type SingleBlockMigrations = ();
|
||||
|
||||
/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from
|
||||
/// [`SoloChainDefaultConfig`](`struct@frame_system::config_preludes::SolochainDefaultConfig`),
|
||||
/// but overridden as needed.
|
||||
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
/// The block type for the runtime.
|
||||
type Block = Block;
|
||||
/// Block & extrinsics weights: base values and limits.
|
||||
type BlockWeights = RuntimeBlockWeights;
|
||||
/// The maximum length of a block (in bytes).
|
||||
type BlockLength = RuntimeBlockLength;
|
||||
/// The identifier used to distinguish between accounts.
|
||||
type AccountId = AccountId;
|
||||
/// The type for storing how many extrinsics an account has signed.
|
||||
type Nonce = Nonce;
|
||||
/// The type for hashing blocks and tries.
|
||||
type Hash = Hash;
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
type BlockHashCount = BlockHashCount;
|
||||
/// The weight of database operations that the runtime can invoke.
|
||||
type DbWeight = RocksDbWeight;
|
||||
/// Version of the runtime.
|
||||
type Version = Version;
|
||||
/// The data to be stored in an account.
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
/// This is used as an identifier of the chain. 42 is the generic substrate prefix.
|
||||
type SS58Prefix = SS58Prefix;
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type SingleBlockMigrations = SingleBlockMigrations;
|
||||
}
|
||||
|
||||
impl pallet_aura::Config for Runtime {
|
||||
type AuthorityId = AuraId;
|
||||
type DisabledValidators = ();
|
||||
type MaxAuthorities = ConstU32<32>;
|
||||
type AllowMultipleBlocksPerSlot = ConstBool<false>;
|
||||
type SlotDuration = pallet_aura::MinimumPeriodTimesTwo<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_grandpa::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
|
||||
type WeightInfo = ();
|
||||
type MaxAuthorities = ConstU32<32>;
|
||||
type MaxNominators = ConstU32<0>;
|
||||
type MaxSetIdSessionEntries = ConstU64<0>;
|
||||
|
||||
type KeyOwnerProof = sp_core::Void;
|
||||
type EquivocationReportSystem = ();
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Runtime {
|
||||
/// A timestamp: milliseconds since the unix epoch.
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = Aura;
|
||||
type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Runtime {
|
||||
type MaxLocks = ConstU32<50>;
|
||||
type MaxReserves = ();
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
/// The type for recording an account's balance.
|
||||
type Balance = Balance;
|
||||
/// The ubiquitous event type.
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ConstU128<EXISTENTIAL_DEPOSIT>;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
|
||||
type FreezeIdentifier = RuntimeFreezeReason;
|
||||
type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
|
||||
type RuntimeHoldReason = RuntimeHoldReason;
|
||||
type RuntimeFreezeReason = RuntimeFreezeReason;
|
||||
type DoneSlashHandler = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub FeeMultiplier: Multiplier = Multiplier::one();
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type OnChargeTransaction = FungibleAdapter<Balances, ()>;
|
||||
type OperationalFeeMultiplier = ConstU8<5>;
|
||||
type WeightToFee = IdentityFee<Balance>;
|
||||
type LengthToFee = IdentityFee<Balance>;
|
||||
type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
|
||||
type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_sudo::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type WeightInfo = pallet_sudo::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
|
||||
/// Configure the pallet-template in pallets/template.
|
||||
impl pallet_template::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = pallet_template::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{AccountId, BalancesConfig, RuntimeGenesisConfig, SudoConfig};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use frame_support::build_struct_json_patch;
|
||||
use serde_json::Value;
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_consensus_grandpa::AuthorityId as GrandpaId;
|
||||
use sp_genesis_builder::{self, PresetId};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
// Returns the genesis config presets populated with given parameters.
|
||||
fn testnet_genesis(
|
||||
initial_authorities: Vec<(AuraId, GrandpaId)>,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
root: AccountId,
|
||||
) -> Value {
|
||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
||||
balances: BalancesConfig {
|
||||
balances: endowed_accounts
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|k| (k, 1u128 << 60))
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
aura: pallet_aura::GenesisConfig {
|
||||
authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect::<Vec<_>>(),
|
||||
},
|
||||
grandpa: pallet_grandpa::GenesisConfig {
|
||||
authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::<Vec<_>>(),
|
||||
},
|
||||
sudo: SudoConfig { key: Some(root) },
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the development genesis config.
|
||||
pub fn development_config_genesis() -> Value {
|
||||
testnet_genesis(
|
||||
vec![(
|
||||
sp_keyring::Sr25519Keyring::Alice.public().into(),
|
||||
sp_keyring::Ed25519Keyring::Alice.public().into(),
|
||||
)],
|
||||
vec![
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
Sr25519Keyring::Bob.to_account_id(),
|
||||
Sr25519Keyring::AliceStash.to_account_id(),
|
||||
Sr25519Keyring::BobStash.to_account_id(),
|
||||
],
|
||||
sp_keyring::Sr25519Keyring::Alice.to_account_id(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the local genesis config preset.
|
||||
pub fn local_config_genesis() -> Value {
|
||||
testnet_genesis(
|
||||
vec![
|
||||
(
|
||||
sp_keyring::Sr25519Keyring::Alice.public().into(),
|
||||
sp_keyring::Ed25519Keyring::Alice.public().into(),
|
||||
),
|
||||
(
|
||||
sp_keyring::Sr25519Keyring::Bob.public().into(),
|
||||
sp_keyring::Ed25519Keyring::Bob.public().into(),
|
||||
),
|
||||
],
|
||||
Sr25519Keyring::iter()
|
||||
.filter(|v| v != &Sr25519Keyring::One && v != &Sr25519Keyring::Two)
|
||||
.map(|v| v.to_account_id())
|
||||
.collect::<Vec<_>>(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Provides the JSON representation of predefined genesis config for given `id`.
|
||||
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
|
||||
let patch = match id.as_ref() {
|
||||
sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(),
|
||||
sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => local_config_genesis(),
|
||||
_ => return None,
|
||||
};
|
||||
Some(
|
||||
serde_json::to_string(&patch)
|
||||
.expect("serialization to json is expected to work. qed.")
|
||||
.into_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
/// List of supported presets.
|
||||
pub fn preset_names() -> Vec<PresetId> {
|
||||
vec![
|
||||
PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET),
|
||||
PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET),
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||
|
||||
pub mod apis;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarks;
|
||||
pub mod configs;
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::vec::Vec;
|
||||
use sp_runtime::{
|
||||
generic, impl_opaque_keys,
|
||||
traits::{BlakeTwo256, IdentifyAccount, Verify},
|
||||
MultiAddress, MultiSignature,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_version::NativeVersion;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
pub use frame_system::Call as SystemCall;
|
||||
pub use pallet_balances::Call as BalancesCall;
|
||||
pub use pallet_timestamp::Call as TimestampCall;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use sp_runtime::BuildStorage;
|
||||
|
||||
pub mod genesis_config_presets;
|
||||
|
||||
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
|
||||
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
|
||||
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
|
||||
/// to even the core data structures.
|
||||
pub mod opaque {
|
||||
use super::*;
|
||||
use sp_runtime::{
|
||||
generic,
|
||||
traits::{BlakeTwo256, Hash as HashT},
|
||||
};
|
||||
|
||||
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
|
||||
|
||||
/// Opaque block header type.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
/// Opaque block type.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
/// Opaque block identifier type.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
/// Opaque block hash type.
|
||||
pub type Hash = <BlakeTwo256 as HashT>::Output;
|
||||
}
|
||||
|
||||
impl_opaque_keys! {
|
||||
pub struct SessionKeys {
|
||||
pub aura: Aura,
|
||||
pub grandpa: Grandpa,
|
||||
}
|
||||
}
|
||||
|
||||
// To learn more about runtime versioning, see:
|
||||
// https://docs.pezkuwichain.io/main-docs/build/upgrade#runtime-versioning
|
||||
#[sp_version::runtime_version]
|
||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed("solochain-template-runtime"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("solochain-template-runtime"),
|
||||
authoring_version: 1,
|
||||
// The version of the runtime specification. A full node will not attempt to use its native
|
||||
// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
|
||||
// `spec_version`, and `authoring_version` are the same between Wasm and native.
|
||||
// This value is set to 100 to notify Pezkuwi-JS App (https://pezkuwichain.io) to use
|
||||
// the compatible custom types.
|
||||
spec_version: 100,
|
||||
impl_version: 1,
|
||||
apis: apis::RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
system_version: 1,
|
||||
};
|
||||
|
||||
mod block_times {
|
||||
/// This determines the average expected block time that we are targeting. Blocks will be
|
||||
/// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by
|
||||
/// `pallet_timestamp` which is in turn picked up by `pallet_aura` to implement `fn
|
||||
/// slot_duration()`.
|
||||
///
|
||||
/// Change this to adjust the block time.
|
||||
pub const MILLI_SECS_PER_BLOCK: u64 = 6000;
|
||||
|
||||
// NOTE: Currently it is not possible to change the slot duration after the chain has started.
|
||||
// Attempting to do so will brick block production.
|
||||
pub const SLOT_DURATION: u64 = MILLI_SECS_PER_BLOCK;
|
||||
}
|
||||
pub use block_times::*;
|
||||
|
||||
// Time is measured by number of blocks.
|
||||
pub const MINUTES: BlockNumber = 60_000 / (MILLI_SECS_PER_BLOCK as BlockNumber);
|
||||
pub const HOURS: BlockNumber = MINUTES * 60;
|
||||
pub const DAYS: BlockNumber = HOURS * 24;
|
||||
|
||||
pub const BLOCK_HASH_COUNT: BlockNumber = 2400;
|
||||
|
||||
// Unit = the base number of indivisible units for balances
|
||||
pub const UNIT: Balance = 1_000_000_000_000;
|
||||
pub const MILLI_UNIT: Balance = 1_000_000_000;
|
||||
pub const MICRO_UNIT: Balance = 1_000_000;
|
||||
|
||||
/// Existential deposit.
|
||||
pub const EXISTENTIAL_DEPOSIT: Balance = MILLI_UNIT;
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn native_version() -> NativeVersion {
|
||||
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
|
||||
}
|
||||
|
||||
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
|
||||
pub type Signature = MultiSignature;
|
||||
|
||||
/// Some way of identifying an account on the chain. We intentionally make it equivalent
|
||||
/// to the public key of our transaction signing scheme.
|
||||
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
|
||||
|
||||
/// Balance of an account.
|
||||
pub type Balance = u128;
|
||||
|
||||
/// Index of a transaction in the chain.
|
||||
pub type Nonce = u32;
|
||||
|
||||
/// A hash of some data used by the chain.
|
||||
pub type Hash = sp_core::H256;
|
||||
|
||||
/// An index to a block.
|
||||
pub type BlockNumber = u32;
|
||||
|
||||
/// The address format for describing accounts.
|
||||
pub type Address = MultiAddress<AccountId, ()>;
|
||||
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
|
||||
/// A Block signed with a Justification
|
||||
pub type SignedBlock = generic::SignedBlock<Block>;
|
||||
|
||||
/// BlockId type as expected by this runtime.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
|
||||
/// The `TransactionExtension` to the basic transaction logic.
|
||||
pub type TxExtension = (
|
||||
frame_system::AuthorizeCall<Runtime>,
|
||||
frame_system::CheckNonZeroSender<Runtime>,
|
||||
frame_system::CheckSpecVersion<Runtime>,
|
||||
frame_system::CheckTxVersion<Runtime>,
|
||||
frame_system::CheckGenesis<Runtime>,
|
||||
frame_system::CheckEra<Runtime>,
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
|
||||
frame_system::WeightReclaim<Runtime>,
|
||||
);
|
||||
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic =
|
||||
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
|
||||
|
||||
/// The payload being signed in transactions.
|
||||
pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
|
||||
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = frame_executive::Executive<
|
||||
Runtime,
|
||||
Block,
|
||||
frame_system::ChainContext<Runtime>,
|
||||
Runtime,
|
||||
AllPalletsWithSystem,
|
||||
>;
|
||||
|
||||
// Create the runtime by composing the FRAME pallets that were previously configured.
|
||||
#[frame_support::runtime]
|
||||
mod runtime {
|
||||
#[runtime::runtime]
|
||||
#[runtime::derive(
|
||||
RuntimeCall,
|
||||
RuntimeEvent,
|
||||
RuntimeError,
|
||||
RuntimeOrigin,
|
||||
RuntimeFreezeReason,
|
||||
RuntimeHoldReason,
|
||||
RuntimeSlashReason,
|
||||
RuntimeLockId,
|
||||
RuntimeTask,
|
||||
RuntimeViewFunction
|
||||
)]
|
||||
pub struct Runtime;
|
||||
|
||||
#[runtime::pallet_index(0)]
|
||||
pub type System = frame_system;
|
||||
|
||||
#[runtime::pallet_index(1)]
|
||||
pub type Timestamp = pallet_timestamp;
|
||||
|
||||
#[runtime::pallet_index(2)]
|
||||
pub type Aura = pallet_aura;
|
||||
|
||||
#[runtime::pallet_index(3)]
|
||||
pub type Grandpa = pallet_grandpa;
|
||||
|
||||
#[runtime::pallet_index(4)]
|
||||
pub type Balances = pallet_balances;
|
||||
|
||||
#[runtime::pallet_index(5)]
|
||||
pub type TransactionPayment = pallet_transaction_payment;
|
||||
|
||||
#[runtime::pallet_index(6)]
|
||||
pub type Sudo = pallet_sudo;
|
||||
|
||||
// Include the custom logic from the pallet-template in the runtime.
|
||||
#[runtime::pallet_index(7)]
|
||||
pub type Template = pallet_template;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "teyrchain-template"
|
||||
description = "A teyrchain-template helper crate to keep documentation in sync with the template's components."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
docify = { workspace = true }
|
||||
|
||||
[features]
|
||||
generate-readme = []
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM docker.io/paritytech/ci-unified:latest as builder
|
||||
|
||||
WORKDIR /polkadot
|
||||
COPY . /polkadot
|
||||
|
||||
RUN cargo fetch
|
||||
RUN cargo build --workspace --locked --profile production
|
||||
|
||||
FROM docker.io/parity/base-bin:latest
|
||||
|
||||
COPY --from=builder /polkadot/target/production/parachain-template-node /usr/local/bin
|
||||
|
||||
USER root
|
||||
RUN useradd -m -u 1001 -U -s /bin/sh -d /polkadot polkadot && \
|
||||
mkdir -p /data /polkadot/.local/share && \
|
||||
chown -R polkadot:polkadot /data && \
|
||||
ln -s /data /polkadot/.local/share/polkadot && \
|
||||
# unclutter and minimize the attack surface
|
||||
rm -rf /usr/bin /usr/sbin && \
|
||||
# check if executable works in this container
|
||||
/usr/local/bin/parachain-template-node --version
|
||||
|
||||
USER polkadot
|
||||
|
||||
EXPOSE 30333 9933 9944 9615
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/parachain-template-node"]
|
||||
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
@@ -0,0 +1,268 @@
|
||||
<div align="center">
|
||||
|
||||
# Pezkuwi SDK's Teyrchain Template
|
||||
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png#gh-dark-mode-only"/>
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png#gh-light-mode-only"/>
|
||||
|
||||
> This is a template for creating a [teyrchain](https://wiki.network.pezkuwichain.io/docs/learn-parachains) based on Pezkuwi SDK.
|
||||
>
|
||||
> This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Intro](#intro)
|
||||
|
||||
- [Template Structure](#template-structure)
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
|
||||
- [Starting a Development Chain](#starting-a-development-chain)
|
||||
|
||||
- [Omni Node](#omni-node-prerequisites)
|
||||
- [Zombienet setup with Omni Node](#zombienet-setup-with-omni-node)
|
||||
- [Teyrchain Template Node](#teyrchain-template-node)
|
||||
- [Connect with the Pezkuwi-JS Apps Front-End](#connect-with-the-pezkuwi-js-apps-front-end)
|
||||
- [Takeaways](#takeaways)
|
||||
|
||||
- [Runtime development](#runtime-development)
|
||||
- [Contributing](#contributing)
|
||||
- [Getting Help](#getting-help)
|
||||
|
||||
## Intro
|
||||
|
||||
- ⏫ This template provides a starting point to build a [teyrchain](https://wiki.network.pezkuwichain.io/docs/learn-parachains).
|
||||
|
||||
- ☁️ It is based on the
|
||||
[Cumulus](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html) framework.
|
||||
|
||||
- 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets
|
||||
such as a [Balances pallet](https://docs.pezkuwichain.io/sdk/master/pallet_balances/index.html).
|
||||
|
||||
- 👉 Learn more about teyrchains [here](https://wiki.network.pezkuwichain.io/docs/learn-parachains)
|
||||
|
||||
## Template Structure
|
||||
|
||||
A Pezkuwi SDK based project such as this one consists of:
|
||||
|
||||
- 🧮 the [Runtime](./runtime/README.md) - the core logic of the teyrchain.
|
||||
- 🎨 the [Pallets](./pallets/README.md) - from which the runtime is constructed.
|
||||
- 💿 a [Node](./node/README.md) - the binary application, not part of the project default-members list and not compiled unless
|
||||
building the project with `--workspace` flag, which builds all workspace members, and is an alternative to
|
||||
[Omni Node](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html).
|
||||
|
||||
## Getting Started
|
||||
|
||||
- 🦀 The template is using the Rust language.
|
||||
|
||||
- 👉 Check the
|
||||
[Rust installation instructions](https://www.rust-lang.org/tools/install) for your system.
|
||||
|
||||
- 🛠️ Depending on your operating system and Rust version, there might be additional
|
||||
packages required to compile this template - please take note of the Rust compiler output.
|
||||
|
||||
Fetch teyrchain template code:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template.git teyrchain-template
|
||||
|
||||
cd teyrchain-template
|
||||
```
|
||||
|
||||
## Starting a Development Chain
|
||||
|
||||
The teyrchain template relies on a hardcoded teyrchain id which is defined in the runtime code
|
||||
and referenced throughout the contents of this file as `{{TEYRCHAIN_ID}}`. Please replace
|
||||
any command or file referencing this placeholder with the value of the `TEYRCHAIN_ID` constant:
|
||||
|
||||
<!-- docify::embed!("runtime/src/genesis_config_presets.rs", TEYRCHAIN_ID)-->
|
||||
|
||||
### Omni Node Prerequisites
|
||||
|
||||
[Omni Node](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) can
|
||||
be used to run the teyrchain template's runtime. `pezkuwi-omni-node` binary crate usage is described at a high-level
|
||||
[on crates.io](https://crates.io/crates/polkadot-omni-node).
|
||||
|
||||
#### Install `pezkuwi-omni-node`
|
||||
|
||||
```sh
|
||||
cargo install pezkuwi-omni-node
|
||||
```
|
||||
|
||||
> For more advanced options, please see the installation section at [`crates.io/omni-node`](https://crates.io/crates/polkadot-omni-node).
|
||||
|
||||
#### Build `teyrchain-template-runtime`
|
||||
|
||||
```sh
|
||||
cargo build --profile production
|
||||
```
|
||||
|
||||
#### Install `staging-chain-spec-builder`
|
||||
|
||||
```sh
|
||||
cargo install staging-chain-spec-builder
|
||||
```
|
||||
|
||||
> For more advanced options, please see the installation section at [`crates.io/staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder).
|
||||
|
||||
#### Use `chain-spec-builder` to generate the `chain_spec.json` file
|
||||
|
||||
```sh
|
||||
chain-spec-builder create --relay-chain "pezkuwichain-local" --runtime \
|
||||
target/release/wbuild/teyrchain-template-runtime/teyrchain_template_runtime.wasm named-preset development
|
||||
```
|
||||
|
||||
**Note**: the `relay-chain` flag is required by Omni Node. The `relay-chain` value is set in accordance
|
||||
with the relay chain ID where this instantiation of teyrchain-template will connect to.
|
||||
|
||||
#### Run Omni Node
|
||||
|
||||
Start Omni Node with the generated chain spec. We'll start it in development mode (without a relay chain config), producing
|
||||
and finalizing blocks based on manual seal, configured below to seal a block with each second.
|
||||
|
||||
```bash
|
||||
pezkuwi-omni-node --chain <path/to/chain_spec.json> --dev --dev-block-time 1000
|
||||
```
|
||||
|
||||
However, such a setup is not close to what would run in production, and for that we need to setup a local
|
||||
relay chain network that will help with the block finalization. In this guide we'll setup a local relay chain
|
||||
as well. We'll not do it manually, by starting one node at a time, but we'll use [zombienet](https://paritytech.github.io/zombienet/intro.html).
|
||||
|
||||
Follow through the next section for more details on how to do it.
|
||||
|
||||
### Zombienet setup with Omni Node
|
||||
|
||||
Assuming we continue from the last step of the previous section, we have a chain spec and we need to setup a relay chain.
|
||||
We can install `zombienet` as described [here](https://paritytech.github.io/zombienet/install.html#installation), and
|
||||
`zombienet-omni-node.toml` contains the network specification we want to start.
|
||||
|
||||
#### Relay chain prerequisites
|
||||
|
||||
Download the `pezkuwi` (and the accompanying `pezkuwi-prepare-worker` and `pezkuwi-execute-worker`) binaries from
|
||||
[Pezkuwi SDK releases](https://github.com/pezkuwichain/pezkuwi-sdk/releases). Then expose them on `PATH` like so:
|
||||
|
||||
```sh
|
||||
export PATH="$PATH:<path/to/binaries>"
|
||||
```
|
||||
|
||||
#### Update `zombienet-omni-node.toml` with a valid chain spec path
|
||||
|
||||
To simplify the process of using the teyrchain-template with zombienet and Omni Node, we've added a pre-configured
|
||||
development chain spec (dev_chain_spec.json) to the teyrchain template. The zombienet-omni-node.toml file of this
|
||||
template points to it, but you can update it to an updated chain spec generated on your machine. To generate a
|
||||
chain spec refer to [staging-chain-spec-builder](https://crates.io/crates/staging-chain-spec-builder)
|
||||
|
||||
Then make the changes in the network specification like so:
|
||||
|
||||
```toml
|
||||
# ...
|
||||
[[teyrchains]]
|
||||
id = "<TEYRCHAIN_ID>"
|
||||
chain_spec_path = "<TO BE UPDATED WITH A VALID PATH>"
|
||||
# ...
|
||||
```
|
||||
|
||||
#### Start the network
|
||||
|
||||
```sh
|
||||
zombienet --provider native spawn zombienet-omni-node.toml
|
||||
```
|
||||
|
||||
### Teyrchain Template Node
|
||||
|
||||
As mentioned in the `Template Structure` section, the `node` crate is optionally compiled and it is an alternative
|
||||
to `Omni Node`. Similarly, it requires setting up a relay chain, and we'll use `zombienet` once more.
|
||||
|
||||
#### Install the `teyrchain-template-node`
|
||||
|
||||
```sh
|
||||
cargo install --path node --locked
|
||||
```
|
||||
|
||||
#### Setup and start the network
|
||||
|
||||
For setup, please consider the instructions for `zombienet` installation [here](https://paritytech.github.io/zombienet/install.html#installation)
|
||||
and [relay chain prerequisites](#relay-chain-prerequisites).
|
||||
|
||||
We're left just with starting the network:
|
||||
|
||||
```sh
|
||||
zombienet --provider native spawn zombienet.toml
|
||||
```
|
||||
|
||||
### Connect with the Pezkuwi-JS Apps Front-End
|
||||
|
||||
- 🌐 You can interact with your local node using the
|
||||
hosted version of the Pezkuwi/Substrate Portal:
|
||||
[relay chain](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9944)
|
||||
and [teyrchain](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9988).
|
||||
|
||||
- 🪐 A hosted version is also
|
||||
available on [IPFS](https://dotapps.io/).
|
||||
|
||||
- 🧑🔧 You can also find the source code and instructions for hosting your own instance in the
|
||||
[`pezkuwi-js/apps`](https://github.com/polkadot-js/apps) repository.
|
||||
|
||||
### Takeaways
|
||||
|
||||
Development teyrchains:
|
||||
|
||||
- 🔗 Connect to relay chains, and we showcased how to connect to a local one.
|
||||
- 🧹 Do not persist the state.
|
||||
- 💰 Are preconfigured with a genesis state that includes several prefunded development accounts.
|
||||
- 🧑⚖️ Development accounts are used as validators, collators, and `sudo` accounts.
|
||||
|
||||
## Runtime development
|
||||
|
||||
We recommend using [`chopsticks`](https://github.com/AcalaNetwork/chopsticks) when the focus is more on the runtime
|
||||
development and `OmniNode` is enough as is.
|
||||
|
||||
### Install chopsticks
|
||||
|
||||
To use `chopsticks`, please install the latest version according to the installation [guide](https://github.com/AcalaNetwork/chopsticks?tab=readme-ov-file#install).
|
||||
|
||||
### Build a raw chain spec
|
||||
|
||||
Build the `teyrchain-template-runtime` as mentioned before in this guide and use `chain-spec-builder`
|
||||
again but this time by passing `--raw-storage` flag:
|
||||
|
||||
```sh
|
||||
chain-spec-builder create --raw-storage --relay-chain "pezkuwichain-local" --runtime \
|
||||
target/release/wbuild/teyrchain-template-runtime/teyrchain_template_runtime.wasm named-preset development
|
||||
```
|
||||
|
||||
### Start `chopsticks` with the chain spec
|
||||
|
||||
```sh
|
||||
npx @acala-network/chopsticks@latest --chain-spec <path/to/chain_spec.json>
|
||||
```
|
||||
|
||||
### Alternatives
|
||||
|
||||
`OmniNode` can be still used for runtime development if using the `--dev` flag, while `teyrchain-template-node` doesn't
|
||||
support it at this moment. It can still be used to test a runtime in a full setup where it is started alongside a
|
||||
relay chain network (see [Teyrchain Template node](#teyrchain-template-node) setup).
|
||||
|
||||
## Contributing
|
||||
|
||||
- 🔄 This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
- ➡️ Any pull requests should be directed to this [source](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/teyrchain).
|
||||
|
||||
- 😇 Please refer to the monorepo's
|
||||
[contribution guidelines](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and
|
||||
[Code of Conduct](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Getting Help
|
||||
|
||||
- 🧑🏫 To learn about Pezkuwi in general, [docs.Pezkuwi.com](https://docs.pezkuwichain.io/) website is a good starting point.
|
||||
|
||||
- 🧑🔧 For technical introduction, [here](https://github.com/pezkuwichain/pezkuwi-sdk#-documentation) are
|
||||
the Pezkuwi SDK documentation resources.
|
||||
|
||||
- 👥 Additionally, there are [GitHub issues](https://github.com/pezkuwichain/pezkuwi-sdk/issues) and
|
||||
[Substrate StackExchange](https://exchange.pezkuwichain.app/).
|
||||
- 👥You can also reach out on the [Official Pezkuwi discord server](https://polkadot-discord.w3f.tools/)
|
||||
- 🧑Reach out on [Telegram](https://t.me/substratedevs) for more questions and discussions
|
||||
@@ -0,0 +1,270 @@
|
||||
<div align="center">
|
||||
|
||||
# Pezkuwi SDK's Teyrchain Template
|
||||
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png#gh-dark-mode-only"/>
|
||||
<img height="70px" alt="Pezkuwi SDK Logo" src="https://github.com/pezkuwichain/pezkuwi-sdk/raw/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png#gh-light-mode-only"/>
|
||||
|
||||
> This is a template for creating a [teyrchain](https://wiki.network.pezkuwichain.io/docs/learn-parachains) based on Pezkuwi SDK.
|
||||
>
|
||||
> This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Intro](#intro)
|
||||
|
||||
- [Template Structure](#template-structure)
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
|
||||
- [Starting a Development Chain](#starting-a-development-chain)
|
||||
|
||||
- [Omni Node](#omni-node-prerequisites)
|
||||
- [Zombienet setup with Omni Node](#zombienet-setup-with-omni-node)
|
||||
- [Teyrchain Template Node](#teyrchain-template-node)
|
||||
- [Connect with the Pezkuwi-JS Apps Front-End](#connect-with-the-pezkuwi-js-apps-front-end)
|
||||
- [Takeaways](#takeaways)
|
||||
|
||||
- [Runtime development](#runtime-development)
|
||||
- [Contributing](#contributing)
|
||||
- [Getting Help](#getting-help)
|
||||
|
||||
## Intro
|
||||
|
||||
- ⏫ This template provides a starting point to build a [teyrchain](https://wiki.network.pezkuwichain.io/docs/learn-parachains).
|
||||
|
||||
- ☁️ It is based on the
|
||||
[Cumulus](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html) framework.
|
||||
|
||||
- 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets
|
||||
such as a [Balances pallet](https://docs.pezkuwichain.io/sdk/master/pallet_balances/index.html).
|
||||
|
||||
- 👉 Learn more about teyrchains [here](https://wiki.network.pezkuwichain.io/docs/learn-parachains)
|
||||
|
||||
## Template Structure
|
||||
|
||||
A Pezkuwi SDK based project such as this one consists of:
|
||||
|
||||
- 🧮 the [Runtime](./runtime/README.md) - the core logic of the teyrchain.
|
||||
- 🎨 the [Pallets](./pallets/README.md) - from which the runtime is constructed.
|
||||
- 💿 a [Node](./node/README.md) - the binary application, not part of the project default-members list and not compiled unless
|
||||
building the project with `--workspace` flag, which builds all workspace members, and is an alternative to
|
||||
[Omni Node](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html).
|
||||
|
||||
## Getting Started
|
||||
|
||||
- 🦀 The template is using the Rust language.
|
||||
|
||||
- 👉 Check the
|
||||
[Rust installation instructions](https://www.rust-lang.org/tools/install) for your system.
|
||||
|
||||
- 🛠️ Depending on your operating system and Rust version, there might be additional
|
||||
packages required to compile this template - please take note of the Rust compiler output.
|
||||
|
||||
Fetch teyrchain template code:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template.git teyrchain-template
|
||||
|
||||
cd teyrchain-template
|
||||
```
|
||||
|
||||
## Starting a Development Chain
|
||||
|
||||
The teyrchain template relies on a hardcoded teyrchain id which is defined in the runtime code
|
||||
and referenced throughout the contents of this file as `{{TEYRCHAIN_ID}}`. Please replace
|
||||
any command or file referencing this placeholder with the value of the `TEYRCHAIN_ID` constant:
|
||||
|
||||
```rust,ignore
|
||||
pub const TEYRCHAIN_ID: u32 = 1000;
|
||||
```
|
||||
|
||||
### Omni Node Prerequisites
|
||||
|
||||
[Omni Node](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) can
|
||||
be used to run the teyrchain template's runtime. `pezkuwi-omni-node` binary crate usage is described at a high-level
|
||||
[on crates.io](https://crates.io/crates/polkadot-omni-node).
|
||||
|
||||
#### Install `pezkuwi-omni-node`
|
||||
|
||||
```sh
|
||||
cargo install pezkuwi-omni-node
|
||||
```
|
||||
|
||||
> For more advanced options, please see the installation section at [`crates.io/omni-node`](https://crates.io/crates/polkadot-omni-node).
|
||||
|
||||
#### Build `teyrchain-template-runtime`
|
||||
|
||||
```sh
|
||||
cargo build --profile production
|
||||
```
|
||||
|
||||
#### Install `staging-chain-spec-builder`
|
||||
|
||||
```sh
|
||||
cargo install staging-chain-spec-builder
|
||||
```
|
||||
|
||||
> For more advanced options, please see the installation section at [`crates.io/staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder).
|
||||
|
||||
#### Use `chain-spec-builder` to generate the `chain_spec.json` file
|
||||
|
||||
```sh
|
||||
chain-spec-builder create --relay-chain "pezkuwichain-local" --runtime \
|
||||
target/release/wbuild/teyrchain-template-runtime/teyrchain_template_runtime.wasm named-preset development
|
||||
```
|
||||
|
||||
**Note**: the `relay-chain` flag is required by Omni Node. The `relay-chain` value is set in accordance
|
||||
with the relay chain ID where this instantiation of teyrchain-template will connect to.
|
||||
|
||||
#### Run Omni Node
|
||||
|
||||
Start Omni Node with the generated chain spec. We'll start it in development mode (without a relay chain config), producing
|
||||
and finalizing blocks based on manual seal, configured below to seal a block with each second.
|
||||
|
||||
```bash
|
||||
pezkuwi-omni-node --chain <path/to/chain_spec.json> --dev --dev-block-time 1000
|
||||
```
|
||||
|
||||
However, such a setup is not close to what would run in production, and for that we need to setup a local
|
||||
relay chain network that will help with the block finalization. In this guide we'll setup a local relay chain
|
||||
as well. We'll not do it manually, by starting one node at a time, but we'll use [zombienet](https://paritytech.github.io/zombienet/intro.html).
|
||||
|
||||
Follow through the next section for more details on how to do it.
|
||||
|
||||
### Zombienet setup with Omni Node
|
||||
|
||||
Assuming we continue from the last step of the previous section, we have a chain spec and we need to setup a relay chain.
|
||||
We can install `zombienet` as described [here](https://paritytech.github.io/zombienet/install.html#installation), and
|
||||
`zombienet-omni-node.toml` contains the network specification we want to start.
|
||||
|
||||
#### Relay chain prerequisites
|
||||
|
||||
Download the `pezkuwi` (and the accompanying `pezkuwi-prepare-worker` and `pezkuwi-execute-worker`) binaries from
|
||||
[Pezkuwi SDK releases](https://github.com/pezkuwichain/pezkuwi-sdk/releases). Then expose them on `PATH` like so:
|
||||
|
||||
```sh
|
||||
export PATH="$PATH:<path/to/binaries>"
|
||||
```
|
||||
|
||||
#### Update `zombienet-omni-node.toml` with a valid chain spec path
|
||||
|
||||
To simplify the process of using the teyrchain-template with zombienet and Omni Node, we've added a pre-configured
|
||||
development chain spec (dev_chain_spec.json) to the teyrchain template. The zombienet-omni-node.toml file of this
|
||||
template points to it, but you can update it to an updated chain spec generated on your machine. To generate a
|
||||
chain spec refer to [staging-chain-spec-builder](https://crates.io/crates/staging-chain-spec-builder)
|
||||
|
||||
Then make the changes in the network specification like so:
|
||||
|
||||
```toml
|
||||
# ...
|
||||
[[teyrchains]]
|
||||
id = "<TEYRCHAIN_ID>"
|
||||
chain_spec_path = "<TO BE UPDATED WITH A VALID PATH>"
|
||||
# ...
|
||||
```
|
||||
|
||||
#### Start the network
|
||||
|
||||
```sh
|
||||
zombienet --provider native spawn zombienet-omni-node.toml
|
||||
```
|
||||
|
||||
### Teyrchain Template Node
|
||||
|
||||
As mentioned in the `Template Structure` section, the `node` crate is optionally compiled and it is an alternative
|
||||
to `Omni Node`. Similarly, it requires setting up a relay chain, and we'll use `zombienet` once more.
|
||||
|
||||
#### Install the `teyrchain-template-node`
|
||||
|
||||
```sh
|
||||
cargo install --path node --locked
|
||||
```
|
||||
|
||||
#### Setup and start the network
|
||||
|
||||
For setup, please consider the instructions for `zombienet` installation [here](https://paritytech.github.io/zombienet/install.html#installation)
|
||||
and [relay chain prerequisites](#relay-chain-prerequisites).
|
||||
|
||||
We're left just with starting the network:
|
||||
|
||||
```sh
|
||||
zombienet --provider native spawn zombienet.toml
|
||||
```
|
||||
|
||||
### Connect with the Pezkuwi-JS Apps Front-End
|
||||
|
||||
- 🌐 You can interact with your local node using the
|
||||
hosted version of the Pezkuwi/Substrate Portal:
|
||||
[relay chain](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9944)
|
||||
and [teyrchain](https://pezkuwichain.io/#/explorer?rpc=ws://localhost:9988).
|
||||
|
||||
- 🪐 A hosted version is also
|
||||
available on [IPFS](https://dotapps.io/).
|
||||
|
||||
- 🧑🔧 You can also find the source code and instructions for hosting your own instance in the
|
||||
[`pezkuwi-js/apps`](https://github.com/polkadot-js/apps) repository.
|
||||
|
||||
### Takeaways
|
||||
|
||||
Development teyrchains:
|
||||
|
||||
- 🔗 Connect to relay chains, and we showcased how to connect to a local one.
|
||||
- 🧹 Do not persist the state.
|
||||
- 💰 Are preconfigured with a genesis state that includes several prefunded development accounts.
|
||||
- 🧑⚖️ Development accounts are used as validators, collators, and `sudo` accounts.
|
||||
|
||||
## Runtime development
|
||||
|
||||
We recommend using [`chopsticks`](https://github.com/AcalaNetwork/chopsticks) when the focus is more on the runtime
|
||||
development and `OmniNode` is enough as is.
|
||||
|
||||
### Install chopsticks
|
||||
|
||||
To use `chopsticks`, please install the latest version according to the installation [guide](https://github.com/AcalaNetwork/chopsticks?tab=readme-ov-file#install).
|
||||
|
||||
### Build a raw chain spec
|
||||
|
||||
Build the `teyrchain-template-runtime` as mentioned before in this guide and use `chain-spec-builder`
|
||||
again but this time by passing `--raw-storage` flag:
|
||||
|
||||
```sh
|
||||
chain-spec-builder create --raw-storage --relay-chain "pezkuwichain-local" --runtime \
|
||||
target/release/wbuild/teyrchain-template-runtime/teyrchain_template_runtime.wasm named-preset development
|
||||
```
|
||||
|
||||
### Start `chopsticks` with the chain spec
|
||||
|
||||
```sh
|
||||
npx @acala-network/chopsticks@latest --chain-spec <path/to/chain_spec.json>
|
||||
```
|
||||
|
||||
### Alternatives
|
||||
|
||||
`OmniNode` can be still used for runtime development if using the `--dev` flag, while `teyrchain-template-node` doesn't
|
||||
support it at this moment. It can still be used to test a runtime in a full setup where it is started alongside a
|
||||
relay chain network (see [Teyrchain Template node](#teyrchain-template-node) setup).
|
||||
|
||||
## Contributing
|
||||
|
||||
- 🔄 This template is automatically updated after releases in the main [Pezkuwi SDK monorepo](https://github.com/pezkuwichain/pezkuwi-sdk).
|
||||
|
||||
- ➡️ Any pull requests should be directed to this [source](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/teyrchain).
|
||||
|
||||
- 😇 Please refer to the monorepo's
|
||||
[contribution guidelines](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and
|
||||
[Code of Conduct](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Getting Help
|
||||
|
||||
- 🧑🏫 To learn about Pezkuwi in general, [docs.Pezkuwi.com](https://docs.pezkuwichain.io/) website is a good starting point.
|
||||
|
||||
- 🧑🔧 For technical introduction, [here](https://github.com/pezkuwichain/pezkuwi-sdk#-documentation) are
|
||||
the Pezkuwi SDK documentation resources.
|
||||
|
||||
- 👥 Additionally, there are [GitHub issues](https://github.com/pezkuwichain/pezkuwi-sdk/issues) and
|
||||
[Substrate StackExchange](https://exchange.pezkuwichain.app/).
|
||||
- 👥You can also reach out on the [Official Pezkuwi discord server](https://polkadot-discord.w3f.tools/)
|
||||
- 🧑Reach out on [Telegram](https://t.me/substratedevs) for more questions and discussions
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "teyrchain-template-node"
|
||||
description = "A teyrchain node template built with Substrate and Cumulus, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
codec = { workspace = true, default-features = true }
|
||||
color-print = { workspace = true }
|
||||
docify = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
jsonrpsee = { features = ["server"], workspace = true }
|
||||
log = { workspace = true, default-features = true }
|
||||
serde = { features = ["derive"], workspace = true, default-features = true }
|
||||
|
||||
pezkuwi-sdk = { workspace = true, features = ["node", "pezkuwi-primitives"] }
|
||||
|
||||
teyrchain-template-runtime = { workspace = true }
|
||||
|
||||
# Substrate
|
||||
prometheus-endpoint = { workspace = true, default-features = true }
|
||||
|
||||
[build-dependencies]
|
||||
pezkuwi-sdk = { workspace = true, features = ["substrate-build-script-utils"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["log/std", "pezkuwi-sdk/std", "teyrchain-template-runtime/std"]
|
||||
runtime-benchmarks = [
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
"teyrchain-template-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"pezkuwi-sdk/try-runtime",
|
||||
"teyrchain-template-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Node
|
||||
|
||||
ℹ️ A node - in PezkuwiChain - is a binary executable, whose primary purpose is to execute the [runtime](../runtime/README.md).
|
||||
|
||||
🔗 It communicates with other nodes in the network, and aims for
|
||||
[consensus](https://wiki.network.pezkuwichain.io/docs/learn-consensus) among them.
|
||||
|
||||
⚙️ It acts as a remote procedure call (RPC) server, allowing interaction with the blockchain.
|
||||
|
||||
👉 Learn more about the architecture, and the difference between a node and a runtime
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/wasm_meta_protocol/index.html).
|
||||
|
||||
👇 Here are the most important files in this node template:
|
||||
|
||||
- [`chain_spec.rs`](./src/chain_spec.rs): A chain specification is a source code file that defines the chain's
|
||||
initial (genesis) state.
|
||||
- [`service.rs`](./src/service.rs): This file defines the node implementation.
|
||||
It's a place to configure consensus-related topics.
|
||||
@@ -0,0 +1,7 @@
|
||||
use pezkuwi_sdk::substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
|
||||
|
||||
fn main() {
|
||||
generate_cargo_keys();
|
||||
|
||||
rerun_if_git_head_changed();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
|
||||
use sc_service::ChainType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use teyrchain_template_runtime as runtime;
|
||||
|
||||
/// Specialized `ChainSpec` for the normal teyrchain runtime.
|
||||
pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;
|
||||
/// The relay chain that you want to configure this teyrchain to connect to.
|
||||
pub const RELAY_CHAIN: &str = "pezkuwichain-local";
|
||||
|
||||
/// The extensions for the [`ChainSpec`].
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
|
||||
pub struct Extensions {
|
||||
/// The relay chain of the Teyrchain.
|
||||
#[serde(alias = "relayChain", alias = "RelayChain")]
|
||||
pub relay_chain: String,
|
||||
}
|
||||
|
||||
impl Extensions {
|
||||
/// Try to get the extension from the given `ChainSpec`.
|
||||
pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
|
||||
sc_chain_spec::get_extension(chain_spec.extensions())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn development_chain_spec() -> ChainSpec {
|
||||
// Give your base currency a unit name and decimal places
|
||||
let mut properties = sc_chain_spec::Properties::new();
|
||||
properties.insert("tokenSymbol".into(), "UNIT".into());
|
||||
properties.insert("tokenDecimals".into(), 12.into());
|
||||
properties.insert("ss58Format".into(), 42.into());
|
||||
|
||||
ChainSpec::builder(
|
||||
runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"),
|
||||
Extensions { relay_chain: RELAY_CHAIN.into() },
|
||||
)
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET)
|
||||
.with_properties(properties)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn local_chain_spec() -> ChainSpec {
|
||||
// Give your base currency a unit name and decimal places
|
||||
let mut properties = sc_chain_spec::Properties::new();
|
||||
properties.insert("tokenSymbol".into(), "UNIT".into());
|
||||
properties.insert("tokenDecimals".into(), 12.into());
|
||||
properties.insert("ss58Format".into(), 42.into());
|
||||
|
||||
ChainSpec::builder(
|
||||
runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"),
|
||||
Extensions { relay_chain: RELAY_CHAIN.into() },
|
||||
)
|
||||
.with_name("Local Testnet")
|
||||
.with_id("local_testnet")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_preset_name(sc_chain_spec::LOCAL_TESTNET_RUNTIME_PRESET)
|
||||
.with_protocol_id("template-local")
|
||||
.with_properties(properties)
|
||||
.build()
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use pezkuwi_sdk::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Sub-commands supported by the collator.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
/// Build a chain specification.
|
||||
/// DEPRECATED: `build-spec` command will be removed after 1/04/2026. Use `export-chain-spec`
|
||||
/// command instead.
|
||||
#[deprecated(
|
||||
note = "build-spec command will be removed after 1/04/2026. Use export-chain-spec command instead"
|
||||
)]
|
||||
BuildSpec(sc_cli::BuildSpecCmd),
|
||||
|
||||
/// Export the chain specification.
|
||||
ExportChainSpec(sc_cli::ExportChainSpecCmd),
|
||||
|
||||
/// Validate blocks.
|
||||
CheckBlock(sc_cli::CheckBlockCmd),
|
||||
|
||||
/// Export blocks.
|
||||
ExportBlocks(sc_cli::ExportBlocksCmd),
|
||||
|
||||
/// Export the state of a given block into a chain spec.
|
||||
ExportState(sc_cli::ExportStateCmd),
|
||||
|
||||
/// Import blocks.
|
||||
ImportBlocks(sc_cli::ImportBlocksCmd),
|
||||
|
||||
/// Revert the chain to a previous state.
|
||||
Revert(sc_cli::RevertCmd),
|
||||
|
||||
/// Remove the whole chain.
|
||||
PurgeChain(cumulus_client_cli::PurgeChainCmd),
|
||||
|
||||
/// Export the genesis head data of the teyrchain.
|
||||
///
|
||||
/// Head data is the encoded block header.
|
||||
#[command(alias = "export-genesis-state")]
|
||||
ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
|
||||
|
||||
/// Export the genesis wasm of the teyrchain.
|
||||
ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
|
||||
|
||||
/// Sub-commands concerned with benchmarking.
|
||||
/// The pallet benchmarking moved to the `pallet` sub-command.
|
||||
#[command(subcommand)]
|
||||
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
|
||||
}
|
||||
|
||||
const AFTER_HELP_EXAMPLE: &str = color_print::cstr!(
|
||||
r#"<bold><underline>Examples:</></>
|
||||
<bold>teyrchain-template-node build-spec --disable-default-bootnode > plain-teyrchain-chainspec.json</>
|
||||
Export a chainspec for a local testnet in json format.
|
||||
<bold>teyrchain-template-node --chain plain-teyrchain-chainspec.json --tmp -- --chain pezkuwichain-local</>
|
||||
Launch a full node with chain specification loaded from plain-teyrchain-chainspec.json.
|
||||
<bold>teyrchain-template-node</>
|
||||
Launch a full node with default teyrchain <italic>local-testnet</> and relay chain <italic>pezkuwichain-local</>.
|
||||
<bold>teyrchain-template-node --collator</>
|
||||
Launch a collator with default teyrchain <italic>local-testnet</> and relay chain <italic>pezkuwichain-local</>.
|
||||
"#
|
||||
);
|
||||
#[derive(Debug, clap::Parser)]
|
||||
#[command(
|
||||
propagate_version = true,
|
||||
args_conflicts_with_subcommands = true,
|
||||
subcommand_negates_reqs = true
|
||||
)]
|
||||
#[clap(after_help = AFTER_HELP_EXAMPLE)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub run: cumulus_client_cli::RunCmd,
|
||||
|
||||
/// Disable automatic hardware benchmarks.
|
||||
///
|
||||
/// By default these benchmarks are automatically ran at startup and measure
|
||||
/// the CPU speed, the memory bandwidth and the disk speed.
|
||||
///
|
||||
/// The results are then printed out in the logs, and also sent as part of
|
||||
/// telemetry, if telemetry is enabled.
|
||||
#[arg(long)]
|
||||
pub no_hardware_benchmarks: bool,
|
||||
|
||||
/// Relay chain arguments
|
||||
#[arg(raw = true)]
|
||||
pub relay_chain_args: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RelayChainCli {
|
||||
/// The actual relay chain cli object.
|
||||
pub base: pezkuwi_cli::RunCmd,
|
||||
|
||||
/// Optional chain id that should be passed to the relay chain.
|
||||
pub chain_id: Option<String>,
|
||||
|
||||
/// The base path that should be used by the relay chain.
|
||||
pub base_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl RelayChainCli {
|
||||
/// Parse the relay chain CLI parameters using the para chain `Configuration`.
|
||||
pub fn new<'a>(
|
||||
para_config: &sc_service::Configuration,
|
||||
relay_chain_args: impl Iterator<Item = &'a String>,
|
||||
) -> Self {
|
||||
let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec);
|
||||
let chain_id = extension.map(|e| e.relay_chain.clone());
|
||||
let base_path = para_config.base_path.path().join("pezkuwi");
|
||||
Self {
|
||||
base_path: Some(base_path),
|
||||
chain_id,
|
||||
base: clap::Parser::parse_from(relay_chain_args),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions;
|
||||
use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
|
||||
use log::info;
|
||||
use sc_cli::{
|
||||
ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
|
||||
NetworkParams, Result, RpcEndpoint, SharedParams, SubstrateCli,
|
||||
};
|
||||
use sc_service::config::{BasePath, PrometheusConfig};
|
||||
use teyrchain_template_runtime::Block;
|
||||
|
||||
use crate::{
|
||||
chain_spec,
|
||||
cli::{Cli, RelayChainCli, Subcommand},
|
||||
service::new_partial,
|
||||
};
|
||||
|
||||
fn load_spec(id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
|
||||
Ok(match id {
|
||||
"dev" => Box::new(chain_spec::development_chain_spec()),
|
||||
"template-pezkuwichain" => Box::new(chain_spec::local_chain_spec()),
|
||||
"" | "local" => Box::new(chain_spec::local_chain_spec()),
|
||||
path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
|
||||
})
|
||||
}
|
||||
|
||||
impl SubstrateCli for Cli {
|
||||
fn impl_name() -> String {
|
||||
"Teyrchain Collator Template".into()
|
||||
}
|
||||
|
||||
fn impl_version() -> String {
|
||||
env!("SUBSTRATE_CLI_IMPL_VERSION").into()
|
||||
}
|
||||
|
||||
fn description() -> String {
|
||||
format!(
|
||||
"Teyrchain Collator Template\n\nThe command-line arguments provided first will be \
|
||||
passed to the teyrchain node, while the arguments provided after -- will be passed \
|
||||
to the relay chain node.\n\n\
|
||||
{} <teyrchain-args> -- <relay-chain-args>",
|
||||
Self::executable_name()
|
||||
)
|
||||
}
|
||||
|
||||
fn author() -> String {
|
||||
env!("CARGO_PKG_AUTHORS").into()
|
||||
}
|
||||
|
||||
fn support_url() -> String {
|
||||
"https://github.com/pezkuwichain/pezkuwi-sdk/issues/new".into()
|
||||
}
|
||||
|
||||
fn copyright_start_year() -> i32 {
|
||||
2020
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
|
||||
load_spec(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubstrateCli for RelayChainCli {
|
||||
fn impl_name() -> String {
|
||||
"Teyrchain Collator Template".into()
|
||||
}
|
||||
|
||||
fn impl_version() -> String {
|
||||
env!("SUBSTRATE_CLI_IMPL_VERSION").into()
|
||||
}
|
||||
|
||||
fn description() -> String {
|
||||
format!(
|
||||
"Teyrchain Collator Template\n\nThe command-line arguments provided first will be \
|
||||
passed to the teyrchain node, while the arguments provided after -- will be passed \
|
||||
to the relay chain node.\n\n\
|
||||
{} <teyrchain-args> -- <relay-chain-args>",
|
||||
Self::executable_name()
|
||||
)
|
||||
}
|
||||
|
||||
fn author() -> String {
|
||||
env!("CARGO_PKG_AUTHORS").into()
|
||||
}
|
||||
|
||||
fn support_url() -> String {
|
||||
"https://github.com/pezkuwichain/pezkuwi-sdk/issues/new".into()
|
||||
}
|
||||
|
||||
fn copyright_start_year() -> i32 {
|
||||
2020
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
|
||||
pezkuwi_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}}
|
||||
}
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run() -> Result<()> {
|
||||
let cli = Cli::from_args();
|
||||
|
||||
match &cli.subcommand {
|
||||
#[allow(deprecated)]
|
||||
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::ExportChainSpec(cmd)) => {
|
||||
let chain_spec = cli.load_spec(&cmd.chain)?;
|
||||
cmd.run(chain_spec)
|
||||
},
|
||||
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 pezkuwi_cli = RelayChainCli::new(
|
||||
&config,
|
||||
[RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()),
|
||||
);
|
||||
|
||||
let pezkuwi_config = SubstrateCli::create_configuration(
|
||||
&pezkuwi_cli,
|
||||
&pezkuwi_cli,
|
||||
config.tokio_handle.clone(),
|
||||
)
|
||||
.map_err(|err| format!("Relay chain argument error: {err}"))?;
|
||||
|
||||
cmd.run(config, pezkuwi_config)
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportGenesisHead(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| {
|
||||
let partials = new_partial(&config)?;
|
||||
|
||||
cmd.run(partials.client)
|
||||
})
|
||||
},
|
||||
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)
|
||||
})
|
||||
},
|
||||
Some(Subcommand::Benchmark(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
// Switch on the concrete benchmark sub-command-
|
||||
match cmd {
|
||||
BenchmarkCmd::Pallet(cmd) =>
|
||||
if cfg!(feature = "runtime-benchmarks") {
|
||||
runner.sync_run(|config| cmd.run_with_spec::<sp_runtime::traits::HashingFor<Block>, ReclaimHostFunctions>(Some(config.chain_spec)))
|
||||
} else {
|
||||
Err("Benchmarking wasn't enabled when building the node. \
|
||||
You can enable it with `--features runtime-benchmarks`."
|
||||
.into())
|
||||
},
|
||||
BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {
|
||||
let partials = new_partial(&config)?;
|
||||
cmd.run(partials.client)
|
||||
}),
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input(
|
||||
"Compile with --features=runtime-benchmarks \
|
||||
to enable storage benchmarks."
|
||||
.into(),
|
||||
)),
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {
|
||||
let partials = new_partial(&config)?;
|
||||
let db = partials.backend.expose_db();
|
||||
let storage = partials.backend.expose_storage();
|
||||
let shared_cache = partials.backend.expose_shared_trie_cache();
|
||||
|
||||
cmd.run(config, partials.client.clone(), db, storage, shared_cache)
|
||||
}),
|
||||
BenchmarkCmd::Machine(cmd) =>
|
||||
runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())),
|
||||
// NOTE: this allows the Client to leniently implement
|
||||
// new benchmark commands without requiring a companion MR.
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => Err("Benchmarking sub-command unsupported".into()),
|
||||
}
|
||||
},
|
||||
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(|| {
|
||||
config.database.path().map(|database_path| {
|
||||
let _ = std::fs::create_dir_all(database_path);
|
||||
sc_sysinfo::gather_hwbench(
|
||||
Some(database_path),
|
||||
&SUBSTRATE_REFERENCE_HARDWARE,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let pezkuwi_cli = RelayChainCli::new(
|
||||
&config,
|
||||
[RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()),
|
||||
);
|
||||
|
||||
let tokio_handle = config.tokio_handle.clone();
|
||||
let pezkuwi_config =
|
||||
SubstrateCli::create_configuration(&pezkuwi_cli, &pezkuwi_cli, tokio_handle)
|
||||
.map_err(|err| format!("Relay chain argument error: {err}"))?;
|
||||
|
||||
info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
|
||||
|
||||
crate::service::start_teyrchain_node(
|
||||
config,
|
||||
pezkuwi_config,
|
||||
collator_options,
|
||||
hwbench,
|
||||
)
|
||||
.await
|
||||
.map(|r| r.0)
|
||||
.map_err(Into::into)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultConfigurationValues for RelayChainCli {
|
||||
fn p2p_listen_port() -> u16 {
|
||||
30334
|
||||
}
|
||||
|
||||
fn rpc_listen_port() -> u16 {
|
||||
9945
|
||||
}
|
||||
|
||||
fn prometheus_listen_port() -> u16 {
|
||||
9616
|
||||
}
|
||||
}
|
||||
|
||||
impl CliConfiguration<Self> 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<Option<BasePath>> {
|
||||
Ok(self
|
||||
.shared_params()
|
||||
.base_path()?
|
||||
.or_else(|| self.base_path.clone().map(Into::into)))
|
||||
}
|
||||
|
||||
fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
|
||||
self.base.base.rpc_addr(default_listen_port)
|
||||
}
|
||||
|
||||
fn prometheus_config(
|
||||
&self,
|
||||
default_listen_port: u16,
|
||||
chain_spec: &Box<dyn ChainSpec>,
|
||||
) -> Result<Option<PrometheusConfig>> {
|
||||
self.base.base.prometheus_config(default_listen_port, chain_spec)
|
||||
}
|
||||
|
||||
fn init<F>(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce(&mut sc_cli::LoggerBuilder),
|
||||
{
|
||||
unreachable!("PezkuwiCli is never initialized; qed");
|
||||
}
|
||||
|
||||
fn chain_id(&self, is_dev: bool) -> Result<String> {
|
||||
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<sc_service::Role> {
|
||||
self.base.base.role(is_dev)
|
||||
}
|
||||
|
||||
fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
|
||||
self.base.base.transaction_pool(is_dev)
|
||||
}
|
||||
|
||||
fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
|
||||
self.base.base.trie_cache_maximum_size()
|
||||
}
|
||||
|
||||
fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
|
||||
self.base.base.rpc_methods()
|
||||
}
|
||||
|
||||
fn rpc_max_connections(&self) -> Result<u32> {
|
||||
self.base.base.rpc_max_connections()
|
||||
}
|
||||
|
||||
fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
|
||||
self.base.base.rpc_cors(is_dev)
|
||||
}
|
||||
|
||||
fn default_heap_pages(&self) -> Result<Option<u64>> {
|
||||
self.base.base.default_heap_pages()
|
||||
}
|
||||
|
||||
fn force_authoring(&self) -> Result<bool> {
|
||||
self.base.base.force_authoring()
|
||||
}
|
||||
|
||||
fn disable_grandpa(&self) -> Result<bool> {
|
||||
self.base.base.disable_grandpa()
|
||||
}
|
||||
|
||||
fn max_runtime_instances(&self) -> Result<Option<usize>> {
|
||||
self.base.base.max_runtime_instances()
|
||||
}
|
||||
|
||||
fn announce_block(&self) -> Result<bool> {
|
||||
self.base.base.announce_block()
|
||||
}
|
||||
|
||||
fn telemetry_endpoints(
|
||||
&self,
|
||||
chain_spec: &Box<dyn ChainSpec>,
|
||||
) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
|
||||
self.base.base.telemetry_endpoints(chain_spec)
|
||||
}
|
||||
|
||||
fn node_name(&self) -> Result<String> {
|
||||
self.base.base.node_name()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//! Substrate Teyrchain Node Template CLI
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
mod chain_spec;
|
||||
mod cli;
|
||||
mod command;
|
||||
mod rpc;
|
||||
mod service;
|
||||
|
||||
fn main() -> sc_cli::Result<()> {
|
||||
command::run()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//! A collection of node-specific RPC methods.
|
||||
//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
|
||||
//! used by Substrate nodes. This file extends those RPC definitions with
|
||||
//! capabilities that are specific to this project's runtime configuration.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use teyrchain_template_runtime::{opaque::Block, AccountId, Balance, Nonce};
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use sc_transaction_pool_api::TransactionPool;
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_block_builder::BlockBuilder;
|
||||
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
|
||||
|
||||
/// A type representing all RPC extensions.
|
||||
pub type RpcExtension = jsonrpsee::RpcModule<()>;
|
||||
|
||||
/// Full client dependencies
|
||||
pub struct FullDeps<C, P> {
|
||||
/// The client instance to use.
|
||||
pub client: Arc<C>,
|
||||
/// Transaction pool instance.
|
||||
pub pool: Arc<P>,
|
||||
}
|
||||
|
||||
/// Instantiate all RPC extensions.
|
||||
pub fn create_full<C, P>(
|
||||
deps: FullDeps<C, P>,
|
||||
) -> Result<RpcExtension, Box<dyn std::error::Error + Send + Sync>>
|
||||
where
|
||||
C: ProvideRuntimeApi<Block>
|
||||
+ HeaderBackend<Block>
|
||||
+ HeaderMetadata<Block, Error = BlockChainError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
|
||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
|
||||
C::Api: BlockBuilder<Block>,
|
||||
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 } = deps;
|
||||
|
||||
module.merge(System::new(client.clone(), pool).into_rpc())?;
|
||||
module.merge(TransactionPayment::new(client).into_rpc())?;
|
||||
Ok(module)
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
|
||||
|
||||
// std
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
// Local Runtime Types
|
||||
use teyrchain_template_runtime::{
|
||||
apis::RuntimeApi,
|
||||
opaque::{Block, Hash},
|
||||
};
|
||||
|
||||
use codec::Encode;
|
||||
use pezkuwi_sdk::{cumulus_client_service::TeyrchainTracingExecuteBlock, *};
|
||||
|
||||
// Cumulus Imports
|
||||
use cumulus_client_bootnodes::{start_bootnode_tasks, StartBootnodeTasksParams};
|
||||
use cumulus_client_cli::CollatorOptions;
|
||||
use cumulus_client_collator::service::CollatorService;
|
||||
#[docify::export(lookahead_collator)]
|
||||
use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams};
|
||||
use cumulus_client_consensus_common::TeyrchainBlockImport as TTeyrchainBlockImport;
|
||||
use cumulus_client_service::{
|
||||
build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks,
|
||||
BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams,
|
||||
TeyrchainHostFunctions,
|
||||
};
|
||||
#[docify::export(cumulus_primitives)]
|
||||
use cumulus_primitives_core::{
|
||||
relay_chain::{CollatorPair, ValidationCode},
|
||||
GetTeyrchainInfo, ParaId,
|
||||
};
|
||||
use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface};
|
||||
|
||||
// Substrate Imports
|
||||
use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE;
|
||||
use pezkuwi_sdk::sc_network::PeerId;
|
||||
use prometheus_endpoint::Registry;
|
||||
use sc_client_api::Backend;
|
||||
use sc_consensus::ImportQueue;
|
||||
use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY};
|
||||
use sc_network::{NetworkBackend, NetworkBlock};
|
||||
use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager};
|
||||
use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle};
|
||||
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_keystore::KeystorePtr;
|
||||
|
||||
#[docify::export(wasm_executor)]
|
||||
type TeyrchainExecutor = WasmExecutor<TeyrchainHostFunctions>;
|
||||
|
||||
type TeyrchainClient = TFullClient<Block, RuntimeApi, TeyrchainExecutor>;
|
||||
|
||||
type TeyrchainBackend = TFullBackend<Block>;
|
||||
|
||||
type TeyrchainBlockImport = TTeyrchainBlockImport<Block, Arc<TeyrchainClient>, TeyrchainBackend>;
|
||||
|
||||
/// Assembly of PartialComponents (enough to run chain ops subcommands)
|
||||
pub type Service = PartialComponents<
|
||||
TeyrchainClient,
|
||||
TeyrchainBackend,
|
||||
(),
|
||||
sc_consensus::DefaultImportQueue<Block>,
|
||||
sc_transaction_pool::TransactionPoolHandle<Block, TeyrchainClient>,
|
||||
(TeyrchainBlockImport, Option<Telemetry>, Option<TelemetryWorkerHandle>),
|
||||
>;
|
||||
|
||||
/// Starts a `ServiceBuilder` for a full service.
|
||||
///
|
||||
/// Use this macro if you don't actually need the full service, but just the builder in order to
|
||||
/// be able to perform chain operations.
|
||||
#[docify::export(component_instantiation)]
|
||||
pub fn new_partial(config: &Configuration) -> Result<Service, 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 heap_pages = config
|
||||
.executor
|
||||
.default_heap_pages
|
||||
.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ });
|
||||
|
||||
let executor = TeyrchainExecutor::builder()
|
||||
.with_execution_method(config.executor.wasm_method)
|
||||
.with_onchain_heap_alloc_strategy(heap_pages)
|
||||
.with_offchain_heap_alloc_strategy(heap_pages)
|
||||
.with_max_runtime_instances(config.executor.max_runtime_instances)
|
||||
.with_runtime_cache_size(config.executor.runtime_cache_size)
|
||||
.build();
|
||||
|
||||
let (client, backend, keystore_container, task_manager) =
|
||||
sc_service::new_full_parts_record_import::<Block, RuntimeApi, _>(
|
||||
config,
|
||||
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
|
||||
executor,
|
||||
true,
|
||||
)?;
|
||||
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 = Arc::from(
|
||||
sc_transaction_pool::Builder::new(
|
||||
task_manager.spawn_essential_handle(),
|
||||
client.clone(),
|
||||
config.role.is_authority().into(),
|
||||
)
|
||||
.with_options(config.transaction_pool.clone())
|
||||
.with_prometheus(config.prometheus_registry())
|
||||
.build(),
|
||||
);
|
||||
|
||||
let block_import = TeyrchainBlockImport::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),
|
||||
})
|
||||
}
|
||||
|
||||
/// Build the import queue for the teyrchain runtime.
|
||||
fn build_import_queue(
|
||||
client: Arc<TeyrchainClient>,
|
||||
block_import: TeyrchainBlockImport,
|
||||
config: &Configuration,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
task_manager: &TaskManager,
|
||||
) -> sc_consensus::DefaultImportQueue<Block> {
|
||||
cumulus_client_consensus_aura::equivocation_import_queue::fully_verifying_import_queue::<
|
||||
sp_consensus_aura::sr25519::AuthorityPair,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
client,
|
||||
block_import,
|
||||
move |_, _| async move {
|
||||
let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
|
||||
Ok(timestamp)
|
||||
},
|
||||
&task_manager.spawn_essential_handle(),
|
||||
config.prometheus_registry(),
|
||||
telemetry,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn start_consensus(
|
||||
client: Arc<TeyrchainClient>,
|
||||
backend: Arc<TeyrchainBackend>,
|
||||
block_import: TeyrchainBlockImport,
|
||||
prometheus_registry: Option<&Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
task_manager: &TaskManager,
|
||||
relay_chain_interface: Arc<dyn RelayChainInterface>,
|
||||
transaction_pool: Arc<sc_transaction_pool::TransactionPoolHandle<Block, TeyrchainClient>>,
|
||||
keystore: KeystorePtr,
|
||||
relay_chain_slot_duration: Duration,
|
||||
para_id: ParaId,
|
||||
collator_key: CollatorPair,
|
||||
collator_peer_id: PeerId,
|
||||
overseer_handle: OverseerHandle,
|
||||
announce_block: Arc<dyn Fn(Hash, Option<Vec<u8>>) + Send + Sync>,
|
||||
) -> Result<(), sc_service::Error> {
|
||||
let proposer = sc_basic_authorship::ProposerFactory::with_proof_recording(
|
||||
task_manager.spawn_handle(),
|
||||
client.clone(),
|
||||
transaction_pool,
|
||||
prometheus_registry,
|
||||
telemetry.clone(),
|
||||
);
|
||||
let collator_service = CollatorService::new(
|
||||
client.clone(),
|
||||
Arc::new(task_manager.spawn_handle()),
|
||||
announce_block,
|
||||
client.clone(),
|
||||
);
|
||||
|
||||
let params = AuraParams {
|
||||
create_inherent_data_providers: move |_, ()| async move { Ok(()) },
|
||||
block_import,
|
||||
para_client: client.clone(),
|
||||
para_backend: backend,
|
||||
relay_client: relay_chain_interface,
|
||||
code_hash_provider: move |block_hash| {
|
||||
client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
|
||||
},
|
||||
keystore,
|
||||
collator_key,
|
||||
collator_peer_id,
|
||||
para_id,
|
||||
overseer_handle,
|
||||
relay_chain_slot_duration,
|
||||
proposer,
|
||||
collator_service,
|
||||
authoring_duration: Duration::from_millis(2000),
|
||||
reinitialize: false,
|
||||
max_pov_percentage: None,
|
||||
};
|
||||
let fut = aura::run::<Block, sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _>(
|
||||
params,
|
||||
);
|
||||
task_manager.spawn_essential_handle().spawn("aura", None, fut);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a node with the given teyrchain `Configuration` and relay chain `Configuration`.
|
||||
#[sc_tracing::logging::prefix_logs_with("Teyrchain")]
|
||||
pub async fn start_teyrchain_node(
|
||||
teyrchain_config: Configuration,
|
||||
pezkuwi_config: Configuration,
|
||||
collator_options: CollatorOptions,
|
||||
hwbench: Option<sc_sysinfo::HwBench>,
|
||||
) -> sc_service::error::Result<(TaskManager, Arc<TeyrchainClient>)> {
|
||||
let teyrchain_config = prepare_node_config(teyrchain_config);
|
||||
|
||||
let params = new_partial(&teyrchain_config)?;
|
||||
let (block_import, mut telemetry, telemetry_worker_handle) = params.other;
|
||||
|
||||
let prometheus_registry = teyrchain_config.prometheus_registry().cloned();
|
||||
let net_config = sc_network::config::FullNetworkConfiguration::<
|
||||
_,
|
||||
_,
|
||||
sc_network::NetworkWorker<Block, Hash>,
|
||||
>::new(&teyrchain_config.network, prometheus_registry.clone());
|
||||
|
||||
let client = params.client.clone();
|
||||
let backend = params.backend.clone();
|
||||
let mut task_manager = params.task_manager;
|
||||
|
||||
let relay_chain_fork_id = pezkuwi_config.chain_spec.fork_id().map(ToString::to_string);
|
||||
let teyrchain_fork_id = teyrchain_config.chain_spec.fork_id().map(ToString::to_string);
|
||||
let advertise_non_global_ips = teyrchain_config.network.allow_non_globals_in_dht;
|
||||
let teyrchain_public_addresses = teyrchain_config.network.public_addresses.clone();
|
||||
|
||||
let (relay_chain_interface, collator_key, relay_chain_network, paranode_rx) =
|
||||
build_relay_chain_interface(
|
||||
pezkuwi_config,
|
||||
&teyrchain_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 validator = teyrchain_config.role.is_authority();
|
||||
let transaction_pool = params.transaction_pool.clone();
|
||||
let import_queue_service = params.import_queue.service();
|
||||
|
||||
// Take teyrchain id from runtime.
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
let para_id = client
|
||||
.runtime_api()
|
||||
.teyrchain_id(best_hash)
|
||||
.map_err(|_| "Failed to retrieve teyrchain id from runtime. Make sure you implement `cumulus_primitives_core::GetParachaiNidentity` runtime API.")?;
|
||||
|
||||
// NOTE: because we use Aura here explicitly, we can use `CollatorSybilResistance::Resistant`
|
||||
// when starting the network.
|
||||
let (network, system_rpc_tx, tx_handler_controller, sync_service) =
|
||||
build_network(BuildNetworkParams {
|
||||
teyrchain_config: &teyrchain_config,
|
||||
net_config,
|
||||
client: client.clone(),
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
para_id,
|
||||
spawn_handle: task_manager.spawn_handle(),
|
||||
relay_chain_interface: relay_chain_interface.clone(),
|
||||
import_queue: params.import_queue,
|
||||
sybil_resistance_level: CollatorSybilResistance::Resistant, // because of Aura
|
||||
metrics: sc_network::NetworkWorker::<Block, Hash>::register_notification_metrics(
|
||||
teyrchain_config.prometheus_config.as_ref().map(|config| &config.registry),
|
||||
),
|
||||
})
|
||||
.await?;
|
||||
let collator_peer_id = network.local_peer_id();
|
||||
|
||||
if teyrchain_config.offchain_worker.enabled {
|
||||
use futures::FutureExt;
|
||||
|
||||
let offchain_workers =
|
||||
sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
|
||||
runtime_api_provider: client.clone(),
|
||||
keystore: Some(params.keystore_container.keystore()),
|
||||
offchain_db: backend.offchain_storage(),
|
||||
transaction_pool: Some(OffchainTransactionPoolFactory::new(
|
||||
transaction_pool.clone(),
|
||||
)),
|
||||
network_provider: Arc::new(network.clone()),
|
||||
is_validator: teyrchain_config.role.is_authority(),
|
||||
enable_http_requests: false,
|
||||
custom_extensions: move |_| vec![],
|
||||
})?;
|
||||
task_manager.spawn_handle().spawn(
|
||||
"offchain-workers-runner",
|
||||
"offchain-work",
|
||||
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
let rpc_builder = {
|
||||
let client = client.clone();
|
||||
let transaction_pool = transaction_pool.clone();
|
||||
|
||||
Box::new(move |_| {
|
||||
let deps =
|
||||
crate::rpc::FullDeps { client: client.clone(), pool: transaction_pool.clone() };
|
||||
|
||||
crate::rpc::create_full(deps).map_err(Into::into)
|
||||
})
|
||||
};
|
||||
|
||||
sc_service::spawn_tasks(sc_service::SpawnTasksParams {
|
||||
rpc_builder,
|
||||
client: client.clone(),
|
||||
transaction_pool: transaction_pool.clone(),
|
||||
task_manager: &mut task_manager,
|
||||
config: teyrchain_config,
|
||||
keystore: params.keystore_container.keystore(),
|
||||
backend: backend.clone(),
|
||||
network: network.clone(),
|
||||
sync_service: sync_service.clone(),
|
||||
system_rpc_tx,
|
||||
tx_handler_controller,
|
||||
telemetry: telemetry.as_mut(),
|
||||
tracing_execute_block: Some(Arc::new(TeyrchainTracingExecuteBlock::new(client.clone()))),
|
||||
})?;
|
||||
|
||||
if let Some(hwbench) = hwbench {
|
||||
sc_sysinfo::print_hwbench(&hwbench);
|
||||
// Here you can check whether the hardware meets your chains' requirements. Putting a link
|
||||
// in there and swapping out the requirements for your own are probably a good idea. The
|
||||
// requirements for a para-chain are dictated by its relay-chain.
|
||||
match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) {
|
||||
Err(err) if validator => {
|
||||
log::warn!(
|
||||
"⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.",
|
||||
err
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
if let Some(ref mut telemetry) = telemetry {
|
||||
let telemetry_handle = telemetry.handle();
|
||||
task_manager.spawn_handle().spawn(
|
||||
"telemetry_hwbench",
|
||||
None,
|
||||
sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let announce_block = {
|
||||
let sync_service = sync_service.clone();
|
||||
Arc::new(move |hash, data| sync_service.announce_block(hash, data))
|
||||
};
|
||||
|
||||
let relay_chain_slot_duration = Duration::from_secs(6);
|
||||
|
||||
let overseer_handle = relay_chain_interface
|
||||
.overseer_handle()
|
||||
.map_err(|e| sc_service::Error::Application(Box::new(e)))?;
|
||||
|
||||
start_relay_chain_tasks(StartRelayChainTasksParams {
|
||||
client: client.clone(),
|
||||
announce_block: announce_block.clone(),
|
||||
para_id,
|
||||
relay_chain_interface: relay_chain_interface.clone(),
|
||||
task_manager: &mut task_manager,
|
||||
da_recovery_profile: if validator {
|
||||
DARecoveryProfile::Collator
|
||||
} else {
|
||||
DARecoveryProfile::FullNode
|
||||
},
|
||||
import_queue: import_queue_service,
|
||||
relay_chain_slot_duration,
|
||||
recovery_handle: Box::new(overseer_handle.clone()),
|
||||
sync_service: sync_service.clone(),
|
||||
prometheus_registry: prometheus_registry.as_ref(),
|
||||
})?;
|
||||
|
||||
start_bootnode_tasks(StartBootnodeTasksParams {
|
||||
embedded_dht_bootnode: collator_options.embedded_dht_bootnode,
|
||||
dht_bootnode_discovery: collator_options.dht_bootnode_discovery,
|
||||
para_id,
|
||||
task_manager: &mut task_manager,
|
||||
relay_chain_interface: relay_chain_interface.clone(),
|
||||
relay_chain_fork_id,
|
||||
relay_chain_network,
|
||||
request_receiver: paranode_rx,
|
||||
teyrchain_network: network,
|
||||
advertise_non_global_ips,
|
||||
teyrchain_genesis_hash: client.chain_info().genesis_hash.encode(),
|
||||
teyrchain_fork_id,
|
||||
teyrchain_public_addresses,
|
||||
});
|
||||
|
||||
if validator {
|
||||
start_consensus(
|
||||
client.clone(),
|
||||
backend,
|
||||
block_import,
|
||||
prometheus_registry.as_ref(),
|
||||
telemetry.as_ref().map(|t| t.handle()),
|
||||
&task_manager,
|
||||
relay_chain_interface,
|
||||
transaction_pool,
|
||||
params.keystore_container.keystore(),
|
||||
relay_chain_slot_duration,
|
||||
para_id,
|
||||
collator_key.expect("Command line arguments do not allow this. qed"),
|
||||
collator_peer_id,
|
||||
overseer_handle,
|
||||
announce_block,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok((task_manager, client))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Pallets
|
||||
|
||||
ℹ️ A pallet is a unit of encapsulated logic, with a clearly defined responsibility. A pallet is analogous to a
|
||||
module in the runtime.
|
||||
|
||||
💁 In this template, there is a simple custom pallet based on the FRAME framework.
|
||||
|
||||
👉 Learn more about FRAME
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
||||
|
||||
🧑🏫 Please refer to
|
||||
[this guide](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html)
|
||||
to learn how to write a basic pallet.
|
||||
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "pallet-teyrchain-template"
|
||||
description = "FRAME pallet template for defining custom runtime logic."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
|
||||
frame = { workspace = true, default-features = false, features = [
|
||||
"experimental",
|
||||
"runtime",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
runtime-benchmarks = ["frame/runtime-benchmarks"]
|
||||
std = ["codec/std", "frame/std", "scale-info/std"]
|
||||
try-runtime = ["frame/try-runtime"]
|
||||
@@ -0,0 +1,33 @@
|
||||
//! Benchmarking setup for pallet-template
|
||||
|
||||
use super::*;
|
||||
use frame::{deps::frame_benchmarking::v2::*, prelude::*};
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
#[cfg(test)]
|
||||
use crate::pallet::Pallet as Template;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
#[benchmark]
|
||||
fn do_something() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
#[extrinsic_call]
|
||||
do_something(RawOrigin::Signed(caller), 100);
|
||||
|
||||
assert_eq!(Something::<T>::get().map(|v| v.block_number), Some(100u32.into()));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn cause_error() {
|
||||
Something::<T>::put(CompositeStruct { block_number: 100u32.into() });
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
#[extrinsic_call]
|
||||
cause_error(RawOrigin::Signed(caller));
|
||||
|
||||
assert_eq!(Something::<T>::get().map(|v| v.block_number), Some(101u32.into()));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
//! # Template Pallet
|
||||
//!
|
||||
//! A pallet with minimal functionality to help developers understand the essential components of
|
||||
//! writing a FRAME pallet. It is typically used in beginner tutorials or in Pezkuwi SDK template
|
||||
//! as a starting point for creating a new pallet and **not meant to be used in production**.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This template pallet contains basic examples of:
|
||||
//! - declaring a storage item that stores a single block-number
|
||||
//! - declaring and using events
|
||||
//! - declaring and using errors
|
||||
//! - a dispatchable function that allows a user to set a new value to storage and emits an event
|
||||
//! upon success
|
||||
//! - another dispatchable function that causes a custom error to be thrown
|
||||
//!
|
||||
//! Each pallet section is annotated with an attribute using the `#[pallet::...]` procedural macro.
|
||||
//! This macro generates the necessary code for a pallet to be aggregated into a FRAME runtime.
|
||||
//!
|
||||
//! To get started with pallet development, consider using this tutorial:
|
||||
//!
|
||||
//! <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html>
|
||||
//!
|
||||
//! And reading the main documentation of the `frame` crate:
|
||||
//!
|
||||
//! <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html>
|
||||
//!
|
||||
//! And looking at the frame [`kitchen-sink`](https://docs.pezkuwichain.io/sdk/master/pallet_example_kitchensink/index.html)
|
||||
//! pallet, a showcase of all pallet macros.
|
||||
//!
|
||||
//! ### Pallet Sections
|
||||
//!
|
||||
//! The pallet sections in this template are:
|
||||
//!
|
||||
//! - A **configuration trait** that defines the types and parameters which the pallet depends on
|
||||
//! (denoted by the `#[pallet::config]` attribute). See: [`Config`].
|
||||
//! - A **means to store pallet-specific data** (denoted by the `#[pallet::storage]` attribute).
|
||||
//! See: [`storage_types`].
|
||||
//! - A **declaration of the events** this pallet emits (denoted by the `#[pallet::event]`
|
||||
//! attribute). See: [`Event`].
|
||||
//! - A **declaration of the errors** that this pallet can throw (denoted by the `#[pallet::error]`
|
||||
//! attribute). See: [`Error`].
|
||||
//! - A **set of dispatchable functions** that define the pallet's functionality (denoted by the
|
||||
//! `#[pallet::call]` attribute). See: [`dispatchables`].
|
||||
//!
|
||||
//! Run `cargo doc --package pallet-template --open` to view this pallet's documentation.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod weights;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html>
|
||||
// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html>
|
||||
//
|
||||
// To see a full list of `pallet` macros and their use cases, see:
|
||||
// <https://docs.pezkuwichain.io/sdk/master/pallet_example_kitchensink/index.html>
|
||||
// <https://docs.pezkuwichain.io/sdk/master/frame_support/pallet_macros/index.html>
|
||||
#[frame::pallet]
|
||||
pub mod pallet {
|
||||
use frame::prelude::*;
|
||||
|
||||
/// Configure the pallet by specifying the parameters and types on which it depends.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
#[allow(deprecated)]
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// A type representing the weights required by the dispatchables of this pallet.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// A struct to store a single block-number. Has all the right derives to store it in storage.
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/frame_storage_derives/index.html>
|
||||
#[derive(
|
||||
Encode, Decode, MaxEncodedLen, TypeInfo, CloneNoBound, PartialEqNoBound, DefaultNoBound,
|
||||
)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CompositeStruct<T: Config> {
|
||||
/// A block number.
|
||||
pub(crate) block_number: BlockNumberFor<T>,
|
||||
}
|
||||
|
||||
/// The pallet's storage items.
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#storage>
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/frame_support/pallet_macros/attr.storage.html>
|
||||
#[pallet::storage]
|
||||
pub type Something<T: Config> = StorageValue<_, CompositeStruct<T>>;
|
||||
|
||||
/// Pallets use events to inform users when important changes are made.
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#event-and-error>
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// We usually use passive tense for events.
|
||||
SomethingStored { block_number: BlockNumberFor<T>, who: T::AccountId },
|
||||
}
|
||||
|
||||
/// Errors inform users that something went wrong.
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#event-and-error>
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Error names should be descriptive.
|
||||
NoneValue,
|
||||
/// Errors should have helpful documentation associated with them.
|
||||
StorageOverflow,
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
/// Dispatchable functions allows users to interact with the pallet and invoke state changes.
|
||||
/// These functions materialize as "extrinsics", which are often compared to transactions.
|
||||
/// Dispatchable functions must be annotated with a weight and must return a DispatchResult.
|
||||
/// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html#dispatchables>
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// An example dispatchable that takes a singles value as a parameter, writes the value to
|
||||
/// storage and emits an event. This function must be dispatched by a signed extrinsic.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
|
||||
pub fn do_something(origin: OriginFor<T>, bn: u32) -> DispatchResultWithPostInfo {
|
||||
// Check that the extrinsic was signed and get the signer.
|
||||
// This function will return an error if the extrinsic is not signed.
|
||||
// <https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/frame_origin/index.html>
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
// Convert the u32 into a block number. This is possible because the set of trait bounds
|
||||
// defined in [`frame_system::Config::BlockNumber`].
|
||||
let block_number: BlockNumberFor<T> = bn.into();
|
||||
|
||||
// Update storage.
|
||||
<Something<T>>::put(CompositeStruct { block_number });
|
||||
|
||||
// Emit an event.
|
||||
Self::deposit_event(Event::SomethingStored { block_number, who });
|
||||
|
||||
// Return a successful [`DispatchResultWithPostInfo`] or [`DispatchResult`].
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// An example dispatchable that may throw a custom error.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().reads_writes(1,1))]
|
||||
pub fn cause_error(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
|
||||
let _who = ensure_signed(origin)?;
|
||||
|
||||
// Read a value from storage.
|
||||
match <Something<T>>::get() {
|
||||
// Return an error if the value has not been set.
|
||||
None => Err(Error::<T>::NoneValue)?,
|
||||
Some(mut old) => {
|
||||
// Increment the value read from storage; will error in the event of overflow.
|
||||
old.block_number = old
|
||||
.block_number
|
||||
.checked_add(&One::one())
|
||||
// ^^ equivalent is to:
|
||||
// .checked_add(&1u32.into())
|
||||
// both of which build a `One` instance for the type `BlockNumber`.
|
||||
.ok_or(Error::<T>::StorageOverflow)?;
|
||||
// Update the value in storage with the incremented result.
|
||||
<Something<T>>::put(old);
|
||||
// Explore how you can rewrite this using
|
||||
// [`frame_support::storage::StorageValue::mutate`].
|
||||
Ok(().into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use frame::{
|
||||
deps::{frame_support::weights::constants::RocksDbWeight, frame_system::GenesisConfig},
|
||||
prelude::*,
|
||||
runtime::prelude::*,
|
||||
testing_prelude::*,
|
||||
};
|
||||
|
||||
// Configure a mock runtime to test the pallet.
|
||||
#[frame_construct_runtime]
|
||||
mod test_runtime {
|
||||
#[runtime::runtime]
|
||||
#[runtime::derive(
|
||||
RuntimeCall,
|
||||
RuntimeEvent,
|
||||
RuntimeError,
|
||||
RuntimeOrigin,
|
||||
RuntimeFreezeReason,
|
||||
RuntimeHoldReason,
|
||||
RuntimeSlashReason,
|
||||
RuntimeLockId,
|
||||
RuntimeTask,
|
||||
RuntimeViewFunction
|
||||
)]
|
||||
pub struct Test;
|
||||
|
||||
#[runtime::pallet_index(0)]
|
||||
pub type System = frame_system;
|
||||
#[runtime::pallet_index(1)]
|
||||
pub type Template = crate;
|
||||
}
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Nonce = u64;
|
||||
type Block = MockBlock<Test>;
|
||||
type BlockHashCount = ConstU64<250>;
|
||||
type DbWeight = RocksDbWeight;
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
// Build genesis storage according to the mock runtime.
|
||||
pub fn new_test_ext() -> TestState {
|
||||
GenesisConfig::<Test>::default().build_storage().unwrap().into()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::{mock::*, Error, Something};
|
||||
use frame::testing_prelude::*;
|
||||
|
||||
#[test]
|
||||
fn it_works_for_default_value() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Dispatch a signed extrinsic.
|
||||
assert_ok!(Template::do_something(RuntimeOrigin::signed(1), 42));
|
||||
// Read pallet storage and assert an expected result.
|
||||
assert_eq!(Something::<Test>::get().map(|v| v.block_number), Some(42));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correct_error_for_none_value() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Ensure the expected error is thrown when no value is present.
|
||||
assert_noop!(Template::cause_error(RuntimeOrigin::signed(1)), Error::<Test>::NoneValue);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
//! Autogenerated weights for pallet_template
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `_`, CPU: `<UNKNOWN>`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// ../../target/release/node-template
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain
|
||||
// dev
|
||||
// --pallet
|
||||
// pallet_template
|
||||
// --extrinsic
|
||||
// *
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --wasm-execution=compiled
|
||||
// --output
|
||||
// pallets/template/src/weights.rs
|
||||
// --template
|
||||
// ../../.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame::{deps::frame_support::weights::constants::RocksDbWeight, prelude::*};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_template.
|
||||
pub trait WeightInfo {
|
||||
fn do_something() -> Weight;
|
||||
fn cause_error() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_template using the Substrate node and recommended hardware.
|
||||
#[cfg_attr(
|
||||
not(feature = "std"),
|
||||
deprecated(
|
||||
note = "SubstrateWeight is auto-generated and should not be used in production. Replace it with runtime benchmarked weights."
|
||||
)
|
||||
)]
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
/// Storage: Template Something (r:0 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn do_something() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 8_000_000 picoseconds.
|
||||
Weight::from_parts(9_000_000, 0)
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: Template Something (r:1 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn cause_error() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `32`
|
||||
// Estimated: `1489`
|
||||
// Minimum execution time: 6_000_000 picoseconds.
|
||||
Weight::from_parts(6_000_000, 1489)
|
||||
.saturating_add(T::DbWeight::get().reads(1_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
/// Storage: Template Something (r:0 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn do_something() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 8_000_000 picoseconds.
|
||||
Weight::from_parts(9_000_000, 0)
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: Template Something (r:1 w:1)
|
||||
/// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
fn cause_error() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `32`
|
||||
// Estimated: `1489`
|
||||
// Minimum execution time: 6_000_000 picoseconds.
|
||||
Weight::from_parts(6_000_000, 1489)
|
||||
.saturating_add(RocksDbWeight::get().reads(1_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
[package]
|
||||
name = "teyrchain-template-runtime"
|
||||
description = "A teyrchain runtime template built with Substrate and Cumulus, part of Pezkuwi Sdk."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
docify = { workspace = true }
|
||||
hex-literal = { optional = true, workspace = true, default-features = true }
|
||||
log = { workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
serde_json = { workspace = true, default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
smallvec = { workspace = true, default-features = true }
|
||||
|
||||
# Local
|
||||
pallet-teyrchain-template = { workspace = true }
|
||||
|
||||
pezkuwi-sdk = { workspace = true, default-features = false, features = [
|
||||
"pallet-aura",
|
||||
"pallet-authorship",
|
||||
"pallet-balances",
|
||||
"pallet-message-queue",
|
||||
"pallet-session",
|
||||
"pallet-sudo",
|
||||
"pallet-timestamp",
|
||||
"pallet-transaction-payment",
|
||||
"pallet-transaction-payment-rpc-runtime-api",
|
||||
|
||||
"pallet-xcm",
|
||||
"pezkuwi-runtime-common",
|
||||
"pezkuwi-teyrchain-primitives",
|
||||
"staging-xcm",
|
||||
"staging-xcm-builder",
|
||||
"staging-xcm-executor",
|
||||
|
||||
"cumulus-pallet-aura-ext",
|
||||
"cumulus-pallet-session-benchmarking",
|
||||
"cumulus-pallet-weight-reclaim",
|
||||
"cumulus-pallet-xcm",
|
||||
"cumulus-pallet-xcmp-queue",
|
||||
"cumulus-primitives-aura",
|
||||
"cumulus-primitives-core",
|
||||
"cumulus-primitives-utility",
|
||||
"pallet-collator-selection",
|
||||
"staging-teyrchain-info",
|
||||
"teyrchains-common",
|
||||
|
||||
"runtime",
|
||||
] }
|
||||
|
||||
# Cumulus
|
||||
cumulus-pallet-teyrchain-system = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
docify = { workspace = true }
|
||||
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"cumulus-pallet-teyrchain-system/std",
|
||||
"log/std",
|
||||
"pallet-teyrchain-template/std",
|
||||
"pezkuwi-sdk/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
"substrate-wasm-builder",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"cumulus-pallet-teyrchain-system/runtime-benchmarks",
|
||||
"hex-literal",
|
||||
"pallet-teyrchain-template/runtime-benchmarks",
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
"substrate-wasm-builder?/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"cumulus-pallet-teyrchain-system/try-runtime",
|
||||
"pallet-teyrchain-template/try-runtime",
|
||||
"pezkuwi-sdk/try-runtime",
|
||||
]
|
||||
|
||||
# Enable the metadata hash generation.
|
||||
#
|
||||
# This is hidden behind a feature because it increases the compile time.
|
||||
# The wasm binary needs to be compiled twice, once to fetch the metadata,
|
||||
# generate the metadata hash and then a second time with the
|
||||
# `RUNTIME_METADATA_HASH` environment variable set for the `CheckMetadataHash`
|
||||
# extension.
|
||||
metadata-hash = ["substrate-wasm-builder/metadata-hash"]
|
||||
|
||||
# A convenience feature for enabling things when doing a build
|
||||
# for an on-chain release.
|
||||
on-chain-release-build = ["metadata-hash"]
|
||||
@@ -0,0 +1,10 @@
|
||||
# Runtime
|
||||
|
||||
ℹ️ The runtime (in other words, a state transition function), refers to the core logic of the teyrchain that is
|
||||
responsible for validating blocks and executing the state changes they define.
|
||||
|
||||
💁 The runtime in this template is constructed using ready-made FRAME pallets that ship with
|
||||
[Pezkuwi SDK](https://github.com/pezkuwichain/pezkuwi-sdk), and a [template for a custom pallet](../pallets/README.md).
|
||||
|
||||
👉 Learn more about FRAME
|
||||
[here](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
||||
@@ -0,0 +1,17 @@
|
||||
#[cfg(all(feature = "std", feature = "metadata-hash"))]
|
||||
#[docify::export(template_enable_metadata_hash)]
|
||||
fn main() {
|
||||
substrate_wasm_builder::WasmBuilder::init_with_defaults()
|
||||
.enable_metadata_hash("UNIT", 12)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(feature = "metadata-hash")))]
|
||||
fn main() {
|
||||
substrate_wasm_builder::WasmBuilder::build_using_defaults();
|
||||
}
|
||||
|
||||
/// The wasm builder is deactivated when compiling
|
||||
/// this crate for wasm to speed up the compilation.
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn main() {}
|
||||
@@ -0,0 +1,328 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
// External crates imports
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use pezkuwi_sdk::{staging_teyrchain_info as teyrchain_info, *};
|
||||
|
||||
use cumulus_primitives_core::ParaId;
|
||||
use frame_support::{
|
||||
genesis_builder_helper::{build_state, get_preset},
|
||||
weights::Weight,
|
||||
};
|
||||
use pallet_aura::Authorities;
|
||||
use sp_api::impl_runtime_apis;
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
|
||||
use sp_runtime::{
|
||||
traits::Block as BlockT,
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult,
|
||||
};
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
// Local module imports
|
||||
use super::{
|
||||
AccountId, Balance, Block, ConsensusHook, Executive, InherentDataExt, Nonce, Runtime,
|
||||
RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TeyrchainSystem, TransactionPayment,
|
||||
SLOT_DURATION, VERSION,
|
||||
};
|
||||
|
||||
// we move some impls outside so we can easily use them with `docify`.
|
||||
impl Runtime {
|
||||
#[docify::export]
|
||||
fn impl_slot_duration() -> sp_consensus_aura::SlotDuration {
|
||||
sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION)
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
fn impl_can_build_upon(
|
||||
included_hash: <Block as BlockT>::Hash,
|
||||
slot: cumulus_primitives_aura::Slot,
|
||||
) -> bool {
|
||||
ConsensusHook::can_build_upon(included_hash, slot)
|
||||
}
|
||||
}
|
||||
|
||||
impl_runtime_apis! {
|
||||
impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
|
||||
fn slot_duration() -> sp_consensus_aura::SlotDuration {
|
||||
Runtime::impl_slot_duration()
|
||||
}
|
||||
|
||||
fn authorities() -> Vec<AuraId> {
|
||||
Authorities::<Runtime>::get().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
|
||||
fn relay_parent_offset() -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl cumulus_primitives_aura::AuraUnincludedSegmentApi<Block> for Runtime {
|
||||
fn can_build_upon(
|
||||
included_hash: <Block as BlockT>::Hash,
|
||||
slot: cumulus_primitives_aura::Slot,
|
||||
) -> bool {
|
||||
Runtime::impl_can_build_upon(included_hash, slot)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_api::Core<Block> for Runtime {
|
||||
fn version() -> RuntimeVersion {
|
||||
VERSION
|
||||
}
|
||||
|
||||
fn execute_block(block: <Block as BlockT>::LazyBlock) {
|
||||
Executive::execute_block(block)
|
||||
}
|
||||
|
||||
fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
|
||||
Executive::initialize_block(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_api::Metadata<Block> for Runtime {
|
||||
fn metadata() -> OpaqueMetadata {
|
||||
OpaqueMetadata::new(Runtime::metadata().into())
|
||||
}
|
||||
|
||||
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
|
||||
Runtime::metadata_at_version(version)
|
||||
}
|
||||
|
||||
fn metadata_versions() -> Vec<u32> {
|
||||
Runtime::metadata_versions()
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_support::view_functions::runtime_api::RuntimeViewFunction<Block> for Runtime {
|
||||
fn execute_view_function(id: frame_support::view_functions::ViewFunctionId, input: Vec<u8>) -> Result<Vec<u8>, frame_support::view_functions::ViewFunctionDispatchError> {
|
||||
Runtime::execute_view_function(id, input)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_block_builder::BlockBuilder<Block> for Runtime {
|
||||
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
|
||||
Executive::apply_extrinsic(extrinsic)
|
||||
}
|
||||
|
||||
fn finalize_block() -> <Block as BlockT>::Header {
|
||||
Executive::finalize_block()
|
||||
}
|
||||
|
||||
fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
|
||||
data.create_extrinsics()
|
||||
}
|
||||
|
||||
fn check_inherents(
|
||||
block: <Block as BlockT>::LazyBlock,
|
||||
data: sp_inherents::InherentData,
|
||||
) -> sp_inherents::CheckInherentsResult {
|
||||
data.check_extrinsics(&block)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(
|
||||
source: TransactionSource,
|
||||
tx: <Block as BlockT>::Extrinsic,
|
||||
block_hash: <Block as BlockT>::Hash,
|
||||
) -> TransactionValidity {
|
||||
Executive::validate_transaction(source, tx, block_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
|
||||
fn offchain_worker(header: &<Block as BlockT>::Header) {
|
||||
Executive::offchain_worker(header)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_session::SessionKeys<Block> for Runtime {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
SessionKeys::generate(seed)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
|
||||
SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
|
||||
fn account_nonce(account: AccountId) -> Nonce {
|
||||
System::account_nonce(account)
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {
|
||||
fn query_info(
|
||||
uxt: <Block as BlockT>::Extrinsic,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
|
||||
TransactionPayment::query_info(uxt, len)
|
||||
}
|
||||
fn query_fee_details(
|
||||
uxt: <Block as BlockT>::Extrinsic,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::FeeDetails<Balance> {
|
||||
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<Block, Balance, RuntimeCall>
|
||||
for Runtime
|
||||
{
|
||||
fn query_call_info(
|
||||
call: RuntimeCall,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
|
||||
TransactionPayment::query_call_info(call, len)
|
||||
}
|
||||
fn query_call_fee_details(
|
||||
call: RuntimeCall,
|
||||
len: u32,
|
||||
) -> pallet_transaction_payment::FeeDetails<Balance> {
|
||||
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<Block> for Runtime {
|
||||
fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {
|
||||
TeyrchainSystem::collect_collation_info(header)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
impl frame_try_runtime::TryRuntime<Block> for Runtime {
|
||||
fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
|
||||
use super::configs::RuntimeBlockWeights;
|
||||
|
||||
let weight = Executive::try_runtime_upgrade(checks).unwrap();
|
||||
(weight, RuntimeBlockWeights::get().max_block)
|
||||
}
|
||||
|
||||
fn execute_block(
|
||||
block: <Block as BlockT>::LazyBlock,
|
||||
state_root_check: bool,
|
||||
signature_check: bool,
|
||||
select: frame_try_runtime::TryStateSelect,
|
||||
) -> Weight {
|
||||
// NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to
|
||||
// have a backtrace here.
|
||||
Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl frame_benchmarking::Benchmark<Block> for Runtime {
|
||||
fn benchmark_metadata(extra: bool) -> (
|
||||
Vec<frame_benchmarking::BenchmarkList>,
|
||||
Vec<pezkuwi_sdk::frame_support::traits::StorageInfo>,
|
||||
) {
|
||||
use frame_benchmarking::BenchmarkList;
|
||||
use pezkuwi_sdk::frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
use super::*;
|
||||
|
||||
let mut list = Vec::<BenchmarkList>::new();
|
||||
list_benchmarks!(list, extra);
|
||||
|
||||
let storage_info = AllPalletsWithSystem::storage_info();
|
||||
(list, storage_info)
|
||||
}
|
||||
|
||||
#[allow(non_local_definitions)]
|
||||
fn dispatch_benchmark(
|
||||
config: frame_benchmarking::BenchmarkConfig
|
||||
) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, alloc::string::String> {
|
||||
use frame_benchmarking::{BenchmarkError, BenchmarkBatch};
|
||||
use super::*;
|
||||
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
impl frame_system_benchmarking::Config for Runtime {
|
||||
fn setup_set_code_requirements(code: &Vec<u8>) -> Result<(), BenchmarkError> {
|
||||
TeyrchainSystem::initialize_for_set_code_benchmark(code.len() as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_set_code() {
|
||||
System::assert_last_event(cumulus_pallet_teyrchain_system::Event::<Runtime>::ValidationFunctionStored.into());
|
||||
}
|
||||
}
|
||||
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
|
||||
|
||||
use pezkuwi_sdk::frame_support::traits::WhitelistedStorageKeys;
|
||||
let whitelist = AllPalletsWithSystem::whitelisted_storage_keys();
|
||||
|
||||
let mut batches = Vec::<BenchmarkBatch>::new();
|
||||
let params = (&config, &whitelist);
|
||||
add_benchmarks!(params, batches);
|
||||
|
||||
if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
|
||||
Ok(batches)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
|
||||
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
|
||||
build_state::<RuntimeGenesisConfig>(config)
|
||||
}
|
||||
|
||||
fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
|
||||
get_preset::<RuntimeGenesisConfig>(id, crate::genesis_config_presets::get_preset)
|
||||
}
|
||||
|
||||
fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
|
||||
crate::genesis_config_presets::preset_names()
|
||||
}
|
||||
}
|
||||
|
||||
impl cumulus_primitives_core::GetTeyrchainInfo<Block> for Runtime {
|
||||
fn teyrchain_id() -> ParaId {
|
||||
teyrchain_info::Pallet::<Runtime>::teyrchain_id()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
pezkuwi_sdk::frame_benchmarking::define_benchmarks!(
|
||||
[frame_system, SystemBench::<Runtime>]
|
||||
[pallet_balances, Balances]
|
||||
[pallet_session, SessionBench::<Runtime>]
|
||||
[pallet_timestamp, Timestamp]
|
||||
[pallet_message_queue, MessageQueue]
|
||||
[pallet_sudo, Sudo]
|
||||
[pallet_collator_selection, CollatorSelection]
|
||||
[cumulus_pallet_teyrchain_system, TeyrchainSystem]
|
||||
[cumulus_pallet_xcmp_queue, XcmpQueue]
|
||||
[cumulus_pallet_weight_reclaim, WeightReclaim]
|
||||
);
|
||||
@@ -0,0 +1,348 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors
|
||||
// of this software dedicate any and all copyright interest in the
|
||||
// software to the public domain. We make this dedication for the benefit
|
||||
// of the public at large and to the detriment of our heirs and
|
||||
// successors. We intend this dedication to be an overt act of
|
||||
// relinquishment in perpetuity of all present and future rights to this
|
||||
// software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org>
|
||||
|
||||
mod xcm_config;
|
||||
|
||||
use pezkuwi_sdk::{staging_teyrchain_info as teyrchain_info, staging_xcm as xcm, *};
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
use pezkuwi_sdk::{staging_xcm_builder as xcm_builder, staging_xcm_executor as xcm_executor};
|
||||
|
||||
// Substrate and Pezkuwi dependencies
|
||||
use cumulus_pallet_teyrchain_system::RelayNumberMonotonicallyIncreases;
|
||||
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
|
||||
use frame_support::{
|
||||
derive_impl,
|
||||
dispatch::DispatchClass,
|
||||
parameter_types,
|
||||
traits::{
|
||||
ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin, VariantCountOf,
|
||||
},
|
||||
weights::{ConstantMultiplier, Weight},
|
||||
PalletId,
|
||||
};
|
||||
use frame_system::{
|
||||
limits::{BlockLength, BlockWeights},
|
||||
EnsureRoot,
|
||||
};
|
||||
use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
|
||||
use pezkuwi_runtime_common::{
|
||||
xcm_sender::ExponentialPrice, BlockHashCount, SlowAdjustingFeeUpdate,
|
||||
};
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_runtime::Perbill;
|
||||
use sp_version::RuntimeVersion;
|
||||
use teyrchains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
|
||||
use xcm::latest::prelude::{AssetId, BodyId};
|
||||
|
||||
// Local module imports
|
||||
use super::{
|
||||
weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},
|
||||
AccountId, Aura, Balance, Balances, Block, BlockNumber, CollatorSelection, ConsensusHook, Hash,
|
||||
MessageQueue, Nonce, PalletInfo, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason,
|
||||
RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, SessionKeys, System, TeyrchainSystem,
|
||||
WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, CENTS, EXISTENTIAL_DEPOSIT, HOURS,
|
||||
MAXIMUM_BLOCK_WEIGHT, MICRO_UNIT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
|
||||
};
|
||||
use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin};
|
||||
|
||||
parameter_types! {
|
||||
pub const Version: RuntimeVersion = VERSION;
|
||||
|
||||
// This part is copied from Substrate's `bin/node/runtime/src/lib.rs`.
|
||||
// The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the
|
||||
// `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize
|
||||
// the lazy contract deletion.
|
||||
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);
|
||||
// Operational transactions have some extra reserved space, so that they
|
||||
// are included even if block reached `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 SS58Prefix: u16 = 42;
|
||||
}
|
||||
|
||||
/// All migrations of the runtime, aside from the ones declared in the pallets.
|
||||
///
|
||||
/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`.
|
||||
#[allow(unused_parens)]
|
||||
type SingleBlockMigrations = ();
|
||||
|
||||
/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from
|
||||
/// [`TeyrchainDefaultConfig`](`struct@frame_system::config_preludes::TeyrchainDefaultConfig`),
|
||||
/// but overridden as needed.
|
||||
#[derive_impl(frame_system::config_preludes::TeyrchainDefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
/// The identifier used to distinguish between accounts.
|
||||
type AccountId = AccountId;
|
||||
/// The index type for storing how many extrinsics an account has signed.
|
||||
type Nonce = Nonce;
|
||||
/// The type for hashing blocks and tries.
|
||||
type Hash = Hash;
|
||||
/// The block type.
|
||||
type Block = Block;
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
type BlockHashCount = BlockHashCount;
|
||||
/// Runtime version.
|
||||
type Version = Version;
|
||||
/// The data to be stored in an account.
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
/// The weight of database operations that the runtime can invoke.
|
||||
type DbWeight = RocksDbWeight;
|
||||
/// Block & extrinsics weights: base values and limits.
|
||||
type BlockWeights = RuntimeBlockWeights;
|
||||
/// The maximum length of a block (in bytes).
|
||||
type BlockLength = RuntimeBlockLength;
|
||||
/// This is used as an identifier of the chain. 42 is the generic substrate prefix.
|
||||
type SS58Prefix = SS58Prefix;
|
||||
/// The action to take on a Runtime Upgrade
|
||||
type OnSetCode = cumulus_pallet_teyrchain_system::TeyrchainSetCode<Self>;
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type SingleBlockMigrations = SingleBlockMigrations;
|
||||
}
|
||||
|
||||
/// Configure the palelt weight reclaim tx.
|
||||
impl cumulus_pallet_weight_reclaim::Config for Runtime {
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Runtime {
|
||||
/// A timestamp: milliseconds since the unix epoch.
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = Aura;
|
||||
type MinimumPeriod = ConstU64<0>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl pallet_authorship::Config for Runtime {
|
||||
type FindAuthor = pallet_session::FindAccountFromAuthorIndex<Self, Aura>;
|
||||
type EventHandler = (CollatorSelection,);
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Runtime {
|
||||
type MaxLocks = ConstU32<50>;
|
||||
/// The type for recording an account's balance.
|
||||
type Balance = Balance;
|
||||
/// The ubiquitous event type.
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
|
||||
type MaxReserves = ConstU32<50>;
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
type RuntimeHoldReason = RuntimeHoldReason;
|
||||
type RuntimeFreezeReason = RuntimeFreezeReason;
|
||||
type FreezeIdentifier = RuntimeFreezeReason;
|
||||
type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
|
||||
type DoneSlashHandler = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// Relay Chain `TransactionByteFee` / 10
|
||||
pub const TransactionByteFee: Balance = 10 * MICRO_UNIT;
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter<Balances, ()>;
|
||||
type WeightToFee = WeightToFee;
|
||||
type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
|
||||
type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
|
||||
type OperationalFeeMultiplier = ConstU8<5>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl pallet_sudo::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4);
|
||||
pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4);
|
||||
pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
|
||||
}
|
||||
|
||||
impl cumulus_pallet_teyrchain_system::Config for Runtime {
|
||||
type WeightInfo = ();
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type OnSystemEvent = ();
|
||||
type SelfParaId = teyrchain_info::Pallet<Runtime>;
|
||||
type OutboundXcmpMessageSource = XcmpQueue;
|
||||
type DmpQueue = frame_support::traits::EnqueueWithOrigin<MessageQueue, RelayOrigin>;
|
||||
type ReservedDmpWeight = ReservedDmpWeight;
|
||||
type XcmpMessageHandler = XcmpQueue;
|
||||
type ReservedXcmpWeight = ReservedXcmpWeight;
|
||||
type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
|
||||
type ConsensusHook = ConsensusHook;
|
||||
type RelayParentOffset = ConstU32<0>;
|
||||
}
|
||||
|
||||
impl teyrchain_info::Config for Runtime {}
|
||||
|
||||
parameter_types! {
|
||||
pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block;
|
||||
}
|
||||
|
||||
impl pallet_message_queue::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
|
||||
cumulus_primitives_core::AggregateMessageOrigin,
|
||||
>;
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
type MessageProcessor = xcm_builder::ProcessXcmMessage<
|
||||
AggregateMessageOrigin,
|
||||
xcm_executor::XcmExecutor<xcm_config::XcmConfig>,
|
||||
RuntimeCall,
|
||||
>;
|
||||
type Size = u32;
|
||||
// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
|
||||
type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
|
||||
type QueuePausedQuery = NarrowOriginToSibling<XcmpQueue>;
|
||||
type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>;
|
||||
type MaxStale = sp_core::ConstU32<8>;
|
||||
type ServiceWeight = MessageQueueServiceWeight;
|
||||
type IdleMaxServiceWeight = ();
|
||||
}
|
||||
|
||||
impl cumulus_pallet_aura_ext::Config for Runtime {}
|
||||
|
||||
parameter_types! {
|
||||
/// The asset ID for the asset that we use to pay for message delivery fees.
|
||||
pub FeeAssetId: AssetId = AssetId(xcm_config::RelayLocation::get());
|
||||
/// The base fee for the message delivery fees.
|
||||
pub const ToSiblingBaseDeliveryFee: u128 = CENTS.saturating_mul(3);
|
||||
pub const ToParentBaseDeliveryFee: u128 = CENTS.saturating_mul(3);
|
||||
}
|
||||
|
||||
/// The price for delivering XCM messages to sibling teyrchains.
|
||||
pub type PriceForSiblingTeyrchainDelivery =
|
||||
ExponentialPrice<FeeAssetId, ToSiblingBaseDeliveryFee, TransactionByteFee, XcmpQueue>;
|
||||
|
||||
/// The price for delivering XCM messages to relay chain.
|
||||
pub type PriceForParentDelivery =
|
||||
ExponentialPrice<FeeAssetId, ToParentBaseDeliveryFee, TransactionByteFee, TeyrchainSystem>;
|
||||
|
||||
impl cumulus_pallet_xcmp_queue::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ChannelInfo = TeyrchainSystem;
|
||||
type VersionWrapper = ();
|
||||
// Enqueue XCMP messages from siblings for later processing.
|
||||
type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
|
||||
type MaxInboundSuspended = sp_core::ConstU32<1_000>;
|
||||
type MaxActiveOutboundChannels = ConstU32<128>;
|
||||
type MaxPageSize = ConstU32<{ 1 << 16 }>;
|
||||
type ControllerOrigin = EnsureRoot<AccountId>;
|
||||
type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
|
||||
type WeightInfo = ();
|
||||
type PriceForSiblingDelivery = PriceForSiblingTeyrchainDelivery;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const Period: u32 = 6 * HOURS;
|
||||
pub const Offset: u32 = 0;
|
||||
}
|
||||
|
||||
impl pallet_session::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ValidatorId = <Self as frame_system::Config>::AccountId;
|
||||
// we don't have stash and controller, thus we don't need the convert as well.
|
||||
type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
|
||||
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
|
||||
type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
|
||||
type SessionManager = CollatorSelection;
|
||||
// Essentially just Aura, but let's be pedantic.
|
||||
type SessionHandler = <SessionKeys as sp_runtime::traits::OpaqueKeys>::KeyTypeIdProviders;
|
||||
type Keys = SessionKeys;
|
||||
type DisablingStrategy = ();
|
||||
type WeightInfo = ();
|
||||
type Currency = Balances;
|
||||
type KeyDeposit = ();
|
||||
}
|
||||
|
||||
#[docify::export(aura_config)]
|
||||
impl pallet_aura::Config for Runtime {
|
||||
type AuthorityId = AuraId;
|
||||
type DisabledValidators = ();
|
||||
type MaxAuthorities = ConstU32<100_000>;
|
||||
type AllowMultipleBlocksPerSlot = ConstBool<true>;
|
||||
type SlotDuration = ConstU64<SLOT_DURATION>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const PotId: PalletId = PalletId(*b"PotStake");
|
||||
pub const SessionLength: BlockNumber = 6 * HOURS;
|
||||
// StakingAdmin pluralistic body.
|
||||
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
|
||||
}
|
||||
|
||||
/// We allow root and the StakingAdmin to execute privileged collator selection operations.
|
||||
pub type CollatorSelectionUpdateOrigin = EitherOfDiverse<
|
||||
EnsureRoot<AccountId>,
|
||||
EnsureXcm<IsVoiceOfBody<RelayLocation, StakingAdminBodyId>>,
|
||||
>;
|
||||
|
||||
impl pallet_collator_selection::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
type UpdateOrigin = CollatorSelectionUpdateOrigin;
|
||||
type PotId = PotId;
|
||||
type MaxCandidates = ConstU32<100>;
|
||||
type MinEligibleCollators = ConstU32<4>;
|
||||
type MaxInvulnerables = ConstU32<20>;
|
||||
// should be a multiple of session or things will get inconsistent
|
||||
type KickThreshold = Period;
|
||||
type ValidatorId = <Self as frame_system::Config>::AccountId;
|
||||
type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
|
||||
type ValidatorRegistration = Session;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Configure the pallet template in pallets/template.
|
||||
impl pallet_teyrchain_template::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = pallet_teyrchain_template::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
use super::PriceForParentDelivery;
|
||||
use crate::{
|
||||
AccountId, AllPalletsWithSystem, Balances, PezkuwiXcm, Runtime, RuntimeCall, RuntimeEvent,
|
||||
RuntimeOrigin, TeyrchainInfo, TeyrchainSystem, WeightToFee, XcmpQueue,
|
||||
};
|
||||
|
||||
use pezkuwi_sdk::{
|
||||
staging_xcm as xcm, staging_xcm_builder as xcm_builder, staging_xcm_executor as xcm_executor, *,
|
||||
};
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{ConstU32, Contains, Everything, Nothing},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_system::EnsureRoot;
|
||||
use pallet_xcm::XcmPassthrough;
|
||||
use pezkuwi_runtime_common::impls::ToAuthor;
|
||||
use pezkuwi_sdk::{
|
||||
pezkuwi_sdk_frame::traits::Disabled,
|
||||
staging_xcm_builder::{DenyRecursively, DenyThenTry},
|
||||
};
|
||||
use pezkuwi_teyrchain_primitives::primitives::Sibling;
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_builder::{
|
||||
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom,
|
||||
DenyReserveTransferToRelayChain, EnsureXcmOrigin, FixedWeightBounds,
|
||||
FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, ParentIsPreset,
|
||||
RelayChainAsNative, SiblingTeyrchainAsNative, SiblingTeyrchainConvertsVia,
|
||||
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
|
||||
TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic,
|
||||
};
|
||||
use xcm_executor::XcmExecutor;
|
||||
|
||||
parameter_types! {
|
||||
pub const RelayLocation: Location = Location::parent();
|
||||
pub const RelayNetwork: Option<NetworkId> = None;
|
||||
pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
|
||||
// For the real deployment, it is recommended to set `RelayNetwork` according to the relay chain
|
||||
// and prepend `UniversalLocation` with `GlobalConsensus(RelayNetwork::get())`.
|
||||
pub UniversalLocation: InteriorLocation = Teyrchain(TeyrchainInfo::teyrchain_id().into()).into();
|
||||
}
|
||||
|
||||
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
|
||||
/// when determining ownership of accounts for asset transacting and when attempting to use XCM
|
||||
/// `Transact` in order to determine the dispatch Origin.
|
||||
pub type LocationToAccountId = (
|
||||
// The parent (Relay-chain) origin converts to the parent `AccountId`.
|
||||
ParentIsPreset<AccountId>,
|
||||
// Sibling teyrchain origins convert to AccountId via the `ParaId::into`.
|
||||
SiblingTeyrchainConvertsVia<Sibling, AccountId>,
|
||||
// Straight up local `AccountId32` origins just alias directly to `AccountId`.
|
||||
AccountId32Aliases<RelayNetwork, AccountId>,
|
||||
);
|
||||
|
||||
/// Means for transacting assets on this chain.
|
||||
pub type LocalAssetTransactor = FungibleAdapter<
|
||||
// Use this currency:
|
||||
Balances,
|
||||
// Use this currency when it is a fungible asset matching the given location or name:
|
||||
IsConcrete<RelayLocation>,
|
||||
// Do a simple punn to convert an AccountId32 Location into a native chain account ID:
|
||||
LocationToAccountId,
|
||||
// Our chain's account ID type (we can't get away without mentioning it explicitly):
|
||||
AccountId,
|
||||
// We don't track any teleports.
|
||||
(),
|
||||
>;
|
||||
|
||||
/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
|
||||
/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
|
||||
/// biases the kind of local `Origin` it will become.
|
||||
pub type XcmOriginToTransactDispatchOrigin = (
|
||||
// Sovereign account converter; this attempts to derive an `AccountId` from the origin location
|
||||
// using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
|
||||
// foreign chains who want to have a local sovereign account on this chain which they control.
|
||||
SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
|
||||
// Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when
|
||||
// recognized.
|
||||
RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
|
||||
// Native converter for sibling Teyrchains; will convert to a `SiblingPara` origin when
|
||||
// recognized.
|
||||
SiblingTeyrchainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
|
||||
// Native signed account converter; this just converts an `AccountId32` origin into a normal
|
||||
// `RuntimeOrigin::Signed` origin of the same 32-byte value.
|
||||
SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
|
||||
// Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
|
||||
XcmPassthrough<RuntimeOrigin>,
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
// One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
|
||||
pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
|
||||
pub const MaxInstructions: u32 = 100;
|
||||
pub const MaxAssetsIntoHolding: u32 = 64;
|
||||
}
|
||||
|
||||
pub struct ParentOrParentsExecutivePlurality;
|
||||
impl Contains<Location> for ParentOrParentsExecutivePlurality {
|
||||
fn contains(location: &Location) -> bool {
|
||||
matches!(location.unpack(), (1, []) | (1, [Plurality { id: BodyId::Executive, .. }]))
|
||||
}
|
||||
}
|
||||
|
||||
pub type Barrier = TrailingSetTopicAsId<
|
||||
DenyThenTry<
|
||||
DenyRecursively<DenyReserveTransferToRelayChain>,
|
||||
(
|
||||
TakeWeightCredit,
|
||||
WithComputedOrigin<
|
||||
(
|
||||
AllowTopLevelPaidExecutionFrom<Everything>,
|
||||
AllowExplicitUnpaidExecutionFrom<ParentOrParentsExecutivePlurality>,
|
||||
// ^^^ Parent and its exec plurality get free execution
|
||||
),
|
||||
UniversalLocation,
|
||||
ConstU32<8>,
|
||||
>,
|
||||
),
|
||||
>,
|
||||
>;
|
||||
|
||||
pub struct XcmConfig;
|
||||
impl xcm_executor::Config for XcmConfig {
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type XcmSender = XcmRouter;
|
||||
type XcmEventEmitter = PezkuwiXcm;
|
||||
// How to withdraw and deposit an asset.
|
||||
type AssetTransactor = LocalAssetTransactor;
|
||||
type OriginConverter = XcmOriginToTransactDispatchOrigin;
|
||||
type IsReserve = NativeAsset;
|
||||
type IsTeleporter = (); // Teleporting is disabled.
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type Barrier = Barrier;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
|
||||
type Trader =
|
||||
UsingComponents<WeightToFee, RelayLocation, AccountId, Balances, ToAuthor<Runtime>>;
|
||||
type ResponseHandler = PezkuwiXcm;
|
||||
type AssetTrap = PezkuwiXcm;
|
||||
type AssetClaims = PezkuwiXcm;
|
||||
type SubscriptionService = PezkuwiXcm;
|
||||
type PalletInstancesInfo = AllPalletsWithSystem;
|
||||
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
|
||||
type AssetLocker = ();
|
||||
type AssetExchanger = ();
|
||||
type FeeManager = ();
|
||||
type MessageExporter = ();
|
||||
type UniversalAliases = Nothing;
|
||||
type CallDispatcher = RuntimeCall;
|
||||
type SafeCallFilter = Everything;
|
||||
type Aliasers = Nothing;
|
||||
type TransactionalProcessor = FrameTransactionalProcessor;
|
||||
type HrmpNewChannelOpenRequestHandler = ();
|
||||
type HrmpChannelAcceptedHandler = ();
|
||||
type HrmpChannelClosingHandler = ();
|
||||
type XcmRecorder = PezkuwiXcm;
|
||||
}
|
||||
|
||||
/// Converts a local signed origin into an XCM location. Forms the basis for local origins
|
||||
/// sending/executing XCMs.
|
||||
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
|
||||
|
||||
/// The means for routing XCM messages which are not for local execution into the right message
|
||||
/// queues.
|
||||
pub type XcmRouter = WithUniqueTopic<(
|
||||
// Two routers - use UMP to communicate with the relay chain:
|
||||
cumulus_primitives_utility::ParentAsUmp<TeyrchainSystem, PezkuwiXcm, PriceForParentDelivery>,
|
||||
// ..and XCMP to communicate with the sibling chains.
|
||||
XcmpQueue,
|
||||
)>;
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
|
||||
type XcmRouter = XcmRouter;
|
||||
type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
|
||||
type XcmExecuteFilter = Nothing;
|
||||
// ^ Disable dispatchable execute on the XCM pallet.
|
||||
// Needs to be `Everything` for local testing.
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
type XcmTeleportFilter = Everything;
|
||||
type XcmReserveTransferFilter = Nothing;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
|
||||
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||
// ^ Override for AdvertisedXcmVersion default
|
||||
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||
type Currency = Balances;
|
||||
type CurrencyMatcher = ();
|
||||
type TrustedLockers = ();
|
||||
type SovereignAccountOf = LocationToAccountId;
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type WeightInfo = pallet_xcm::TestWeightInfo;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
// Aliasing is disabled: xcm_executor::Config::Aliasers is set to `Nothing`.
|
||||
type AuthorizedAliasConsideration = Disabled;
|
||||
}
|
||||
|
||||
impl cumulus_pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
use crate::{
|
||||
AccountId, BalancesConfig, CollatorSelectionConfig, PezkuwiXcmConfig, RuntimeGenesisConfig,
|
||||
SessionConfig, SessionKeys, SudoConfig, TeyrchainInfoConfig, EXISTENTIAL_DEPOSIT,
|
||||
};
|
||||
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use pezkuwi_sdk::{staging_xcm as xcm, *};
|
||||
|
||||
use cumulus_primitives_core::ParaId;
|
||||
use frame_support::build_struct_json_patch;
|
||||
use serde_json::Value;
|
||||
use sp_genesis_builder::PresetId;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use teyrchains_common::AuraId;
|
||||
|
||||
/// The default XCM version to set in genesis config.
|
||||
const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION;
|
||||
/// Teyrchain id used for genesis config presets of teyrchain template.
|
||||
#[docify::export_content]
|
||||
pub const TEYRCHAIN_ID: u32 = 1000;
|
||||
|
||||
/// Generate the session keys from individual elements.
|
||||
///
|
||||
/// The input must be a tuple of individual keys (a single arg for now since we have just one key).
|
||||
pub fn template_session_keys(keys: AuraId) -> SessionKeys {
|
||||
SessionKeys { aura: keys }
|
||||
}
|
||||
|
||||
fn testnet_genesis(
|
||||
invulnerables: Vec<(AccountId, AuraId)>,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
root: AccountId,
|
||||
id: ParaId,
|
||||
) -> Value {
|
||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
||||
balances: BalancesConfig {
|
||||
balances: endowed_accounts
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|k| (k, 1u128 << 60))
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
teyrchain_info: TeyrchainInfoConfig { teyrchain_id: id },
|
||||
collator_selection: CollatorSelectionConfig {
|
||||
invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect::<Vec<_>>(),
|
||||
candidacy_bond: EXISTENTIAL_DEPOSIT * 16,
|
||||
},
|
||||
session: SessionConfig {
|
||||
keys: invulnerables
|
||||
.into_iter()
|
||||
.map(|(acc, aura)| {
|
||||
(
|
||||
acc.clone(), // account id
|
||||
acc, // validator id
|
||||
template_session_keys(aura), // session keys
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
pezkuwi_xcm: PezkuwiXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) },
|
||||
sudo: SudoConfig { key: Some(root) },
|
||||
})
|
||||
}
|
||||
|
||||
fn local_testnet_genesis() -> Value {
|
||||
testnet_genesis(
|
||||
// initial collators.
|
||||
vec![
|
||||
(Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()),
|
||||
(Sr25519Keyring::Bob.to_account_id(), Sr25519Keyring::Bob.public().into()),
|
||||
],
|
||||
Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
TEYRCHAIN_ID.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn development_config_genesis() -> Value {
|
||||
testnet_genesis(
|
||||
// initial collators.
|
||||
vec![
|
||||
(Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()),
|
||||
(Sr25519Keyring::Bob.to_account_id(), Sr25519Keyring::Bob.public().into()),
|
||||
],
|
||||
Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
TEYRCHAIN_ID.into(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Provides the JSON representation of predefined genesis config for given `id`.
|
||||
pub fn get_preset(id: &PresetId) -> Option<vec::Vec<u8>> {
|
||||
let patch = match id.as_ref() {
|
||||
sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => local_testnet_genesis(),
|
||||
sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(),
|
||||
_ => return None,
|
||||
};
|
||||
Some(
|
||||
serde_json::to_string(&patch)
|
||||
.expect("serialization to json is expected to work. qed.")
|
||||
.into_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
/// List of supported presets.
|
||||
pub fn preset_names() -> Vec<PresetId> {
|
||||
vec![
|
||||
PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET),
|
||||
PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET),
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
// Make the WASM binary available.
|
||||
#[cfg(feature = "std")]
|
||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||
|
||||
pub mod apis;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarks;
|
||||
pub mod configs;
|
||||
mod genesis_config_presets;
|
||||
mod weights;
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::vec::Vec;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use pezkuwi_sdk::{staging_teyrchain_info as teyrchain_info, *};
|
||||
|
||||
use sp_runtime::{
|
||||
generic, impl_opaque_keys,
|
||||
traits::{BlakeTwo256, IdentifyAccount, Verify},
|
||||
MultiSignature,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_version::NativeVersion;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
use frame_support::weights::{
|
||||
constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients,
|
||||
WeightToFeePolynomial,
|
||||
};
|
||||
pub use genesis_config_presets::TEYRCHAIN_ID;
|
||||
pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
pub use sp_runtime::{MultiAddress, Perbill, Permill};
|
||||
|
||||
use weights::ExtrinsicBaseWeight;
|
||||
|
||||
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
|
||||
pub type Signature = MultiSignature;
|
||||
|
||||
/// Some way of identifying an account on the chain. We intentionally make it equivalent
|
||||
/// to the public key of our transaction signing scheme.
|
||||
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
|
||||
|
||||
/// Balance of an account.
|
||||
pub type Balance = u128;
|
||||
|
||||
/// Index of a transaction in the chain.
|
||||
pub type Nonce = u32;
|
||||
|
||||
/// A hash of some data used by the chain.
|
||||
pub type Hash = sp_core::H256;
|
||||
|
||||
/// An index to a block.
|
||||
pub type BlockNumber = u32;
|
||||
|
||||
/// The address format for describing accounts.
|
||||
pub type Address = MultiAddress<AccountId, ()>;
|
||||
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
|
||||
/// A Block signed with a Justification
|
||||
pub type SignedBlock = generic::SignedBlock<Block>;
|
||||
|
||||
/// BlockId type as expected by this runtime.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
|
||||
/// The extension to the basic transaction logic.
|
||||
#[docify::export(template_signed_extra)]
|
||||
pub type TxExtension = cumulus_pallet_weight_reclaim::StorageWeightReclaim<
|
||||
Runtime,
|
||||
(
|
||||
frame_system::AuthorizeCall<Runtime>,
|
||||
frame_system::CheckNonZeroSender<Runtime>,
|
||||
frame_system::CheckSpecVersion<Runtime>,
|
||||
frame_system::CheckTxVersion<Runtime>,
|
||||
frame_system::CheckGenesis<Runtime>,
|
||||
frame_system::CheckEra<Runtime>,
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
|
||||
),
|
||||
>;
|
||||
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic =
|
||||
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
|
||||
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = frame_executive::Executive<
|
||||
Runtime,
|
||||
Block,
|
||||
frame_system::ChainContext<Runtime>,
|
||||
Runtime,
|
||||
AllPalletsWithSystem,
|
||||
>;
|
||||
|
||||
/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the
|
||||
/// node's balance type.
|
||||
///
|
||||
/// This should typically create a mapping between the following ranges:
|
||||
/// - `[0, MAXIMUM_BLOCK_WEIGHT]`
|
||||
/// - `[Balance::min, Balance::max]`
|
||||
///
|
||||
/// Yet, it can be used for any other sort of change to weight-fee. Some examples being:
|
||||
/// - Setting it to `0` will essentially disable the weight fee.
|
||||
/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
|
||||
pub struct WeightToFee;
|
||||
impl WeightToFeePolynomial for WeightToFee {
|
||||
type Balance = Balance;
|
||||
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
|
||||
// in Pezkuwichain, extrinsic base weight (smallest non-zero weight) is mapped to 1
|
||||
// MILLI_UNIT: in our template, we map to 1/10 of that, or 1/10 MILLI_UNIT
|
||||
let p = MILLI_UNIT / 10;
|
||||
let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time());
|
||||
smallvec![WeightToFeeCoefficient {
|
||||
degree: 1,
|
||||
negative: false,
|
||||
coeff_frac: Perbill::from_rational(p % q, q),
|
||||
coeff_integer: p / q,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
|
||||
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
|
||||
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
|
||||
/// to even the core data structures.
|
||||
pub mod opaque {
|
||||
use super::*;
|
||||
pub use pezkuwi_sdk::sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
|
||||
use pezkuwi_sdk::sp_runtime::{
|
||||
generic,
|
||||
traits::{BlakeTwo256, Hash as HashT},
|
||||
};
|
||||
|
||||
/// Opaque block header type.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
/// Opaque block type.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
/// Opaque block identifier type.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
/// Opaque block hash type.
|
||||
pub type Hash = <BlakeTwo256 as HashT>::Output;
|
||||
}
|
||||
|
||||
impl_opaque_keys! {
|
||||
pub struct SessionKeys {
|
||||
pub aura: Aura,
|
||||
}
|
||||
}
|
||||
|
||||
#[sp_version::runtime_version]
|
||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed("teyrchain-template-runtime"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("teyrchain-template-runtime"),
|
||||
authoring_version: 1,
|
||||
spec_version: 1,
|
||||
impl_version: 0,
|
||||
apis: apis::RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
system_version: 1,
|
||||
};
|
||||
|
||||
#[docify::export]
|
||||
mod block_times {
|
||||
/// This determines the average expected block time that we are targeting. Blocks will be
|
||||
/// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by
|
||||
/// `pallet_timestamp` which is in turn picked up by `pallet_aura` to implement `fn
|
||||
/// slot_duration()`.
|
||||
///
|
||||
/// Change this to adjust the block time.
|
||||
pub const MILLI_SECS_PER_BLOCK: u64 = 6000;
|
||||
|
||||
// NOTE: Currently it is not possible to change the slot duration after the chain has started.
|
||||
// Attempting to do so will brick block production.
|
||||
pub const SLOT_DURATION: u64 = MILLI_SECS_PER_BLOCK;
|
||||
}
|
||||
pub use block_times::*;
|
||||
|
||||
// Time is measured by number of blocks.
|
||||
pub const MINUTES: BlockNumber = 60_000 / (MILLI_SECS_PER_BLOCK as BlockNumber);
|
||||
pub const HOURS: BlockNumber = MINUTES * 60;
|
||||
pub const DAYS: BlockNumber = HOURS * 24;
|
||||
|
||||
// Unit = the base number of indivisible units for balances
|
||||
pub const UNIT: Balance = 1_000_000_000_000;
|
||||
pub const CENTS: Balance = UNIT / 100;
|
||||
pub const MILLI_UNIT: Balance = 1_000_000_000;
|
||||
pub const MICRO_UNIT: Balance = 1_000_000;
|
||||
|
||||
/// The existential deposit. Set to 1/10 of the Connected Relay Chain.
|
||||
pub const EXISTENTIAL_DEPOSIT: Balance = MILLI_UNIT;
|
||||
|
||||
/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
|
||||
/// used to limit the maximal weight of a single extrinsic.
|
||||
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);
|
||||
|
||||
/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by
|
||||
/// `Operational` extrinsics.
|
||||
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
|
||||
|
||||
#[docify::export(max_block_weight)]
|
||||
/// We allow for 2 seconds of compute with a 6 second average block time.
|
||||
const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(
|
||||
WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2),
|
||||
cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64,
|
||||
);
|
||||
|
||||
#[docify::export]
|
||||
mod async_backing_params {
|
||||
/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
|
||||
/// into the relay chain.
|
||||
pub(crate) const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3;
|
||||
/// How many teyrchain blocks are processed by the relay chain per parent. Limits the
|
||||
/// number of blocks authored per slot.
|
||||
pub(crate) const BLOCK_PROCESSING_VELOCITY: u32 = 1;
|
||||
/// Relay chain slot duration, in milliseconds.
|
||||
pub(crate) const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
|
||||
}
|
||||
pub(crate) use async_backing_params::*;
|
||||
|
||||
#[docify::export]
|
||||
/// Aura consensus hook
|
||||
type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
|
||||
Runtime,
|
||||
RELAY_CHAIN_SLOT_DURATION_MILLIS,
|
||||
BLOCK_PROCESSING_VELOCITY,
|
||||
UNINCLUDED_SEGMENT_CAPACITY,
|
||||
>;
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn native_version() -> NativeVersion {
|
||||
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
|
||||
}
|
||||
|
||||
// Create the runtime by composing the FRAME pallets that were previously configured.
|
||||
#[frame_support::runtime]
|
||||
mod runtime {
|
||||
#[runtime::runtime]
|
||||
#[runtime::derive(
|
||||
RuntimeCall,
|
||||
RuntimeEvent,
|
||||
RuntimeError,
|
||||
RuntimeOrigin,
|
||||
RuntimeFreezeReason,
|
||||
RuntimeHoldReason,
|
||||
RuntimeSlashReason,
|
||||
RuntimeLockId,
|
||||
RuntimeTask,
|
||||
RuntimeViewFunction
|
||||
)]
|
||||
pub struct Runtime;
|
||||
|
||||
#[runtime::pallet_index(0)]
|
||||
pub type System = frame_system;
|
||||
#[runtime::pallet_index(1)]
|
||||
pub type TeyrchainSystem = cumulus_pallet_teyrchain_system;
|
||||
#[runtime::pallet_index(2)]
|
||||
pub type Timestamp = pallet_timestamp;
|
||||
#[runtime::pallet_index(3)]
|
||||
pub type TeyrchainInfo = teyrchain_info;
|
||||
#[runtime::pallet_index(4)]
|
||||
pub type WeightReclaim = cumulus_pallet_weight_reclaim;
|
||||
|
||||
// Monetary stuff.
|
||||
#[runtime::pallet_index(10)]
|
||||
pub type Balances = pallet_balances;
|
||||
#[runtime::pallet_index(11)]
|
||||
pub type TransactionPayment = pallet_transaction_payment;
|
||||
|
||||
// Governance
|
||||
#[runtime::pallet_index(15)]
|
||||
pub type Sudo = pallet_sudo;
|
||||
|
||||
// Collator support. The order of these 4 are important and shall not change.
|
||||
#[runtime::pallet_index(20)]
|
||||
pub type Authorship = pallet_authorship;
|
||||
#[runtime::pallet_index(21)]
|
||||
pub type CollatorSelection = pallet_collator_selection;
|
||||
#[runtime::pallet_index(22)]
|
||||
pub type Session = pallet_session;
|
||||
#[runtime::pallet_index(23)]
|
||||
pub type Aura = pallet_aura;
|
||||
#[runtime::pallet_index(24)]
|
||||
pub type AuraExt = cumulus_pallet_aura_ext;
|
||||
|
||||
// XCM helpers.
|
||||
#[runtime::pallet_index(30)]
|
||||
pub type XcmpQueue = cumulus_pallet_xcmp_queue;
|
||||
#[runtime::pallet_index(31)]
|
||||
pub type PezkuwiXcm = pallet_xcm;
|
||||
#[runtime::pallet_index(32)]
|
||||
pub type CumulusXcm = cumulus_pallet_xcm;
|
||||
#[runtime::pallet_index(33)]
|
||||
pub type MessageQueue = pallet_message_queue;
|
||||
|
||||
// Template
|
||||
#[runtime::pallet_index(50)]
|
||||
pub type TemplatePallet = pallet_teyrchain_template;
|
||||
}
|
||||
|
||||
#[docify::export(register_validate_block)]
|
||||
cumulus_pallet_teyrchain_system::register_validate_block! {
|
||||
Runtime = Runtime,
|
||||
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod constants {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
weights::{constants, Weight},
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
/// Importing a block with 0 Extrinsics.
|
||||
pub const BlockExecutionWeight: Weight =
|
||||
Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(5_000_000), 0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_weights {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::weights::constants;
|
||||
|
||||
/// Checks that the weight exists and is sane.
|
||||
// NOTE: If this test fails but you are sure that the generated values are fine,
|
||||
// you can delete it.
|
||||
#[test]
|
||||
fn sane() {
|
||||
let w = super::constants::BlockExecutionWeight::get();
|
||||
|
||||
// At least 100 µs.
|
||||
assert!(
|
||||
w.ref_time() >= 100u64 * constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Weight should be at least 100 µs."
|
||||
);
|
||||
// At most 50 ms.
|
||||
assert!(
|
||||
w.ref_time() <= 50u64 * constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Weight should be at most 50 ms."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod constants {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
weights::{constants, Weight},
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
/// Executing a NO-OP `System::remarks` Extrinsic.
|
||||
pub const ExtrinsicBaseWeight: Weight =
|
||||
Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS.saturating_mul(125_000), 0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_weights {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::weights::constants;
|
||||
|
||||
/// Checks that the weight exists and is sane.
|
||||
// NOTE: If this test fails but you are sure that the generated values are fine,
|
||||
// you can delete it.
|
||||
#[test]
|
||||
fn sane() {
|
||||
let w = super::constants::ExtrinsicBaseWeight::get();
|
||||
|
||||
// At least 10 µs.
|
||||
assert!(
|
||||
w.ref_time() >= 10u64 * constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Weight should be at least 10 µs."
|
||||
);
|
||||
// At most 1 ms.
|
||||
assert!(
|
||||
w.ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Weight should be at most 1 ms."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Expose the auto generated weight files.
|
||||
|
||||
pub mod block_weights;
|
||||
pub mod extrinsic_weights;
|
||||
pub mod paritydb_weights;
|
||||
pub mod rocksdb_weights;
|
||||
|
||||
pub use block_weights::constants::BlockExecutionWeight;
|
||||
pub use extrinsic_weights::constants::ExtrinsicBaseWeight;
|
||||
pub use rocksdb_weights::constants::RocksDbWeight;
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod constants {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
weights::{constants, RuntimeDbWeight},
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
/// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights
|
||||
/// are available for brave runtime engineers who may want to try this out as default.
|
||||
pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight {
|
||||
read: 8_000 * constants::WEIGHT_REF_TIME_PER_NANOS,
|
||||
write: 50_000 * constants::WEIGHT_REF_TIME_PER_NANOS,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_db_weights {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use super::constants::ParityDbWeight as W;
|
||||
use frame_support::weights::constants;
|
||||
|
||||
/// Checks that all weights exist and have sane values.
|
||||
// NOTE: If this test fails but you are sure that the generated values are fine,
|
||||
// you can delete it.
|
||||
#[test]
|
||||
fn sane() {
|
||||
// At least 1 µs.
|
||||
assert!(
|
||||
W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Read weight should be at least 1 µs."
|
||||
);
|
||||
assert!(
|
||||
W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Write weight should be at least 1 µs."
|
||||
);
|
||||
// At most 1 ms.
|
||||
assert!(
|
||||
W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Read weight should be at most 1 ms."
|
||||
);
|
||||
assert!(
|
||||
W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Write weight should be at most 1 ms."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod constants {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
weights::{constants, RuntimeDbWeight},
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
/// By default, Substrate uses `RocksDB`, so this will be the weight used throughout
|
||||
/// the runtime.
|
||||
pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight {
|
||||
read: 25_000 * constants::WEIGHT_REF_TIME_PER_NANOS,
|
||||
write: 100_000 * constants::WEIGHT_REF_TIME_PER_NANOS,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_db_weights {
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use super::constants::RocksDbWeight as W;
|
||||
use frame_support::weights::constants;
|
||||
|
||||
/// Checks that all weights exist and have sane values.
|
||||
// NOTE: If this test fails but you are sure that the generated values are fine,
|
||||
// you can delete it.
|
||||
#[test]
|
||||
fn sane() {
|
||||
// At least 1 µs.
|
||||
assert!(
|
||||
W::get().reads(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Read weight should be at least 1 µs."
|
||||
);
|
||||
assert!(
|
||||
W::get().writes(1).ref_time() >= constants::WEIGHT_REF_TIME_PER_MICROS,
|
||||
"Write weight should be at least 1 µs."
|
||||
);
|
||||
// At most 1 ms.
|
||||
assert!(
|
||||
W::get().reads(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Read weight should be at most 1 ms."
|
||||
);
|
||||
assert!(
|
||||
W::get().writes(1).ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
|
||||
"Write weight should be at most 1 ms."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Cumulus 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.
|
||||
|
||||
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// The teyrchain-template crate helps with keeping the README.md in sync
|
||||
// with code sections across the components under the template: node,
|
||||
// pallets & runtime, by using `docify`.
|
||||
|
||||
#[cfg(feature = "generate-readme")]
|
||||
docify::compile_markdown!("README.docify.md", "README.md");
|
||||
@@ -0,0 +1,22 @@
|
||||
[relaychain]
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
validator = true
|
||||
ws_port = 9944
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
validator = true
|
||||
ws_port = 9955
|
||||
|
||||
[[teyrchains]]
|
||||
id = 1000
|
||||
chain_spec_path = "./dev_chain_spec.json"
|
||||
|
||||
[teyrchains.collator]
|
||||
name = "charlie"
|
||||
ws_port = 9988
|
||||
command = "pezkuwi-omni-node"
|
||||
@@ -0,0 +1,21 @@
|
||||
[relaychain]
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
validator = true
|
||||
ws_port = 9944
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
validator = true
|
||||
ws_port = 9955
|
||||
|
||||
[[teyrchains]]
|
||||
id = 1000
|
||||
|
||||
[teyrchains.collator]
|
||||
name = "charlie"
|
||||
ws_port = 9988
|
||||
command = "teyrchain-template-node"
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "template-zombienet-tests"
|
||||
description = "Zombienet test for templates."
|
||||
version = "0.0.0"
|
||||
license = "Unlicense"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread"] }
|
||||
zombienet-sdk = { workspace = true }
|
||||
|
||||
[features]
|
||||
zombienet = []
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,187 @@
|
||||
//! This test is setup to run with the `native` provider and needs these binaries in your PATH
|
||||
//! `pezkuwi`, `pezkuwi-prepare-worker`, `pezkuwi-execute-worker`, `teyrchain-template-node`.
|
||||
//! You can follow these steps to compile and export the binaries:
|
||||
//! `cargo build --release -features fast-runtime --bin pezkuwi --bin pezkuwi-execute-worker --bin
|
||||
//! pezkuwi-prepare-worker`
|
||||
//! `cargo build --package teyrchain-template-node --release`
|
||||
//! `export PATH=<path-to-pezkuwi-sdk-repo>/target/release:$PATH
|
||||
//!
|
||||
//! There are also some tests related to omni node which run basaed on pre-generated chain specs,
|
||||
//! so to be able to run them you would need to generate the right chain spec (just minimal and
|
||||
//! teyrchain tests supported for now).
|
||||
//!
|
||||
//! You can run the following command to generate a minimal chainspec, once the runtime wasm file is
|
||||
//! compiled:
|
||||
//!`chain-spec-builder create --relay-chain <relay_chain_id> -r \
|
||||
//! <path_to_template_wasm_file> named-preset development`
|
||||
//!
|
||||
//! Once the files are generated, you must export an environment variable called
|
||||
//! `CHAIN_SPECS_DIR` which should point to the absolute path of the directory
|
||||
//! that holds the generated chain spec. The chain specs file names should be
|
||||
//! `teyrchain_chain_spec.json` for teyrchain
|
||||
//! templates.
|
||||
//!
|
||||
//! To start all tests here we should run:
|
||||
//! `cargo test -p template-zombienet-tests --features zombienet`
|
||||
|
||||
#[cfg(feature = "zombienet")]
|
||||
mod smoke {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use zombienet_sdk::{NetworkConfig, NetworkConfigBuilder, NetworkConfigExt};
|
||||
|
||||
const CHAIN_SPECS_DIR_PATH: &str = "CHAIN_SPECS_DIR";
|
||||
const TEYRCHAIN_ID: u32 = 1000;
|
||||
|
||||
#[inline]
|
||||
fn expect_env_var(var_name: &str) -> String {
|
||||
std::env::var(var_name)
|
||||
.unwrap_or_else(|_| panic!("{CHAIN_SPECS_DIR_PATH} environment variable is set. qed."))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct NetworkSpec {
|
||||
relaychain_cmd: &'static str,
|
||||
relaychain_spec_path: Option<PathBuf>,
|
||||
// TODO: update the type to something like Option<Vec<Arg>> after
|
||||
// `zombienet-sdk` exposes `shared::types::Arg`.
|
||||
relaychain_cmd_args: Option<Vec<(&'static str, &'static str)>>,
|
||||
para_cmd: Option<&'static str>,
|
||||
para_chain_spec_path: Option<PathBuf>,
|
||||
para_cmd_args: Option<Vec<(&'static str, &'static str)>>,
|
||||
}
|
||||
|
||||
fn get_config(network_spec: NetworkSpec) -> Result<NetworkConfig, anyhow::Error> {
|
||||
let chain =
|
||||
if network_spec.relaychain_cmd == "pezkuwi" { "pezkuwichain-local" } else { "dev" };
|
||||
let config = NetworkConfigBuilder::new().with_relaychain(|r| {
|
||||
let mut r = r.with_chain(chain).with_default_command(network_spec.relaychain_cmd);
|
||||
if let Some(path) = network_spec.relaychain_spec_path {
|
||||
r = r.with_chain_spec_path(path);
|
||||
}
|
||||
|
||||
if let Some(args) = network_spec.relaychain_cmd_args {
|
||||
r = r.with_default_args(args.into_iter().map(|arg| arg.into()).collect());
|
||||
}
|
||||
|
||||
r.with_node(|node| node.with_name("alice"))
|
||||
.with_node(|node| node.with_name("bob"))
|
||||
});
|
||||
|
||||
let config = if let Some(para_cmd) = network_spec.para_cmd {
|
||||
config.with_teyrchain(|p| {
|
||||
let mut p = p.with_id(TEYRCHAIN_ID).with_default_command(para_cmd);
|
||||
if let Some(args) = network_spec.para_cmd_args {
|
||||
p = p.with_default_args(args.into_iter().map(|arg| arg.into()).collect());
|
||||
}
|
||||
if let Some(path) = network_spec.para_chain_spec_path {
|
||||
p = p.with_chain_spec_path(path);
|
||||
}
|
||||
p.with_collator(|n| n.with_name("collator"))
|
||||
})
|
||||
} else {
|
||||
config
|
||||
};
|
||||
|
||||
config.build().map_err(|e| {
|
||||
let errs = e.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join(" ");
|
||||
anyhow!("config errs: {errs}")
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn teyrchain_template_block_production_test() -> Result<(), anyhow::Error> {
|
||||
let _ = env_logger::try_init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||
);
|
||||
|
||||
let config = get_config(NetworkSpec {
|
||||
relaychain_cmd: "pezkuwi",
|
||||
para_cmd: Some("teyrchain-template-node"),
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let network = config.spawn_native().await?;
|
||||
|
||||
// wait 6 blocks of the para
|
||||
let collator = network.get_node("collator")?;
|
||||
assert!(collator
|
||||
.wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn solochain_template_block_production_test() -> Result<(), anyhow::Error> {
|
||||
let _ = env_logger::try_init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||
);
|
||||
|
||||
let config = get_config(NetworkSpec {
|
||||
relaychain_cmd: "solochain-template-node",
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let network = config.spawn_native().await?;
|
||||
|
||||
// wait 6 blocks
|
||||
let alice = network.get_node("alice")?;
|
||||
assert!(alice
|
||||
.wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn minimal_template_block_production_test() -> Result<(), anyhow::Error> {
|
||||
let _ = env_logger::try_init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||
);
|
||||
|
||||
let config = get_config(NetworkSpec {
|
||||
relaychain_cmd: "minimal-template-node",
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let network = config.spawn_native().await?;
|
||||
|
||||
// wait 6 blocks
|
||||
let alice = network.get_node("alice")?;
|
||||
assert!(alice
|
||||
.wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn omni_node_with_teyrchain_runtime_block_production_test() -> Result<(), anyhow::Error> {
|
||||
let _ = env_logger::try_init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||
);
|
||||
|
||||
let chain_spec_path = expect_env_var(CHAIN_SPECS_DIR_PATH) + "/teyrchain_chain_spec.json";
|
||||
let config = get_config(NetworkSpec {
|
||||
relaychain_cmd: "pezkuwi",
|
||||
para_cmd: Some("pezkuwi-omni-node"),
|
||||
para_chain_spec_path: Some(PathBuf::from(chain_spec_path)),
|
||||
..Default::default()
|
||||
})?;
|
||||
let network = config.spawn_native().await?;
|
||||
|
||||
// wait 6 blocks
|
||||
let alice = network.get_node("collator")?;
|
||||
assert!(alice
|
||||
.wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user